ES6学习笔记

238 阅读26分钟

es5严格模式

  1. 理解:除了正常运行模式(混杂模式),es5添加了第二种运行模式:”严格模式“

    这种模式使得js在更严格的语法条件下运行

  2. 目的/作用

    • 消除js语法的一些不合理,不严谨之处,减少一些怪异行为

    • 消除代码运行的一些不安全之处,为代码的安全运行保驾护航。

    • 为未来新版本的js做好铺垫

  3. 使用

    • 在全局或函数的第一条语句定义为:‘use strict’;

    • 如果浏览器不支持,只解析为一条简单的语句,没有任何副作用

  4. 语法和行为改变

    • 必须用var声明变量

    • 禁止自定义的函数中的this指向window

    • 创建eval作用域

    • 对象不能有重名的属性

json对象扩展

  1. JSON.stringify(obj/arr)

    • js对象(数组)转换为json对象(数组)
  2. JSON.parse(json)

    • json对象(数组)转换为js对象(数组)

Object对象方法扩展

  • es5给Object扩展了一些静态方法,常用的有两个
  1. Object.create(prototype,[descriptors])

    • 作用:以指定对象为原型创建新的对象

    • 为新的对象制定新的属性,并对属性进行描述

    • value:指定值

    • configurable:标识当前属性是否可以被删除,默认为false

    • writable:标识当前属性值是否是可修改的,默认为false

    • enumerable:标识当前属性是否能用for in 枚举 默认为false

  2. Object.defineProperties(object,descriptors)

    • 作用:为指定对象定义扩展多个属性

    • get:用来获取当前属性值的回调函数

      获取扩展属性值的时候自动调用

    • set:修改(监听)当前属性值的触发的回调函数,并且实参即为修改后的值

    • 存取器属性:settergetter一个用来存值,一个用来取值

对象本身的两个方法

  • get propertyName(){ }用来得到当前属性值的回调函数

  • set propertyName(){ }用来监视当前属性值变化的回调函数

数组的扩展

  1. Array.prototype.indexOf(value) : 得到值在数组中的第一个下标

  2. Array.prototype.lastIndexOf(value) : 得到值在数组中的最后一个下标

  3. Array.prototype.forEach(function(item, index){}) : 遍历数组

  4. Array.prototype.map(function(item, index){}) : 遍历数组返回一个新的数组,返回加工之后的值

  5. Array.prototype.filter(function(item, index){}) : 遍历过滤出一个新的子数组, 返回条件为true的值

字符串的扩展

  1. String.fromCodePoint方法,可以识别0xFFFF的字符

  2. ES6为字符串添加了遍历器接口,使得字符串可以被for...of循环遍历。这个遍历器最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。

  3. 字符串实例的at方法,可以识别Unicode编号大于0xFFFF的字符,返回正确的字符。

  4. ES6提供字符串实例的normalize()方法,用来将字符的不同表示方法统一为同样的形式,这称为Unicode正规化。

  5. includes():返回布尔值,表示是否找到了参数字符串。

    startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。

    endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。

  6. repeat方法返回一个新字符串,表示将原字符串重复n次

  7. 如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全

  8. String.raw方法,往往用来充当模板字符串的处理函数,返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,对应于替换变量后的模板字符串

    String.rawHi\n${2+3}!; // "Hi\\n5!" String.rawHi\u000A!; // 'Hi\\u000A!'

正则表达式的扩展

  1. ES6中,如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。

  2. 字符串对象共有4个方法,可以使用正则表达式:match()、replace()、search()和split()。ES6将这4个方法,在语言内部全部调用RegExp的实例方法,从而做到所有与正则相关的方法,全都定义在RegExp对象上。

  3. ES6对正则表达式添加了u修饰符,含义为“Unicode模式”,用来正确处理大于\uFFFFUnicode字符。

  4. 点(.)字符在正则表达式中,含义是除了换行符以外的任意单个字符。对于码点大于0xFFFFUnicode字符,点字符不能识别,必须加上u修饰符。

  5. ES6新增了使用大括号表示Unicode字符,这种表示法在正则表达式中必须加上u修饰符,才能识别。

  6. ES6为正则表达式新增了flags属性,会返回正则表达式的修饰符。

