Skip to content

数据类型与BFC(Day1 3/3) ​

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

学习目标 ​

  1. 能区分整型与浮点数的表示形式,说明 0、0x 前缀的含义,并预测 012、0x12、67.2 等字面量的输出结果。
  2. 能解释 0.1 + 0.2 的运算结果及浮点精度原因,并能用科学计数法(如 1.3e4、2.3e-2)写出或判断对应的十进制值。
  3. 能说明 boolean、null、undefined 的取值与典型产生场景,并判断未赋值变量在使用时的结果。
  4. 能说明 NaN 的类型及运算、比较特征,能使用 isNaN() 判断给定数据是否为 NaN 或转换为 number 后是否为 NaN。
  5. 能说明 Number.MAX_VALUE、Number.MIN_VALUE 与正负 Infinity 的含义,判断超出有效范围、下溢为 0 及 isFinite() 的返回结果。
  6. 能使用单引号或双引号定义字符串,正确处理引号嵌套,并使用 \n、\t、\`、\uXXXX` 等转义字符输出指定内容。

知识点 ​

先看 number:整数和浮点不是一回事 ​

为什么需要:JS 对外只有一个 number 类型,但内部要处理整数和带小数点的数;不理解这一点,就容易把 012、0x12、0.1 + 0.2 当成“数学课上那样当然正确”。

怎么想:整数可以较精确地转成二进制;很多十进制小数转二进制是无限小数,只能近似存储。所以 number 里仍有“整型/浮点”的差异。

怎么做:看几个字面量和输出:

js
let N01 = 764;      // 十进制整数
let N02 = 012;      // 八进制表示,等于十进制 10
let N03 = 0x12;     // 十六进制表示,等于十进制 18
let N04 = 67.2;     // 带小数点就是浮点

console.log(N01);   // 764
console.log(N02);   // 10
console.log(N03);   // 18
console.log(0.1 + 0.2); // 类似 0.30000000000000004

推导:八进制 12 是 1*8+2=10;十六进制 12 是 1*16+2=18。0.1 + 0.2 出现长尾,是因为两个小数先转二进制近似值,运算后再转回十进制。

易错点:八进制、十六进制只能表示整数,给 0x12.5 这类写法会报错;浮点结果不要直接当成精确的 0.3,但普通显示精度通常够用。

科学计数法:极端数字的写法 ​

为什么需要:很大或很小的数直接写很多 0 很累,也容易数错。

怎么想:沿用数学里的科学计数法,JS 用 e 表示“乘以 10 的几次方”。

怎么做:

js
let N05 = 1.3e4;    // 1.3 * 10^4
let N06 = 2.3e-2;   // 2.3 * 10^-2

console.log(N05);   // 13000
console.log(N06);   // 0.023

推导:1.3e4 小数点右移 4 位得 13000;2.3e-2 小数点左移 2 位得 0.023。数太大时,控制台也可能用科学计数法显示,但那只是显示形式,数值本身没变。

易错点:e 不是变量,是语法的一部分;负指数表示很小的小数,不要误以为得到负数。

boolean、null、undefined:状态与空值 ​

为什么需要:程序要表达“真/假”和“暂时没有值”。

怎么想:boolean 只有 true、false;null 和 undefined 都表示“空”,初学不必硬背一堆玄学区别。变量声明后没有赋值,使用时通常是 undefined。

怎么做:

js
let ok = true;
let no = false;
let empty = null;
let unset;
console.log(unset); // undefined

易错点:不要主动到处赋 null 来表示“暂时不知道”,也可以先不赋值;后续计算中可能自然得到 undefined 或 null,通常都意味着“没拿到有效结果”。

NaN:不是有效数字的 number ​

为什么需要:类型转换或非法计算可能得不到有效数字,需要有个值来占位。

怎么想:NaN 全称 “Not a Number”,但它仍是 number 类型,表示“不是有效数字”。

怎么做:

js
console.log(NaN + 0);      // NaN
console.log(NaN / 189);    // NaN
console.log(250 === 250);  // true
console.log(NaN === NaN);  // false
console.log(isNaN('hello')); // true
console.log(isNaN(250));     // false

推导:NaN 与任何数字计算仍得 NaN;它和任何值都不相等,包括自己。isNaN('hello') 为 true,是因为 'hello' 转 number 失败会得到 NaN。

易错点:判断相等实际开发更推荐 ===;isNaN 会先做转换,不只看“是不是 NaN 本身”。

数字有效范围:Infinity 与 isFinite ​

为什么需要:计算机能表示的数有上限和下限,超出范围要有特殊表示。

