主题
数值新增特性-二进制和八进制表示方式与数值新增特性-Numb(Day2 1/4)
本页对应 Day02_ES6,共 6 段视频,约 1 小时 1 分。
学习目标
- 能够复述并区分
let与const在重复声明、变量提升、全局对象属性、块级作用域及值是否可修改方面的差异,并用代码示例验证。 - 能够使用
0o/0O八进制和0b/0B二进制字面量表示数值,说明严格模式下旧式0开头八进制的限制,并正确输出对应十进制结果。 - 能够列举并演示
Number构造函数新增属性与方法:MAX_SAFE_INTEGER、MIN_SAFE_INTEGER、EPSILON、Number.isInteger、Number.isSafeInteger,并解释安全整数与精度含义。 - 能够使用
Math.trunc、Math.sign、Math.cbrt、Math.hypot完成截取整数、判断符号、求立方根、求平方和平方根计算,并说明其与已有Math方法的区别。 - 能够声明
bigint字面量(后缀n),说明其原始类型特征、仅表示整数、不能与Number混算、可与同类型运算、可通过Number()转换,并演示不同进制表示。 - 能够使用数字间隔符下划线提高数值字面量可读性,遵守只能位于数字之间、不可连续或位于首尾的限制,并验证其不影响数值结果。
知识点
以下按转写稿校正同音字: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 都是用来创建变量的,它们共同具备四条特点:
- 不能重复声明;
- 不能提升;
- 全局变量不会作为全局对象的属性;
- 具有块级作用域。
怎么想?先把四条共同点记牢,再看差异: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 不能改;块级作用域要能区分大括号和对象;解构赋值先明确“取出来赋给变量”,再记数组按索引、对象按属性名;模板字符串仍是字符串,但换行和 ${} 更方便;字符串新增方法先记用途,细节查文档。这样回顾完,写代码时才能快速选对语法,而不是只背条目。
课堂画面






代码示例
示例一: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”的方式定义。 - 数字分隔符
_纯粹提升可读性,对数值没有任何影响;只能写在数字之间,不能出现在首尾、也不能连续写多个。
常见错误
- 现象:严格模式下写
const n = 010直接报语法错误;原因:旧式前导 0 八进制在严格模式禁用;修法:改用0o10或十进制8。 - 现象:
10n + 1报TypeError;原因:BigInt 不能与 Number 等其他类型直接做数学运算;修法:统一为10n + 1n,或显式转换后再算。 - 现象:
Number.isInteger(Number.MAX_SAFE_INTEGER + 1)返回true就以为安全;原因:isInteger只看是否为整数,不看是否在安全范围;修法:用Number.isSafeInteger()判断。 - 现象:把
{a, b}解构{b: 2, a: 1}时以为a得2;原因:对象解构按属性名匹配,不按位置;修法:按属性名取值,需要改名用{a: x, b: y}。 - 现象:
'a-b-a'.replace('a', 'x')只替换第一个a;原因:字符串参数的replace默认只替换首个匹配;修法:用replaceAll('a', 'x')或正则/a/g。 - 现象:写
1_000_或1__000报语法错误;原因:数字分隔符只能放在数字之间且不可连续;修法:改为1_000。 - 现象:
0.1 + 0.2 === 0.3得到false后误判代码有 bug;原因:浮点数精度限制;修法:用Math.abs(a - b) < Number.EPSILON做近似比较。
练习
- 改错题:在严格模式下,
const a = 010; const b = 0b101; const c = 0o75;哪一行需要改?改成合法写法,并写出三个变量的十进制输出。 - 解构题:给定
const data = { user: { name: 'Tom', age: 18 }, list: [10, 20, 30] };,用解构一次取出name、age、list的第二项,并把name重命名为userName。 - 函数题:写
isNear(a, b),用Number.EPSILON判断两数是否近似相等;再用它验证0.1 + 0.2与0.3。 - BigInt 题:用 BigInt 表示
9007199254740993,加1n后输出;再尝试与 Number 的1相加,记录报错并改为正确写法。 - 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 | 数值新增特性-新增原始数据类型bigint | 14:04 |
| 6 | 数值新增特性-数字间隔符 | 02:28 |
小结
- 新代码中八进制、二进制优先用
0o、0b,不要依赖旧式前导 0。 - “整数”与“安全整数”不是一回事,涉及大数精确计算先看安全范围。
- BigInt 与 Number 是两种类型,运算前先统一类型。
- 解构赋值先看右侧数据形态:数组按索引,对象按属性名。
- 浮点相等不要直接用
===,用Number.EPSILON做容差判断。 - 批量字符串替换优先考虑
replaceAll,但要确认运行环境兼容性。 - 数字分隔符只提升可读性,不改变数值,位置和连续下划线要合规。
- Math 新增方法能直接用就不要手写,但要先确认参数和返回值语义。