Skip to content

数值新增特性-二进制和八进制表示方式与数值新增特性-Numb(Day2 1/4) ​

本页对应 Day02_ES6,共 6 段视频,约 1 小时 1 分。

学习目标 ​

  1. 能够复述并区分 let 与 const 在重复声明、变量提升、全局对象属性、块级作用域及值是否可修改方面的差异,并用代码示例验证。
  2. 能够使用 0o/0O 八进制和 0b/0B 二进制字面量表示数值,说明严格模式下旧式 0 开头八进制的限制,并正确输出对应十进制结果。
  3. 能够列举并演示 Number 构造函数新增属性与方法:MAX_SAFE_INTEGER、MIN_SAFE_INTEGER、EPSILON、Number.isInteger、Number.isSafeInteger,并解释安全整数与精度含义。
  4. 能够使用 Math.trunc、Math.sign、Math.cbrt、Math.hypot 完成截取整数、判断符号、求立方根、求平方和平方根计算,并说明其与已有 Math 方法的区别。
  5. 能够声明 bigint 字面量(后缀 n),说明其原始类型特征、仅表示整数、不能与 Number 混算、可与同类型运算、可通过 Number() 转换,并演示不同进制表示。
  6. 能够使用数字间隔符下划线提高数值字面量可读性,遵守只能位于数字之间、不可连续或位于首尾的限制,并验证其不影响数值结果。

知识点 ​

以下按转写稿校正同音字:let/const、解构赋值、块级作用域、严格模式、0o/0b、BigInt。本节笔记未附图片,故只保留可核对的代码与调试结果。

回顾:let/const、解构赋值与字符串 ​

为什么需要:用块级作用域避免循环变量互相污染,用常量表达不可改的值;从数组/对象取数、拼接字符串和查找替换是日常高频操作。怎么想:let 与 const 都有块级作用域、不能重复声明、不提升为全局对象属性;const 额外要求值不能改。解构赋值的目的就是“把里面的东西拿出来赋给变量”,数组按索引匹配,对象按属性名匹配;模板字符串仍是字符串,只是反引号定义,可换行、可插值。怎么做:

js
const o = { a: 1, b: 2 };
const { a, b } = o;          // 对象按属性名
const [x, y] = [10, 20];     // 数组按索引
const msg = `x=${x}, y=${y}`;
'abc'.includes('b');         // true
'abc'.replaceAll('a', 'x');  // xbc

易错点:let 不能重复声明但能改;const 不能改;对象解构时属性名本身不能被解构出来;模板字符串不是新类型;replace 只替换第一个,replaceAll 才替换全部。转写稿里的“结构赋值”应写作“解构赋值”。

数值的二进制与八进制表示 ​

为什么需要:旧式“0 开头”的八进制写法有争议,严格模式下会报语法错误,需要标准、清晰的表示方式。怎么想:先验证旧写法 010 在严格模式下不可用;ES6 新增 0o/0O 表示八进制、0b/0B 表示二进制。怎么做:

js
'use strict';
// 010;        // SyntaxError
console.log(0o10); // 8
console.log(0b10); // 2

易错点:0o 是字母 o,不是数字 0;输出仍按十进制显示;即使不写 'use strict',也要把严格模式记在心里。

Number 构造函数本身新增属性方法 ​

为什么需要:大整数超过安全范围后,表面仍是整数,但内部可能按浮点数存储,计算不再保证精确;需要判断“整数/安全整数”,也需要理解精度。怎么想:MAX_SAFE_INTEGER 约为 2^53-1,MIN_SAFE_INTEGER 为其负值;EPSILON 是大于 1 的最小浮点差值,可理解为精度。isInteger 判断整数,isSafeInteger 判断是否在安全整数范围。怎么做:

js
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991
console.log(Number.EPSILON);          // 约 2.22e-16
console.log(Number.isInteger(789));   // true
console.log(Number.isInteger(78.9));  // false
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1)); // false

