Skip to content

数据类型转换(Day2 2/4) ​

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

学习目标 ​

  1. 能针对给定参数分别写出 Number、parseInt、parseFloat 的返回值,并说明非字符串、纯数字字符串、以数字开头字符串、空字符串、带空格字符串、十六进制和科学计数法等情形下的转换差异。
  2. 能判断算术运算中是否发生自动类型转换,并按与 Number 一致的规则写出布尔值、字符串等参与运算后的结果。
  3. 能识别表达式中的运算符与操作数,判断表达式是否有值、值的数据类型,并说明原始表达式和副作用的含义。
  4. 能按操作数个数将运算符分为一元、二元、三元,按功能分为算术、关系、逻辑、位、赋值和其他运算符,并各举一例。
  5. 能对 + - * / % 及一元正负号写出操作数个数、类型要求、表达式值类型和有无副作用,并计算给定表达式的值。
  6. 能计算包含 ++、-- 前置和后置的表达式值与变量最终值,说明副作用相同但表达式值不同,并判断直接量使用 ++、-- 会报错。

知识点 ​

回顾的目标:把零散知识点串成可验证的规则 ​

这一节叫“回顾”,不是重新背概念,而是把“怎么引入 JS、怎么写基本语法、怎么声明变量、怎么判断数据类型”串成一套能上机验证的规则。转写稿里反复强调一个思路:不要凭感觉记,写个案例看结果;同时要区分“表达式的值”和“操作数本身有没有变”。这个思路同样适合回顾数据类型。

在HTML中使用JS ​

为什么需要:JS 不能凭空运行,必须先挂到 HTML 文档里,浏览器才知道什么时候执行。
怎么想:先确定脚本放在哪里、是内联还是外链,再考虑执行顺序。
怎么做:用 <script> 标签写 JS;外链用 src 引入;调试输出优先用 console.log()。
易错点:src 引入后标签内的代码通常不再执行;脚本位置会影响它能否拿到后面的 DOM。

JS基本语法 ​

为什么需要:语法是后面所有代码的地基,语句边界、大小写、注释、输出方式都必须清楚。
怎么想:把“语句结束符、大小写、注释、输出”当成四件独立的事逐项检查。
怎么做:语句结束可用分号或换行;JS 严格区分大小写;单行注释 //,多行注释 /* */;输出可用 alert()、document.write()、console.log()。
易错点:依赖换行在压缩、拼接时可能出问题,初学建议写分号;document.write() 会往文档流里写内容,调试更适合 console.log();变量 age 和 Age 是两个名字。

变量 ​

为什么需要:数据要反复使用,就必须有名字保存,否则每次都要重新写字面量。
怎么想:先声明,再赋值,再使用;名字要能表达含义。
怎么做:例如 var age = 18;,先声明变量再参与运算。命名按课程约定:不能以数字开头,不能使用关键字,区分大小写,建议用驼峰命名。
易错点:未声明直接使用;把关键字当变量名;大小写写错导致“变量不存在”。

数据类型与typeof ​

为什么需要:同样是“1”,可能是数字,也可能是字符串;类型不同,运算结果就不同。
怎么想:先用 typeof 看类型,再看值是否符合预期。
怎么做:typeof 123 得到 "number",typeof "123" 得到 "string",typeof true 得到 "boolean"。
易错点:typeof null 返回 "object",这是历史遗留问题,不能据此认为 null 是对象;typeof 看的是“当前值”的类型,不是转换后的类型。

number类型 ​

为什么需要:数值要支持整数、小数、科学计数法,还要表示“不是有效数字”的情况。
怎么想:先判断是不是合法数字,再判断是否超出有效范围,最后看特殊值 NaN。
怎么做:整数、浮点、科学计数法都属 number;NaN 的类型也是 number;有效范围可查 Number.MAX_VALUE、Number.MIN_VALUE,整数精度可关注安全整数范围。
易错点:NaN 不等于任何值,包括它自己;判断可用 isNaN();浮点数相加可能出现精度问题。

string类型 ​

为什么需要:文本是最常见的数据,必须能表示引号、换行等特殊字符。
怎么想:先选单引号或双引号,再处理内部引号和转义。
怎么做:'abc'、"abc" 都可以;转义用反斜杠,如 \'、\"、\n。
易错点:单引号字符串里直接写单引号会提前结束;引号必须成对;字符串 "123" 不是数字。

