第三章:语言基础
3.1 语法
3.1.4 严格模式
"use strict";
function doSomething() {
"use strict";
// do something
}
3.3 变量
3.3.1 var 关键字
- 不初始化的情况下,变量值为 undefined
var a;
console.log(a); // output: undefined
a = 1;
console.log(a); // output: 1
- var 声明作用域
1.1 使用 var 定义的变量会成为其所在函数的局部变量
1.2 省略 var 会创建全局变量
1.1 代码示例:
function test() {
var char = 'a';
}
test(); // 调用时创建变量并赋值,调用后销毁
console.log(char); // output:char is not defined
1.2 代码示例
function test() {
// var char = 'a';
char = 'a';
}
test();
console.log(char); // output:a
- var 声明提升
function test() {
console.log(char);
var char = 'a';
}
test(); // output: undefined
上面的代码等价于:
function test() {
var char;
console.log(char);
char = 'a';
}
test(); // output: undefined
3.3.2 let 声明
- let 声明的范围是块作用域
if (true) {
let name = 'jiy';
console.log(name); // output: jiy
}
console.log(name); // output:name is not defined
- var 声明的范围是函数作用域
if (true) {
var name = 'jiy';
console.log(name); // output: jiy
}
console.log(name); // output: jiy
-
暂时性死区
- let 没有变量提升
- 在 let 声明前的执行瞬间被称为暂时性死区
-
全局声明
- 与 var 不同,let 在全局作用域中声明的变量不会成为 window对象的属性
-
for 循环中的 let 声明
- 使用 let 声明迭代变量时,后台会为每个迭代循环声明一个新的迭代变量。每个 setTimeout 引用的都是不同的变量实例
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 0);
}
// output: 5 5 5 5 5
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 0);
}
// output: 0 1 2 3 4
3.3.3 const 声明
- const 声明的限制只适用于它指向的变量的引用
const person = {};
person.name = 'Jiy'; // ok
3.4 数据类型
- ES6 有六种简单数据类型:
- Undefined
- Null
- Boolean
- Number
- String
- Symbol(ES6新增)
- 还有一种复杂数据类型:
- Object
3.4.1 typeof 操作符
- 返回值
- “boolean”
- “string”
- “number”
- “object”:值为对象或null
- “function”
- “symbol”
3.4.2 Undefined 类型
let test1;
// let test2;
console.log(typeof test1); // output: undefined
console.log(typeof test2); // output: undefined
if (test1) {
// 不会执行
}
if (!test1) {
// 会执行
}
if (test2) {
// 报错
}
3.4.3 Null 类型
- null 值表示一个空对象指针
- 注意:
console.log(undefined == null); // output: true
3.4.4 Boolean 类型
数据类型 | 转换为 true 的值 | 转换为 false 的值 |
---|---|---|
Boolean | true | false |
String | 非空字符串 | “” |
Number | 非零数值(包括无穷值) | 0,NaN |
Object | 任意对象 | null |
Undefined | N/A(不存在) | undefined |
console.log(Boolean("")); // output: false
3.4.5 Number 类型
- 注意:
- 八进制字面量在严格模式下会导致引擎抛出语法错误
- 正零和负零在所有情况下都被认为是等同的
- 浮点值
- 值的范围
- 精确度最高可达 17 位小数
- 可表示最小值
Number.MIN_VALUE
- 可表示最大值
Number.MAX_VALUE
- 超出范围会被转换成
Infinity
- 可表示最小值
2.
和2.0
会被当成2
处理0.1 + 0.2 == 0.300 000 000 000 000 04
- 精确度最高可达 17 位小数
let result = Number.MAX_VALUE + Number.MIN_VALUE;
console.log(isFinite(result)); // output: false
- NaN:Not A Number
- 表示本来要返回数值的操作失败了
console.log(0 / 0); // output: NaN
console.log(-5 / 0); // output: -Infinity
console.log(NaN == NaN); // output: false
console.log(isNaN(true)); // output:false ( true -> 1 )
- 数值转换
Number()
Number(true) == 1
Number(null) == 0
Number(undefined) == NaN
parseInt()
parseFloat()
3.4.6 String 类型
-
字符字面量
- 转义序列只算一个字符的长度
-
字符串的特点
- 字符串不可变,修改时会重新创建
-
转换为字符串
toString()
- true 调用该方法返回 ‘true’
- null 和 undefined 没有该方法
String()
- null 和 undefined 分别返回自身的字符串
-
模板字面量
-
字符串插值
-
标签函数:必须使用模板字面量` `
let a = 6;
let b = 9;
function simpleTest(strings, ...expressions) {
console.log(strings)
return strings[0] + expressions.map((e, i) => `${e}${strings[i + 1]}`).join('');
}
const result = simpleTest`${a}+${b}=${a + b}`;
console.log(result) // output: 6+9=15
- 原始字符串
console.log(String.raw`\u00A9`); // output: \u00A9
// 也可以通过标签函数第一个参数的 raw 属性获取
3.4.7 Symbol 类型
- 符号是原始值,符号实例唯一且不可变
- 用途:确保对象属性使用唯一标识符,不会发生属性冲突
- 基本用法
let a1 = Symbol();
let a2 = Symbol();
let b1 = Symbol('test');
let b2 = Symbol('test');
let c = {b1:2};
c[b1] = 1;
c[b2] = 3;
console.log(a1 == a2); // output: false
console.log(b1 == b2); // output: false
console.log(b1); // output: Symbol(test)
console.log(c); // output: { b1: 2, [Symbol(test)]: 1, [Symbol(test)]: 3 }
let test1 = new Symbol(); // 报错
let test2 = Symbol();
let test3 = Object(test2); // √
- 使用全局符号注册表
Symbol.for()
Symbol.keyFor(): string | undefined
let globalSymbol = Symbol.for('test'); // 创建新符号,添加到注册表
let other = Symbol.for('test'); // 重用已有符号
let other2 = Symbol('test');
console.log(globalSymbol == other); // output: true
console.log(globalSymbol == other2); // output: false
console.log(Symbol.keyFor(globalSymbol)); // output: test
console.log(Symbol.keyFor(other2)); // output: undefined
- 使用符号作为属性
- 可以使用字符串或数值作为属性的地方,都可以使用符号
let s1 = Symbol('test1'),
s2 = Symbol('test2'),
s3 = Symbol('test3'),
s4 = Symbol('test4');
let obj = {
s0: 'p0',
[s1]: "p1"
};
console.log(Object.getOwnPropertySymbols(obj)); // output: [ Symbol(test1) ]
Object.defineProperty(obj, s2, { value: 'p2' });
console.log(Object.getOwnPropertySymbols(obj)); // output: [ Symbol(test1), Symbol(test2) ]
Object.defineProperties(obj, {
[s3]: { value: 'p3' },
[s4]: { value: 'p4' }
});
console.log(Object.getOwnPropertySymbols(obj)); // output: [ Symbol(test1), Symbol(test2), Symbol(test3), Symbol(test4) ]
// console.log(Object.getOwnPropertyDescriptors(obj)); // 返回包含常规和符号属性描述符的对象
console.log(Reflect.ownKeys(obj)); // output: [ 's0', Symbol(test1), Symbol(test2), Symbol(test3), Symbol(test4) ]
- 常用内置符号
@@iterator
==Symbol.iterator
- 通过重新定义内置符号可以改变原生结构的行为
3.4.8 Object 类型
3.5 操作符
3.5.2 位操作符
- p59(第四版 中文)
- ES 用64位存储数值,位操作时先转换成32位,之后把结果转换到64位
- 有符号整数使用32位的前31位表示整数值,第三十二位表示符号(0为正,1为负)
- 负数:
1. 确定绝对值的二进制表示
2. 取反码
3. 结果加1
以 -18 为例:
1. 18的二进制表示为:
0000 0000 0000 0000 0000 0000 0001 0010
2. 反码为:
1111 1111 1111 1111 1111 1111 1110 1101
3. 结果加1即为最终结果:
1111 1111 1111 1111 1111 1111 1110 1110
let num = -18;
console.log(num.toString(2)); // output: "-10010"
NaN
和Infinity
在位处理中被当成 0 处理
- 按位非 ~
- 返回数值的反码
- 最终效果是对数值取反并减一
- 按位与 &
- 将两个数的每一位对齐
- 只有两个位都是 1 时返回1,任意一位是 0 时返回0
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011
------------------------------------------------
AND = 0000 0000 0000 0000 0000 0000 0000 0001
- 按位或 |
- 只有两个位都是 0 时返回0,任意一位是 1 时返回1
- 按位异或 ^
- 两位都是 1 或 0 时,返回 0
- 左移 <<
- 按照指定的位数将数值的所有位向左移动,用 0 补全、
- 保留符号位
- 有符号右移 >>
- 是左移的逆运算
- 空位用符号位的值补全
- 无符号右移 >>>
- 正数的结果与 >> 相同
- 负数用 0 补全空位,结果为正数
3.5.3 布尔操作符
- !
console.log(!{}); // false
console.log(!""); // true
console.log(!"abc"); // false
console.log(!0); // true
console.log(!9); // false
console.log(!Infinity); // false
console.log(!null); // true
console.log(!NaN); // true
console.log(!undefined); // true
-
&&
- 第一个操作数是对象,返回第二个操作数
- 第二个操作数是对象,只有第一个求值为
true
时返回该对象 - 都是操作数,返回第二个操作数
- 有一个操作数为
null | NaN | undefined
,返回null | NaN | undefined
- 短路性
-
||
- 第一个操作数是对象,返回第一个操作数
- 第一个操作数是
false
,返回第二个操作数 - 都是操作数,返回第一个操作数
- 有一个操作数为
null | NaN | undefined
,返回null | NaN | undefined
- 短路性
3.5.4 乘性操作符
- 乘法操作符
Infinity * 0 = NaN
- 除法操作符
Infinity / Infinity = NaN
0 / 0 = NaN
- 取模操作符
Infinity % 有限值 = NaN
有限值 % 0 = NaN
Infinity % Infinity = NaN
有限值 % 无限制 = 有限值
0 % 非0 = 0
3.5.5 指数操作符 ** Math.pow()
3.5.6 加性操作符
- 加法操作符
- 任一为 NaN,返回NaN
Infinity + Infinity = Infinity
Infinity + -Infinity = NaN
+0 + +0 = +0
+0 + -0 = +0
- 减法操作符
+0 - -0 = -0
3.5.7 关系操作符
- 涉及
NaN
时返回false
3.5.8 相等操作符
- == / !=
undefined == null
undefined
,null
不进行类型转换NaN
出现时一律不相等- 都为对象时,两者指向同一对象时相等
class Test {}
const a = new Test();
const b = new Test();
console.log(a == b); // output: false
- === / !==
undefined !== null
3.5.9 条件操作符(三元表达式)
3.5.10 赋值操作符
3.5.11 逗号操作符
let num = (1, 2, 3, 4, 5, 0); // num = 0
3.6 语句
3.6.1 if 语句
3.6.2 do-while 语句
3.6.3 while 语句
3.6.4 for 语句
3.6.5 for-in 语句
- 只迭代非符号键属性
- 要迭代的变量是
null
,undefined
时,不会执行循环体
3.6.6 for-of 语句
- 按照可迭代对象的
next()
方法产生值的顺序进行迭代(详见第七章) - ES2018 新增了异步迭代
for-await-of
,详见附录A
3.6.7 标签语句
- 可通过
break
和continue
引用
start: for(const item of array){
console.log(item);
}
3.6.8 break 和 continue 语句
let num = 0;
outermost:
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
if (i == 5 && j == 5) {
break outermost; // 标签让 break 同时也退出了外层循环
// continue outermost; // 标签会让外层循环继续执行,而不是内层
}
num++;
}
}
console.log(num); // output: 55
3.6.9 with 语句
- 将代码作用域设置为特定的对象
- 严格模式不允许使用
- 影响性能,难以调试,不推荐使用
let qs = location.search.substring(1);
let hostName = location.hostname;
let url = location.href;
|
V
with(location) {
let qs = search.substring(1);
let hostName = hostname;
let url = href;
}
3.6.10 switch 语句
- 条件值可以是变量或表达式
- 不会进行类型转换
- 特殊用法:
let num = 25;
switch (true) {
case num < 0:
console.log('num < 0');
break;
case num >= 0 && num <= 10:
console.log('num between 0 and 10');
break;
case num > 10 && num <= 20:
console.log('num between 10 and 20');
break;
default:
console.log('num > 20');
}
3.7 函数
- 详见第十章