(一)const和let
1.let
a.暂时性死区(“暂时性死区”也意味着typeof
不再是一个百分之百安全的操作),且let
不允许在相同作用域内,重复声明同一个变量
2. const
a.const
声明一个只读的常量。一旦声明,常量的值就不能改变,且const
一旦声明变量,就必须立即初始化,不能留到以后赋值。
b.和let一样存在暂时性死区
关于const和let ES6增加了两种定义变量的方法
(关于简单类型的数据和复合类型的数据指针问题以及延伸的对象深拷贝--补)
(二)解构赋值
1.数组的解构赋值(主要)
2.对象的解构赋值(主要)
3.字符串的解构赋值
4.数值和布尔值的解构赋值
5.数值和布尔值的解构赋值
列举了一些主要常用的地方可以在自己的代码中试验一下
(三)字符串的扩展
1.ES6 为字符串添加了遍历器接口
for (let codePoint of 'foo') {
console.log(codePoint)
}
2.模板字符串
a.用反引号(`)标识
b.模板字符串中嵌入变量,需要将变量名写在${}
之中
JQ
$('#result').append(
'There are <b>' + basket.count + '</b> ' +
'items in your basket, ' +
'<em>' + basket.onSale +
'</em> are on sale!'
);
ES6
$('#result').append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`);
可以把之前jq里的动态添加dom变为字符串模版
(四)函数的扩展
1.ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面
function log(x, y) {
y = y || 'World';
console.log(x, y);
}
ES6
function log(x, y = 'World') {
console.log(x, y);
}
2.ES6 引入 rest 参数(形式为...变量名
),用于获取函数的多余参数 ,rest 参数之后不能再有其他参数(即只能是最后一个参数)要注意的是
函数的length
属性,不包括 rest 参数
(function(a) {}).length // 1
(function(...a) {}).length // 0
(function(a, ...b) {}).length // 1
3. 箭头函数(重要)
a.如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分
b.如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来
c.如果箭头函数的代码块部分只有一条return语句,return可以省略
d.上面四点中,第一点尤其值得注意。this
对象的指向是可变的,但是在箭头函数中,它是固定的。箭头函数里面根本没有自己的this
,而是引用外层的this
。
(五)数组的扩展
1.扩展运算符
a.扩展运算符(spread)是三个点(...
)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。可以替代函数的 apply 方法
Math.max和push都可以很简单的使用扩展运算符
// ES5 的写法
Math.max.apply(null, [14, 3, 77])
// ES6 的写法
Math.max(...[14, 3, 77])
// ES5的 写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);
// ES6 的写法
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);
2.复制数组
下面代码中,a2
并不是a1
的克隆,而是指向同一份数据的另一个指针。修改a2
,会直接导致a1
的变化。
const a1 = [1, 2];
const a2 = a1;
a2[0] = 2;
a1 // [2, 2]
ES5用了下面的方法来进行,concat()和
const a1 = [1, 2];
const a2 = a1.concat();
a2[0] = 2;
a1 // [1, 2]
ES6用扩展运算符
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;
(浅拷贝和深拷贝还不是很清楚)
3.如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错
4.Array.from可以将一些数据结构转为数组
还可以接受第二个参数,作用类似于数组的map
方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);
5. includes(和indexOf功能相仿
)
indexOf
方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1
,表达起来不够直观。二是,它内部使用严格相等运算符(===
)进行判断,这会导致对NaN
的误判。
(六)对象的扩展
1. 属性的简洁表示法
属性简写:
function f(x, y) {
return {x, y};
}
// 等同于
function f(x, y) {
return {x: x, y: y};
}
f(1, 2) // Object {x: 1, y: 2}
方法简写:
const o = {
method() {
return "Hello!";
}
};
// 等同于
const o = {
method: function() {
return "Hello!";
}
};
2.name
函数的name
属性,返回函数名。对象方法也是函数,因此也有name
属性
3.属性的可枚举性和遍历
对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor
方法可以获取该属性的描述对象。描述对象的enumerable
属性,称为“可枚举性”,如果该属性为false
,就表示某些操作会忽略当前属性。
对象原型的toString
方法,以及数组的length
属性,就通过“可枚举性”,从而避免被for...in
遍历到。
Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumerable
// false
Object.getOwnPropertyDescriptor([], 'length').enumerable
// false
目前,有四个操作会忽略enumerable
为false
的属性。
for...in
循环:只遍历对象自身的和继承的可枚举的属性。Object.keys()
:返回对象自身的所有可枚举的属性的键名。JSON.stringify()
:只串行化对象自身的可枚举的属性。Object.assign()
: 忽略enumerable
为false
的属性,只拷贝对象自身的可枚举的属性。
4.字符串新增方法
- includes():返回布尔值,表示是否找到了参数字符串。
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
- repeat():
repeat
方法返回一个新字符串,表示将原字符串重复n
次
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""
如果某个字符串不够指定长度,会在头部或尾部补全。padStart()
用于头部补全,padEnd()
用于尾部补全。
adStart()
的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串
'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"
另一个用途是提示字符串格式
'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
(七)symbol对象
ES6 引入了一种新的原始数据类型Symbol
,表示独一无二的值。对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突
let s = Symbol();
typeof s
// "symbol"
有时,我们希望重新使用同一个 Symbol 值,Symbol.for
方法可以做到这一点
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
s1 === s2 // true
Symbol.for()
与Symbol()
这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()
不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key
是否已经存在,如果不存在才会新建一个值
(八)Set 和 Map 数据结构
1.Set(它类似于数组,但是成员的值都是唯一的,没有重复的值。)
Set
本身是一个构造函数,用来生成 Set 数据结构
const s = new Set();
// 去除数组的重复成员
[...new Set(array)]
//去除字符串里面的重复字符。
[...new Set('ababbc')].join('')
// "abc"
Array.from
方法可以将 Set 结构转为数组。(数组去重)
function dedupe(array) {
return Array.from(new Set(array));
}
dedupe([1, 1, 2, 3]) // [1, 2, 3]
2.Map(是一种数据结构并非数组的map方法)
1.各种方法
size()(返回 Map 结构的成员总数)
set()(设置键名key
对应的键值为value
,然后返回整个 Map 结构。如果key
已经有值,则键值会被更新,否则就新生成该键,set
方法返回的是当前的Map
对象,因此可以采用链式写法)
get()(get
方法读取key
对应的键值,如果找不到key
,返回undefined
)
has()(返回一个布尔值,表示某个键是否在当前 Map 对象之中。)
delete()(delete
方法删除某个键,返回true
。如果删除失败,返回false
。)
clear()(清除所有成员,没有返回值)
2.Map 结构转为数组结构,比较快速的方法是使用扩展运算符(...
)
(九)promise对象
(十)Iterator
1.Iterator
Iterator 的遍历过程是这样的。
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next
方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next
方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next
方法,直到它指向数据结构的结束位置。
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
};
}
2.原生具备 Iterator 接口的数据结构
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
3.调用 Iterator 接口的场合(有一些场合会默认调用 Iterator 接口(即Symbol.iterator
方法))
1)解构赋值
2)扩展运算符
3)yield*
4)其他场合
- for...of
- Array.from()
- Map(), Set(), WeakMap(), WeakSet()(比如
new Map([['a',1],['b',2]])
) - Promise.all()
- Promise.race()
类似数组的对象包括好几类。for...of
循环可用于字符串、DOM NodeList 对象、arguments
对象。
4.对于普通的对象,for...of
结构不能直接使用,会报错,必须部署了 Iterator 接口后才能使用。但是,这样情况下,for...in
循环依然可以用来遍历键名
let es6 = {
edition: 6,
committee: "TC39",
standard: "ECMA-262"
};
for (let e in es6) {
console.log(e);
}
// edition
// committee
// standard
for (let e of es6) {
console.log(e);
}
// TypeError: es6[Symbol.iterator] is not a function
for...in
循环主要是为遍历对象而设计的,不适用于遍历数组
相对于forEach方法和for循环方法,for..of循环作为数组的遍历有显著的优点
- 有着同
for...in
一样的简洁语法,但是没有for...in
那些缺点。 - 不同于
forEach
方法,它可以与break
、continue
和return
配合使用。 - 提供了遍历所有数据结构的统一操作接口。
for (var n of fibonacci) {
if (n > 1000)
break;
console.log(n);
}
(十一)Generator 函数
1.yield 表达式
(1)遇到yield
表达式,就暂停执行后面的操作,并将紧跟在yield
后面的那个表达式的值,作为返回的对象的value
属性值。
(2)下一次调用next
方法时,再继续往下执行,直到遇到下一个yield
表达式。
(3)如果没有再遇到新的yield
表达式,就一直运行到函数结束,直到return
语句为止,并将return
语句后面的表达式的值,作为返回的对象的value
属性值。
(4)如果该函数没有return
语句,则返回的对象的value
属性值为undefined
。
2.
function* f() {
console.log('执行了!')
}
var generator = f();
setTimeout(function () {
generator.next()
}, 2000);
上面代码中,函数f
如果是普通函数,在为变量generator
赋值时就会执行。但是,函数f
是一个 Generator 函数,就变成只有调用next
方法时,函数f
才会执行。f变为了暂缓执行函数