boolean、null和undefined ​

为什么需要:程序要表达“真/假”“空值”“未赋值”等状态。
怎么想:boolean 只有 true 和 false;null 表示“空”,undefined 表示“未定义/未赋值”。
怎么做:用 typeof 验证:typeof true 是 "boolean",typeof undefined 是 "undefined",typeof null 是 "object"。
易错点:不要把 null 和 undefined 混为一谈;typeof null 的返回值容易误导。

用控制台做一次回顾调试 ​

转写稿里讲 parseInt、parseFloat 和自动类型转换时,核心提醒是:转换规则要看转换函数,不要凭感觉。回顾数据类型时也可以这样验证:

js
console.log(typeof 123);        // "number"
console.log(typeof "123");      // "string"
console.log(Number(""));        // 0
console.log(parseInt("45ABC")); // 45
console.log(parseInt("hello100")); // NaN
console.log(parseInt(23.89));   // 23

转写稿里说的“pas in”就是 parseInt,“pass float”就是 parseFloat。它们不是标准类型转换函数,本意是“提取字符串中的数字”:非字符串如 true、false、null、undefined 往往得到 NaN;空字符串也提取不到数字,得到 NaN;parseInt 只取整数部分,且直接去掉小数,不四舍五入。自动类型转换的规则则与 Number 一致,而不是与 parseInt 一致。调试时先看 typeof 原值,再看转换结果,就不容易把“值变了”和“只是参与运算时临时转换”混在一起。

说明:本节笔记未附图片;下面保留并整理转写稿中的代码,同音字已改为 parseInt、parseFloat、Boolean、console.log 等正确写法。

一、显式转换:Number、String、Boolean ​

为什么需要:当类型不一致时,需要明确告诉浏览器“把数据转成目标类型”。
怎么想:看到转换函数就知道目标类型:Number 转数字,String 转字符串,Boolean 转布尔。
怎么做:

js
Number(true);   // 1
Number(false);  // 0
Number("");     // 0
Number("abc");  // NaN

易错点:Number 是标准强制转换函数;自动类型转换转数字时,规则与 Number 一致,而不是与 parseInt、parseFloat 一致。

二、parseInt 与 parseFloat:提取字符串中的数字 ​

为什么需要:Number("45ABC") 会得到 NaN,但有些场景只想提取开头的数字。
怎么想:它们的本意是“从字符串中提取数字”。非字符串参数先全部得到 NaN;纯数字字符串或以数字开头的字符串才可能转成有效数字。
怎么做:

js
parseInt("45ABC");       // 45
parseInt("hello100");    // NaN
parseInt("23.89ABC");    // 23
parseFloat("23.89ABC");  // 23.89
parseInt(3.14);          // 3
parseInt(" 23 ");        // 23
parseInt("");            // NaN
parseInt(true);          // NaN

易错点:parseInt 只取整数部分,直接截掉小数,不四舍五入;空字符串是 NaN,不是 0;非字符串如 true、false 也是 NaN。调试时可用 console.log 分别看 parseInt 和 parseFloat 的结果。

三、自动类型转换 ​

为什么需要:运算符需要特定类型的数据,而 JS 允许在运算时自动转换。
怎么想:先看当前运算环境需要什么类型,这通常由运算符决定;不是所需类型,就自动转换。
怎么做:

js
true * 67;        // 67
"60" * false;     // 0
"hello" * false;  // NaN

易错点:自动转换不是副作用,操作数本身没有变,只是转成所需类型后参与运算;转数字规则与 Number 一致,不要混用 parseInt 的规则。

四、表达式、运算符、操作数 ​

为什么需要:读代码、讲代码时经常说“这个表达式的值”,必须明确概念。
怎么想:运算符是参与运算的符号;操作数是与运算符一起运算的变量、直接量或数据;数据和运算符共同组成表达式,表达式有计算结果,叫表达式的值。
怎么做:

js
1 + 1; // 表达式,有值

变量、直接量也可算原始表达式;多个简单表达式可组成复杂表达式。
易错点:表达式一定有值;运算符是表达式的核心,决定怎么算、结果是什么类型、有没有副作用。