数值的扩展

  1. ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示。

  2. 如果要将0b和0o前缀的字符串数值转为十进制,要使用Number方法。

  3. Number.isFinite()用来检查一个数值是否为有限的

  4. Number.isNaN()用来检查一个值是否为NaN

  5. ES6将全局方法parseInt()parseFloat(),移植到Number对象上面,行为完全保持不变。

6.Number.isInteger()用来判断一个值是否为整数。

7.ES6在Number对象上面,新增一个极小的常量Number.EPSILON

Math对象的扩展

  1. Math.trunc方法用于去除一个数的小数部分,返回整数部分

  2. Math.sign方法用来判断一个数到底是正数、负数、还是零

  3. Math.cbrt方法用于计算一个数的立方根。

  4. JavaScript的整数使用32位二进制形式表示,Math.clz32方法返回一个数的32位无符号整数形式有多少个前导0

  5. Math.imul方法返回两个数以32位带符号整数形式相乘的结果,返回的也是一个32位的带符号整数

  6. Math.fround方法返回一个数的单精度浮点数形式

  7. Math.hypot方法返回所有参数的平方和的平方根

  8. Math.expm1(x)返回ex - 1,即Math.exp(x) - 1

  9. Math.log1p(x)方法返回1 + x的自然对数,即Math.log(1 + x)

  10. Math.log10(x)返回以10为底的x的对数。如果x小于0,则返回NaN

  11. Math.log2(x)返回以2为底的x的对数。如果x小于0,则返回NaN

  12. ES2016 新增了一个指数运算符(**)。

数组的扩展

  1. Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。类似数组的对象,本质特征只有一点,即必须有length属性,Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。

  2. Array.of方法用于将一组值,转换为数组。

  3. 数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。它接受三个参数。

    • target(必需):从该位置开始替换数据。
    • start(可选):从该位置开始读取数据,默认为0。如果为负值,表示倒数。
    • end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。
  4. 数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined

  5. 数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

  6. fill方法使用给定值,填充一个数组。

  7. ES6提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

  8. Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。该方法属于ES7,但Babel转码器已经支持。

  9. Map结构的has方法,是用来查找键名的

    Set结构的has方法,是用来查找值的

  10. 数组的空位指,数组的某一个位置没有任何值。空位不是undefined,一个位置的值等于undefined,依然是有值的。空位是没有任何值

数组(Array)和对象(Object),ES6又添加了MapSet。这样就有了四种数据集合

函数的扩展

  1. ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

  2. 如果参数默认值是变量,那么参数就不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。

  3. 有默认值的参数都不是尾参数。这时,无法只省略该参数,而不省略它后面的参数,除非显式输入undefined

  4. 函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真,如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。

  5. ES6 引入 rest 参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

  6. 扩展运算符(spread)是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错

  7. 函数的name属性,返回该函数的函数名。Function构造函数返回的函数实例,name属性的值为anonymousbind返回的函数,name属性值会加上bound前缀

  8. 函数绑定运算符是并排的两个双冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。

  9. 尾调用就是指某个函数的最后一步是调用另一个函数。尾调用优化”,即只保留内层函数的调用帧。如果所有函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这将大大节省内存。这就是“尾调用优化”的意义。

    注意,只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则就无法进行“尾调用优化”。

  10. 函数调用自身,称为递归。如果尾调用自身,就称为尾递归。递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。

  11. 柯里化,意思是将多参数的函数转换成单参数的形式。这里也可以使用柯里化。

