ECMAScript 6 新特性(一)
ECMAScript 6 新特性(一)
1. let、const
1.1 let 关键字
let 关键字用来声明变量,使用 let 声明的变量有几个特点
- 不允许重复声明
- 块级作用域
- 不存在变量提升
- 不影响作用域链
1.2 const 关键字
const 关键字用来声明常量,const 声明有以下特点
- 声明必须赋初始值
- 标识符一般为大写
- 值不允许修改
- 块级作用域
- 对数组和对象的元素修改,不算做对常量的修改
使用技巧:声明变量使用 let、声明对象类型使用 const。
2. 解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
-
数组的解构赋值
const F4 = ["刘德华", "张学友", "郭富城", "黎明"]; const [B1, B2, B3, B4] = F4; console.log(B1); /* 刘德华 */ console.log(B2); /* 张学友 */ console.log(B3); /* 郭富城 */ console.log(B4); /* 黎明 */
-
对象的解构赋值
const obj = { name: "小周", age: 18, run() { console.log("小周正在奔跑..."); }, }; const { name, age, run } = obj; console.log(name); /* 小周 */ console.log(age); /* 18 */ run();
频繁使用对象方法、数组元素,就可以使用解构赋值形式
3. 模板字符串
模板字符串(template string)是增强版的字符串,用反引号(``)标识
特点:
- 字符串中可以出现换行符
- 可以使用
${xxx}
形式输出变量
// 模板字符串的声明
let str = `Hello`;
console.log(str, typeof str); /* Hello string */
// 1. 字符串中可以出现换行符
let str1 = `<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>`;
// 2. 字符串中可以出现变量
let name = "John";
let str2 = `Hello ${name}`;
console.log(str2); /* Hello John */
// 3. 字符串中可以出现表达式
let age = 25;
let str3 = `I am ${age + 1} years old`;
console.log(str3); /* I am 26 years old */
// 4. 字符串中可以出现函数调用
let str4 = `The result is ${add(1, 2)}`;
function add(a, b) {
return a + b;
}
console.log(str4); /* The result is 3 */
4. 箭头函数
ES6 允许使用「箭头」(=>
)定义函数。
// 声明一个基本的函数
let fn1 = function (a, b) {
return a + b;
};
fn1(1, 2); // 调用
// 声明一个箭头函数
let fn2 = (a, b) => {
return a + b;
};
fn2(1, 2); // 调用
箭头函数的注意点:
-
如果形参只有一个,则小括号可以省略(不建议)
let fn = a => { ... };
-
函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
let fn = (a, b) => a + b; // qiu'h
-
箭头函数没有自己的 this(静态 this),它会捕获其所在作用域的 this 值 —— this 的静态特性
function getName1() { console.log(this.name); } let getName2 = () => { console.log(this.name); }; window.name = "学院"; let obj = { name: "xueyuan" }; // 正常调用 getName1(); // 学院 getName2(); // 学院 // call 方法调用 getName1.call(obj); // xueyuan getName2.call(obj); // 学院,this 指向的是箭头函数所在作用域(window)的 this 值
-
不能作为构造函数,不能使用 new 关键字实例化对象
let Person = (name, age) => { this.name = name; this.age = age; }; // let me = new Person("张三", 19); // 报错,Person 不能作为构造函数 // console.log(me);
-
不能使用 arguments 变量
let fn3 = () => { console.log(arguments); // 报错,arguments 变量在箭头函数中不存在 } fn3(1, 2, 3);
5. rest 参数
ES6 引入 rest 参数,用于获取函数的实参,代替 arguments
// ES5 获取实参的方式
function fn1() {
console.log(arguments);
}
fn1("张三", "李四", "王五");
// ES6 使用 rest 参数
function fn2(...args) {
console.log(args);
}
fn2("张三", "李四", "王五");
注意:无论函数的参数有多少个,只要使用了 rest 参数,则 rest 参数必须是最后一个形参。
function fn(a, b, ...., ...args) { ... }
rest 参数非常适合不定个数参数函数的场景
6. spread 扩展运算符
扩展运算符(spread)也是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包
const arr = [1, 2, 3];
function fn() {
console.log(arguments);
}
fn(...arr);
6.1 扩展运算符的应用
-
数组的合并
const arr1 = [1, 2]; const arr2 = [3, 4]; const arr3 = [...arr1, ...arr2]; // [1, 2, 3, 4]
-
数组的克隆
const arr4 = [...arr3]; console.log(arr4); // [1, 2, 3, 4]
-
将伪数组转为数组
const divs = document.querySelectorAll("div"); const arr5 = [...divs]; console.log(arr5); let str = "Hello"; let arr6 = [...str]; console.log(arr6); // ['H', 'e', 'l', 'l', 'o']
7. Symbol
7.1 Symbol 基本使用
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
Symbol 特点
- Symbol 的值是唯一的,用来解决命名冲突的问题
- Symbol 值不能与其他数据进行运算
- Symbol 定义 的 对象属 性 不能 使 用 for…in 循 环遍 历 ,但 是可 以 使 用
Reflect.ownKeys
来获取对象的所有键名
创建 Symbol
-
基本创建(此时 Symbol 称为对象)
let s = Symbol(); console.log(s); // Symbol() console.log(typeof s); // symbol let s1 = Symbol("张三"); let s2 = Symbol("张三"); console.log(s1 == s2); // false console.log(s1 === s2); // false
-
使用
Symbol.for
创建(此时 Symbol 称为函数)—— 函数对象let s3 = Symbol.for('张三'); let s4 = Symbol.for('张三'); console.log(typeof s3); // symbol console.log(s3 == s4); // true console.log(s3 === s4); // true
JS 的七种数据类型:
undefined
、string
、symbol
、object
、null
、number
、boolean
遇到唯一性的场景时要想到 Symbol,作用:给对象添加属性和方法。
7.2 向对象中添加方法
-
在不清楚对象的内容、对象较为复杂时,可以使用Symbol向对象添加属性
let obj = { up: function () { console.log("上升"); }, down: function () { console.log("下降"); }, ... }; // 声明一个对象 let methods = { up: Symbol(), down: Symbol() }; // 添加方法 obj[methods.up] = function () { console.log("up"); }; obj[methods.down] = function () { console.log("down"); };
-
在对象内容较为简单时,可以使用Symbol作为属性名,来创建对象属性
let youxi = { [Symbol("say")]: function () { console.log("发言"); }, [Symbol("zibao")]: function () { console.log("自爆"); }, ... };
7.3 内置值
除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。
-
Symbol.hasInstance
:当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会先调用这个方法。
class Person { static [Symbol.hasInstance](param) { console.log(param); // { name: "张三" } console.log("检测类型对象"); } } let o = { name: "张三" }; console.log(o instanceof Person); // false
-
Symbol.isConcatSpreadable
:对象的属性等于的是一个布尔值,表示该对象用于
Array.prototype.concat()
时,是否可以展开。const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; arr2[Symbol.isConcatSpreadable] = false; console.log(arr1.concat(arr2)); // [1, 2, 3, [4, 5, 6]] // arr2[Symbol.isConcatSpreadable] = true; // console.log(arr1.concat(arr2)); // [1, 2, 3, 4, 5, 6]
-
Symbol.species
:该属性用于在继承的时候指定一个类的类别。
-
Symbol.match/replace/search/split
:这四个方法允许我们以对象的方式自定义 String 的 match、replace、search、split 方法,当该对象被
str.match/replace/search/split(myObject)
方法调用时,会返回该方法的返回值。 -
Symbol.iterator
:对象进行 for…of 循环时,会调用该方法,返回该对象的默认遍历器。
-
Symbol.toPrimitive
:该对象被转为原始类型的值时,会调该方法,返回该对象对应的原始类型值。
-
Symbol.toStringTag
:在该对象上面调用 toString 方法时,返回该方法的返回值
-
Symbol.unscopables
:该对象指定了使用 with 关键字时,哪些属性会被 with 环境排除。
8. 迭代器
遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
-
ES6 创造了一种新的遍历命令 for…of 循环,Iterator 接口主要供 for…of 消费
for…of 与 for…in 的区别:
for (let v of arr) { console.log(v); // 输出数组元素 }
for (let v in arr) { console.log(v); // 输出索引 }
-
原生具备 iterator 接口的数据(可用 for of 遍历)
- Array
- Arguments
- Set
- Map
- String
- TypedArray
- NodeList
-
工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
- 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
- 每调用 next 方法返回一个包含 value 和 done 属性的对象
const xiyou = ["唐僧", "孙悟空", "猪八戒", "沙和尚"];
for (let v of xiyou) {
console.log(v);
}
// console.log(xiyou); // 在原型对象上可以看到 Symbol(Symbol.iterator)
let iterator = xiyou[Symbol.iterator](); // 获取数组的迭代器
// 调用 next 方法
console.log(iterator.next()); // { value: "唐僧", done: false }
console.log(iterator.next()); // { value: "孙悟空", done: false }
console.log(iterator.next()); // { value: "猪八戒", done: false }
console.log(iterator.next()); // { value: "沙和尚", done: false }
console.log(iterator.next()); // { value: undefined, done: true }
注:需要自定义遍历数据的时候,要想到迭代器。
8.1 自定义迭代器
// 声明一个对象
let banji = {
name: "终极一班",
stus: ["小明", "小红", "小刚", "小李"],
[Symbol.iterator]() {
let index = 0; // 索引变量
return {
next: () => {
if (index < this.stus.length) {
let result = { value: this.stus[index], done: false };
index++;
return result;
} else {
return { value: undefined, done: true };
}
},
};
},
};
// 遍历对象
for (let key of banji) {
console.log(key);
}