ES6 常用特性总结
一、ES6 基本认识
1、什么是 ES6?
ES6 指的是 ECMAScript 6.0,是JavaScript 语言的一个标准。其目标是使JavaScript 可以用来编写复杂的大型的应用程序,成为企业级开发的语言。
2、ES6 与 JavaScript 的区别?
ES6 是 JavaScript 的一个标准,JavaScript 是 ES6 的具体实现。
3、Babel 转码器?
Babel 是一个被广泛使用的 ES6 转码器,其可以将 ES6 代码转为 ES5 代码,从而在现有环境下执行,即使用 ES6 编写代码而无需担心不能运行。
简单的讲就是 一些 浏览器 不支持 ES6 部分语法,可以使用 Babel 转码器将 ES6 语法 转为 ES5 语法,从而被 浏览器 识别。
比如:
ES6 可以使用 箭头函数来 替代 普通函数,通过 Babel 转码器,可以将 箭头函数 转为 普通函数。这样就不需要去担心浏览器是否支持这种语法。
【ES6】 input.map(item => item + 1); 【ES5】 input.map(function (item) { return item + 1; });
二、常用特性
1、let 命令
(1)基本内容
let 命令通常用来声明局部变量。
特性:局部有效、不存在变量提升、暂时性死区、不允许重复声明。
(2)特性一:局部有效。
let 命令类似于 var,用来声明变量,但是 var 是全局有效,let 只在其所在的代码块内生效,出了代码块就获取不到该值。
如下例:(看的可能有点绕,多瞅两遍)
var 定义的是全局变量,对于 for 循环来说,整个循环都只对一个 变量进行操作。看下面例子的第一个循环,在循环体内操作 i 会对循环有影响。由于进行了两次 i++,所以数组有部分值为 空,且只进行了部分循环。
let 定义的是局部变量,对于 for 循环来说,每次循环都是不同的作用域,且 let 只对当前作用域有效。更有趣的是,循环语句内部是一个子作用域(即 在内部定义一个同名的 let 变量,不会影响外部的 let 变量)。看下面例子的第二个循环,每次循环操作,j 都是不同的值,且 循环内部 定义了 同名的 let 变量 j ,由于作用域的问题,其并不会影响循环语句中的 j,所以执行了全部循环。
【举例:】 var a = []; // 用来记录每次循环需要打印的初始值 var b = []; // 用来记录每次循环的初始值 var count = 0; // 用来记录循环的次数 for(var i = 0; i < 10; i++, count++) { a[i] = function() { console.log("当前循环的值 i 为: " + i); }; b[i] = i++; } console.log("当前循环执行次数为: " + count); // 由于 i 为全局变量,每次循环都会进行两次 i++,所以真实循环次数小于 10,所以输出为 5 a[6](); // 由于操作的都是同一变量,所以函数调用的是 最后一次修改的 i 值,所以输出为 10 console.log("每次循环的初始值 i 为: " + b); // 用于只进行了部分循环,所有数组有些并没有赋值,即为空值。所以输出为 0,,2,,4,,6,,8 console.log("循环执行后的 i 值: " + i); // i = 10 时退出循环, i 为全局变量,所以输出 为 10 var c = []; // 用来记录每次循环需要打印的初始值 var d = []; // 用来记录每次循环的初始值 var count = 0; // 用来记录循环的次数 for(let j = 0; j < 10; j++, count++) { let j = 5; c[j] = function() { console.log("当前循环的值 j 为: " + j); }; d[j] = j++; } console.log("当前循环执行次数为: " + count); // 由于 j 为 局部变量,循环内部定义的 let 同名变量 j (子作用域)不会影响 循环语句的 j,真实循环执行 10 次,所以输出为 10 c[5](); // 每次操作都是不同的变量,且执行了 j++ 操作,所以输出为 6 console.log("每次循环的初始值 j 为: " + d); // 由于内部每次都给 d[5] 赋值,其余元素均为空值,所以输出为 ,,,,,5 console.log("循环执行后的 j 值: " + j); // 由于 j 为局部变量,只能存在于 for 循环代码块中, 所以此处会报错,输出 ReferenceError: j is not defined
(3)特性二:不存在变量提升
变量提升指的是 变量可以在声明前使用。
let 不存在变量提升,即声明变量后,才可以使用该变量,不能在声明前使用,否则会报错。
【举例:】 console.log(a); // 不报错,输出 undefined console.log(b); // 报错,输出 ReferenceError: b is not defined var a = 10; let b = 20;
(4)特性三:暂时性死区
暂时性死区指的是 刚开始进入当前作用域,所要使用的变量就已经存在了,但是不可获取,当变量被声明后,才可以获取该变量。
【举例:】 var tmp = 123; console.log(tmp); //不报错,输出 123 if (true) { console.log(tmp); // 报错,ReferenceError: Cannot access 'tmp' before initialization let tmp; } 第一次定义 tmp 为 var 型,所以可以正常输出 123, 进入 if 语句后,由于存在 let 定义的 tmp,系统判定 tmp 为局部变量而非全局变量。 导致 console.log(tmp) 中 tmp 出现在 变量声明前(变量提升失败), 从而报错,此处即为暂时性死区。
(5)特性四:不重复声明
在同一块 let 作用域中,若使用 let 声明一个变量,则不能再重复声明同一个变量。
【举例:】 // 报错,Identifier 'a' has already been declared { let a = 1; var a = 2; } // 不报错,undefined { var a = 1; var a = 2; } // 报错,Identifier 'a' has already been declared { let a = 1; let a = 2; }
2、const 命令
(1)基本内容
const 通常用来声明一个只读的常量,一旦声明,常量的值不能被修改,且声明时必须初始化。
用法类似于 let,局部有效、不存在变量提升、不重复声明。
【举例:(常量值不可被修改)】 const PI = 3.1415926 console.log(PI); // 输出 3.1415926 PI = 3.14 // 报错,输出 Assignment to constant variable. 【举例:(常量声明时需要初始化)】 const a // 报错,输出 Missing initializer in const declaration 【举例:局部有效】 { const PI = 3.1415926; } console.log(PI); // 报错,输出 PI is not defined 【举例:不存在变量提升】 { console.log(PI); // 报错,输出 Cannot access 'PI' before initialization const PI = 3.1415926; } 【举例:不重复声明】 { var PI = 3.14 const PI = 3.1415926; // 报错,输出 SyntaxError: Identifier 'PI' has already been declared }
(2)若 const 声明的是对象,那么 其不变的是 指向对象的地址,对象的值仍可以改变。可以通过object.freeze() 方法冻结对象(即对象不可修改)。
【举例:var,对象可被修改】 { var f = {name : 'tom', age : '12'}; console.log(f.name + ", " + f.age); // tom, 12 f.name = 'jarry'; f.age = 44; console.log(f.name + ", " + f.age); // jarry, 44 f = {name : 'rick', age : '22'}; console.log(f.name + ", " + f.age); // rick, 22 } 【举例:const,对象内容可被修改,但是对象不可被修改】 { const f = {name : 'tom', age : '12'}; console.log(f.name + ", " + f.age); // tom, 12 f.name = 'jarry'; f.age = 44; console.log(f.name + ", " + f.age); // jarry, 44 f = {name : 'rick', age : '22'}; // TypeError: Assignment to constant variable. } 【举例:freeze,对象不可被修改,对象内容不可被修改】 { const f = Object.freeze({name : 'tom', age : '12'}); console.log(f.name + ", " + f.age); // tom, 12 f.name = 'jarry'; f.age = 44; console.log(f.name + ", " + f.age); // tom, 12 f = {name : 'rick', age : '22'}; // TypeError: Assignment to constant variable. }
3、解构表达式
(1)什么是解构?
解构指的是 ES6 支持按照一定的模式,从数组或者对象中提取值,并将提取的值 对变量进行赋值。
(2)数组的解构赋值
一般情况下,只要 = 左右两侧 的模式相同,左边的变量 就会赋值 上 右边对应的值。
【未使用解构表达式给赋值:】 let a = 10; let b = 20; let c = 30; console.log(a, b, c); 【使用 解构表达式赋值:】 let [a, b, c] = [100, 200, 300]; console.log(a, b, c);
解构不成功时,对应的数据为 undefined。
允许解构赋值指定默认值,默认值可以为一个函数(惰性,用到时才调用)。
【嵌套数组赋值:】 let [a, [b, [c, d]]] = [1, [2, [3, 4]]]; console.log(a, b, c, d); // 输出 1 2 3 4 let [head, ...tail] = [1, 2, 3, 4]; console.log(head); // 输出 1 console.log(tail); // 输出 (3) [2, 3, 4] let [x, y, ...z] = [1]; console.log(x); // 输出 1 console.log(y); // 输出 undefined console.log(z); // 输出 [] 【部分解构:(给匹配上的变量赋值)】 let [x, y, z] = [1, 2]; console.log(x); // 输出 1 console.log(y); // 输出 2 console.log(z); // 输出 undefined let [a, [b], c] = [1, [2, 3], 4]; console.log(a); // 输出 1 console.log(b); // 输出 2 console.log(c); // 输出 4 【解构时使用默认值:(即若赋值失败,可以使用默认值)】 function hello() { return "hello"; } let [x=hello(), y=hello(), z=100] = [1, 2, 3]; console.log(x); // 输出 1 console.log(y); // 输出 2 console.log(z); // 输出 3 let [x2=hello(), y2=hello(), z2=100] = [, 2, ]; console.log(x2); // 输出 hello console.log(y2); // 输出 2 console.log(z2); // 输出 100
(3)对象的解构赋值
对象同样可以进行解构。与数组解构不同的是,对象解构时根据属性名进行匹配,不需要注意顺序。
属性名不匹配时,值为 undefined。
可以自定义属性名,使用 : 去指定。
如下例:
let {name, age} 等价于 let {name: name, age: age}
【根据属性名匹配:】 let {name, age} = {name: "tom", age: 22}; console.log(name); // 输出 tom console.log(age); // 输出 22 【属性名匹配不成功,返回 undefined:】 let {name2, age2} = {name: "tom", age: 22}; console.log(name2); // 输出 undefined console.log(age2); // 输出 undefined 【自定义属性名匹配:】 let {name: name3, age: age3} = {name: "tom", age: 22}; console.log(name3); // 输出 tom console.log(age3); // 输出 22
4、字符串拓展
即加强了字符串处理功能。
(1)字符的 unicode 表示
JavaScript 允许使用使用 \uxxxx 的形式表示一个字符,其中 xxxx 表示 unicode 值。但是这种写法只支持 \u0000 ~ \uffff,超出这个限制需要使用 双字节 进行表示。
比如:
\u1F680 会解析成 \u1F68 和 0。若想正常显示,需使用双字节 \uD83D\uDE80 表示。
【举例:】 console.log("\u0061"); // 输出 a console.log("\u00614"); // 输出 a4
ES6 可以使用 大括号将 xxxxx 括起来,从而正确解读。
【举例:】 console.log("\u{0061}"); console.log("\u{1F680}"); console.log("\uD83D\uDE80");
(2)新增方法 -- includes()、startsWith()、endsWith()
JavaScript 中通过 indexOf() 可以确定某个字符串中是否包含另外一个字符串。
ES6 新增三个方法用于判断字符串中是否包含另一个字符串。
includes() 返回布尔值,true 表示当前字符串中存在另一个字符串,false 表示不存在。
startsWith() 返回布尔值,true 表示当前字符串的头部存在另一个字符串,false 表示不存在。
endsWith() 返回布尔值,true 表示当前字符串的尾部存在另一个字符串,false 表示不存在。
【举例:】 let test = "hello world"; console.log(test.includes("wo")); // 输出 true console.log(test.startsWith("he")); // 输出 true console.log(test.endsWith("ld")); // 输出 true console.log(test.includes("helloworld")); // 输出 false
(3)模板字符串(``)
模板字符串是增强版的字符串,使用 反引号(``) 标识字符串,可以作为普通字符串使用,可以定义多行字符串,内部使用 ${} 可以嵌入变量、函数、表达式等并解析。
【举例:】 let [name, age] = ["tom", 32]; function fun() { return "helloworld"; } let test2 = `${name}, age = ${age - 10}, say ${fun()}`; console.log(test2);
5、对象的拓展
拓展对象的用法。
(1)属性简写
ES6 允许在 对象中 直接写变量,此时 属性为 变量名,属性值为 变量值。即 {a} 等价于 {a: a}
【举例:】 let a = "hello"; let b = {a}; console.log(b); // 输出 {a: "hello"} let c = {a: a}; console.log(c); // 输出 {a: "hello"} let d = {g: "hello"}; console.log(d); // 输出 {g: "hello"}
对象中的方法也可以简写。
【举例:】 let [name, age] = ["tom", 32]; let person = { name, age, hello() { console.log(`${name}, ${age}`); }, hello2: function() { console.log(`${name}, ${age}`); } }; person.hello(); person.hello2();
(2)新增方法 -- assign()
Object.assign() 方法用于对象的合并。
其实现的是浅拷贝,即若 源对象中的某个属性值 仍是一个对象,那么目标对象 中拷贝得到的是这个对象的引用,即对源对象中这个对象进行修改,会影响到目标对象。
【格式:】 Object.assign(target, source1, ...source2); 注: target 为目标对象,source1, ...source2 等都是源对象。 该方法是将 源对象 的值 复制 到 目标对象 中。 若出现同名属性,则后者会覆盖前者。 即 target、source1、source2 中存在同名属性,则最后 target 的那个同名属性为 source2 的属性。 【举例:】 let tom = { name: "tom", age: 32, teacher: { chinese: "rose", english: "jack" } }; let jarry = { name: "jarry", age: 33, email: "jarry@163.com" }; let people = Object.assign({}, tom, jarry); console.log(people); tom.teacher.chinese = "rick"; console.log(people);
如何实现深拷贝嘞:
有一种解决办法:(具体原因没有仔细深究)
先将对象转为 json 字符串,再将 字符串转为对象。
将上例 let people = Object.assign({}, tom, jarry); 改为 let people = Object.assign({}, JSON.parse(JSON.stringify(tom)), jarry);
(3)新增对象遍历方法 -- keys()、values()、entries()
Object.keys() 获取对象 key 形成的数组。
Object.values() 获取对象 value 形成的数组。
Object.entries() 获取对象 key - value 形成的 二维数组。
【举例:】 let people = { name: "tom", age: 22 }; console.log(Object.keys(people)); console.log(Object.values(people)); console.log(Object.entries(people));
(4)扩展运算符(...)
用于取出对象、数组的参数并拷贝到当前对象中。
若数据重复,会覆盖,等同于 Object.assign()。
【举例:】 let people = { name: "tom", age: 22 }; let people2 = { name: "jarry", age: 33 }; console.log({people, people2}); console.log({...people, ...people2}); let a = [3, 1, 2]; console.log(a); console.log(...a);
6、函数的拓展
(1)函数参数默认值
可以在定义函数的同时,指定函数的默认值,调用时,若未传递参数,则使用默认值。
【举例:】 function test (x, y) { y = y || "hello"; console.log(x + "======" + y); } test("tom"); // 输出 tom======hello test("tom", "helloworld"); // 输出 tom======helloworld function test2 (x, y = "hello") { console.log(x + "======" + y); } test2("tom"); // 输出 tom======hello test2("tom", "helloworld"); // 输出 tom======helloworld
(2)rest 参数
rest 参数 ,形式为 ...变量名, 用于接收多个参数,保存在数组中。
若有多个参数,则 rest 必须放在最后,否则会报错。
【举例:】 function test (...values) { // for of 每次获取的是数组的值 for (let j of values) { console.log(j); } } test(8, 7, 9); function test2 (...values) { // for in 每次获取的是数组的下标 for (let j in values) { console.log(values[j]); } } test2(8, 7, 9); function test3 (...values, y) { // 报错,SyntaxError: Rest parameter must be last formal parameter for (let j in values) { console.log(values[j]); } }
(3)箭头函数
ES6 支持 使用 箭头 => 定义函数。
【使用箭头函数定义 函数:】 var f = v => v; console.log(f(3)); // 3 // 等价于 var f = function(v){ return v; } console.log(f(3)); // 3
如果没有参数、或者有多个参数,需使用圆括号 () 代替参数部分。
如果方法体(代码块)只有一条语句,则 return 可以省略。
【使用 () 代替参数:】 var f = () => 5; // 等价于 var f = function () { return 5 }; var sum = (num1, num2) => num1 + num2; // 等价于 var sum = function(num1, num2) { return num1 + num2; };
如果方法体(代码块)存在多条语句,则需要使用大括号 {} 括起来,并使用 return 返回值。
【举例:】 var fun = () => { let num = 7; let num2 = num + 3; return num + num2; }; console.log(fun()); // 17 // 等价于 var fun = function() { let num = 7; let num2 = num + 3; return num + num2; }; console.log(fun()); // 17
若返回的是一个对象,则必须在对象外面加上圆括号 (),否则 {} 会被当成代码块被解析。
【举例:】 var getPeopleItem = id => ({ id: id, name: "Temp" }); console.log(getPeopleItem(3)); // {id: 3, name: "Temp"} var getPeopleItem = id => { id: id, name: "Temp" }; console.log(getPeopleItem(3)); // SyntaxError: Unexpected token :
解构与箭头函数可以一起使用:
【举例:】 let people = { name: "tom", age: 22 }; let fun = (param) => { console.log(param.name + "==========" + param.age); }; fun(people); let fun2 = ({name, age}) => { console.log(name + "==========" + age); }; fun2(people);
7、数组常用方法
参考:https://www.cnblogs.com/l-y-h/p/12150578.html
(1)新增方法 -- reduce()
Array.reduce(callback[, initialValue]) 用于给数组的每一个元素执行一个回调函数。
其中 :
initialValue 为第一次执行 callback 时的参数值,可以省略。
callback 有四个参数,callback(previousValue, currentValue, index, array).
previousValue 指上一次执行回调的函数值,或者初始值。
currentValue 指数组当前被处理的元素
index 指当前数组元素的下标
array 指当前的数组
【举例:】 let arr = [4, 6, 5]; let newArr = arr.reduce((previousValue, currentValue, index, array) => { console.log("上一次处理的值为: " + previousValue); console.log("当前处理的值为: " + currentValue); console.log("当前元素下标为: " + index); console.log("当前数组元素为: " + array[index]); return currentValue * 2; }); console.log(newArr);
8、Promise 对象
(1)什么是 Promise ?
Promise 是一个异步编程的一种解决方案。可以理解为一个容器,里面保存着未来才会结束的某个操作(异步操作)的结果,通过 Promise 对象可以获取异步操作的消息。
(2)Promise 特点
特点一:对象的状态不受外界影响。
Promise 有三种状态,Pending (进行中)、Resolved (解决)、Rejected (失败)。只有异步操作的结果能决定 Promise 处于哪种状态,其余操作无法改变该状态,无法中途取消操作。
特点二:状态改变后,不再变化。
状态改变的情况,Pending -> Resolved 、 Pending -> Rejected。
一旦状态改变,其不会再改变。
(3)Promise 缺点
无法取消Promise,一旦新建便会执行,无法中途取消。
如果不设置回调函数,Promise内部的错误不会向外抛出。
处于Pending时,无法判断该操作是刚开始还是即将完成。
(4)如何使用?
需要使用 new 去实例化一个 Promise 对象,参数为 一个函数。
函数的参数为 resolve、reject ,参数为两个函数,由 JavaScript 引擎提供。
resolve 函数是改变状态, Pending -> Resolved ,即异步操作成功后调用,并将异步操作的成功结果作为参数向外传递。
reject 函数也是改变状态, Pending -> Rejected,即异步操作失败后调用,并将异步操作的失败结果作为参数向外传递。
使用 then 方法可以处理 resolve、reject 传递出来的结果。其接受两个回调函数作为参数。第一个回调函数用来处理 resolve 传递的结果,第二个回调函数用来处理 reject 传递的结果。第二个回调函数可选。
一般情况下,使用 catch 方法处理 reject 传递出来的结果。其作用等价于 then 方法中的第二个回调函数。
【格式:】 var promise = new Promise((resolve, reject) => { if(异步操作成功) { resolve(data); } else { reject(error); } }); promise.then((data) => { // 成功的操作 }).catch((error) => { // 失败的操作 });
9、模块化
(1)什么是模块化?
模块化就是把代码进行拆分,方便重复利用。类似于 Java 中的各种 jar 包。
模块化两个主要命令:export、import。
export:用于规定模块的对外接口,即通过 export 可以获取模块的内容。
import:用于导入模块,即通过 import 可以与其他模块建立起联系。
(2)export 命令
通常一个模块就是一个文件,该文件内部的变量、数组、对象 等外界都不能获取。需要使用 export 将其导出。
export 可以导出 基本类型变量、函数、数组、对象等。
【导出方式一:】 export var test = "hello"; 【导出方式二:】 var a = "hello"; var b = [1, 2, 3]; export {a, b}; 导出方式一、导出方式二 对外暴露的接口名 为 变量名、函数名等。 使用 import 要填写正确的接口名,才能正常引用模块,否则会出错。 【导出方式三:(使用 as 自定义接口名)】 var a = "hello"; var b = [1, 2, 3]; export { a as AA, b as BB }; 【导出方式四:(使用 default 可以忽略接口名,此时 import 可以自定义接口名)】 export default { var a = "hello"; var b = [1, 2, 3]; }
(3)import 命令
export 定义了模块对外的接口后,可以使用 import 导入相应的模块功能。
【导入方式一:(使用 {} 可以一次接受多个模块接口名)】 import {a, b} from 'xx/xx.js'; 【导入方式二:(使用 as 取名)】 import {a as AA, b as BB} from 'xx/xx.js'; 【导入方式三:(对于 export default 的模块,可以任意取名)】 import test from 'xx/xx.js';