怎么想:最大数约 1.79e308;最小正数约 5e-324。超出正方向得 Infinity,超出负方向得 -Infinity,比最小正数还小会变成 0。

怎么做:

js
console.log(Number.MAX_VALUE); // 约 1.79e308
console.log(Number.MIN_VALUE); // 约 5e-324,最小正数
console.log(4e500);            // Infinity
console.log(-4e500);           // -Infinity
console.log(3e-400);           // 0
console.log(isFinite(100));       // true
console.log(isFinite(Infinity));  // false
console.log(isFinite(NaN));       // false

易错点:Number.MIN_VALUE 不是“最小负数”,而是最小正数;Infinity、-Infinity、NaN 都是无效数字,不能当正常数继续算。

string:引号、转义与 Unicode ​

为什么需要:文本是最常见的数据,必须知道引号怎么配对、特殊字符怎么写。

怎么想:单引号和双引号都能表示字符串;外层用什么引号,里面就不能直接再写同一种引号,需换引号或转义。换行不能直接在代码里敲,要用 \n。

怎么做:

js
let msg01 = 'hello 高小乐';
let msg02 = "你好 老头乐";
console.log(msg01, typeof msg01); // hello 高小乐 string

let msg03 = '锄禾日当午\n汗滴禾下土\n谁知盘中餐\n粒粒皆辛苦';
console.log(msg03);

let msg04 = '说明一下\'可以表示字符串\'';
let msg05 = "这里写\"双引号\"";
let msg06 = '在JS中可以使用\\n表示换行';
console.log('\u0065'); // e

推导:\n 是换行符;\'、\" 是转义引号;\` 输出反斜杠本身;\u0065用 4 位十六进制 Unicode 编码表示字符e`。

易错点:单引号字符串里直接写单引号会提前结束字符串,造成语法错误;\u0065 里的 65 是十六进制,不是十进制。

作业目标:先明确“交换”到底交换什么 ​

本节笔记给出的作业是:

js
var a = 100;
var b = 200;

要求交换两个变量的值。也就是说,交换后希望 a 里保存 200,b 里保存 100。

为什么需要这个练习?因为变量不是数学符号,而是内存里保存值的容器。以后做排序、轮换、状态互换时,都会遇到“两个位置的值要调换”的问题。先把这个小动作想清楚,后面才不会把“赋值”误当成“交换”。

怎么想?不要一上来写代码,先问自己:执行完每一步后,a 和 b 分别是什么?易错点是把“把 b 的值给 a”理解成“a 和 b 自动互换”。赋值只发生在等号左边,右边只负责提供值。

直接写 a = b; b = a; 为什么会失败 ​

最直觉的写法是:

js
var a = 100;
var b = 200;
a = b;
b = a;
console.log(a);
console.log(b);

这样写的问题在于:执行 a = b 后,a 变成 200,原来 a 里的 100 已经被覆盖,找不回来了。接着 b = a,只是把现在的 200 又给 b,最后两个变量都成了 200。

为什么需要先分析这一步?因为交换的本质不是“连续赋值”,而是“先把其中一个值暂存起来,再覆盖,再放回”。怎么想?可以把 a 的值先抄到一张“临时纸条”上,等 a 被覆盖后,再用纸条上的值恢复 b。

易错点:不要用 a = b; b = a;,也不要在没有暂存的情况下直接覆盖。转写稿里反复强调用 console.log 看输出结果,这里也应该每一步打印,而不是凭感觉判断。

用临时变量完成交换 ​

做法是引入第三个变量 temp:

js
var a = 100;
var b = 200;

var temp = a;
a = b;
b = temp;

console.log(a); // 200
console.log(b); // 100

逐步推导:

  1. var temp = a;:temp 保存 100,此时 a 仍是 100,b 仍是 200。
  2. a = b;:a 被赋成 200,b 仍是 200,temp 仍是 100。
  3. b = temp;:b 被赋成 100,完成交换。

为什么这样做可靠?因为 temp 保留了即将被覆盖的旧值。怎么想?可以记成“先存旧 a,再让 a 拿 b,最后让 b 拿旧 a”。易错点有三个:temp 必须先声明再使用;三步顺序不能乱;最后一步不能写成 b = a,否则又把已经变成 200 的 a 赋给 b。

调试过程:把中间值打印出来 ​

转写稿里讲 number 类型时,多次用 console.log 输出结果来验证;本节作业也应沿用这个调试习惯。可以写成:

js
var a = 100;
var b = 200;