对象的扩展

  1. 属性,对象简写时如果某个方法的值是一个Generator函数,前面需要加上星号。

    var obj = { * m(){ yield 'hello world'; } };

  2. 属性名表达式与简洁表示法,不能同时使用,会报错

  3. 函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。如果对象的方法使用了取值函数(getter)和存值函数(setter),则name属性不是在该方法上面,而是该方法的属性的描述对象的getset属性上面,返回值是方法名前加上getset

  4. 有两种特殊情况:bind方法创造的函数,name属性返回bound加上原函数的名字;Function构造函数创造的函数,name属性返回anonymous

  5. 如果对象的方法是一个 Symbol 值,那么name属性返回的是这个 Symbol 值的描述。

  6. ES5比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

  7. Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target

  8. Object.assign拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性

  9. 对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。

  10. Object.entries方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。

  11. Object.getOwnPropertyDescriptors方法,返回指定对象所有自身属性(非继承属性)的描述对象

属性的遍历

  1. for...in

    for...in循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。

  2. Object.keys(obj)

    Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举 属性(不含Symbol属性)。

  3. Object.getOwnPropertyNames(obj)

    Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)。

  4. Object.getOwnPropertySymbols(obj)

    Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有Symbol属性。

  5. Reflect.ownKeys(obj)

    Reflect.ownKeys返回一个数组,包含对象自身的所有属性,不管是属性名是Symbol或字符串,也不管是否可枚举。

ES6中零散的知识点

  1. Function.prototype.bind(obj) :

    • 作用: 将函数内的this绑定为obj, 并将函数返回
  2. 面试题: 区别bind()call()apply()?

    • 都能指定函数中的this

    • call()/apply()是立即调用函数

    • bind()是将函数返回,不会立即调用函数

    • call()直接从第二个参数开始,依次传入

    • apply()第二参数必须是数组,传入放在数组里

    • bind传参的方式同call一样

  3. 暂时性死区:在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”

let与const关键字

  • let关键字
  1. 作用:

    • var类似,用于声明一个变量
  2. 特点:

    • 在块级作用域有效

    • 不允许在相同作用域内,重复声明同一个变量

    • 不会预处理,不存在变量提升

  3. 应用:

    • 循环遍历加监听

    • 使用let取代var是趋势

  • const关键字

    1. 作用:定义一个常量

    2. 特点:

      • 不能修改

      • 立即初始化

      • 其他特点同let

    3. 应用:

      • 保存不用改变的数据
    4. 本质:

      • const实际上并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。

块级作用域

  • 外层代码块不受内层代码块的影响

  • 允许块级作用域的任意嵌套

  • 外层作用域无法读取内层作用域的变量

  • 内层作用域可以定义外层作用域的同名变量

  • es5中,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域中声明,但是es6引入了块级作用域,明确允许在块级作用域之中声明函数,但是尽量避免在块级作用域内声明函数。如果确实需要,应该写成函数表达式,而不是函数声明语句,因为函数声明会提升,类似于var

  • es6的块级作用域声明函数时必须使用大括号

  • 块级作用域没有返回值,是一个语句,要想有返回值的话用do表达式,

let 变量名 = do{

}

es6声明变量的六种方法

  • var

  • function

  • let

  • const

  • import

  • class

顶层对象的概念

  • 在浏览器中顶层对象是window

  • 在Node中,顶层对象是global

变量的解构赋值

  1. 理解:

    • 从对象或数组中提取数据,并赋值给变量(多个)
  2. 本质:

    • 这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值
  3. 对象的解构赋值

    let{n,a}=[n:'tom',a:12];

    let { foo: baz } = { foo: "aaa", bar: "bbb" }; baz // "aaa" foo // error: foo is not defined - 上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo

  4. 数组的解构赋值

    let[a,b]=[1,'at'];

  5. 用途

    • 给多个形参赋值
  6. 对象的解构可以指定默认值,默认值生效的条件是,对象的属性值严格等于undefined

  7. 解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错

    // 报错 let {foo: {bar}} = {baz: 'baz'};

  8. 解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefinednull无法转为对象,所以对它们进行解构赋值,都会报错。

  9. 解构赋值的用途:

    1. 交换变量的值

    2. 从函数返回多个值

    3. 函数参数的定义

    4. 提取JSON数据

    5. 设置函数参数的默认值

    6. 遍历Map结构

