摘要:学习 ES6 笔记
0.兼容性
现在的浏览器对ES6的兼容性都不太好,要想正常的使用就必须先处理下浏览器的兼容性。
首先要使用npm安装babel,安装5.x版本的 babel-core 模块获取(这样也只是部分支持,但从 Babel 6.0开始,不再直接提供浏览器版本,而是要用构建工具构建出来)
1 | npm install babel-core@5 |
然后找到 babel-core 下的 browser.js 或 browser.min.js 和 browser-polyfill.js 或 browser-polyfill.min.js,将两者引入。前者提供将 es6 转为浏览器能识别的语法,后者提供修补浏览器的功能。
最后将<script>
标签的type改为”text/babel”
1 | <script src="node_modules/babel-core/browser.min.js"></script> |
使用构建工具构建的话请看Babel 入门教程
1.声明变量 let
let用于声明块级变量,只在自己的块中有效。
var的不足:
1.for循环中的变量i会影响各个块中i的值
2.变量提升
注意:
1.同一个块级作用域内不能重复声明同一个变量
2.函数内不能使用 let 重新声明函数的参数
1 | function say(argument) { |
3.let适合for循环,只在块级域作用,无变量提升
2.声明常量 const
特点:
1.不可修改
2.只在块级域作用
3.无变量提升,先声明,后使用
4.不可重复声明同一个变量
5.声明必须赋值
1 | const name; // 报错,必须赋值 |
注意:如果常量是一个对象,使用传址赋值,保存的是数据地址
1 | const Person = { |
房子随便整修,但门牌号不可改变
3.字符串 String
模板字符串,用于字符串拼接,用反引号包裹,变量在${}
中
1 | let name = "小王"; |
注意:
1.可定义多行字符串,可直接换行,但原样输出(换行、空格和缩进都会输出)
2.${}
中可以放任意的js表达式、对象的属性、函数的调用
标签模板,函数调用的一种特殊形式,模板字符串相当于它的参数。标签模板=标签函数+模板字符串
1 | let name = "小王"; |
由此可以看出:第一个参数是除去变量之外的内容组成的数组,并且该数组是由变量切割、按顺序组成的;后面的参数则是一次对应模板字符串中的变量。
repeat:将字符串重复几次,返回一个新的字符串,不影响目标字符串。
1 | let strA = "hello"; |
includes:判断字符串中是否包含指定的子字符串,返回布尔值,true 表示有,false 表示无。可以有两个参数,第一个为子字符串,第二个选填,表示开始搜索的位置(索引)
1 | let strC = "helloworld"; |
startsWith:判断指定的子字符串是否出现在字符串的开头位置,返回布尔值,true 表示是,false 表示否。可以有两个参数,第一个为子字符串,第二个选填,表示开始搜索的位置(索引)
1 | let strC = "helloworld"; |
endsWith:判断指定的子字符串是否出现在字符串的末端,返回布尔值,true 表示是,false 表示否。可以有两个参数,第一个为子字符串,第二个选填,表示针对前几个字符
1 | let strC = "helloworld"; |
codePointAt:能正确识别4个字节的字符(Unicode 码点大于0xFFFF的字符),并正确返回码点的十进制数,charAt 只能识别2个字节的字符
String.fromCodePoint:能根据码点返回对应的字符,可以识别 Unicode 码点大于0xFFFF的字符,如果有多个参数传入,则它们会被合并成一个字符串返回。String.fromCharCode 方法只识别2个字节的码点
1 | let str4 = "𠮷"; |
String.raw:不加工,原样输出
1 | let strC = `helloworld\nhelloworld`; |
4.Number 对象
Number.isNaN:判断是否为非数值,只针对数值型有效,非数值型一律返回 false。也就是说:返回 false 的可能是数值或非 Number 类型的值。传统的 isNAN 会先试着将参数转化成数值型在判断,而 Number.isNaN 则不转化直接判断。
1 | Number.isNaN(1); // false |
Number.isFinite:判断是否为非无穷,即有穷。也是只针对数值型有效,非数值型一律 false。
1 | Number.isFinite(1); // true |
Number.parseInt:返回一个整数,如果参数是字符串,会先转化解析字符串再返回一个整数
1 | Number.parseInt(12.3); // 12 |
Number.parseFloat:与 Number.parseInt 相似,但它返回一个浮点数
1 | Number.parseFloat("12.3abc"); // 12.3 |
Number.isInteger:判断数否为整数,如果参数是小数点后都是0的浮点数也会被认为是整数。
1 | Number.isInteger(1.1); // false |
Number.EPSILON:定义了一个极小数值的常量,用来判断浮点数的计算误差
Number.MAX_SAFE_INTEGER:安全整数的最大值
Number.MIN_SAFE_INTEGER:安全整数的最小值
Number.isSafeInteger:判断是否在安全范围内(-2^53到2^53),超过这个范围 JavaScript 就无法精确表示
1 | Number.isSafeInteger(Number.MAX_SAFE_INTEGER); // true |
5.Math对象
Math.trunc:截取一个数的整数部分并返回
1 | Math.trunc(3.22); // 3 |
Math.sign:判断一个数是正数、负数还是零。正数返回1,负数返回-1,零就返回0,非数值返回 NaN
1 | Math.sign(-3); // -1 |
6.Array数组
Array.of:可以将一组值转换成数组
1 | Array.of(1,2,3); // [1,2,3] |
Array.from:可以将类似数组的对象或可遍历的对象还有字符串转换成真正的数组,只要是部署了 Iterator 接口的数据结构,Array.from 都能将其转为数组。
1 | Array.from("abc"); // ["a","b","c"] |
find:找出数组中符合条件的第一个元素,如果都不符合返回 undefind,其参数是一个匿名函数
1 | let arrA = [1, 2, 3]; |
findIndex:找出数组中符合条件的第一个成员的位置(索引),如果都不符合返回-1,其参数是一个匿名函数
1 | let arrA = [1, 2, 3]; |
fill(n[,start[,end]]):使用指定的值n填充数组,start 是开始位置索引,end 是截止到该索引之前(不包含该元素),end省略表示包含 start 之后的所有元素
1 | let arrB = [1, 2, 3, 4, 5]; |
keys:对数组的键名进行遍历
values:对数组的键值进行遍历
entrise:对数组的键值对进行遍历
三者均返回一个遍历器,可以用 for…of 对其进行遍历
1 | for (let i of ['a', 'b'].keys()) { |
数组推导:通过简洁的语法操作现有的数组生成新数组
1 | let arrA = [1, 2, 3]; |
7.函数
很多情况下我们要给函数的参数指定一个默认值
传统写法使用运算符“||”来操作:如果第一项为 true 直接返回第一项,否则返回第二项
1 | function person(n, a) { |
但这种写法有缺陷:参数对应的布尔值不能是 false(比如:数字0、空字符串),否则依然会使用默认值。ES6 带来一种新方式:
1 | function person(name = "xiaowang", age = 24) { |
注意:
1.只有当传入的参数为 undefined 时才会触发默认值,而0、false、null 都不会触发默认值
2.函数参数是默认声明的,函数内部再声明会报错(声明变量名不能与形参一样)
3.设定默认值的参数一定要放在最后,有默认值的参数后面不能再跟不需默认值的参数
1 | person(undefined, 0); // xiaowang 0 |
rest参数
rest参数(形式:“…变量名”)用在函数上就代表是获取函数剩下部分的参数,这样就不需要使用 arguments 对象了。变量是一个数组,将多余的参数都存到数组中
1 | function Sum(...val) { // 求和 |
注意:rest参数必须是函数的最后一个参数,后面不能再有其他参数
1 | function success(name,age,...val){} // 正确 |
扩展运算符
扩展运算符即三个点(…),它一般结合数组使用,把数组的元素用逗号分开,将一个数组转成一个对应的参数序列
1 | let arrA = [1,2,3]; |
箭头函数
箭头函数是一种全新的定义函数的方式,使用箭头符号(=>)
1 | // 两种写法是等价的 |
=>之前的 a 代表传进去的参数,之后的 a 表示函数体,传多个参数时
1 | let arrA = [1,2,3]; |
由此看出:如果参数不止一个时,要用小括号()括起来;函数体语句不止一条时,要用大括号{}括起来
还有一点,箭头函数中的 this 指向的是定义时的 this,而不是执行时的 this
1 | window.id = 2; |
8.Object对象
ES6 中对象有更简便的表示方法:变量名就可以表示属性,一起组成对象的内容
1 | var name = "xiaowang"; |
对象的属性名还可以是表达式
1 | var a = "java"; |
Object.is:比较两个值是否严格相等(全等:变量类型和值都一样)
1 | var strA = "123"; |
Object.assign:将后面对象的属性赋值到第一个对象上,如果属性名有相同的,后面的属性值覆盖前面的属性值(浅拷贝)
1 | var first = {"a":1}; |
Object.getPrototypeOf:获取一个对象的 prototype 属性和方法
Object.setPrototypeOf:设置一个对象的 prototype 属性和方法
1 | Object.getPrototypeOf(object); |
9.解构赋值
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)
数组的解构赋值
传统的变量赋值
1 | var arrA = [1, 2, 3]; |
使用解构赋值
1 | var [a, b, c] = [1, 2, 3]; |
将变量作为一个数组的元素后直接赋值
注意:
1.解构赋值可以嵌套
1 | var [a, b, [c1, c2]] = [1, 2, [3.1, 3.2]]; |
2.不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功
1 | let [x, y] = [1, 2, 3]; |
3.解构不成功,变量的值就等于undefined
1 | var [x] = []; |
4.允许设置默认值, 但如果有新的赋值时会覆盖默认值(undefined 不会覆盖默认值)
1 | var [a, b, c = 3] = [1, 2]; |
对象的解构赋值
对象的解构赋值不受排列次序影响(数组则会受影响),变量名和属性名相同的赋值成功,也可以嵌套、设置默认值
1 | var { a, b, c } = { "a": 1, "b": 2, "c": 3 }; |
给一个变量名与属性名不一样的变量解构赋值
1 | var { a: b } = { "a": 1 }; |
字符串的解构赋值
字符串解构赋值时先被转换成一个类似数组的对象再分别赋值
1 | var [a, b, c, d, e] = "hello"; |
解构赋值的用途
1.交换变量的值, 不必引入第三个变量
1 | var x = 1; |
2.提取函数返回的多个值。函数只能返回一个值,可以将多个值装入数组或对象中,用解构赋值快速提取
1 | function demoA() { |
3.定义函数的参数
1 | function demoB({ a, b }) { // 只接收 a 和 b |
4.提取JSON数据
1 | var data = { |
5.设置函数参数的默认值
1 | function demoC({ name = "xiaowang" }) { |
10.Symbol
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值,用来解决对象的属性名冲突。即使参数一样,描述一样,得到的值也不相等,Symbol 永远是独一无二的值。Symbol接受参数,用于对实例值的描述。它是 JavaScript 语言的第七种数据类型,前六种是:Undefined(未定义)、Null(空值)、Boolean(布尔值)、String(字符串)、Number(数值)、Object(对象)
1 | let smA = Symbol(); |
当 symbol 值作为对象属性名时一定要用中括号[],不能用点运算取值,因为点运算符会使 js 把属性名理解为字符串类型而不是 symbol 类型
1 | let name = Symbol(); |
注意:当symbol类型的值作为属性名时,该属性是不会出现在 for…in 和 for…of 中的,也不会被 Object.keys 获取到,这三种方法只能获取 String 类型的属性
获取symbol类型属性的方法:
1、Object.getOwnPropertySymbols:该方法会找到 symbol 类型的属性并返回一个数组,数组的成员就是 symbol 类型的 Symbol 值
1 | let name = Symbol("name"); |
2、Reflext.ownKeys:该函数返回一个数组,数组的内容就是对象的键名,包括 symbol 和 String 类型
1 | let person = { |
Symbol.for:根据参数名去全局环境中搜索是否有以该参数为名的 symbol 值,有就返回它,没有就以该参数名来创建一个新的symbol值。Symbol.for 创建的 symbol 值会被登记在全局环境中,而 Symbol 创建的则不可以
1 | let sym = Symbol("name"); |
Symbol.keyFor:返回一个已经被登记在全局环境中的 symbol 值的 key,没有就报 undefined
1 | let symA = Symbol.for("name"); |
11.Proxy 代理
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
get:用于拦截某个属性的读取操作
set:用于拦截对对象的写操作
注意:要使得 Proxy 起作用,必须针对 Proxy 实例进行操作,而不是针对目标对象进行操作
1 | var person = { "height": 180, "weight": 72 }; // 目标对象 |
对象代理
ownKeys: 拦截操作,拦截过滤 Object.ownKeys 对对象的属性遍历
1 | let person = { "name": "xiaowang", "age": 24, "height": 180 }; |
has: 拦截操作,拦截 key in object 的操作,但不拦截 for…in,判断是否含有指定的键值对,返回一个布尔值
1 | let person = { "name": "xiaowang", "age": 24 }; |
函数代理
apply:用于拦截函数
1 | let fn = function () { |
proxy 本身是一个代理实例对象,代理的是一个函数 fn,把它当做函数调用的时候就会被 apply 拦截。
取消代理
Proxy.revocable:方法返回一个对象,对象中含有一个 proxy 属性,它就是 Proxy 的代理实例对象;还有一个 revoke 方法,用于取消代理
1 | let person = { "name": "xiaowang" }; |
12.遍历
传统的遍历都有各自的缺陷
1、使用 for 循环遍历数组就是代码不够简洁
1 | var arr = [1, 2, 3]; |
2、使用 forEach 遍历却又无法中断停止整个循环
1 | var arr = [1, 2, 3]; |
3、for…in 循环用于对象的循环,如果用于数组遍历,则每次遍历得到的值是字符串类型
1 | var arr = [1, 2, 3]; |
新增的 for…of 循环与 for…in 相似,它的优势:
1.比 for 循环简洁
2.可以用 break 终止整个循环,或用 continute 跳出当前循环,继续后面的循环
3.结合 keys() 获得循环的索引,而且是数字类型,非字符串类型
1 | var arr = [1, 2, 3, 4, 5]; |
for…of 能遍历的对象有:数组、类数组对象、字符串、set 和 map 结构等具有 iterator 接口的数据结构。因为 Object 对象不具有 iterator 接口,所以不能直接使用 for…of 遍历 Object 对象
13.Iterator遍历器
当一个数据结构拥有一个叫Symbol.iterator的方法(Symbol 类型)就可以被 for…of 遍历。当用 for…of 遍历时,Symbol.iterator 方法就会被调用并返回一个 iterator 遍历器对象,然后就在遍历器上不断调用 next() 方法,直到 done 的值为 true 表示遍历结束。
1 | let arr = [1, 2, 3]; |
加深理解:手动给一个普通的 Object 对象添加上类似的方法可以使其能够被 for…of 遍历
1 | let obj = { |
14.Generator生成器
普通函数用function
来声明,Generator 函数用function*
声明,Generator 函数中还有新的关键字yield
。Generator 函数被调用时,先返回一个生成器对象,然后暂停不动,等到生成器对象的 next() 方法被调用后,函数才会继续执行,直到遇到 yield 后又停止执行,并返回一个 Object 对象,等待 next() 调用,直到 done 等于 true
yield:Generator 函数可以有多个 yield,代表暂停执行,通过 next() 方法可以恢复执行
1 | function* text() { |
next() 方法还可以接受一个参数,它的参数会作为上一个 yield 的返回值
1 | function* text() { |
在一个 Generator 函数里调用里一个 Generator 函数,需要用到yield *
,会按顺序将所有程序走完
1 | function* gen1() { |
15.Set
Set 可以理解为值的集合,但它的值不会有重复项,每个值都是唯一的
给Set对象添加值,用数组作为参数直接传入或使用 add() 方法来添加,而且Set结构会自动忽略相同的值,只会保留一个相同的值
1 | var s1 = new Set([1, 2, 3]); |
Set 对象的 size 属性会获取成员的个数
1 | var s1 = new Set([1, 2, 3]); |
delete:删除 Set 结构中指定的值,成功返回 true,失败返回 false
1 | var s1 = new Set([1, 2, 3]); |
clear:清除所有成员
1 | var s1 = new Set([1, 2, 3]); |
has:判断是否含有指定的值,有就返回 true,没有就返回 false
1 | var s1 = new Set([1, 2, 3]); |
entries:返回一个键值对的遍历器,Set 结构的键名和键值是同一个值
keys:返回键名的遍历器
values:返回键值的遍历器
1 | var s1 = new Set([1, 2, 3]); |
使用 for…of 遍历
1 | var s1 = new Set([1, 2, 3]); |
forEach:遍历每一个成员
1 | var s1 = new Set([1, 2, 3]); |
Set 的用途之一,利用Set结构的成员值不能重复的特点来实现数组去重的效果
1 | let arrA = [1, 2, 2, 3, 3, 3]; |
16.WeakSet
WeakSet 与 Set 类似,WeakSet 结构同样不会存储重复的值,但它的成员必须是对象类型的值(严格来说是具有 iterable 接口的对象),而且 WeakSet 结构不可遍历(它的成员都是对象的弱引用,随时会被回收机制回收)
WeakSet结构提供了 add、delete、has 方法,作用与用法跟 Set 结构完全一致。但由于 WeakSet 结构不能遍历,就不会有 keys、values、entride、forEach 方法和 size 属性
1 | let ws1 = new WeakSet([{ name: "xiaowang" }]); |
17.Map
Map 与 Object 对象类似,但Map的键名类型不在局限于字符串,可以是各种类型的值。如果想要在创建实例的同时进行初始化,可以将数组作为参数
1 | let m = new Map([ |
set:给实例设置一堆键值对,返回 map 实例,如果键名已经存在,那么后面的键值会覆盖前面的键值
1 | let m = new Map(); |
size属性来获取实例的成员数
get:获得指定键名的键值,如果不存在就返回 undefined
delete:删除指定的键值对,成功返回 true,失败返回 false
clear:清除所有键值对
has:判断 Map 实例是否包含指定的键值对,有就返回 true,没有则返回 false
1 | let m = new Map([["name", "xiaowang"], ["age", 24], ["height", 180]]); |
Map 结构可以被遍历
entries:返回实例的键值对遍历器
keys:返回实例所有键名的遍历器
values:返回实例所有键值的遍历器
forEach:遍历每一个键值对
1 | let m = new Map([["name", "xiaoming"], ["age", 24]]); |
18.WeakMap
WeakMap 与 Map 结构很类似,不同点在于 WeakMap 的键名只支持数组、对象、函数这样的引用类型数据
WeakMap 也有 get、has、delete 方法,用法用途与Map一样,但不支持clear、keys、values、entries、forEach方法,也没有 size 属性
1 | let wm = new WeakMap(); |
19.Promise对象
Pormise 对象是一个全局对象,用来解决回调地狱。promise 对象有三种状态:
1.pending:刚刚创建一个 Promise 实例,进行初始化
2.fulfilled:resolve 方法调用的时候,表示操作成功
3.rejected:reject 方法调用的时候,表示操作失败
状态只有两种变化:pending -> fulfilled,或pending -> rejected,没有其他情况
1 | let promise = new Promise(function (resolve, reject) { |
then:用于绑定处理操作后的处理程序,then 的两个参数是函数,第一个处理成功后的操作,第二个处理异常时的操作
1 | promise.then(function(resolve){ |
catch:Promise专门处理异常操作的方法
1 | promise.catch(function(error){ |
then和catch调用后都会返回promise对象,结合两者得到:
1 | promise.then(function(resolve){ |
完整示例
1 | let pro = new Promise(function (resolve, reject) { |
Promise.all:其参数是一个由 Promise 实例对象组成的数组,当参数中的实例对象的状态都为 fulfilled (成功)时,该方法会有返回
1 | let pro1 = new Promise(function (resolve) { |
Promise.race:其参数是一个由 Promise 实例对象组成的数组,当参数中的实例对象有一个的状态发生变化(成功或失败),该方法就有返回
1 | let pro1 = new Promise(function (resolve) { |
20.Class(类)
本质:基于原型 prototype 的实现方式做了进一步的封装,实际上也是函数 function 和原型 prototype 实现
使用关键字 class 声明一个类,类名后面花括号{}里面的内容称为类体。构造方法 constructor 是一个类必需且唯一的方法,一个类体不能含有多个 constructor 构造方法。如果没有手动编写 constructor 构造方法,执行时会被加上一个默认的空的 constructor 方法
1 | class People { |
注意:先声明定义类,再创建实例;必须使用 new 来创建类的实例对象
1 | class People { |
静态方法:直接使用类名即可访问的方法称为静态方法
静态方法的定义需要使用 static 关键字来标识,直接同过类名来调用(实例方法需要通过实例对象调用)
1 | class People { |
类的继承:使用 extends 关键字来实现子类继承父类, 使用 super 来引用父类
使用 super 时要注意:
1.子类必须在 constructor 方法中调用 super 方法;
2.必须先调用 super() ,才可以使用 this, 否则报错
1 | class People { |
21.module模块
模块:对其他模块提供自己的属性或方法的文件。
导出Export:模块可以选择性的提供自己的属性和方法供其他模块使用
导入Import:模块可以根据需要引入其他模块提供的属性或方法供自己使用
1 | // Export.js文件 |
批量导出:可以直观的看出模块提供了那些变量
1 | // Export.js文件 |
批量导入:变量名字必须跟导出的一致才能准确获取,位置顺序无要求
1 | // Import.js文件 |
有时候想要给导入的变量换个名字,可以使用关键字 as
1 | import { name as wangname } from "./Export.js"; |
整体导入:使用星号*
将其它模块提供的所有属性和方法整体导入赋值给一个变量对象,使用点运算符来获取它的属性和方法
1 | import * as obj from "./Export.js"; |
默认导出:每个模块支持导出一个没有名字的变量,使用关键语句 export default 来实现。导入时可以给这个匿名函数去任意的名字,并且不需要大括号{}
包裹
1 | // Export.js文件 |
注意:声明的变量对外都是只读的,但如果导出的是对象类型的值则可以修改。如果导入不存在的变量,值为 undefined,不会报错
1 | // Export.js文件 |