var temp = a;
console.log(a, b, temp); // 100 200 100

a = b;
console.log(a, b, temp); // 200 200 100

b = temp;
console.log(a, b, temp); // 200 100 100

这样能清楚看到每一步的值。为什么需要打印?因为交换失败通常不是语法错误,而是逻辑错误,代码能运行但结果不对。怎么想?先验证“暂存是否成功”,再验证“覆盖是否发生”,最后验证“恢复是否正确”。易错点:只看最后结果,不看中间过程;或者把 console.log(a, b) 写成字符串 "a, b",那看到的是字面文字,不是变量值。

易错点与本节小结 ​

第一,a 和 b 是 number 类型,保存的是数值本身,赋值时复制的是值,不是让两个变量变成同一个东西。第二,临时变量名可以用 temp,但不要和已有变量重名。第三,var 声明后可以不写分号,但初学阶段建议写完整,减少歧义。第四,不要依赖“交换函数”之类还没讲的内容;本节作业就用最基础的变量和赋值完成。

转写稿本节主要回顾了 number、科学计数法、boolean、null、undefined、NaN、数字范围和 string,并没有直接给出交换变量的代码;上面的推导是按本节已学的“变量保存值、赋值、console.log 调试”来完成的。最终答案就是:

js
var a = 100;
var b = 200;

var temp = a;
a = b;
b = temp;

console.log(a); // 200
console.log(b); // 100

先暂存旧值,再覆盖,再恢复;这是交换两个变量最稳的基础写法。

材料核对:本节笔记与转写稿不一致 ​

给定的【本节笔记:BFC】只有标题,没有 BFC 的定义、示例代码、图片或调试记录;而转写稿实际讲的是 JavaScript 的 number、boolean、null、undefined、NaN、数字有效范围与 string。因此,不能依据现有材料编造 BFC 教程。下面仅按转写稿中可核验的内容整理成教程正文;未提供的图片不插入,口头代码按语义整理并纠正同音字。

为什么 number 要区分整数与浮点 ​

为什么需要:很多语言有 int、float,因为整数和小数在二进制中的表示能力不同。怎么想:JS 对外只有一个 number 类型,但内部仍要面对“整数可精确转换、多数小数不能精确转换”的事实。怎么做:先用不同字面量观察输出:

js
let N01 = 764;
let N02 = 012;   // 转写稿中的“零开头八进制”写法
let N03 = 0x12;  // 十六进制
let N04 = 67.2;
console.log(N01, N02, N03, N04);

012 按八进制解释为十进制 10,0x12 为十进制 18。现代代码更推荐写 0o12,避免旧八进制写法的歧义。易错点:八进制、十六进制字面量只能表示整数;0x12.5 这类写法会报语法错误。浮点计算要接受精度问题:

js
console.log(0.1 + 0.2); // 0.30000000000000004

原因不是 JS “算错了”,而是 0.1、0.2 多数十进制小数无法精确转成二进制小数,运算后再转回十进制就出现误差。调试时不要用“肉眼相等”判断,应比较误差范围。

科学计数法:大数、小数的简写 ​

为什么需要:极大或极小的数直接写很多 0 很累。怎么想:1.3e4 就是 1.3 × 10^4。怎么做:

js
let N05 = 1.3e4;   // 13000
let N06 = 2.3e-2;  // 0.023
console.log(N05, N06);

易错点:e 只是语法记号;输出时控制台可能按普通形式或科学计数法显示,但“数还是那个数”。负数指数表示小数点前移,不是负一百。

NaN 与数字有效范围 ​

为什么需要:其他类型转 number 时,不一定能得到有效数字,需要占位符;超出范围也需要特殊值。怎么想:NaN 全称 “Not a Number”,但类型仍是 number。怎么做:

js
console.log(typeof NaN);        // "number"
console.log(NaN + 1);           // NaN
console.log(NaN === NaN);       // false
console.log(isNaN("hello"));    // true
console.log(Number.MAX_VALUE);  // 约 1.79e308
console.log(Number.MIN_VALUE);  // 约 5e-324,最小正数
console.log(3e400);             // Infinity
console.log(-3e400);            // -Infinity
console.log(3e-400);            // 0
console.log(isFinite(100));     // true
console.log(isFinite(Infinity));// false

易错点:Number.MIN_VALUE 不是最小负数,而是最小正数;NaN 与任何数计算仍是 NaN,且不等于自身;isNaN("hello") 会先尝试转数字,转不成才为 true。Infinity、-Infinity、NaN 都是无效数字,参与后续计算要尽早拦截。