模板字符串:简化字符串的拼接

  • 模板字符串必须使用``包含

  • 变化的部分使用${xxx}定义

  • 模板字符串之中还能调用函数

  • 模板字符串能嵌套

简化的对象写法

  • 省略同名的属性值

  • 省略方法的function

  • 例如:

    let x =1;

    let y=2;

    let point = {

        x,

        y,

        setX(x){this.x=x}

    };

箭头函数

  • 作用:定义匿名函数

  • 基本语法:

    • 没有参数:()=>console.log('xxx')

    • 一个参数:i=>i+2

    • 大于一个参数:(i,j)=>i+j

    • 函数体不用大括号:默认返回结果

    • 函数体如果有多个语句,需要用{}包围,若有需要返回的内容,需要手动返回

  • 适用场景:多用来定义回调函数

  • 箭头函数的特点:

    1. 简洁

    2. 箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是再定义的时候处在的对象就是它的this

    3. 扩展理解:箭头函数的this看外层的是否有函数。

      如果有,外层函数的this就是内部箭头函数的this

      如果没有,则thiswindow

  • 箭头函数有几个使用注意点。

    1. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

    2. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

    3. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。

    4. 不可以使用yield命令,因此箭头函数不能用作Generator函数。

    5. 除了this,以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:arguments、super、new.target

    三点运算符

  • 用途
  1. rest(可变)参数

    • 用来取代arguments 但比 arguments 灵活,只能是最后部分形参参数
function fun(...values) {

    console.log(arguments);

    arguments.forEach(function (item, index) {

        console.log(item, index);

    });

    console.log(values);

    values.forEach(function (item, index) {

        console.log(item, index);

    })

}

fun(1,2,3);
  1. 扩展运算符

    let arr1 = [1,3,5];

    let arr2 = [2,...arr1,6];

    arr2.push(...arr1);

  • 形参的默认值----当不传入参数的时候默认使用形参里的默认值
function Point(x = 1,y = 2) {

    this.x = x;

    this.y = y;

}

promise对象原理

  1. 特点

    • Promise对象有以下两个特点。

      1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

      2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的

  2. ES6的Promise是一个构造函数, 用来生成promise实例

    • Promise实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数。

    • Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数

    • Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例

    • Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例

    • 有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用

    1. 参数是一个Promise实例

      如果参数是Promise实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。

    2. 参数是一个thenable对象

      thenable对象指的是具有then方法的对象,Promise.resolve方法会将这个对象转为Promise对象,然后就立即执行thenable对象的then方法。

    3. 参数不是具有then方法的对象,或根本就不是对象

      如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的Promise对象,状态为Resolved.

  • done()

    • Promise对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。因此,我们可以提供一个done方法,总是处于回调链的尾端,保证抛出任何可能出现的错误
  • finally()

    • finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。

    1. 不带有任何参数

      Promise.resolve方法允许调用时不带参数,直接返回一个Resolved状态的Promise对象

      Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

  1. 优点和缺点

    • 有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

    Promise也有一些缺点。

    • 首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。

    • 其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。

    • 第三,当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

  2. 使用promise基本步骤(2步):

    • 创建promise对象

let promise = new Promise((resolve, reject) => {
    //初始化promise状态为 pending
    //执行异步操作
    if(异步操作成功) {
        resolve(value);//修改promise的状态为fullfilled
    } else {
        reject(errMsg);//修改promise的状态为rejected
    }
})

* 调用promise的then()
    promise.then(function(

        result => console.log(result),

        errorMsg => alert(errorMsg)

    ))

  1. promise对象的3个状态

    • pending: 初始化状态

    • fullfilled: 成功状态

    • rejected: 失败状态

  2. 应用:

    • 使用promise实现超时处理
  3. 使用promise封装处理ajax请求

let request = new XMLHttpRequest();

request.onreadystatechange = function () {

}

