共计 8107 个字符,预计需要花费 21 分钟才能阅读完成。
ES6 是什么?
JavaScript 的第六版,在 ES5 的基础上增加了许多特性:箭头函数、字符串插值、代理、生成器、结构赋值、块级作用域等等。
一、let 和 const
1. 作用:声明变量
ES6 中明确规定,如果区块中存在 let 和 const 命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域。只要在声明之前就使用这些变量,就会报错(暂时性死区)。
2.let 使用注意:
作用域只局限于当前代码块
使用 let 声明的变量作用域不会被提升
在相同的作用域下不能声明相同的变量
for 循环体现 let 的父子作用域
3.const 使用注意:
const 声明一个只读的常量
只在当前代码块中有效
作用域不会被提升
不能重复声明
声明的常量必须赋值
二、解构赋值
1. 基本用法
let [name, age, sex] = [‘ 李四 ’, 20, ‘ 女 ’];
console.log(name); // 李四
console.log(age); // 20
console.log(sex); // 女
2. 对象的结构赋值
let {
name,
age,
friends,
pet
} = {
name: ‘ 张三 ’,
age: 55,
friends: [‘lulu’, ‘ 王五 ’],
pet: {
name: ‘ 土豆 ’,
age: 5
}
};
console.log(name); // 张三
console.log(age); // 55
console.log(friends); // [“lulu”, “ 王五 ”]
console.log(pet); // {name: “ 土豆 ”, age: 5}
let {
name: str
} = {
name: ‘ 张三 ’
};
console.log(name); //
console.log(str); // 张三
3. 数组的解构赋值
let [arr1, [arr2, arr3, [arr4, arr5]]] = [1, [2, 3, [4, 5]]];
console.log(arr1, arr2, arr3, arr4, arr5);
// 1 2 3 4 5
let [arr1] = [];
console.log(arr1);
// undefined
let [a, , c] = [1, 2, 3];
console.log(a); // 1
console.log(c); // 3
4. 基本类型的解构赋值
let [a, b, c, d, e] = ‘ 我是中国人 ’;
console.log(a); // 我
console.log(b); // 是
console.log(c); // 中
console.log(d); // 国
console.log(e); // 人
三、数据集合 -set
类似于数组,但是成员是唯一的,没有重复。
1. 创建一个集合
let set = new Set([‘ 张三 ’, ‘ 李四 ’, ‘ 王五 ’, ‘ 张三 ’, ‘ 李四 ’]);
console.log(set); //{“ 张三 ”, “ 李四 ”, “ 王五 ”}
console.log(Array.from(set)); // [“ 张三 ”, “ 李四 ”, “ 王五 ”]
2. 一个属性
console.log(set.size); //3
3. 四个方法
//add
console.log(set.add(‘ 刘德华 ’).add(‘LHH’));
//{“ 张三 ”, “ 李四 ”, “ 王五 ”, “ 刘德华 ”, “LHH”}
console.log(set);
//{“ 张三 ”, “ 李四 ”, “ 王五 ”, “ 刘德华 ”, “LHH”}
//delete
console.log(set.delete(‘ 张三 ’)); // true
console.log(set.delete(‘ 李四 ’)); // true
console.log(set);
//has
console.log(set.has(‘ 张三 ’)); // true
console.log(set.has(‘ 张三 1 ’)); // false
//clear
console.log(set.clear()); // undefined
console.log(set); // {}
4.keys values
console.log(set.keys()); // SetIterator {“ 张三 ”, “ 李四 ”, “ 王五 ”}
console.log(set.values()); // SetIterator {“ 张三 ”, “ 李四 ”, “ 王五 ”}
四、数据集合 -map
类似于对象,也是键值对集合,但“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键.
1. 创建一个 Map
let obj1 = {
a: 1
},
obj2 = {
b: 2
};
const map = new Map([
[‘name’, ‘ 张三 ’],
[‘age’, 18],
[‘sex’, ‘ 男 ’],
[obj1, ‘ 今天天气很好 ’],
[obj2, ‘ 适合敲代码 ’],
[
[1, 2], ‘hhh’
]
]);
console.log(map);
console.log(map.size);
2.set 和 get
map.set(‘friends’, [‘ 赵六 ’, ‘ 力气 ’]).set([‘dog’], ‘ 小花 ’);
console.log(map);
console.log(map.get(‘name’));
console.log(map.get(obj1));
3.delete
map.delete(obj1);
console.log(map.delete(‘xxxx’));
console.log(map);
4.has
console.log(map.has(obj1)); // true
console.log(map.has(obj2)); //true
5.clear
map.clear();
console.log(map); // {}
6.keys() values() entries()
console.log(map);
console.log(map.keys());
console.log(map.values());
console.log(map.entries());
7. 遍历
map.forEach(function(value, index) {
console.log(index + ‘:’ + value);
})
8. 注意事项
map.set({}, ‘ 呵呵呵呵呵 ’);
map.set({}, ‘ 哈哈哈哈 ’);
console.log(map);
console.log({} === {});
五、Symbol
1. 定义
let str1 = Symbol();
let str2 = Symbol();
console.log(str1 === str2); // false
console.log(typeof str1); // symbol
console.log(typeof str2); // symbol
2. 描述
let str3 = Symbol(‘name’);
let str4 = Symbol(‘name’);
console.log(str3); // Symbol(name)
console.log(str4); // Symbol(name)
console.log(str3 === str4); // false
3. 对象的属性名
const obj = {};
obj.name = ‘ 张三 ’;
obj.name = ‘ 李四 ’;
obj[Symbol(‘name’)] = ‘ 张三 ’;
obj[Symbol(‘name’)] = ‘ 李四 ’;
let a = Symbol(‘aa’);
let b = Symbol(‘bb’);
obj[a] = ‘ 王五 ’
obj[b] = ‘kk’
console.log(obj);
console.log(Object.getOwnPropertySymbols(obj)); // 获取指定对象的所有 Symbol 属性名
console.log(Reflect.ownKeys(obj)); // 返回所有类型的键名,包括常规键名和 Symbol 键名
注意,Symbol 函数前不能使用 new 命令,因为 Symbol 是一个原始类型的值,不是对象,所以也不能添加属性。
4.Symbol.for() 和 Symbol.keyfor()
var s1 = Symbol.for(‘foo’);
var s2 = Symbol.for(‘foo’);
s1===s2; // true
上面代码中,s1 和 s2 都是 Symbol 的值,但它们都是同样参数的 Symbol.for 方法生成的,所有实际上是同一个值。
Symbol.for()与 Symbol()这两种写法都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,而后者不会。Symbol.for()不会再每次调用时都返回一个新的 Symbol 类型的值,而是会先检查给定的 key 是否已经存在,如果不存在才会新建一个值,比如,如果调用 Symbol.for(‘cat’)30 次,每次都会返回同一个 Symbol 值,但是调用 Symbol(‘cat’)30 次则会返回 30 个不同的 Symbol 值。
Symbol.keyFor()方法返回一个已登记的 Symbol 类型值得 key
var s1 = Symbol.for(“foo”);
Symbol.keyFor(s1); // “foo”
var s2 = Symbol(“foo”);
Symbol.keyFor(s2); // undefined
注:Symbol.for 为 Symbol 值登记的名字是全局环境的,可以在不同的 iframe 或 service worker 中取到同一个值。
iframe = document.createElement(‘iframe’);
iframe.src = String(window.location);
document.body.appendchild(iframe);
iframe.contentWindow.Symbol.for(‘foo’) === Symbol.for(‘foo’); // true
六、class 的基本运用
1. 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype = {
constructor: Person,
print(){
console.log(‘ 我叫 ’ + this.name + ‘, 今年 ’ + this.age + ‘ 岁 ’);
}
};
// 或
//Person.prototype.print = function() {
// console.log(‘ 我叫 ’ + this.name + ‘, 今年 ’ + this.age + ‘ 岁 ’);
//}
let person = new Person(‘ 张三 ’, 19);
console.log(person);
person.print();
2. 通过 class 面向对象
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
print() {
console.log(‘ 我叫 ’ + this.name + ‘, 今年 ’ + this.age + ‘ 岁 ’);
}
}
let person = new Person(‘ 张三 ’, 19);
console.log(person);
person.print();
3.class 实现继承
class Animal {
// 构造函数
constructor(props) {
this.name = props.name || ‘ 未知 ’;
}
eat() {
alert(this.name + “ 在吃东西 …”);
}
}
//class 继承
class Bird extends Animal {
// 构造函数
constructor(props) {
// 调用实现父类的构造函数
super(props);
this.type = props.type || “ 未知 ”;
}
fly() {
alert(this.name + “ 在飞 …”);
}
}
var myBird = new Bird({
name: ‘ 鹦鹉 ’
})
myBird.eat() // 鹦鹉在吃东西 …
myBird.fly() // 鹦鹉在飞 …
七、内置对象扩展
1. 模板字符串
let str = ‘ 适合敲代码!’;
let className = ‘test’;
let html = `<html>
<head></head>
<body>
<p> 今天的天气很好!</p>
<div class=”${className}”>${str}</div>
</body>
</html>`;
console.log(html);
2. 数组的扩展
Array.from
// 在 body 中写了几个 li 节点
let allLis = document.querySelectorAll(‘li’);
console.log(allLis); // NodeList(6) [li, li, li, li, li, li]
console.log(Array.isArray(allLis)); // false
console.log(Array.from(allLis)); // [li, li, li, li, li, li]
console.log(Array.isArray(Array.from(allLis))); // true
Array.of
console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]
console.log(Array.of(‘ 张三 ’, ‘ 李四 ’, ‘ 王五 ’)); // [“ 张三 ”, “ 李四 ”, “ 王五 ”]
3. 对象的扩展
key 和 value 是一样的, 写一个就够了
let name = ‘ 张三 ’;
let age = 18;
let person = {
name,
age
};
console.log(person); // {name: “ 张三 ”, age: 18}
Object.assign()
let obj1 = {
name: ‘ 张三 ’
};
let obj2 = {
age: 18
};
let obj3 = {
sex: ‘ 男 ’
};
let obj4 = {
friends: ‘ 李四 ’
};
let obj = {};
Object.assign(obj, obj1, obj2, obj3, obj4);
console.log(Object.assign(obj, obj1, obj2, obj3, obj4)); // {name: “ 张三 ”, age: 18, sex: “ 男 ”, friends: “ 李四 ”}
console.log(obj); // {name: “ 张三 ”, age: 18, sex: “ 男 ”, friends: “ 李四 ”}
延展操作符
let str = ‘ 今天的天气很好!’;
let strArr = […str];
console.log(strArr); // [“ 今 ”, “ 天 ”, “ 的 ”, “ 天 ”, “ 气 ”, “ 很 ”, “ 好 ”, “!”]
let student = {
name: ‘ 张三 ’,
age: 18,
sex: ‘ 男 ’
}
< Person {…student}/>
let myArr = [1, 2, 10, ‘ 张三 ’, 20, 2, 1];
console.log(new Set(myArr)); // {1, 2, 10, “ 张三 ”, 20}
console.log([…new Set(myArr)]); // [1, 2, 10, “ 张三 ”, 20]
八、函数的扩展
1. 形参设置默认值
function sum(num1 = 20, num2 = 10) {
console.log(num1 + num2);
}
/* function sum(num1, num2) {
num1 = num1 || 10;
num2 = num2 || 10;
console.log(num1 + num2);
} */
sum(10, 30); // 40
sum(); // 30
2. 参数形式 延展操作符
function sum(name, sex, …nums) {
let result = 0;
console.log(name);
console.log(sex);
for (let value of nums) {
result += value;
}
return result;
}
/* function sum() {
let result = 0;
for(let value of arguments){
result += value;
}
return result;
} */
console.log(sum(‘ 张男 ’, ‘ 男 ’, 10, 20, 30, 50));
// 张男
// 男
// 110
3. 箭头函数 () => {}
let sum = (num1, num2)=>{return num1 + num2;};
console.log(sum(100, 300)); // 400
let nameArr = [‘ 张三 ’, ‘ 李四 ’, ‘ 王五 ’];
nameArr.forEach((value, index) => {
console.log(index + ‘:’ + value);
});
// 0: 张三
// 1: 李四
// 2: 王五
this 的指向不同
function demo() {
setTimeout(function() {
console.log(this);
}, 500);
setTimeout(() => {
console.log(this);
}, 1000);
}
let obj = {
a: 1
};
demo.call(obj);
// Window
// {a: 1}
箭头函数的几个注意事项:
函数体内的 this 对象就是定义时所在的对象,而不是使用时所在的对象;
不可以当作构造函数。也就是不可以使用 new 命令,否则报错;
不可以使用 arguments 对象,该对象在函数体内不存在;可以 rest 参数代替(… 参数);
不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数;
九、Iterator 遍历器和 for..of 循环
Iterator 作用:一是为各种数据结构提供统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创建了一种新的遍历命令——for…of 循环,Iterator 接口主要供 for…of 消费
模拟 next 方法返回值得例子:
var it = makeIterator([‘a’, ‘b’]);
console.log(it.next()); // {value: “a”, done: false}
console.log(it.next()); // {value: “b”, done: false}
console.log(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
}
}
}
}
具备 Iterator 接口的数据结构如下:
Array Map Set String TypedArray 函数的 arguments 对象 NodeList 对象
下面例子是数组的 Symbol.iterator 属性:
let arr = [‘a’, ‘b’, ‘c’];
let iter = arr[Symbol.iterator]();
console.log(iter.next()); // {value: “a”, done: false}
console.log(iter.next()); // {value: “b”, done: false}
console.log(iter.next()); // {value: “c”, done: false}
console.log(iter.next()); // {value: undefined, done: true}
注意:对象(Object)之所以没有默认部署 Iterator 接口,是因为对象属性的遍历先后顺序是不确定的。
for…of 循环
一个数据结构只要部署了 Symbol.iterator 属性,就被视为具有 Iterator 接口,就可以用 for…of 循环它的成员。注意:有些数据结构是在现有数据结构的基础上计算生成的,比如 ES6 的数组、Set、Map 都部署了一下三个方法,调用后都返回遍历器对象
entries()返回一个遍历器对象,用于遍历 [键名,键值] 组成的数组。对于数组,键名就是索引值;对于 Set,键名与键值相同。Map 结构的 Iterator 接口默认就是调用 entries 方法
keys()返回一个遍历器对象,用于遍历所有的键名
values()返回一个遍历器对象,用于遍历所有的键值