易错点:MAX_SAFE_INTEGER + 1 仍可能被 isInteger 判为 true,但它已不安全;EPSILON 不是“最小正数”,而是精度差;Number.isNaN 等与全局同名方法并存,分类更合理,但不要误以为必须写 Number. 才能用全局版本。

Math 对象新增方法 ​

为什么需要:截取整数、判断正负、立方根、平方和开方等数学处理更集中。怎么想:trunc 去掉小数只留整数;sign 返回 1/-1/0;cbrt 求立方根;hypot 求所有参数平方和的平方根。怎么做:

js
console.log(Math.trunc(12.45)); // 12
console.log(Math.sign(12.45));  // 1
console.log(Math.sign(-90));    // -1
console.log(Math.sign(0));      // 0
console.log(Math.cbrt(8));      // 2
console.log(Math.hypot(3, 4));  // 5

易错点:trunc 与 parseInt 在“截取整数”上很像,但归属不同;sign(0) 是 0;hypot(3,4) 对应勾股定理,但参数更多时只是平方和开方,不再叫勾股定理。

BigInt ​

为什么需要:超过安全整数甚至超过最大可表示数时,需要精确表示大整数。怎么想:BigInt 是新增原始类型,typeof 返回 "bigint",不是 number;字面量加 n;只能表示整数;不能与 number 混合运算;可用 BigInt() 和 Number() 转换;不能用 new。怎么做:

js
const n1 = 56n;
console.log(typeof n1); // bigint
console.log(n1 + 1n);   // 57n
// console.log(n1 + 1); // TypeError
console.log(Number(n1)); // 56
console.log(1n == 1);    // true
console.log(1n === 1);   // false

易错点:56n + 1 会报错;+56n 不能转成 number;1n === 1 为 false;BigInt 不能表示小数,也不能加正号。

数字分隔符 ​

为什么需要:大数字难读,分隔符只提高可读性,不改变数值。怎么想:下划线只能放在数字之间,不能放头尾,也不能连续。怎么做:

js
const num = 1_234_567;
console.log(num); // 1234567

易错点:它没有计算功能;输出不会保留下划线;不要写成 _123、123_ 或 1__2。

回顾目标:把零散特性串成可用的规则 ​

这一节是对前面内容的回顾,重点不是再背一遍名词,而是把 let/const、解构赋值、字符串新增特性串起来:看到需求时能想到对应语法,写代码时能避开常见坑。转写稿里反复强调“先明确目的,再记规则”,下面按这个思路整理。

let 与 const:先记共同点,再记差异 ​

为什么需要它们?因为过去用 var 时,重复声明、提升、全局变量挂到全局对象、作用域混乱等问题容易让代码失控。let 和 const 都是用来创建变量的,它们共同具备四条特点:

  1. 不能重复声明;
  2. 不能提升;
  3. 全局变量不会作为全局对象的属性;
  4. 具有块级作用域。

怎么想?先把四条共同点记牢,再看差异:const 创建的是值不能修改的变量,也就是常量。转写稿说得很直白:let 能改,const 不能改;除此之外,let 的四条特点 const 也有。

怎么做?可以这样对比:

js
let a = 1;
a = 2;        // 可以修改

const b = 1;
// b = 2;     // 不允许修改

易错点:const 的核心是“值不能修改”,不要和“块级作用域”混在一起记;另外“全局变量不会作为全局对象属性”强调的是全局变量场景,不要扩大成所有变量。

块级作用域:大括号不等于对象 ​

为什么需要块级作用域?因为循环、分支里产生的变量应该只在对应大括号内有效,避免互相污染。转写稿提醒:大括号包起来叫块级作用域,if 分支、循环结构都带大括号。

怎么想?看到大括号要先区分:里面写的是“属性名: 值”的键值对,那才是在创建对象;如果里面不是键值对结构,它就是一个块。

怎么做?转写稿举了一个很实用的场景:给多个元素通过循环监听事件时,用 let 每次循环都会在块级作用域中产生独立变量,不用再自己造闭包,也不会混淆。