request.responseType = 'json';

request.open("GET", url);

request.send();

Symbol

  • 前言:ES5中对象的属性名都是字符串,容易造成重名,污染环境

  • 概念:ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:String, Number, boolean, null, undefined, 对象)

  • Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,不是对象。也就是说,由于Symbol值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

  • 特点:

    1. Symbol属性对应的值是唯一的,解决命名冲突问题

    2. Symbol值不能与其他数据进行计算,包括同字符串拼串

    3. for in, for of遍历时不会遍历symbol属性。

  • 使用:

    1. 调用Symbol函数得到symbol

      let symbol = Symbol();

      let obj = {};

      obj[symbol] = 'hello';

    2. 传参标识

      let symbol = Symbol('one');

      let symbol2 = Symbol('two');

      console.log(symbol);// Symbol('one')

      console.log(symbol2);// Symbol('two')

    3. 内置Symbol

      • 除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。
  • Symbol.iterator

    • 对象的Symbol.iterator属性,指向该对象的默认遍历器方法

    • 有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名。

    • 概念: iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制

    • 作用:

      1. 为各种数据结构,提供一个统一的、简便的访问接口;

      2. 使得数据结构的成员能够按某种次序排列

      3. ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。

    • 工作原理:

      • 创建一个指针对象(遍历器对象),指向数据结构的起始位置。

      • 第一次调用next方法,指针自动指向数据结构的第一个成员

      • 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员

      • 每调用next方法返回的是一个包含valuedone的对象,{value: 当前成员的值,done: 布尔值}

    • value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。

    • 当遍历结束的时候返回的value值是undefineddone值为false

  • 原生具备iterator接口的数据(可用for of遍历)

    1. Array

    2. arguments

    3. set容器

    4. map容器

    5. String

  • 使用三点运算符和解构赋值,默认去调用iterator接口

Generator函数

  • 概念:

    1. ES6提供的解决异步编程的方案之一

    2. Generator函数是一个状态机,内部封装了不同状态的数据,

    3. 用来生成遍历器对象

    4. 可暂停函数(惰性求值), yield可暂停,next方法可启动。每次返回的是yield后的表达式结果

  • 特点:

    1. function 与函数名之间有一个星号

    2. 内部用yield表达式来定义不同的状态

      例如:

    function* generatorExample(){
    
        let result = yield 'hello'; // 状态值为hello
    
        yield 'generator'; // 状态值为generator
    
    }
    
    1. generator函数返回的是指针对象,而不会执行函数内部逻辑

    2. 调用next方法函数内部逻辑开始执行,遇到yield表达式停止,返回{value: yield后的表达式结果undefined, done: false/true}

    3. 再次调用next方法会从上一次停止时的yield处开始,直到最后

    4. yield语句返回结果通常为undefined, 当调用next方法时传参内容会作为启动时yield语句的返回值。

    5. yield语句只能用在 Generator 函数里面,用在其他地方都会报错

    6. yield语句如果用在一个表达式之中,必须放在圆括号里面

    7. yield句本身没有返回值,或者说总是返回undefinednext方法可以带一个参数,该参数就会被当作上一个yield语句的返回值.

    8. for...of循环可以自动遍历Generator函数时生成的Iterator对象,且此时不再需要调用next方法

    9. Generator函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在Generator函数体内捕获.

    10. Generator函数返回的遍历器对象,还有一个return方法,可以返回给定的值,并且终结遍历Generator函数.

    11. 如果在 Generator 函数内部,调用另一个 Generator 函数,默认情况下是没有效果的。

    12. 所谓"异步",简单说就是一个任务不是连续完成的,可以理解成该任务被人为分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。

    13. 所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。

    14. 代码不是纵向发展,而是横向发展,很快就会乱成一团,无法管理。因为多个异步操作形成了强耦合,只要有一个操作需要修改,它的上层回调函数和下层回调函数,可能都要跟着修改。这种情况就称为"回调函数地狱"

    15. "协程",意思是多个线程互相协作,完成异步任务。

