es6(写的很乱,就是简单总结,勿入)

590 阅读30分钟

一. const、let

let只在代码块中有效,var在全局有效
let不能重复声明,var可以
let适合for循环
let不存在变量提升,var有
const声明常量时必须初始化
const保证的是变量指向的内存地址的数据不变,而不是变量的值不变。
 const obj = {
            person: {
                age: 20,
                gender:'nan'
            }
        }
        obj.name = 'zhanghao'
        obj.name = 'hh'
        obj.person.age = 30
//这里是可以改变的   就是允许将引用类型重新赋值  内部变量是可以改变的
        console.log(obj)

	//这里就是指的改变     所以不可以
        // const a = 3
        // a = 4
        // console.log(a)

const的暂时性死区:代码块内如果存在let或者const声明,则代码块会对这些声明的变量从代码块开始就形成了一个封闭的作用域,在代码块内,声明变量之前使用会报错

var PI = "a";
    if (true) {
      console.log(PI); // ReferenceError:无法在初始化之前访问“PI”
      const PI = "3.1415926";
    }
-----------------------------//由于块级作用域后有变量的声明  所以会报错
var str = "Hi!";
    if (true) {
      console.log(str); // ReferenceError:无法在初始化之前访问“str”
      let str = "Hello World!";
    }

二. symbol()

symbol()

  • 表示独一无二的值

  • Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

  • 如果 Symbol 的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol 值。

  • 注意,Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。

  • Symbol 值不能与其他类型的值进行运算,会报错。

  • Symbol 值也可以转为布尔值和字符串,但是不能转为数值。

  • 同理,在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。否则就认为是保存Symbol()的值为键值,而非Symbol()

    let s = Symbol();
    
    let obj = {
      //这里必须使用【】,不然会认为是s为键值
      [s]: function (arg) { ... }
    };
    
    obj[s](123);
    

    image-20200625205150621

  • 注意,Symbol 值作为对象属性名时,不能用点运算符。只能用[]

属性:

  • description,获取Symbol()的描述,就是()内的内容

获取Symbol()作为对象键值

  • Object.getownPropertySymbols()

Symbol.for()

  • image-20200626192421755

  • 接收一个字符串作为唯一标识,也可以为空。

  • 利用这个唯一标识,可以重复利用同一个Symbol()

  • Symbol.for()为 Symbol 值登记的名字,是全局环境的,不管有没有在全局环境运行。

  • 每次调用Symbol.for(),都会被登记,以便下次调用的时候可以找到,如果全局有了一样的symbol(),就不会重新创建,而Symbol(),每次调用都会创建一个不同的Symbol()

Symbol.keyFor()

  • 返回一个已登记(就是利用Symbol.for()创建的)的 Symbol 类型值的key

image-20200514081636746

image-20200514082554547

三. 遍历器(iterator)

image-20200513194506602

image-20200514084241667

image-20200514084805637

image-20200514085502153

四. async await

  • async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

  • image-20200514090134877

  • await 的返回值 是后面的函数的return 语句值

  • image-20200514092619750

五. 克隆

image-20200514095850417

注意: json方法不能克隆数组或者对象中的函数

//判断数据的类型
Object.prototype.toString.call('目标数据').slice(8, -1)
// 深拷贝
/**
 * 
 * @param {Object} newobj 
 * @param {Object} oldobj 
 */
function deepcype(newobj,oldobj){
    for(let i in oldobj){
        let item = oldobj[i];
        // 判断是否是数组  typeof   是判断是什么类型的
        if(item instanceof Array){
            newobj[i] = [];
            deepcype(newobj[i],item);
        }else if(item instanceof Object){
            // 判断类型是否为对象
            newobj[i] = {};
            deepcype(newobj[i],item);
        }else{
            // 普通类型
            newobj[i] = item;
        }
    }
    return newobj;
}

image-20200514114543469

image-20200514114750224

