1.字符串扩展:
//能够正确处理 4 个字节储存的字符
codePointAt(), String.fromCodePoint()
//查找字符串
includes(), startsWith(), endsWith()
//返回一个新字符串,表示将原字符串重复n次。
repeat()
//字符串补全长度
padStart(),padEnd()
2.数值扩展
//是否为有限的,是否为NaN
Number.isFinite(), Number.isNaN()
//判断一个数值是否为整数
Number.isInteger()
// 注意:Number.isInteger(3.0000000000000002) // true
// 由于 JavaScript采用IEEE754标准,数值存储为64位双精度格式,数值精度最多可以达到 53 个
// 二进制位(1个隐藏位与52个有效位)。如果数值的精度超过这个限度,第54位及后面的位就会被丢弃,
// 这种情况下,Number.isInteger可能会误判。
Number.EPSILON //JavaScript能够表示的最小精度,实质是一个可以接受的最小误差范围
Number.isSafeInteger() //JavaScript能够准确表示的整数范围在-2^53到2^53之间(不含两个端点)Math.pow(2, 53)
Number.MAX_SAFE_INTEGER //常量,上限值(9007199254740991)
Number.MIN_SAFE_INTEGER //常量,下限值(-9007199254740991)
Math.trunc() //去除一个数的小数部分,返回整数部分
Math.sign() //判断一个数是正数、负数、还是零
Math.cbrt() //计算一个数的立方根
** //指数运算符 2 ** 2 // 4
//a **= 2; 等同于 a = a * a;
//注意,V8 引擎的指数运算符与Math.pow的实现不相同,对于特别大的运算结果,两者会有细微的差异。
3.函数扩展
- 为函数的参数设置默认值: (...rest)
通常在尾参,会形成一个单独的作用域,指定了默认值后函数的length属性将失真,将返回没有指定默认值的参数个数rest参数(...变量名),rest参数搭配的变量是一个数组。注意:只要函数参数使用了默认值、解构赋值、或者扩展运算符, 那么函数内部就不能显式设定为严格模式,否则会报错。
- 函数的name属性:返回该函数的函数名
- 箭头使用注意点:
1.函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
2.不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
3.不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
4.不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
- 函数绑定运算符 ::
自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。foo::bar 等同于 bar.bind(foo)
- 尾调用
函数式编程的重要概念,避免发生“栈溢出”错误, 尾递归,只存在一个调用帧, 递归本质上是一种循环操作
//尾递归fibonacci 数列:
function fibonacci (n , ac1 = 1 , ac2 = 1) {
if( n <= 1 ) {return ac2};
return fibonacci (n - 1, ac2, ac1 + ac2);
}
4.数组扩展
- 扩展运算符...
可将数组转为用逗号分隔的参数序列,可代替apply扩展运算符背后调用的是遍历器接口(Symbol.iterator)
// 复制数组(浅拷贝)
const a2 = [...a1]
const [...a2] = a1
//合并数组(浅拷贝)
[...arr1, ...arr2, ...arr3]
//与解构赋值结合
const [first, ...rest] = [1, 2, 3, 4, 5]
//将字符串转为数组
[...'hello']
//将部署了Iterator接口的对象转为数组,没有部署 Iterator接口的类数组对象可使用Array.from
- Array.from()
将两类对象转为真正的数组:1.类似数组的对象(array-like object);2.可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map
- Array.of()
替代Array()或new Array(),并且不存在由于参数不同而导致的重载
- copyWithin()
将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组
- find() 和 findIndex()
参数是一个回调函数,find找出符合条件的数组成员(或undefined),findIndex找出符合条件的成员位置(或-1)
- fill()
使用给定值,填充一个数组
['a', 'b', 'c'].fill(7) // [7, 7, 7]
//带起始位置参数
['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c']
- entries(),keys(),values()
都返回一个遍历器对象。keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历
- includes()
返回一个布尔值,表示某个数组是否包含给定的值。和字符串includes类似
[1, 2, NaN].includes(NaN) // true
//第二个参数表示搜索的起始位置,默认为0,
[1, 2, 3].includes(3, 3); // false
- flat()
将嵌套的数组“拉平”,变成一维的数组。返回一个新数组,flat()默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,参数写成一个整数,表示要拉平的层数,不管多少层,可用Infinity关键字作为参数
- flatMap()
参数是一个遍历函数,可以接受三个参数,当前数组成员、当前数组成员位置(从零开始)、原数组。返回一个新数组
- 明确将空位转为undefined
forEach(), filter(),reduce(),every()和some()都会跳过空位。map()会跳过空位,但会保留这个值,join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。
5.对象扩展
- 允许直接写入变量和函数,作为对象的属性和方法
//属性名表达式
obj['a' + 'bc'] = 123;
let obj = {
[propKey]: true,
['a' + 'bc']: 123
};
- Object.is()
比较两个值是否相等,与严格比较运算符===的行为基本一致,不同有两个:一是+0不等于-0,二是NaN等于自身
- Object.assign()
将源对象所有可枚举属性复制到目标对象
注意: Object.assign方法实行的是浅拷贝遇到同名属性,Object.assign的处理方法是替换可以用来处理数组,但是会把数组视为对象 如果要复制的值是一个取值函数,那么将求值后再复制 Object.assign()无法正确拷贝get属性和set属性常见用途:为对象添加属性,方法,克隆对象,合并多个对象,为属性指定默认值
Object.assign(target, source1, source2);
//只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性(enumerable: false)
- Object.fromEntries()
fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。
- 其他
Object.getOwnPropertyDescriptor() //获取对象自身属性的描述对象Object.getOwnPropertyDescriptor(obj)
//获取对象某属性的描述对象,Object.getOwnPropertyDescriptor(obj,attr)
Object.create() //创建原型对象
let obj = Object.create(proto) //obj为空对象,原型对象为proto
Object.getPrototypeOf() //读取原型对象 Object.getPrototypeOf(obj)
Object.setPrototypeOf() //设置原型对象 Object.setPrototypeOf(obj, proto)
super //指向当前对象的原型对象。表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错
Object.keys() //返回一个数组,参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名
Object.values() //返回一个数组,参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值
Object.entries() //返回一个数组,参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组
...扩展运算符 //解构赋值,扩展运算
let aClone = { ...a };
//等同于
let aClone = Object.assign({}, a);
6.Symbol
一种新的原始数据类型,表示独一无二的值。symbol、undefined、null、Boolean、String、Number、Object, Symbol 值通过Symbol函数生成。不能使用new命令,这是因为生成的 Symbol 是一个原始类型的值,不是对象。可以接受一个字符串作为参数,表示对 Symbol 实例的描述,不能与其他类型的值进行运算,可以显式转为字符串,也可以转为布尔值,但是不能转为数值
let s = Symbol(); typeof s // "symbol"
1.作为属性名的 Symbol: 能防止某一个键被不小心改写或覆盖,作为对象属性名不能用点运算符 在对象的内部,使用 Symbol值定义属性时,Symbol值必须放在方括号之中。不放在方括号中,属性名会被解析成字符串
2.消除魔术字符串:常用的消除魔术字符串的方法,就是把它写成一个变量。只要确保不会跟其他属性的值冲突即可适合改用 Symbol 值
3.属性名的遍历:Symbol 作为属性名不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。3.1 Object.getOwnPropertySymbols方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值;
3.2 Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。
Symbol.for()
Symbol.keyFor()
//调用Symbol.for("cat")30 次,每次都会返回同一个 Symbol 值,
//但是调用Symbol("cat")30 次,会返回 30 个不同的 Symbol 值。
- 内置的 Symbol 值:阮一峰es6
Symbol.hasInstance
Symbol.isConcatSpreadable
Symbol.species
Symbol.match
Symbol.replace
Symbol.search
Symbol.split
Symbol.iterator
Symbol.toPrimitive
Symbol.toStringTag
Symbol.unscopables
7.Set 和 Map
- Set
Set类似于数组,但是成员的值都是唯一的,没有重复的值。Set 本身是一个构造函数,用来生成 Set 数据结构;Array.from方法可以将 Set 结构转为数组。遍历:keys(),values(),entries(),forEach(),map和filter方法也可以间接用于 Set
add(value) //添加某个值,返回 Set 结构本身。
delete(value)//删除某个值,返回一个布尔值,表示删除是否成功。
has(value)//返回一个布尔值,表示该值是否为Set的成员。
clear()//清除所有成员,没有返回值。
WeakSet 与 Set 类似,与Set有两个区别。WeakSet的成员只能是对象,其次,WeakSet 中的对象都是弱引用
- Map
Map类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键
//属性和操作:
size
set(key, value)
get(key)
has(key)
delete(key)
clear()
// 遍历:keys(),values(),entries(),forEach()
//注意:Map 的遍历顺序就是插入顺序
WeakMap与Map类似,与Map有两个区别。只接受对象作为键名(null除外),键名所指向的对象,不计入垃圾回收机制
8.Proxy
用于修改某些操作的默认行为。在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 对象可以拦截目标对象的任意属性,这使得它很合适用来写 Web 服务的客户端。同理,Proxy 也可以用来实现数据库的 ORM 层。
//Proxy支持的拦截操作:
//拦截对象属性的读取,比如proxy.foo和proxy['foo']。
get(target, propKey, receiver)
//拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
set(target, propKey, value, receiver)
has(target, propKey) //拦截propKey in proxy的操作,返回一个布尔值。
deleteProperty(target, propKey) //拦截delete proxy[propKey]的操作,返回一个布尔值。
ownKeys(target) //拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
getOwnPropertyDescriptor(target, propKey) //拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
defineProperty(target, propKey, propDesc) //拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
preventExtensions(target) //拦截Object.preventExtensions(proxy),返回一个布尔值。
getPrototypeOf(target) //拦截Object.getPrototypeOf(proxy),返回一个对象。
isExtensible(target) //拦截Object.isExtensible(proxy),返回一个布尔值。
setPrototypeOf(target, proto) //拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
apply(target, object, args) //拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
construct(target, args) //拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
Proxy.revocable() //返回一个可取消的 Proxy 实例。使用场景:目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。
9.Promise
1.对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending、fulfilled 和rejected。只有异步操作的结果可以决定当前是哪种状态,任何其他操作都无法改变这个状态。
2.一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
-
Promise是一个构造函数,生成Promise实例
-
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。也是两个函数,由 JavaScript 引擎提供,不用自己部署。
- resolve函数的作用是将Promise对象的状态从pending 变为 resolved,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
- reject函数的作用是将Promise对象的状态从pending 变为 rejected,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
- then方法分别指定resolved状态和rejected状态的回调函数。第二个是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。 如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;
- resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例
- resolve或reject后面再抛出错误,不会被捕获,等于没有抛出。因为 Promise 的状态一旦改变,就永久保持该状态,不会再变了。
- then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法。
- catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。
- Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
- finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。 finally方法的回调函数不接受任何参数,这意味着没有办法知道Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作是与状态无关的,不依赖于 Promise 的执行结果。finally本质上是then方法的特例。
- all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例 Promise.all方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
- 1.多个Promise的状态都变成fulfilled,all的状态才会变成fulfilled,返回值组成一个数组,传递给all的回调。
- 2.只要有一个Promise被rejected,all的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给all的回调。
-
race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例 与Promise.all的区别在于多个Promise哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
-
Promise.resolve将现有对象转为 Promise 对象
-
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected
10.Iterator 和 for...of
- 它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作
- 默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,一个数据结构只要有Symbol.iterator属性,就可以认为是“可遍历的”。
- 原生具备 Iterator 接口的数据结构: Array,Map,Set,String,TypedArray,函数的 arguments 对象,NodeList 对象
- Iterator 的遍历过程:
- (1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
- (2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
- (3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
- (4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
- 每一次调用next方法,都会返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
- 默认调用 Iterator 接口的场合: 解构赋值,扩展运算符,yield*,任何接受数组作为参数的场合
for...of
本质上就是调用这个接口产生的遍历器,遍历获得键值, 数组的遍历器接口只返回具有数字索引的属性 for...of循环会正确识别 32 位 UTF-16 字符。 并不是所有类似数组的对象都具有 Iterator 接口,一个简便的解决方法,就是使用Array.from方法将其转为数组。 对于普通的对象,for...of循环会报错。可使用Object.keys方法将对象的键名生成一个数组,然后遍历这个数组。 或使用 Generator 函数将对象重新包装一下。
11.Class
可以看作构造函数的另一种写法。 Object.assign方法可以很方便地一次向类添加多个方法: Object.assign(className.prototype, { fn1(){}, fn2(){} }); Class的内部所有定义的方法,都是不可枚举的(non-enumerable)。 类的属性名,可以采用表达式。 类和模块的内部,默认就是严格模式 与函数一样,类也可以使用表达式的形式定义 类不存在变量提升(hoist),ES6 不会把类的声明提升到代码头部
- constructor 方法
通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,会被默认添加一个空的constructor。 constructor方法默认返回实例对象(即this),完全可以指定return另外一个对象。 类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别
- 私有方法和私有属性
- 现有方法:1.在命名上加以区别,如:前下划线 2.将私有方法移出模块,通过call或apply等方法绑定this调用 3.将私有方法的名字命名为一个Symbol值
- 提案: 属性名之前,使用#表示
- this指向
类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。- 1.在构造方法中绑定this,这样就不会找不到
- 2.使用箭头函数。
- 3.使用Proxy,获取方法的时候,自动绑定this
- getter 和 setter
在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。 - Class 的静态方法
在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用 - Class 的静态属性
Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。 只要在实例属性写法前面,加上static关键字 - new.target 属性
在构造函数之中,返回new命令作用于的那个构造函数。如果构造函数不是通过new调用的,new.target返回undefined.可以用来确定构造函数是怎么调用的。 利用这个特点,可以写出不能独立使用、必须继承后才能使用的类
Class 的继承:
- 通过extends关键字实现继承
- 子类必须在constructor中调用super方法,否则新建实例时会报错。因为子类自己的this对象,必须先通过父类的构造函数塑造,得到与父类同样的实例属性和方法然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
- ES5 的继承是先创造子类的实例对象this,再将父类的方法添加到this上面(Parent.apply(this))。
- ES6 的继承是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
- 如果子类没有定义constructor方法,这个方法会被默认添加。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。
- 在子类的构造函数中,只有调用super之后,才能使用this关键字,否则会报错。因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。
- super 关键字:既可以当作函数使用,也可以当作对象使用。
- super作为函数调用时,代表父类的构造函数。但是返回的是子类的实例,即super内部的this指的是子类。所以相当于parent.prototype.constructor.call(this)
- super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
12.Module语法
-
概述: Module的设计思想是静态化,使得编译时就能确定模块依赖关系,以及输入和输出的变量。 CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS模块就是对象,输入时必须查找对象属性。 ES6 模块还有以下好处: 不再需要UMD模块格式了,将来服务器和浏览器都会支持 ES6 模块格式。目前,通过各种工具库,其实已经做到了这一点。 将来浏览器的新 API 就能用模块格式提供,不再必须做成全局变量或者navigator对象的属性。 不再需要对象作为命名空间,未来这些功能可以通过模块提供。
-
严格模式: 自动采用严格模式,不管有没有在模块头部加上"use strict"; 注意:ES6 模块之中,顶层的this指向undefined,不应该在顶层代码使用this。
-
export和import; export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。 as 重命名,from 指定模块文件的位置(相对路径/绝对路径),后缀可省略。如果只是模块名,不带路径,必须有配置文件告诉 js 引擎该模块的位置。 内部的所有变量,外部无法获取。必须使用export输出该变量 export命令可输出变量、函数或类 export输出的接口与其对应的值是动态绑定关系,可以取到模块内部实时的值。CommonJS 模块输出的是值的缓存,不存在动态更新。 export和import可以出现在模块的任何位置,但必须处于模块顶层,否则报错 import命令输入的变量都是只读的,它的本质是输入接口。不允许在加载模块的脚本里改写接口。 如果输入的变量是一个对象,该对象的属性是可以改写的。不过这种写法很难查错,建议凡是输入的变量,都当作完全只读,轻易不要改变它的属性。 import命令具有提升效果,会提升到整个模块的头部,首先执行。因为import是编译阶段执行的,在代码运行之前。 import是静态执行,所以不能使用表达式或把路径写成变量 重复执行同一句import语句,只会执行一次。 Babel可以让CommonJS的require和 ES6的import写在同一个模块里,但是最好不要这样做。执行时间不一样可能会带来问题
-
export default 为模块指定默认输出,其他模块加载该模块时,import命令可以指定任意名字。这时import命令后面,不使用大括号。 export default命令用在非匿名函数前,视同匿名函数加载。 一个模块只能有一个export default命令输出。
-
import()动态加载 返回一个 Promise 对象,import()类似于 Node 的require方法,区别主要是import()是异步加载,require是同步加载。 适用场合: 1.按需加载, 2.条件加载, 3.动态模块路径
-
Module 的加载实现:
- 浏览器加载:浏览器通过<script>标签加载 JavaScript 脚本。默认是同步加载。 <script>标签打开defer或async属性脚本就会异步加载。 defer是“渲染完再执行”(DOM 结构完全生成,以及其他脚本执行完成),async是“下载完就执行”。 defer有顺序,async无顺序。 浏览器加载 ES6 模块,也使用<script>标签,但是要加入type="module"属性。也是异步加载,相当于defer。
- ES6 模块也允许内嵌在网页中,语法行为与加载外部脚本完全一致。
-
ES6 模块与 CommonJS 模块的差异:
- CommonJS 模块的顶层this指向当前模块,ES6 模块之中,顶层的this指向undefined。
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- CommonJS 模块是运行时加载,ES6模块是编译时输出接口。因为CommonJS 加载的是一个对象(即module.exports),该对象只有在脚本运行完才会生成。ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。