Iterator(遍历器)的概念

  • 遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作

  • Iterator的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。

  • Iterator的遍历过程是这样的。

    1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。

    2. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。

    3. 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。

    4. 不断调用指针对象的next方法,直到它指向数据结构的结束位置。

    • 每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含valuedone两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
  • 在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、SetMap结构

  • 字符串是一个类似数组的对象,也原生具有Iterator接口

  • return方法的使用场合是,如果for...of循环提前退出(通常是因为出错,或者有break语句或continue语句),就会调用return方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法

async函数(源自ES2017)

  • 概念: 真正意义上去解决异步回调的问题,同步流程表达异步操作

  • 本质: Generator的语法糖

  • 语法:

    async function foo(){

    await 异步操作;

    await 异步操作;

    }

  • 特点:

    1. 不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行

    2. 返回的总是Promise对象,可以用then方法进行下一步操作

    3. async取代Generator函数的星号*await取代Generatoryield

    4. 语意上更为明确,使用简单,经临床验证,暂时没有任何副作用

    5. async函数内部return语句返回的值,会成为then方法回调函数的参数

      async 函数有多种使用形式。

    6. async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。

    7. 只要一个await语句后面的 Promise 变为reject,那么整个async函数都会中断执行。

    8. for...of循环用于遍历同步的 Iterator 接口。新引入的for await...of循环,则是用于遍历异步的 Iterator 接口。

  • 使用注意点

    1. await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try...catch代码块中。

    2. 多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。

    3. await命令只能用在async函数之中,如果用在普通函数,就会报错。

  • 函数声明 async function foo() {}

  • 函数表达式 const foo = async function () {};

  • 对象的方法 let obj = { async foo() {} }; obj.foo().then(...)

  • Class 的方法

class Storage { 
    constructor() { 
        this.cachePromise = caches.open('avatars'); 
        } 
    async getAvatar(name) { 
        const cache = await this.cachePromise; 
        return cache.match(`/avatars/${name}.jpg`); 
        }
 } 
 const storage = new Storage(); 
 storage.getAvatar('jake').then(…); 
  • 箭头函数 const foo = async () => {};

异步Generator函数

  • 就像 Generator 函数返回一个同步遍历器对象一样,异步 Generator 函数的作用,是返回一个异步遍历器对象。

  • 在语法上,异步 Generator 函数就是async函数与 Generator 函数的结合。

  • 通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

  • 类的内部所有定义的方法,都是不可枚举的

  • 一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

  • Class不存在变量提升

  • 子类的原型的原型,是父类的原型。

  • 类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

  • 如果构造函数不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的

    1. 通过class定义类/实现类的继承

    2. 在类中通过constructor定义构造方法

    3. 通过new来创建类的实例

    4. 通过extends来实现类的继承

    5. 通过super调用父类的构造方法

    6. 重写从父类中继承的一般方法

  • super这个关键字,既可以当作函数使用,也可以当作对象使用。

    • 第一种情况,super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。

    • 第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

es6字符串的扩展

  1. includes(str) : 判断是否包含指定的字符串

  2. startsWith(str) : 判断是否以指定字符串开头

  3. endsWith(str) : 判断是否以指定字符串结尾

  4. repeat(count) : 重复指定次数

es6数值的扩展

  1. 二进制与八进制数值表示法: 二进制用0b, 八进制用0o

  2. Number.isFinite(i) : 判断是否是有限大的数

  3. Number.isNaN(i) : 判断是否是NaN

  4. Number.isInteger(i) : 判断是否是整数

  5. Number.parseInt(str) : 将字符串转换为对应的数值

  6. Math.trunc(i) : 直接去除小数部分