六. set() map()

set()

  • image-20200626094442142

  • 可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。

  • 返回一个具有size的对象。而属性值就是我们加入的元素,而属性名是索引从零开始。但是实际上个对象的键值和键名都是一样的。

  • 向 Set 加入值的时候,不会发生类型转换

  • 它类似于精确相等运算符(===),主要的区别是向 Set 加入值时认为NaN等于自身,而精确相等运算符认为NaN不等于自身。

操作方法

  • Set.prototype.add(value):添加某个值,返回 Set 结构本身。
  • Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。表示该属性值,而不是键值。
  • Set.prototype.clear():清除所有成员,没有返回值。

Array.from方法可以将 Set 结构转为数组。

const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);

数组去重

function dedupe(array) {
  return Array.from(new Set(array));
}

dedupe([1, 1, 2, 3]) // [1, 2, 3]

遍历方法

  • Set.prototype.keys():返回键名的遍历器
  • Set.prototype.values():返回键值的遍历器
  • Set.prototype.entries():返回键值对的遍历器,数组
  • Set.prototype.forEach():使用回调函数遍历每个成员

*由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。*就等同于数组 就是可以去除数组中重复的数据

let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
  console.log(item);//返回键值对数组,而且键和值是一样的
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]

set结构的默认遍历器接口(Symbol.iterator属性),就是values方法。,所以直接就可以用for of遍历,不需要利用values()

Set.prototype[Symbol.iterator] === Set.prototype.entries//true

set() 可以结合... 来利用数组的方法进行操作

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}

//如果想在遍历操作中,同步改变原来的 Set 结构
// 方法一
let set = new Set([1, 2, 3]);
set = new Set([...set].map(val => val * 2));
// set的值是2, 4, 6

// 方法二
let set = new Set([1, 2, 3]);
set = new Set(Array.from(set, val => val * 2));
// set的值是2, 4, 6

WeakSet()

  • WeakSet 的成员只能是对象,而不能是其他类型的值。

    image-20200626092312640

  • WeakSet 可以接受一个数组或类似数组的对象作为参数。(实际上,任何具有 Iterable 接口的对象,都可以作为 WeakSet 的参数。)

Map()

  • image-20200626094415976

  • 由于Object只接受字符串作为键值(会被转换),就引出了Map,可以是任意类型的数据结构作为键值

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

  • Map 也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。

        let secondArr = [['name', 'zh'], ['age', 20]]
        let map = new Map(secondArr)
        console.log(map);
        
        // 等价于
        let map = new Map()
        secondArr.forEach(([key, value]) => map.set(key, value))
        console.log(map)
    
  • 任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数。

  • 如果对同一个键多次赋值,后面的值将覆盖前面的值。

  • 如果读取一个未知的键,则返回undefined

  • 注意,只有对同一个对象的引用,Map 结构才将其视为同一个键。这一点要非常小心。

    const map = new Map();
    //两个数组的引用不同,所以是不同的键名
    map.set(['a'], 555);
    map.get(['a']) // undefined
    
  • Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。他的依据是全等(===),但是NaN除外

  • Map 结构的默认遍历器接口(Symbol.iterator属性),就是entries方法。所以直接可以利用for of 遍历

    Map.prototype[Symbol.iterator] === Map.prototype.entries//true
    

它的参数是一个二维数组 就是一个数组里面只能写两个值 一个当做key 一个当做value

const map = new Map([[key,value], [key, value], ...])

七. proxy(代理)

就是在你对对象进行一系列操作时,他会将你拦截,并做出一些操作,来使得你可以操作成功,如果他没有做出操作,则你对对象的修改等操作就不会成功