boolean、null、undefined:状态与空值 ​

为什么需要:程序要表达真假和“没有值”。怎么想:boolean 只有 true/false;null 表示空;undefined 常表示未定义。怎么做:未赋值变量使用时得到 undefined。易错点:不必过度纠结 null 与 undefined 的“硬对比”,它们都常表示“没拿到有效结果”;不要依赖自动产生空值,关键数据应显式判断。

string 与转义字符 ​

为什么需要:文本必须用引号界定。怎么想:单双引号在 JS 中基本无区别;引号内容里出现同类引号会提前结束字符串。怎么做:

js
let msg1 = 'hello';
let msg2 = "你好";
let msg3 = '锄禾日当午\n汗滴禾下土\n谁知盘中餐\n粒粒皆辛苦';
let msg4 = 'It\'s ok';
let msg5 = "他说:\"你好\"";
let msg6 = '\\n 原样显示反斜杠 n';
let msg7 = '\u0065'; // e
console.log(msg3, msg4, msg5, msg6, msg7);

易错点:字符串里直接敲换行会破坏语法,应写 \n;要显示反斜杠本身写 \`;\u` 后接四位十六进制码点。转写稿中“单引号效率更高”的说法来自其他语言习惯,JS 单双引号本身无性能差异,团队按代码规范即可。

结论 ​

现有材料没有 BFC 知识点,故本节只能按转写稿扩写 JS 数据类型;若需要 BFC 教程,请补充 BFC 的笔记、代码和转写稿。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

1. number 类型:整型、浮点、科学计数法、NaN、有效范围 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>number 类型示例</title>
</head>
<body>
  <script>
    var n01 = 764;
    var n02 = 012;      // 0 开头:八进制表示
    var n03 = 0x12;     // 0x 开头:十六进制表示
    var n04 = 67.2;     // 带小数点:浮点数

    console.log(n01, n02, n03, n04);

    // 浮点数计算精度问题
    console.log(0.1 + 0.2);

    // 科学计数法
    var n05 = 1.3e4;
    var n06 = 1.67e78;
    var n07 = 2.3e-2;
    console.log(n05, n06, n07);

    // NaN:not a number,但类型仍是 number
    console.log(NaN, typeof NaN);
    console.log(NaN * 0, NaN / 189);
    console.log(250 == 250, 250 == 150, NaN == NaN);
    console.log(isNaN(NaN), isNaN('hello'), isNaN(250));

    // 数字有效范围
    console.log(Number.MAX_VALUE);
    console.log(Number.MIN_VALUE); // 最小正数,不是最小数

    var n08 = 3e400;   // 超出范围:正无穷
    var n09 = -3e400;  // 负无穷
    var n10 = 3e-400;  // 比最小正数还小:变成 0
    console.log(n08, n09, n10);

    console.log(isFinite(100), isFinite(Infinity), isFinite(NaN));

    // boolean、null、undefined
    var t = true;
    var f = false;
    var empty = null;
    var undef;
    console.log(t, f, empty, undef);
  </script>
</body>
</html>

要点解释:

  • 012 是八进制表示,输出为十进制的 10;0x12 是十六进制表示,输出为十进制的 18。
  • 0.1 + 0.2 不会精确等于 0.3,而是 0.30000000000000004,这是十进制小数转二进制小数不能精确表示导致的。
  • NaN 的类型是 number,它与任何数字计算结果还是 NaN,并且 NaN == NaN 为 false。
  • Number.MIN_VALUE 表示最小正数,不是最小数;最小数可用 -Number.MAX_VALUE 表示。
  • 3e400 超出范围会变成 Infinity,3e-400 会变成 0;isFinite(NaN) 为 false。
  • null 表示空,undefined 表示未定义;声明变量但未赋值时得到 undefined。
  • 易错点:0 开头和 0x 开头只能表示整数,浮点数不能这样写;开启严格模式后 012 这类八进制写法可能报错。

2. string 类型:单双引号、转义字符、换行、Unicode ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>string 类型示例</title>
</head>
<body>
  <script>
    var msg01 = 'hello高小乐';
    var msg02 = "你好老头乐";

    console.log(msg01, typeof msg01);
    console.log(msg02, typeof msg02);

    // 字符串内容中写同类引号:需要转义
    var msg03 = '他说:\'你好\'';
    var msg04 = "他说:\"你好\"";
    console.log(msg03);
    console.log(msg04);

    // 反斜杠本身:用 \\ 表示
    var msg05 = '在GS中可以使用\\n表示换行';
    console.log(msg05);

    // \n 表示换行
    var msg06 = '锄禾日当午\n汗滴禾下土\n谁知盘中餐\n粒粒皆辛苦';
    console.log(msg06);

    // Unicode 编码:\u 后接四位十六进制数字
    console.log('\u0065');
    console.log('\u38AF');
  </script>