es6数组的扩展

  1. Array.from(v) : 将伪数组对象或可遍历对象转换为真数组

  2. Array.of(v1, v2, v3) : 将一系列值转换成数组

  3. find(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素

  4. findIndex(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素下标

es6对象方法的扩展

  1. Object.is(v1, v2)

    • 判断2个数据是否完全相等
  2. Object.assign(target, source1, source2..)

    • 将源对象的属性复制到目标对象上
  3. 直接操作__proto__属性

    let obj2 = {};

    obj2.__proto__ = obj1;

拷贝

  1. 数据类型:

    • 数据分为基本的数据类型(String, Number, boolean, Null, Undefined)和对象数据类型

    • 基本数据类型:

      特点: 存储的是该对象的实际数据

    • 对象数据类型:

      特点: 存储的是该对象在栈中引用,真实的数据存放在堆内存里

  2. 复制数据

    • 基本数据类型存放的就是实际的数据,可直接复制

      let number2 = 2;

      let number1 = number2;

    • 克隆数据:对象/数组

  3. 区别: 浅拷贝/深度拷贝

    • 判断: 拷贝是否产生了新的数据还是拷贝的是数据的引用

    • 知识点:对象数据存放的是对象在栈内存的引用,直接复制的是对象的引用

      let obj = {username: 'kobe'}

      let obj1 = obj; // obj1 复制了obj在栈内存的引用

  4. 常用的拷贝技术

    1. arr.concat(): 数组浅拷贝

    2. arr.slice(): 数组浅拷贝

      arr.assign():数组浅拷贝

      直接赋值给一个变量:浅拷贝

    3. JSON.parse(JSON.stringify(arr/obj)): 数组或对象深拷贝, 但不能处理函数数据

    4. 浅拷贝包含函数数据的对象/数组

    5. 深拷贝包含函数数据的对象/数组

  5. 浅拷贝(对象/数组):

    • 特点“拷贝的引用,修改拷贝以后的数据会影响原数据,使得原数据不安全
  6. 深拷贝(深度克隆):

    • 特点:拷贝的时候生成新数据,修改拷贝以后的数据不会影响原数据
  7. 思考:如何实现深拷贝

    • 拷贝的数据里有对象/数组

    • 拷贝的数据里不能有对象/数组,

    • 即使有对象/数组可以继续遍历对象,数组拿到里边每一项值,一直到拿到是基本数据类型,然后再去复制,就是深拷贝

  8. 知识点储备

    • 如何判断数据类型: arr---->Array null ---->Null

    1. typeof返回的数据类型:String,Number,Boolean,Undefined,Object,Function

    2. Object.prototype.toString.call(obj)

    let result = ‘abcd’;

    result = null;

    result=[1,3];

    console.log(Object.prototype.toString.call(result).slice(8,-1));

  9. for in 循环对象

    let obj = {username:‘kobe’,age:39};
    
    for(let i in obj){
    
        console.log(i);
    
    }
    
    let arr=[1,3,'abc'];
    
    for(let i in arr){
    
        console.log(i);
    
    }
    
    
  10. 定义检测数据类型的功能函数

    function checkedType(target ){
    
        return Object.prototype.toString.call(target).slice(8,-1)
    
    }
    
    
  11. 实现深度克隆--->对象/数组

    function clone(target){
    
        判断拷贝的数据类型
    
        let result,targetType = checkedType(target);
    
        if(targetType ==='Object'){
    
            result={ };
    
        }else if(targetType ==='Array'){
    
            result=[ ];
    
        }else{
    
            return target;
        }
    }
    
    
  12. 遍历目标数据

for(let i in target){

    获取遍历数据结构的每一项值

    let value = target[i];

    if(checkedType(value)==='Object'|| checkedType(value)==='Array'){

        对象/数组里嵌套了对象/数组,继续遍历获取到的value值
        result[i]=clone(value)
        }else{
        result[i]=value;
        }
    }
    return result;
}

set和get

  1. Set容器 : 无序不可重复的多个value的集合体

    Set实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)

    • Set()

    • Set(array)

    • add(value)添加某个值,返回Set结构本身

    • delete(value)删除某个值,返回一个布尔值,表示删除是否成功

    • has(value)返回一个布尔值,表示该值是否为Set的成员

    • clear()清除所有成员,没有返回值

    • size属性返回Set结构的成员总数

  2. Map容器 : 无序的 key不重复的多个key-value的集合体

    类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

    • Map()

    • Map(array)

    • set(key, value)set方法设置key所对应的键值,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就新生成该键

    • get(key)get方法读取key对应的键值,如果找不到key,返回undefined

    • delete(key)delete方法删除某个键,返回true。如果删除失败,返回false

    • has(key)has方法返回一个布尔值,表示某个键是否在Map数据结构中

    • clear()clear方法清除所有成员,没有返回值

    • size属性返回Map结构的成员总数

  • for(let value of target){}循环遍历

    1. 遍历数组

    2. 遍历Set

    3. 遍历Map

    4. 遍历字符串

    5. 遍历伪数组

  1. 指数运算符(幂): **

  2. Array.prototype.includes(value): 判断数组中是否包含指定value