原理: 改变了目标对象中的this指向,this指向了proxy

  • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.fooproxy['foo']

    • get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身**(严格地说,是操作行为所针对的对象)**,其中最后一个参数可选。
    • 数组实现负数取值
    function createArray(...elements) {
      let handler = {
        get(target, propKey, receiver) {
          let index = Number(propKey);
          if (index < 0) {
            propKey = String(target.length + index);
          }
          return Reflect.get(target, propKey, receiver);
        }
      };
    
      let target = [];
      target.push(...elements);
      return new Proxy(target, handler);
    }
    
    let arr = createArray('a', 'b', 'c');
    arr[-1] // c
    

    image-20200624131134689

  • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。

    • set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
    • image-20200624142417172
    • image-20200624142644540
  • has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。

    • has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。
    • has方法可以接受两个参数,分别是目标对象、需查询的属性名。
    • image-20200624144042585
    • image-20200624150106098
  • deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。

    • deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。
  • ownKeys(target):拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。

    • 该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。

    • image-20200624153709100

    • 如果目标对象自身包含不可配置的属性,则该属性必须被ownKeys()方法返回,否则报错。

    • var obj = {};
          Object.defineProperty(obj, 'a', {
            configurable: false,
            enumerable: true,
            value: 10
          }
          );
      
          var p = new Proxy(obj, {
            ownKeys: function (target) {
              return ['b'];
            }
          });
      
          Object.getOwnPropertyNames(p)//报错,必须返回不可配置的属性
      
    • 另外,如果目标对象是不可扩展的(non-extensible),这时ownKeys()方法返回的数组之中,必须包含原对象的所有属性,且不能包含多余的属性,否则报错。

    • var obj = {
        a: 1
      };
      
      Object.preventExtensions(obj);//不可拓展
      
      var p = new Proxy(obj, {
        ownKeys: function(target) {
          return ['a', 'b'];
        }
      });
      
      Object.getOwnPropertyNames(p)
      // Uncaught TypeError: 'ownKeys' on proxy: trap returned extra keys but proxy target is non-extensible
      
  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。

    • getOwnPropertyDescriptor()方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined
    • image-20200624155507834
  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。

    • 参数一:目标对象
    • 参数二:属性值
    • 参数三:对象属性的描述
      const obj = {name: 'zh'}
        const proxy = new Proxy(obj, {
          defineProperty(target, prop, descriptor) {
            // 拦截后添加对象和描述,才是正真的添加,什么事都不做不会添加的
            Object.defineProperty(target, prop, descriptor)
            return true
          }
        })
        console.log(proxy)
    
        Object.defineProperty(proxy, 'age', {value: 20})
        console.log(proxy.age)
    
  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。

  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。

    • getPrototypeOf()方法主要用来拦截获取对象原型。具体来说,拦截下面这些操作。
      • Object.prototype.__proto__
      • Object.prototype.isPrototypeOf()
      • Object.getPrototypeOf()
      • Reflect.getPrototypeOf()
      • instanceof
  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。

    • isExtensible()方法拦截Object.isExtensible()操作。
    • image-20200624153241807
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。

    • image-20200624155004470
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)

    • apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

    • construct方法可以接受三个参数。
      • target:目标对象
      • args:构造函数的参数对象
      • newTarget:创造实例对象时,new命令作用的构造函数(下面例子的p
  • Proxy.revocable(): 取消代理

    • image-20200624155308686

var proxy = new Proxy(target, handler);(其实就可以把proxy实例看成一个对象)

new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

如果handler没有设置任何拦截,那就等同于直接通向原对象。

var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
proxy.a = 'b';
target.a // "b"

上面代码中,handler是一个空对象,没有任何拦截效果,访问proxy就等同于访问target

八. promise

第一次还是用promise()来写,下面要确定成功后就用promise.resolve()调用就行

image-20200522101855626

九.模板字符串

  • 模板字符串中嵌入变量,需要将变量名写在${}之中。

  • 大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。

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

  • 模板字符串甚至还能嵌套。

十. 字符串方法

静态方法

  • String.fromCodePoint()
    • 作用,就是将unicode码转换成字符串
    • 为了弥补String.fromCharCode()的不足
  • String.raw()

    • 不懂

实例方法

  • codePointAt()

    • JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符(Unicode 码点大于0xFFFF的字符),JavaScript 会认为它们是两个字符。

    • 返回字符串对应位置的编码

    • charCodeAt()的增强版

    • 如果不传入下标就返回的是第一个字符的编码

    • image-20200624170557519

    • 测试字符是由2个字节编码的还是4个字节编码的

      function is32Bit(c) {
        return c.codePointAt(0) > 0xFFFF;//两个字符的最大编码
      }
      
      is32Bit("𠮷") // true
      is32Bit("a") // false
      
  • includes(),startsWith(),endsWith()

    • includes():返回布尔值,表示是否找到了参数字符串。
    • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
    • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
    • es5的方法
      • indexOf(),返回下标值,没找到返回-1
      • lastIndexOf(),返回下标值,没找到返回-1
    以上方法都有两个参数,参一:匹配的字符串,参二:开始查询的下标
    • search(): 返回下标,如果没有找到返回-1,只有一个参数:字符串/正则
  • repeat()

    • 返回一个新字符串,表示将原字符串重复n次。
  • padStart(),padEnd()

    • padStart()用于头部补全。

      • 返回值:指定长度的字符串,
      • 第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串
    • padEnd()用于尾部补全。

      • 返回值:指定长度的字符串,

      • 第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串

    注意:

    • 如果原字符串的长度,等于或大于最大长度,则字符串补全不生效,返回原字符串。

    • 如果用来补全的字符串与原字符串,两者的长度之和超过了最大长度,则会截去超出位数的补全字符串。

    • 如果省略第二个参数,默认使用空格补全长度。

    用途:

    • 补充数位
    • 提示字符串格式
      '12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
      '09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
      
  • trimStart(),trimEnd() ,trimLeft(),trimRight()

    • trimStart(),trimLeft()只消除头部空格

    • trimEnd(),trimRight()只消除尾部空格

    • 除了空格键,这两个方法对字符串头部(或尾部)的 tab 键、换行符等不可见的空白符号也有效。

  • matchAll()

    • 一次性取出所有匹配。

    • 返回的是一个遍历器(Iterator),而不是数组。
      const string = 'test1test2test3';
      const regex = /t(e)(st(\d?))/g;
      
      for (const match of string.matchAll(regex)) {
        console.log(match);
      }
      // ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"]
      // ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"]
      // ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"]
      
    • es5的match

       const str = 'test1test2test3'
       //正则不加标识时,会返回同整个exec方法一样的数组,如果加上了g,则只会返回匹配到的字符串
       const reg = /t(e)(st\d{1})/    
       console.log(str.match(reg))
      

      image-20200624175407571

       const str = 'test1test2test3'
       //正则不加标识时,会返回同整个exec方法一样的数组,如果加上了g,则只会返回匹配到的字符串
       const reg = /t(e)(st\d{1})/g    
       console.log(str.match(reg))
      

      image-20200624175505436

十一. 数值

静态方法

  • Number.isFinite(), Number.isNaN()

    • 只对传入的数字有效,不会调用Number()进行转换

    • Number.isFinite()对于传入的非数字的直接返回false

    • Number.isNaN() 只有真正的NaN才会返回true

    • es5的全局方法

      • isFinite(),先调用Number()将非数值的值转为数值,再进行判断
      • isNaN(),先调用Number()将非数值的值转为数值,再进行判断
  • Number.parseInt(),Number.parseFloat()

    • 和全局方法使用完全一样

    • 目的:是逐步减少全局性方法,使得语言逐步模块化。

  • Number.isInteger()

    • 判断一个数是否为整数

    • 会因为精度的不同而出错

    • 如果参数不是数值,返回false

  • Number.EPSILON

    • 表示最小精度的数

    • 等于 2 的 -52 次方。

  • Number.isSafeInteger()

    • 用来判断一个整数是否落在这个范围之内。(Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER)

    • 非整数的参数都返回false

十二.函数

函数形参最后可以加上逗号,不会报错

默认参数

  • 参数变量是默认声明的,所以不能用letconst再次声明。

  • 使用参数默认值时,函数不能有同名参数。没有默认参数时,可以有相同的形参

  • 参数默认值是惰性求值的。每次调用都会重新计算

  • 如果函数形参中有对象,如果不传参数就会报错,所以我们可以给对象赋一个空对象,然后就可结构赋值。如果传递参会会把默认对象替换掉

默认参数的位置

  • 默认参数一般不放在中间,只有这样,才可以省略参数

  • 如果放中间,我们只能用undefined来触发参数等于默认值,而null,就不可以触发默认值,会将函数参数直接赋值为null

函数的length属性

  • 指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。

  • length属性表示函数希望(预期)接收的命名参数的个数

  • 如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。

    (function(...args) {}).length // 0
    (function (a = 0, b, c) {}).length // 0
    (function (a, b = 1, c) {}).length // 1
    

默认函数的作用域

  • 如果形参有默认值,则函数的形参会自立一个独立的作用域,不会和函数体内的作用域冲突

严格模式

  • 只要参数使用了默认值、解构赋值、或者扩展运算符,就不能显式指定严格模式。

  • 原因:就是只有在函数内部才可以知道参数是否合理

    • 函数内部的严格模式,同时适用于函数体和函数参数。

    • 但是,函数执行的时候,先执行函数参数,然后再执行函数体。

    • 这样就有一个不合理的地方,只有从函数体之中,才能知道参数是否应该以严格模式执行,但是参数却应该先于函数体执行。

  • 解决:就是既可以检验参数的合理性又可以检测函数体的合理性

    • image-20200624211838681

函数的name属性

  • image-20200624212212309

尾调用

  • 函数执行的最后一步是执行另一个函数

  • 尾调用模式仅在严格模式下生效。

  • 尾调用优化:简单来说就是当函数执行到最后一步,就可以不必记录他的状态,将活动对象指向内部执行的函数。但是如果内部函数还是需要用到外部函数的变量,则还是得记录外部函数的调用帧

    • 尾调用之所以与其他调用不同,就在于它的特殊的调用位置。

    • 我们知道,函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。

    • 如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧。等到B运行结束,将结果返回到AB的调用帧才会消失。如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。

    • 尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。

  • image-20200624215939893

尾递归:不懂

try catch中catch后的参数可以省略

当你不需要接收错误时,你不必要写catch后的参数,但是catch一定要写

try {
      throw new Error('这是一个错误')
    } catch {
    }

十三.数组

扩展运算符

  • 该运算符主要用于函数调用。只能出现在形参的最后,实参可以是任何位置

  • 如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。

  • 注意,只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。

  • 扩展运算符后面还可以放置表达式。

  • 如果扩展运算符后面是一个空数组,则不产生任何效果。

  • 扩展运算符可以与解构赋值结合起来,用于生成数组。

  • 如果扩展运算符未接收到参数,则为空数组

  • 扩展运算符还可以将字符串转为真正的数组。

    [...'hello'] == ['h', 'e', 'l', 'l', 'o']
    

    image-20200625073228148

  • 任何定义了遍历器(Iterator)接口的对象,都可以用扩展运算符转为真正的数组。

  • 扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符。

静态方法

  • Array.from()

    • 将两类对象(类似数组的对象,可遍历)转为真正的数组

    • 类似数组的对象本质特点: 必须有length属性,如果转换后想要有值,则须要对象的键是数字

      image-20200625075021291

    • 参数:

      • 参数一: 对象

      • 参数二: 函数(同数组的map方法),就是对转化后的数组做一些统一的操作

      • 如果map函数里面用到了this关键字,还可以传入Array.from的第三个参数,用来绑定this

    • 返回值:一个处理后的数组

      Array.from(arrayLike, x => x * x);
      // 等同于
      Array.from(arrayLike).map(x => x * x);
      
      Array.from([1, 2, 3], (x) => x * x)
      // [1, 4, 9]
      
    • Array.from()的另一个应用是,将字符串转为数组,然后返回字符串的长度。

      function length(str) {
      	return Array.from(str).length
      }
      
  • Array.of()

    • 将参数组成一个数组

    • 返回参数值组成的数组。如果没有参数,就返回一个空数组。

    • 为了弥补Array(),new Array() 一个参数时的不足

实例方法

  • copyWithin()

    • 参数:

      • target(必需):从该位置开始被替换。如果为负值,表示倒数。

      • start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。

      • end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。

    • 返回值: 改变的原数组

  • find()

    • 用于找到第一个符合条件的值,然后返回,如果没找到,返回undefined

    • 参数: 一个回调函数,改变this指向

  • findIndex()

    • 用于找到第一个符合条件值得下标,然后返回,如果没找到,返回-1

    • 参数: 一个回调函数,改变this指向

    • image-20200625082225787

  • fill()

    • 使用给定值,填充一个数组。会替换掉数组中已有的元素

    • 如果只有一个参数时,则默认全部填充

    • 参数:

      • 填充的数

      • 填充的起始位置

      • 填充的结束位置(不包括)

    • 注意,如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。

  • entries(),keys() 和 values()

    • 遍历数组

    • keys()是对下标的遍历

    • values()是对元素的遍历

    • entries()是对下标和元素的遍历

    • 它们都返回一个遍历器对象

    • 结合for of遍历数组 或者 遍历器对象的next()方法

  • includes()

    • 判断参数是否存在数组元素中

    • 参数:

      • 元素

      • 开始查找的下标(可以为负数)如果这时它大于数组长度,则会重置为从0开始

    • es5的indexOf()

      • 他在查找时是利用的全等运算符,所以NaN === NaN会返回-1

      • 但是[NaN].includes(NaN)会返回true

  • flat()

    • 将多维数组转换成一维数组

    • 参数:想要展开的层数。默认为一层

    • 该方法返回一个新数组,对原数据没有影响。

    • 如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数。

    • 如果原数组有空位,会舍弃空位。

  • flatMap()

    • 将多维数组进行一些映射操作后,展开一层

    • 其实我们可以利用数组的map方法和flat方法做到flatMap而且还可以展开处理后的多层数组

    • 参数:

      • 一个函数

      • 改变this指向

数组的空位

  • image-20200625093752138

  • es6中的空位都是undefined

十四.对象

一些细节:

  • 如果我们用【】的方式给对象设置属性,如果【】内直接写字符,而不是字符串,则会报错,除非该字符是已经定义过的变量。a[b] 和 a['b']是不一样的。

  • 我们必须要知道一点,对象的键值都是字符串,即使我们写的是数字,他也会转换成字符串

简写:

  • 简写的对象方法不能用作构造函数,会报错。

函数名:

  • 如果是存值函数和取值函数,则函数名会在前面加上get或set

enumerablefalse的属性,下列操作会忽略该属性:

  • for...in循环:只遍历对象自身的和继承的可枚举的属性。

  • Object.keys():返回对象自身的所有可枚举的属性的键名。

  • JSON.stringify():只串行化对象自身的可枚举的属性。

  • Object.assign(): 忽略enumerablefalse的属性,只拷贝对象自身的可枚举的属性。

in操作符:

具体请看这里

  • prop in objectName

    • prop

      一个字符串类型或者 symbol 类型的属性名或者数组索引(非symbol类型将会强制转为字符串)。

    • objectName

      检查它(或其原型链)是否包含具有指定名称的属性的对象。

super关键字

  • super,指向当前对象的原型对象。

  • 注意,super关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。

  • 目前,只有对象方法的简写法可以让 JavaScript 引擎确认,定义的是对象的方法。

    image-20200625132312730

扩展运算符、解构赋值

注意:解构赋值和扩展运算符没有任何关系
  • 使用扩展运算符时,只能将去除的键和值放在对象中,其他任何地方都会报错

    let obj = {
          name: 'zh',
          age: 20
        }
    let obj1 = {...obj}
    console.log(obj1)
    
    console.log(...obj)//报错
    
  • 对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的、但尚未被读取的属性,分配到指定的对象上面。

  • 由于解构赋值要求等号右边是一个对象,所以如果等号右边是undefinednull,就会报错,因为它们无法转为对象。

  • 解构赋值和扩展运算符结合使用时,扩展运算符只能写在最后一个位置,来获取没有被获取的值

  • 如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。

  • 注意,解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。

  • 另外,扩展运算符的解构赋值,不能复制继承自原型对象的属性。

  • 变量声明语句之中,如果使用解构赋值,扩展运算符后面必须是一个变量名,而不能是一个解构赋值表达式。如果没有用解构,可以直接使用{}的

    image-20200625133908914

链判断运算符

  • 如果读取对象内部的某个属性,往往需要判断一下该对象是否存在。

  • 对象是否为nullundefined。如果是的,就不再往下运算,而是返回undefined

  • 为了避免过多的逻辑判断,就出现了?.

    let yy = {a: {b:{c:1}}}
    console.log(yy?.a?.b?.c?.d)//undefined
    
  • 判断对象方法是否存在,如果存在就立即执行的例子。

    • 函数名?.()

    • ?.运算符相当于一种短路机制,只要不满足条件,就不再往下执行。

    • 如果属性链有圆括号,链判断运算符对圆括号外部没有影响,只对圆括号内部有影响。

    • 右侧不得为十进制数值,会报错

Null判断运算符

  • 就是当对象的属性值为false,空字符串,0,如果用||来给对象赋默认值,那么就会使得前面情况的值也被赋值成默认值
  • ??

  • 它的行为类似||,但是只有运算符左侧的值为nullundefined时,才会返回右侧的值。

静态方法:

  • Object.is()

    • 等同于===,但是解决了NaN 不等于NaN 和 0 === -0得问题

    • es5得解决方案:

      Object.defineProperty(Object, 'is', {
        value: function(x, y) {
          if (x === y) {
            // 针对+0 不等于 -0的情况
            return x !== 0 || 1 / x === 1 / y;
          }
          // 针对NaN的情况
          return x !== x && y !== y;
        },
        configurable: true,
        enumerable: false,
        writable: true
      });
      
  • Object.assign()

    • 将源对象(source)的所有可枚举属性,复制到目标对象(target)。

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

    • 属性名为 Symbol 值的属性,也会被Object.assign拷贝。

    • 方法的第一个参数是目标对象,后面的参数都是源对象。

    • 注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

    • 如果只有一个参数,Object.assign会直接返回该参数。即目标对象。

    • 如果该参数不是对象,则会先转成对象,然后返回。

    • 由于undefinednull无法转成对象,所以如果它们作为参数,就会报错。

    • image-20200626193600453

    • image-20200626194300375

  • Object.getOwnPropertyDescriptors()

    • 返回指定对象所有自身属性(非继承属性)的描述对象。

十五. reflect

  • Object对象的一些明显属于语言内部的方法,放到Reflect对象上。

静态方法:

以下方法的参数:target:是对象, args: 是数组,name: 是属性名, value是属性值,prototype: 是原型,receiver: 不详, desc: 是对象属性的描述,thisArg: 是this指向的对象

Reflect的静态方法第一个参数不是对象的都会报错,而Object可能报错可能不报错

  • Reflect.apply(target, thisArg, args)

    • target表示调用的函数,thisArg表示this指向,args表示参数

    • image-20200626200333561

  • Reflect.construct(target, args)

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

    • 得到对象的属性值

  • Reflect.set(target, name, value, receiver)

    • 设置对象得属性值

  • Reflect.defineProperty(target, name, desc)

  • Reflect.deleteProperty(target, name)

  • Reflect.has(target, name)

    • 判断该对象是否有传入的属性
  • Reflect.ownKeys(target)

    • 返回该对象的所有属性,但是不包含继承的
    • Reflect.ownKeys方法用于返回对象的所有属性,基本等同于Object.getOwnPropertyNamesObject.getOwnPropertySymbols之和。
  • Reflect.isExtensible(target)

    • 判断对象是否可以扩展
  • Reflect.preventExtensions(target)

    • 阻止对象的扩展
  • Reflect.getOwnPropertyDescriptor(target, name)

    • image-20200625220752473
  • Reflect.getPrototypeOf(target)

    • image-20200625215949019
  • Reflect.setPrototypeOf(target, prototype)

    • image-20200625220310777

十六. 迭代器(iterator)

  • ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”

  • Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。

  • 有了遍历器接口,数据结构就可以用for...of循环遍历,也可以使用while循环遍历。通过判断对象的done是否为true

具有iteractor接口的数据

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

iterator的作用

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

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

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

Iterator 的遍历过程:

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

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

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

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

  • 每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含valuedone两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

利用遍历器对象遍历数组

  • 就是调用数组的Symbol.iterator方法,然后返回一个遍历器对象接口

image-20200626161238510

  • 对于原生部署 Iterator 接口的数据结构,不用自己写遍历器生成函数,for...of循环会自动遍历它们。除此之外,其他数据结构(主要是对象)的 Iterator 接口,都需要自己在Symbol.iterator属性上面部署,这样才会被for...of循环遍历。

    const obj = {
      //第一iterator对象属性
      [Symbol.iterator] : function () {
        //返回一个具有next函数的对象
        return {
          next: function () {
            return {
              value: 1,
              done: true
            };
          }
        };
      }
    };
    
  • 一个类部署iterator接口对象

    class RangeIterator {
      constructor(start, stop) {
        this.value = start;
        this.stop = stop;
      }
      
    	//返回该类的实例(即一个iterator对象)
      [Symbol.iterator]() { return this; }
    
      //实例方法,实例(iterator对象)调用该方法
      next() {
        var value = this.value;
        if (value < this.stop) {
          this.value++;
          return {done: false, value: value};
        }
        return {done: true, value: undefined};
      }
    }
    
    function range(start, stop) {
      //返回该类的实例
      return new RangeIterator(start, stop);
    }
    
    for (var value of range(0, 3)) {
      console.log(value); // 0, 1, 2
    }
    

类数组添加iterator最佳方法

  • 并不是所有类似数组的对象都具有 Iterator 接口,一个简便的解决方法,就是使用Array.from方法将其转为数组。

  • image-20200626172832001

调用iterator的场合

  • 解构赋值

    • 对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法。

  • 扩展运算符

    • 扩展运算符(...)也会调用默认的 Iterator 接口。

  • yield*

    • yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
  • 其他场合

    • 由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。

自定义iterator对象

  var str = new String("hi");

    // [...str] // ["h", "i"]

    str[Symbol.iterator] = function () {
      return {
        next: function () {
          if (this._first) {
            this._first = false;
            return { value: "bye", done: false };
          } else {
            return { done: true };
          }
        },
        _first: true
      };
    };

    // [...str] // ["bye"]
    // str // "hi"
    // 调用iterator接口,就是通过判断每次调用后的done是否为true,不断调用next方法
    for(let item of str) {
      console.log(item)//bye
    }

for in / for of的区别

  • image-20200626175724171

其他遍历数组的缺点

  • image-20200626180512739

参考资料