五、运算符分类与算术运算符 ​

为什么需要:按功能学比按符号死记更清楚。
怎么想:可按操作数个数分一元、二元、三元;JS 中三元运算符只有一个,后面再讲。学习时按功能分:算术、关系、逻辑、位、赋值、其他。
怎么做:

js
45 % 10; // 5
50 % 10; // 0

加减乘除和取余通常要求操作数为 number,表达式值也是 number,且无副作用。
易错点:% 是取余,不是百分比;正号和负号是一元运算符,加号和减号是二元运算符,写法相同要看操作数个数。

六、正负号:看表达式的值,不改变操作数 ​

为什么需要:初学容易把“表达式结果”误认为“操作数被改了”。
怎么想:+true 得到的是表达式的值 1,但 true 本身仍是 true。
怎么做:

js
+89;    // 89
+true;  // 1

let v1 = false;
console.log(+v1); // 0
console.log(v1);  // false

let v2 = -89.12;
console.log(+v2); // -89.12
console.log(-v1); // -0,等价于 0
console.log(-v2); // 89.12

易错点:正号不会把负数变正;负号也没有修改 v2,v2 仍是 -89.12;正负号表达式无副作用。

七、累加累减与副作用 ​

为什么需要:++、-- 是编程中特有的写法,会真正改变变量。
怎么想:副作用是“除了得到表达式结果,还修改参与运算的操作数”;有副作用时,操作数必须是变量。
怎么做:

js
let num = 18;
num++;
console.log(num); // 19
++num;
console.log(num); // 20
--num;
console.log(num); // 19
num--;
console.log(num); // 18

易错点:5++ 会报错,因为直接量不能被重新赋值;前后缀的副作用一样,都是让变量加一或减一,不同的是表达式的值。

八、前后缀表达式值与复杂表达式调试 ​

为什么需要:考试和调试常把“变量当前值”和“表达式的值”混在一起。
怎么想:后缀 v++ 先取旧值作为表达式值,再让变量加一;前缀 ++v 先让变量加一,再取新值作为表达式值。
怎么做:

js
let v1 = 10;
console.log(v1++); // 10
console.log(v1);   // 11

let v2 = 10;
console.log(++v2); // 11
console.log(v2);   // 11

let v3 = 10;
console.log(v3--); // 10
console.log(v3);   // 9

let v4 = 10;
console.log(--v4); // 9
console.log(v4);   // 9

复杂表达式要从左到右算,每一步记录副作用:

js
let number = 10;
let res = number++ - number-- + ++number - --number + number++;
console.log(res, number);

易错点:不能一直用初始值算后面的部分;前面的 number++ 已改变 number,后面要用改变后的值;调试时先输出表达式值,再输出变量,观察副作用。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

示例一:parseInt / parseFloat 与 Number 的区别 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>parseInt / parseFloat 提取字符串中的数字</title>
</head>
<body>
<script>
  // 1. 参数不是字符串:parseInt / parseFloat 一律返回 NaN
  console.log(parseInt(true));    // NaN   (Number(true) 是 1)
  console.log(parseInt(false));   // NaN   (Number(false) 是 0)
  console.log(parseInt(''));      // NaN   (Number('') 是 0)
  console.log(parseInt(NaN));     // NaN

  // 2. 字符串:纯数字、或以数字开头的字符串,都能提取出有效数字
  console.log(parseInt('123'));       // 123
  console.log(parseInt('45ABC'));     // 45    (Number('45ABC') 是 NaN)
  console.log(parseInt('hello100'));  // NaN   (不是以数字开头)
  console.log(parseInt('0xAB'));      // 171   (十六进制)
  console.log(parseInt('28e4'));      // 28    (只提取 e 前面的整数部分)
  console.log(parseInt(' 23 '));      // 23    (会去掉两边空格)

  // 3. parseInt 与 parseFloat 的区别:一个只取整数,一个取所有数字
  console.log(parseInt('23.89ABC'));    // 23
  console.log(parseFloat('23.89ABC'));  // 23.89

  // 4. parseInt 的常用技巧:直接取数字的整数部分
  console.log(parseInt(3.14));  // 3  (直接把小数部分去掉,不四舍五入)