易错点:单写一个大括号时,不要条件反射当成对象;循环里用 var 和 let 的作用域表现不同,回顾时要能说清“每次循环独立变量”这个点。

解构赋值:先明确目的,再记匹配规则 ​

为什么需要解构?目的是从数组或对象中把里面的东西取出来,赋值给变量;函数传参本质上也是赋值给形参,所以也能用解构。

怎么想?先分两种:数组解构、对象解构。数组解构右边通常是数组或类似数组结构,左边把变量写进数组结构中,左右按索引匹配。对象解构右边是对象类型数据,左边把变量写进对象结构中,左右按属性名匹配;如果属性名和变量名一样,可以简写。

怎么做?

js
const arr = [10, 20];
const [x, y] = arr;          // 按索引匹配

const obj = { name: 'Tom', age: 18 };
const { name, age } = obj;   // 按属性名匹配,可简写

易错点:数组解构左边不是“数组类型的数据”,它只是语法结构;对象解构按属性名匹配,属性名本身解构不出来;对象简写要求属性名和变量名一致。转写稿还提到“一切皆对象”,所以右边是数组时,左边也可以用对象解构,但匹配规则不同:一个按索引,一个按属性名。

模板字符串:反引号仍然是字符串 ​

为什么需要?过去拼接字符串要断开、加号连接,插入变量和表达式很麻烦。模板字符串用反引号定义,类型仍然是字符串,不是新类型。

怎么想?它和单引号、双引号有共性,比如反引号里不能再直接写反引号;特性主要有两点:可以直接写换行;用 ${} 插入变量或表达式非常方便,表达式会参与运算。

怎么做?

js
const name = 'Tom';
const msg = `你好,${name}
下一行可以直接写`;

易错点:不要以为反引号创建了新数据类型;${} 里可以写变量,也可以写表达式;换行是直接手打,不需要转义字符。

字符串新增方法:先记“有这个方法”,细节查文档 ​

为什么需要?很多字符串判断、填充、去空格、替换需求,过去要自己写或查资料。新增方法让常用操作更直接。

怎么想?先记方法名和大致用途,具体参数、返回值查文档。笔记中列出的方法有:

js
includes()
startsWith()
endsWith()
padStart()
padEnd()
trim()
trimStart()
trimEnd()
replaceAll()

怎么做?includes() 判断字符串中是否包含某个值,转写稿还提到可以指定从哪一部分开始往后找;startsWith()、endsWith() 判断以哪个值开始或结尾;padStart()、padEnd() 做填充;trim() 去掉两端空格,trimStart()、trimEnd() 分别处理两端;replaceAll() 与 replace() 的区别是:replace() 只替换第一个匹配,replaceAll() 能匹配到的都替换。

易错点:这些方法属于字符串对象,不要和全局方法混淆;不要死记参数细节,先做到“脑子里知道有这个功能”,用的时候能快速翻到文档。

小结:回顾要落到“能识别、能选对” ​

这一节回顾的落点是:let/const 先记四条共同点再记 const 不能改;块级作用域要能区分大括号和对象;解构赋值先明确“取出来赋给变量”,再记数组按索引、对象按属性名;模板字符串仍是字符串,但换行和 ${} 更方便;字符串新增方法先记用途,细节查文档。这样回顾完,写代码时才能快速选对语法,而不是只背条目。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

