ES6基础部分

【阅读笔记】本文内容均来自:ECMAScript 6 入门

变量

let

  • 不存在变量提升
    • 暂时性死区
  • 不允许重复声明
  • 与顶层对象的属性脱钩
    • let a = 1 不会挂到 window

块级作用域

  • ES5 只有全局作用域和函数作用域
  • ES6 允许块级作用域的任意嵌套
  • 块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了
  • ES6 明确允许在块级作用域之中声明函数,但在浏览器的ES6实现中仍把块级作用域的函数声明当作var处理,建议写成函数表达式。

const

  • 只在声明所在的块级作用域内有效,特性同let
  • const 声明变量时必须复制,以后不能赋值
    • const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。
    • 对于基础数据类型,const 指向的就是其值;而对于对象类型,const 指向其引用地址,对象的地址不可改变,但对象本身的属性值仍可以改。
    • 如果必须将对象冻结(地址和值都不能改),应该使用Object.freeze

解构赋值

数组解构形式

1
2
3
4
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
  • 只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。
1
2
3
4
5
6
7
8
9
10
11
function* fibs() {
let a = 0;
let b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
let [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5
  • 解构赋值允许指定默认值,默认值生效的条件是:数组成员的值严格等于undefined
1
2
3
4
5
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null

对象解构形式

1
2
3
let { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined
  • 默认值生效的条件是:对象的属性值严格等于undefined
1
2
3
4
5
var {x = 3} = {x: undefined};
x // 3
var {x = 3} = {x: null};
x // null
  • 如果要将一个已经声明的变量用于解构赋值,必须非常小心。因为 JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。

  • 解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefinednull无法转为对象,所以对它们进行解构赋值,都会报错。

  • 常用场景:let { log, sin, cos } = Math;

函数参数的解构赋值

1
2
3
4
5
[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]
[1, undefined, 3].map((x = 'yes') => x);
// [ 1, 'yes', 3 ]

变量解构赋值的用途

  • 提取对象数据(或用于遍历数据结构)
  • 函数参数的映射及默认值
  • 输入模块的指定方法

字符串的扩展

  • ES6为字符串添加了Iterator接口,使得字符串可以被for...of循环遍历。

  • 新增:includes(), startsWith(), endsWith(), repeat()

模板字符串

  • 模板字符串中嵌入变量,需要将变量名写在${}之中。
  • 大括号内部可以放入任意的JavaScript表达式,可以进行运算,以及引用对象属性。
  • 模板字符串之中还能调用函数

高级用法

1
2
3
4
5
6
7
8
const tmplFn = addrs => `
<table>
${addrs.map(addr => `
<tr><td>${addr.first}</td></tr>
<tr><td>${addr.last}</td></tr>
`).join('')}
</table>
`;

模板编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var template = `
<ul>
<% for(var i=0; i < data.supplies.length; i++) { %>
<li><%= data.supplies[i] %></li>
<% } %>
</ul>
`;
function compile(template){
var evalExpr = /<%=(.+?)%>/g;
var expr = /<%([\s\S]+?)%>/g;
template = template
.replace(evalExpr, '`); \n echo( $1 ); \n echo(`')
.replace(expr, '`); \n $1 \n echo(`');
template = 'echo(`' + template + '`);';
var script =
`(function parse(data){
var output = "";
function echo(html){
output += html;
}
${ template }
return output;
})`;
return script;
}

正则的扩展

以下非 es6 新增

正则表达式中,点.是一个特殊字符,代表任意的单个字符,但是行终止符(line terminator character)除外。以下四个字符属于”行终止符“:

  • U+000A 换行符\n
  • U+000D 回车符\r
  • U+2028 行分隔符(line separator)
  • U+2029 段分隔符(paragraph separator)

但是,很多时候我们希望匹配的是任意单个字符,有一种变通的写法[^]

数值的扩展

新增

  • Number.isFinite()
  • Number.isNaN()
  • Number.parseInt()
  • Number.parseFloat()
  • Number.isInteger()
  • Number.EPSILON 2.220446049250313e-16
  • Number.isSafeInteger() -2^53 ~ 2^53

函数的扩展

函数参数的默认值

  • 参数变量是默认声明的,所以不能用letconst再次声明。
  • 参数默认值不是传值的,而是每次都重新计算默认值表达式的值。
    • 也就是说,参数默认值是惰性求值的。
1
2
3
4
5
6
7
8
9
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo() // 100
x = 100;
foo() // 101

rest 参数

rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

1
2
3
4
5
6
7
// arguments变量的写法
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
// rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();

注意

  • rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
  • 函数的length属性,不包括 rest 参数。

箭头函数

  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。(但是箭头可以作为 async 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function foo() {
return () => {
return () => {
return () => {
console.log('id:', this.id);
};
};
};
}
var f = foo.call({id: 1});
var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1

注意:由于箭头函数没有自己的this,所以当然也就不能用call()apply()bind()这些方法去改变this的指向。