Proxy

  • Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改

  • Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

  • get方法用于拦截某个属性的读取操作

  • set方法用来拦截某个属性的赋值操作

  • apply方法拦截函数的调用、callapply操作。

  • apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。

  • has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。

  • construct方法用于拦截new命令

  • deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除

  • defineProperty方法拦截了Object.defineProperty操作

  • getOwnPropertyDescriptor方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined

  • getPrototypeOf方法主要用来拦截获取对象原型。

  • isExtensible方法拦截Object.isExtensible操作

  • ownKeys方法用来拦截对象自身属性的读取操作。

  • preventExtensions方法拦截Object.preventExtensions()。该方法必须返回一个布尔值,否则会被自动转为布尔值。

  • setPrototypeOf方法主要用来拦截Object.setPrototypeOf方法。

  • Proxy.revocable方法返回一个可取消的 Proxy 实例。

  • Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理

reflect

  • Reflect.get(target, name, receiver)

  • Reflect.get方法查找并返回target对象的name属性,如果没有该属性,则返回undefined

  • Reflect.has方法对应name in obj里面的in运算符

  • Reflect.construct方法等同于new target(...args),这提供了一种不使用new,来调用构造函数的方法

  • Reflect.getPrototypeOf方法用于读取对象的__proto__属性,对应Object.getPrototypeOf(obj)Reflect.getPrototypeOfObject.getPrototypeOf的一个区别是,如果参数不是对象,Object.getPrototypeOf会将这个参数转为对象,然后再运行,而Reflect.getPrototypeOf会报错。

  • Reflect.apply方法等同于Function.prototype.apply.call(func, thisArg, args),用于绑定this对象后执行给定函数。

  • 一般来说,如果要绑定一个函数的this对象,可以这样写fn.apply(obj, args),但是如果函数定义了自己的apply方法,就只能写成Function.prototype.apply.call(fn, obj, args)

  • Reflect.defineProperty方法基本等同于Object.defineProperty,用来为对象定义属性

  • Reflect.getOwnPropertyDescriptor基本等同于Object.getOwnPropertyDescriptor

  • Reflect.isExtensible方法对应Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展

  • Reflect.preventExtensions对应Object.preventExtensions方法,用于让一个对象变为不可扩展。它返回一个布尔值,表示是否操作成功

  • Reflect.ownKeys方法用于返回对象的所有属性,基本等同于Object.getOwnPropertyNamesObject.getOwnPropertySymbols之和

  • 观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行

  • 修饰器(Decorator)是一个函数,用来修改类的行为。

  • 修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。

export 命令

  • 模块功能主要由两个命令构成:exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。其他 JS 文件就可以通过import命令加载这个模块

  • <script>标签打开deferasync属性,脚本就会异步加载。渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。

  • deferasync的区别是:前者要等到整个页面正常渲染结束,才会执行;后者一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。

  • 浏览器加载 ES6 模块,也使用<script>标签,但是要加入type="module"属性。

  • 采用require命令加载 ES6 模块时,ES6 模块的所有输出接口,会成为输入对象的属性