</script>
</body>
</html>

要点解释

  • parseInt / parseFloat 的本意是“从字符串中提取数字”,所以参数不是字符串时一律 NaN,这点和 Number 完全不同(Number(true)=1、Number('')=0)。
  • 能提取的前提是:纯数字字符串,或以数字开头的字符串。'45ABC' 能得 45,'hello100' 只能得 NaN。
  • parseInt('23.89') 得到 23,是直接截掉小数,不是四舍五入;要小数位就用 parseFloat。
  • parseInt(3.14) 参数传数字也能用,常用来取整;parseInt(' 23 ') 会去掉两边空格。
  • 易错点:parseInt('') 是 NaN,而 Number('') 是 0;别把两者的规则记混。

示例二:自动类型转换、取余与一元正负号 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>自动类型转换与算术运算符</title>
</head>
<body>
<script>
  // 1. 自动类型转换:运算符需要 number,操作数不是 number 就自动转
  console.log(true * 67);        // 67    true → 1
  console.log('60' * false);     // 0     '60' → 60,false → 0
  console.log('hello' * false);  // NaN   'hello' → NaN,NaN 参与运算还是 NaN

  // 2. 取余 %(不是百分比),常用来判断能不能被整除
  console.log(45 / 10);  // 4.5   商
  console.log(45 % 10);  // 5     余数
  console.log(50 % 10);  // 0     余数为 0 说明能整除

  // 3. 一元正负号:有表达式的值,但【没有副作用】,操作数本身不变
  let v1 = false;
  console.log(+true);  // 1
  console.log(+v1);    // 0
  console.log(v1);     // false  ← 变量本身没被改变

  let v2 = -89.12;
  console.log(+v2);    // -89.12  正号不改变正负
  console.log(-v2);    // 89.12
  console.log(v2);     // -89.12  ← 变量本身还是负数
  console.log(-v1);    // -0

  // 4. 加号/减号是“正负号”还是“加减”:看操作数个数
  console.log(+89);    // 89   1 个操作数 → 一元正号
  console.log(1 + 2);  // 3    2 个操作数 → 二元加法
</script>
</body>
</html>

要点解释

  • 自动类型转换:数据不是当前运算环境需要的类型时自动转换,转换规则与强制类型转换(Number)一致,和 parseInt/parseFloat 不一致。
  • 转换发生在“参与运算”这一步,操作数本身没变,所以自动类型转换不算副作用;+v1 输出 0,但 v1 依然是 false。
  • % 是取余不是百分比,余数为 0 表示能整除,这是开发中最常用的判断方式。
  • 一元正负号只是“把操作数加上符号”产生一个新的表达式值,不会把变量改成相反数;+ 也不会把负数变正。
  • 易错点:不要把“表达式的值”当成“变量的值”来看,+true 是 1,但 true 本身还是 true。

示例三:累加累减的副作用与表达式的值 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>累加累减:副作用与表达式的值</title>
</head>
<body>
<script>
  // 1. 副作用:除了算出表达式的结果,还把操作数本身改了
  let num = 18;
  num++;
  console.log(num);  // 19
  ++num;
  console.log(num);  // 20
  num--;
  console.log(num);  // 19

  // 2. 有副作用的表达式,操作数必须是变量;直接量会报错
  // 5++;  // 报错:不能给直接量重新赋值,一就是一二就是二

  // 3. 操作数不是 number 会先转再算,但变量本身被改成了 number
  let v = true;
  v++;
  console.log(v);  // 2  (对比示例二的 +true:变量不会变)

  // 4. ++ / -- 在前在后:副作用一样,表达式的值不一样
  let v1 = 10;
  console.log(v1++);  // 10  操作数在前:先取值当表达式的值,再自增
  console.log(v1);    // 11

  let v2 = 10;
  console.log(++v2);  // 11  运算符在前:先自增,用自增后的值当表达式的值
  console.log(v2);    // 11

  let v3 = 10;
  console.log(v3--);  // 10
  let v4 = 10;
  console.log(--v4);  // 9

  // 5. 复杂表达式:从左到右依次计算,每步副作用都会改变 number,
  //    后面的运算必须用“改过之后”的值,不能一直按 10 算
  let number = 10;
  let res = (number++) - (number--) + (++number) + (--number) + (number++);
  console.log(res);     // 整个表达式的值
  console.log(number);  // 经历多步副作用之后,变量的值