示例一:let/const、解构赋值与字符串新增特性 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Day2 回顾:let/const、解构赋值、字符串特性</title>
</head>
<body>
  <ul id="list">
    <li>条目一</li>
    <li>条目二</li>
    <li>条目三</li>
  </ul>

  <script>
    // 1. let 的块级作用域:循环中每次迭代都会产生一个独立的变量
    const items = document.querySelectorAll('#list li')
    for (let i = 0; i < items.length; i++) {
      items[i].addEventListener('click', function () {
        console.log('点击了第 ' + i + ' 个')   // 换成 var,这里永远是 3
      })
    }

    // 2. const 创建常量:值不能修改
    const PI = 3.1415926

    // 3. 数组解构赋值:左右两边按【索引】匹配
    const arr = [10, 20, 30]
    const [a, b, c] = arr
    console.log(a, b, c)          // 10 20 30

    // 4. 对象解构赋值:左右两边按【属性名】匹配,属性名相同可简写
    const user = { name: '张三', age: 20 }
    const { name, age } = user
    console.log(name, age)        // 张三 20

    // 5. 模板字符串:直接换行 + ${} 插入变量和表达式
    const score = [88, 92, 76]
    const html = `
      <p>姓名:${name}</p>
      <p>年龄:${age}</p>
      <p>总分:${score.reduce((sum, n) => sum + n, 0)}</p>
    `
    console.log(html)

    // 6. 字符串对象新增方法
    const str = 'Hello ES6'
    console.log(str.includes('ES6'))            // true
    console.log(str.startsWith('Hello'))        // true
    console.log(str.endsWith('ES6'))            // true
    console.log('7'.padStart(3, '0'))           // 007
    console.log('7'.padEnd(3, '0'))             // 700
    console.log('  hi  '.trim())                // hi
    console.log('  hi  '.trimStart())           // 'hi  '
    console.log('  hi  '.trimEnd())             // '  hi'
    console.log('a-b-c'.replaceAll('-', '+'))   // a+b+c
  </script>
</body>
</html>

关键行与易错点

  • for (let i = ...) 里的 i 每轮循环都在块级作用域中重新产生一个,所以闭包拿到的是各自的 i;写成 var 则三个事件回调都共享同一个 i,最终全部输出 3。
  • let 与 const 的共同点:不能重复声明、不能提升、全局声明不会成为全局对象(window)的属性、具有块级作用域;const 只多一条——值不能修改。
  • 块级作用域就是大括号:注意区分“单独一个大括号”和“用大括号创建对象”,后者里面必须是属性名冒号加值的键值对结构。
  • 数组解构按索引匹配,等号右边只要“有类似数组的结构”(可迭代对象)即可;等号左边并不是数组类型的数据,只是把变量写进数组结构的一种语法。
  • 对象解构按属性名匹配,{ name, age } 是 { name: name, age: age } 的简写;注意属性名本身是解构不出来的。
  • 模板字符串用反引号定义,仍然是字符串类型,不是新类型;${} 里可以放变量,也可以放表达式(如 reduce 求和),省掉了过去“断开字符串 + 加号拼接”的写法。
  • replace 只替换第一个匹配项,replaceAll 才会全部替换。

示例二:数值新增特性——进制表示、Number 属性方法与 Math 新增方法 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>数值新增特性</title>
</head>
<body>
  <script>
    // 1. 二进制 / 八进制 / 十六进制表示方式
    const num1 = 0b10   // 二进制 10 → 十进制 2
    const num2 = 0o10   // 八进制 10 → 十进制 8
    const num3 = 0x10   // 十六进制 10 → 十进制 16
    console.log(num1, num2, num3)   // 2 8 16

    // 2. Number 构造函数本身的属性
    console.log(Number.MAX_SAFE_INTEGER)  // 9007199254740991(2 的 53 次方 - 1)
    console.log(Number.MIN_SAFE_INTEGER)  // -9007199254740991
    console.log(Number.EPSILON)           // 2.220446049250313e-16(两个数字之间的最小差值)

    // 3. Number 构造函数本身的方法(与全局对象上的同名方法功能一致)
    console.log(Number.isNaN(NaN))            // true
    console.log(Number.isFinite(100))         // true
    console.log(Number.parseInt('12.5px'))    // 12
    console.log(Number.parseFloat('12.5px'))  // 12.5
    console.log(Number.isInteger(789))        // true
    console.log(Number.isInteger(78.9))       // false
    console.log(Number.isSafeInteger(189))    // true
    console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1))  // false

    // 4. Math 对象新增方法
    console.log(Math.trunc(4.9))   // 4(截取整数部分)
    console.log(Math.sign(12.45))  // 1
    console.log(Math.sign(-90))    // -1
    console.log(Math.sign(0))      // 0
    console.log(Math.cbrt(8))      // 2(立方根)
    console.log(Math.hypot(3, 4))  // 5(所有参数平方和的平方根,勾三股四弦五)
  </script>