</body>
</html>

要点解释:

  • 单引号和双引号都可以表示字符串,typeof 结果都是 'string'。
  • 单引号字符串里直接写单引号会提前结束字符串,导致语法错误;应换成双引号或使用 \' 转义。
  • \n 表示换行,字符串中不能直接敲一个真实换行来换行。
  • \&#96; 表示反斜杠本身,避免 \n` 被解析成换行。
  • \u0065 表示 Unicode 编码对应的字符 e;\u38AF 表示另一个 Unicode 字符。
  • 易错点:\u 后面必须是四位十六进制数字;字符串中直接写未转义的同类引号会报语法错误。

3. 作业:交换两个变量的值 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>交换两个变量</title>
</head>
<body>
  <script>
    var a = 100;
    var b = 200;

    console.log('交换前:', a, b);

    var temp = a;
    a = b;
    b = temp;

    console.log('交换后:', a, b);
  </script>
</body>
</html>

要点解释:

  • 先用 temp 暂存 a 的原值,避免 a 被覆盖后丢失。
  • 如果直接写 a = b; b = a;,最后 a 和 b 都会变成 200。
  • 交换顺序必须是:先暂存 a,再让 a = b,最后让 b = temp。
  • 易错点:不要在没有临时变量的情况下直接互相赋值,否则原值会被覆盖。

常见错误 ​

  1. 现象:var n = 012; console.log(n) 得到 10,误以为 12 → 原因:0 开头被按八进制解析 → 修法:整数统一写十进制,避免前导 0;需要八进制时显式写 0o12。
  2. 现象:0x1.8、0X12.3 报错 → 原因:八进制/十六进制字面量只适用于整数 → 修法:浮点数只用十进制或科学计数法表示。
  3. 现象:0.1 + 0.2 === 0.3 为 false → 原因:十进制小数不能精确转成二进制小数 → 修法:浮点比较用误差阈值,如 Math.abs(a - b) < Number.EPSILON;展示用 toFixed()。
  4. 现象:x === NaN 永远为 false,无法判断 NaN → 原因:NaN 与任何值(包括自身)都不相等 → 修法:用 Number.isNaN(x) 或 Object.is(x, NaN)。
  5. 现象:isNaN('hello') 为 true,把字符串误判成 NaN 本身 → 原因:isNaN() 会先做 number 转换 → 修法:先用 Number.isNaN() 判断值,或先检查 typeof 再转换。
  6. 现象:单引号字符串里直接写单引号,或字符串里直接敲换行导致语法错误 → 原因:引号提前闭合,换行提前结束语句 → 修法:外层换双引号、用 \' 转义;换行写 \n。
  7. 现象:a = b; b = a; 后两个变量值相同 → 原因:a 的原值被覆盖 → 修法:先用临时变量保存原值,或用解构 [a, b] = [b, a]。

练习 ​

  1. 写出下列表达式的结果,并说明原因:012、0x12、1.2e3、1.2e-3。
  2. 用至少两种方法交换 var a = 100; var b = 200;,并说明每种方法的限制。
  3. 写一个函数 isValidNumber(x):只有正常有效数字返回 true,NaN、Infinity、-Infinity 返回 false。
  4. 写一个函数 isEqual(a, b),用于安全比较两个浮点数是否“相等”,并处理 NaN。
  5. 用字符串输出两行内容,第一行包含 He said: "It's ok",第二行包含反斜杠 \n 字样,要求不产生语法错误。

原视频对照 ​

#视频时长
1number-整型和浮点30:03
2number-科学计数法06:42
3boolean、null、undefined02:13
4number-NaN09:26
5number-数字有效范围09:57
6string13:34

小结 ​

  1. number 对外是一个类型,但内部仍存在整数与浮点的运算差异。
  2. 整数可用八进制、十六进制表示;浮点不能使用这些字面量形式。
  3. 浮点数不要直接用 === 判断相等,应使用误差范围。
  4. NaN 是 number 类型,但属于无效数字,且不自等。
  5. NaN、Infinity、-Infinity 都不应当作正常有效数字继续参与业务计算。
  6. 字符串单双引号等价;嵌套引号靠换外层引号或转义解决,换行用 \n。
  7. 交换变量必须先保留原值,避免覆盖后再赋值。