</script>
</body>
</html>

要点解释

  • 累加累减是目前见到的第一个有副作用的运算符:除了得到表达式的值,还会修改操作数本身。
  • 有副作用的表达式要求操作数必须是变量形式,写 5++ 会报错——直接量没法被重新赋值。
  • ++/-- 在前在后,副作用完全相同(都是给操作数加一或减一),不一样的只是表达式的值:操作数在前先取值、运算符在前先运算。
  • 判断“谁在前”的口诀:操作数在前 → 用没变之前的值当表达式值;运算符在前 → 用变完之后的值当表达式值。
  • 复杂表达式要从左往右算,前一步的副作用会影响后一步的取值,这是最容易算错的地方。
  • 易错点:不要把 +true(无副作用,变量还是 true)和 v++(有副作用,变量被改成 number)混为一谈。

常见错误 ​

  1. 现象:把 parseInt 当成通用数字转换函数,遇到 true、null、undefined 期望得到 1/0。
    原因:混淆了 parseInt/parseFloat 与 Number 的转换规则。
    修法:先明确目标是“提取数字”还是“整体转数字”;需要整体转换时优先用 Number()。

  2. 现象:认为 parseInt("12.9") 会四舍五入,或认为 parseFloat("12.9px") 会报错。
    原因:把数学取整、严格转换和“从头提取数字”混为一谈。
    修法:写小案例验证 parseInt、parseFloat、Number 三者的差异,按业务选择。

  3. 现象:自动类型转换时凭感觉猜结果,尤其遇到 +、true、false、字符串。
    原因:没有先判断当前运算符要求什么类型,也没有区分一元 + 和二元 +。
    修法:先看运算符和上下文,再按 Number 规则推演;不确定就打印中间值。

  4. 现象:let v = false; v++; 后仍以为 v 是 false。
    原因:忽略自增自减的副作用,或把“表达式值”和“变量值”混在一起。
    修法:分别输出变量本身和表达式结果,明确副作用是否改变操作数。

  5. 现象:let a = 1; console.log(a++ + ++a); 算错,且没有跟踪 a 的中间变化。
    原因:只看最终表达式,没有按从左到右顺序记录副作用。
    修法:每算一步就更新变量值,并标注该步表达式返回的是旧值还是新值。

  6. 现象:写 5++、++(a + b) 之类代码,期望修改变量。
    原因:不知道有副作用的表达式要求操作数是变量。
    修法:把自增自减只用于可修改的变量;复杂表达式先拆成多步。

练习 ​

  1. 分别写出下列表达式的结果,并说明理由:Number("")、parseInt("")、parseFloat("12.3px")、parseInt("0x10")、Number("0x10")。
  2. 判断并写出输出:console.log(true * "6", "hello" * false, false + 1, "2" + 1)。
  3. 阅读代码并写出两次输出及最终 a 的值:let a = 10; console.log(a++); console.log(++a); console.log(a);
  4. 求整个表达式的值和变量 n 的最终值:let n = 5; let r = n++ + ++n - n-- + n++; console.log(r, n);
  5. 找出下面代码的错误并改正:let x = 1; 5++; let y = ++(x + 1);

原视频对照 ​

#视频时长
1parseInt和parseFloat12:06
2自动类型转换05:38
3表达式和运算符07:21
4运算符的分类03:30
5算术运算符17:51
6累加累减运算符18:54

小结 ​

  1. parseInt/parseFloat 是“提取数字”的思路,不等同于 Number 的整体转换。
  2. 判断转换结果前,先看转换函数、参数类型和字符串内容。
  3. 自动类型转换由运算符或上下文驱动,不能脱离表达式单独猜。
  4. 一元 + 与二元 + 的语义不同,遇到 + 先数操作数个数。
  5. 自增自减要同时看两件事:变量是否被修改、表达式返回旧值还是新值。
  6. 复杂表达式必须按求值顺序逐步跟踪变量,不能只看最终形态。
  7. 有副作用的表达式要求操作数可修改,实际编码中优先拆步骤、加中间输出。