</body>
</html>

关键行与易错点

  • 0o10、0b10 是 ES6 新增的标准写法;过去用 010(数字前加 0)表示八进制,在严格模式下会直接报语法错误,属于有争议的旧写法,不要再使用。
  • 进制只是一种“表示方式”,输出和参与运算时都是十进制的结果:0b10 就是 2,0o10 就是 8。
  • Number.MAX_SAFE_INTEGER 是最大安全整数(约 2 的 53 次方),超过它以后数值表面看还是整数,但内存中已按浮点数存储,计算不再精确——这就是“安全”的含义。
  • Number.EPSILON 是比 1 大的最小浮点差值,即 JS 的数字精度;文档上那句“可表示的大于一的最小浮点数之间的差值”直接看很难懂,配合这个例子理解。
  • Number.isInteger(78.9) 返回 false;注意超安全范围的值 isInteger 依然返回 true,所以还要用 Number.isSafeInteger 判断是否在安全范围内。
  • Math.trunc 只把小数部分切掉保留整数部分,效果与 parseInt 一致;Math.sign 返回 1 / -1 / 0 三种结果;Math.cbrt 是立方根(区别于平方根 Math.sqrt);Math.hypot 是平方和的平方根,可用于勾股定理计算。

示例三:BigInt 大整数与数字分隔符 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>BigInt 与数字分隔符</title>
</head>
<body>
  <script>
    // 1. BigInt:数字后面加后缀 n
    const n1 = 56n
    console.log(n1, typeof n1)   // 56n bigint(不是 number)

    // 2. 只能表示整数,带小数会直接报错
    // const n2 = 1.5n           // SyntaxError

    // 3. 同样支持二进制、八进制、十六进制表示
    const bin = 0b101n   // 5n
    const oct = 0o75n    // 61n
    const hex = 0xab1n   // 2737n
    console.log(bin, oct, hex)

    // 4. 不能与其他类型(主要是 number)做数学运算
    // console.log(56n + 1)      // TypeError: Cannot mix BigInt and other types
    console.log(56n + 1n)        // 57n,只能和自己运算

    // 5. BigInt 与 number 可以相互转换
    console.log(Number(56n))     // 56
    console.log(BigInt(56))      // 56n
    // console.log(+56n)         // 报错:一元 + 不能用于 BigInt

    // 6. 比较:类型不同
    console.log(56n == 56)       // true(宽松相等会转换)
    console.log(56n === 56)      // false(全等要求类型也相同)

    // 7. 数字分隔符:只为提高可读性,不改变数值
    const million = 1_250_000
    const yi = 12_0000_0000      // 按中文习惯四位一断,12 亿
    const foreign = 1_250_000_000
    console.log(million, yi, foreign)  // 1250000 1200000000 1250000000

    // 分隔符不能出现在头部/尾部,也不能连续写
    // const bad1 = _1250        // SyntaxError
    // const bad2 = 1250_        // SyntaxError
    // const bad3 = 1__250       // SyntaxError
  </script>
</body>
</html>

关键行与易错点

  • 后缀加 n 就得到 BigInt,typeof 返回 'bigint',它是新的原始类型,与 number 是两个不同类型,不要混为一谈。
  • BigInt 只能表示整数,1.5n 直接语法错误;它用来解决安全整数范围之外、以及 2 的 1024 次方以上变成 Infinity 的问题——范围几乎不受限(只看内存),且计算是精确的。
  • 不能与 number 混合运算,56n + 1 会抛 TypeError,必须写成 56n + 1n。
  • 二者可以通过 Number() 和 BigInt() 互相转换;但一元 +(+56n)不能用于 BigInt。
  • 56n === 56 为 false(类型不同),56n == 56 为 true(宽松相等会做转换)。
  • BigInt 是新增的原始类型,不具备对象特性,不能 new BigInt(),只能用“数字 + n”的方式定义。
  • 数字分隔符 _ 纯粹提升可读性,对数值没有任何影响;只能写在数字之间,不能出现在首尾、也不能连续写多个。

常见错误 ​

  1. 现象:严格模式下写 const n = 010 直接报语法错误;原因:旧式前导 0 八进制在严格模式禁用;修法:改用 0o10 或十进制 8。
  2. 现象:10n + 1 报 TypeError;原因:BigInt 不能与 Number 等其他类型直接做数学运算;修法:统一为 10n + 1n,或显式转换后再算。
  3. 现象:Number.isInteger(Number.MAX_SAFE_INTEGER + 1) 返回 true 就以为安全;原因:isInteger 只看是否为整数,不看是否在安全范围;修法:用 Number.isSafeInteger() 判断。
  4. 现象:把 {a, b} 解构 {b: 2, a: 1} 时以为 a 得 2;原因:对象解构按属性名匹配,不按位置;修法:按属性名取值,需要改名用 {a: x, b: y}。
  5. 现象:'a-b-a'.replace('a', 'x') 只替换第一个 a;原因:字符串参数的 replace 默认只替换首个匹配;修法:用 replaceAll('a', 'x') 或正则 /a/g。
  6. 现象:写 1_000_ 或 1__000 报语法错误;原因:数字分隔符只能放在数字之间且不可连续;修法:改为 1_000。
  7. 现象:0.1 + 0.2 === 0.3 得到 false 后误判代码有 bug;原因:浮点数精度限制;修法:用 Math.abs(a - b) < Number.EPSILON 做近似比较。

练习 ​

  1. 改错题:在严格模式下,const a = 010; const b = 0b101; const c = 0o75; 哪一行需要改?改成合法写法,并写出三个变量的十进制输出。
  2. 解构题:给定 const data = { user: { name: 'Tom', age: 18 }, list: [10, 20, 30] };,用解构一次取出 name、age、list 的第二项,并把 name 重命名为 userName。
  3. 函数题:写 isNear(a, b),用 Number.EPSILON 判断两数是否近似相等;再用它验证 0.1 + 0.2 与 0.3。
  4. BigInt 题:用 BigInt 表示 9007199254740993,加 1n 后输出;再尝试与 Number 的 1 相加,记录报错并改为正确写法。
  5. Math 题:用 Math.hypot(3, 4) 求斜边、Math.cbrt(8) 求立方根、Math.sign(-5) 判断符号,写出输出并说明各返回值含义。

原视频对照 ​

#视频时长
1回顾11:20
2数值新增特性-二进制和八进制表示方式04:56
3数值新增特性-Number构造函数本身的属性方法16:41
4数值新增特性-Math对象的属性方法11:39
5数值新增特性-新增原始数据类型bigint14:04
6数值新增特性-数字间隔符02:28

小结 ​

  1. 新代码中八进制、二进制优先用 0o、0b,不要依赖旧式前导 0。
  2. “整数”与“安全整数”不是一回事,涉及大数精确计算先看安全范围。
  3. BigInt 与 Number 是两种类型,运算前先统一类型。
  4. 解构赋值先看右侧数据形态:数组按索引,对象按属性名。
  5. 浮点相等不要直接用 ===,用 Number.EPSILON 做容差判断。
  6. 批量字符串替换优先考虑 replaceAll,但要确认运行环境兼容性。
  7. 数字分隔符只提升可读性,不改变数值,位置和连续下划线要合规。
  8. Math 新增方法能直接用就不要手写,但要先确认参数和返回值语义。