1. ES6及以后新增的常用API解析

1.1. 1. let 和 const 与 var 的区别

  1. let 和 const 引入了块级作用域,var是全局作用域
  2. let 和 const 不存在变量提升,var会变量提升
  3. const 声明的是常量,声明后不可更改,引用类型的数据可以修改属性值。let和var声明的变量,声明之后可以更改
  4. const 声明的时候必须赋值, let 和 var 声明时可以不赋值
  5. var允许重复声明变量,后一个变量会覆盖前一个变量。let 和 const 在同一作用域不允许重复声明变量,会报错。

1.2. 2. 箭头函数

1. 箭头函数与普通函数的最大区别:箭头函数里的this是定义的时候决定的, 普通函数里的this是使用的时候决定的。
2. 简写箭头函数
// 单行不需要写return,单个参数可以不加括号
const square = a => a * a
// 可以用()包裹代替return
const func = () => ({})
3. 箭头函数不能被用作构造函数

构造函数会干嘛? 改变this指向到新实例出来的对象. 箭头函数会干嘛?this指向是定义的时候决定的.

  • new 关键字干了啥?(const whitePlayer = new Player())
    1. 一个继承自 Player.prototype 的新对象 whitePlayer 被创建
    2. whitePlayer.__proto__ 指向 Player.prototype,即 whitePlayer.__proto__ = Player.prototype
    3. 将 this 指向新创建的对象 whitePlayer
    4. 返回新对象
      • 4.1 如果构造函数没有显式返回值,则返回 this
      • 4.2 如果构造函数有显式返回值,是基本类型,比如 number,string,boolean, 那么还是返回 this
      • 4.3 如果构造函数有显式返回值,是对象类型,比如{ a: 1 }, 则返回这个对象{ a: 1 }

1.3. 3. class

class Test {
  _name = ''
  constructor () {
    this.name = 'tommy'
  }

  static getName() {
    return `${this.name}-mark`
  }

  get name() {
    return this._name
  }

  set name(name) {
    this._name = name
  }
}

1.4. 4. 模板字符串

1. 换行方便
console.log(`first
second
third`)
2. 变量拼接方便
const a = 'test'
console.log(`${a}`123)
3. js函数模拟模板字符串
const year = '2021'; 
const month = '10'; 
const day = '01'; 

let template = '${year}-${month}-${day}';
let context = { year, month, day };

const render = tem => {
  return (obj) => {
    // ?的意思是非贪婪模式(https://regex101.com/)
    return tem.replace(/\$\{(.+?)\}/g, (args, key) => {
      return context[key]
    })
  }
}

const str = render(template)({year,month,day});

console.log(str) // 2021-10-01

1.5. 5. 解构

1. 解构默认值和重命名
let { f1 = 'test1', f2: rename = 'test2' } = { f1: 'current1', f2: 'current2'}
console.log(f1, rename) // current1, current2
2. 解构的原理

针对可迭代对象的Iterator接口,通过遍历器按顺序获取对应的值进行赋值.

3. 那么 Iterator 是什么?

Iterator是一种接口,为各种不一样的数据解构提供统一的访问机制。任何数据解构只要有Iterator接口,就能通过遍历操作,依次按顺序处理数据结构内所有成员。ES6中的for of的语法相当于遍历器,会在遍历数据结构时,自动寻找Iterator接口。

4. Iterator有什么用?
  • 为各种数据解构提供统一的访问接口
  • 使得数据解构能按次序排列处理
  • 可以使用ES6最新命令 for of进行遍历
function generateIterator(array) {
    let nextIndex = 0
    return {
        next: () => nextIndex < array.length ? {
            value: array[nextIndex++],
            done: false
        } : {
            value: undefined,
            done: true
        }
    };
}

const iterator = generateIterator([0, 1, 2])

console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
5. 可迭代对象是什么?

可迭代对象是Iterator接口的实现。这是ECMAScript 2015的补充,它不是内置或语法,而仅仅是协议。任何遵循该协议点对象都能成为可迭代对象。可迭代对象得有两个协议:可迭代协议和迭代器协议。

  • 可迭代协议:对象必须实现iterator方法。即对象或其原型链上必须有一个名叫Symbol.iterator的属性。该属性的值为无参函数,函数返回迭代器协议。

  • 迭代器协议:定义了标准的方式来产生一个有限或无限序列值。其要求必须实现一个next()方法,该方法返回对象有done(boolean)和value属性。

6. 我们自己来实现一个可以for of遍历的对象?

通过以上可知,自定义数据结构,只要拥有Iterator接口,并将其部署到自己的Symbol.iterator属性上,就可以成为可迭代对象,能被for of循环遍历。

const obj = {
    count: 0,
    [Symbol.iterator]: () => {
        return {
            next: () => {
                obj.count++;
                if (obj.count <= 10) {
                    return {
                        value: obj.count,
                        done: false
                    }
                } else {
                    return {
                        value: undefined,
                        done: true
                    }
                }
            }
        }
    }
}

for (const item of obj) {
    console.log(item)
}

或者

const iterable = {
    0: 'a',
    1: 'b',
    2: 'c',
    length: 3,
    [Symbol.iterator]: Array.prototype[Symbol.iterator],
};

for (const item of iterable) {
    console.log(item);
}

1.6. 6. 遍历

for in 遍历 键名, for of 遍历 键值。

for of 仅遍历当前对象, 不遍历原型链上的可枚举属性。

for of 获取键名的方法:for (let [key, value] of obj.entries()) { }。

1.6.1. 1. for in

遍历数组时,key为数组下标字符串;遍历对象,key为对象字段名。

let obj = {a: 'test1', b: 'test2'}
for (let key in obj) {
    console.log(key, obj[key])
}

缺点:

  • for in 不仅会遍历当前对象,还包括原型链上的可枚举属性
  • for in 不适合遍历数组,主要应用为对象

1.6.2. 2. for of

可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments对象,NodeList对象)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。

let arr = [{age: 1}, {age: 5}, {age: 100}, {age: 34}]
for(let {age} of arr) {
    if (age > 10) {
        break // for of 允许中断
    }
    console.log(age)
}

优点:

  • for of 仅遍历当前对象

1.7. 7. Object

1. Object.keys
2. Object.values
3. Object.entries
4. Object.getOwnPropertyNames
5. Object.getOwnPropertyDescriptor
6. Object.create()
// 创建空对象
const obj1 = {};
const obj2 = Object.create(null);
const obj3 = new Object();
7. Object.assign
8. Object.defineProperty

1.8. 8. Array

1. Array.flat
const arr = [1, 2, [3, 4, [5, 6]], 7]
arr.flat()
arr.flat(2)
arr.flat(Infinity)

// 数组扁平化
const newArr = [[1, 2], [3, 4], [5, 6]].reduce((prev, next, index, arr) => {
  console.log({prev, next})
  return prev.concat(next)
}, [])
console.log(newArr)

// reduce
const flatter = (arr) => {
  const res = []
  return arr.reduce((prev, next) => {
    if (Object.prototype.toString.call(next) === '[object Array]') {
      return prev.concat(flatter(next))
    } else {
      return prev.concat(next)
    }
  }, [])
}

console.log(flatter([[1, 2], [3, 4], [5, 6]]))

// forEach
const flatten = (arr) => {
  let res = []
  arr.forEach(item => {
    if (Object.prototype.toString.call(item) === '[object Array]') {
      res = [...res, ...flatter(item)]
    } else {
      res.push(item)
    }
  })
  return res
}

console.log(flatten([1, 2, [3, 4], [5, 6]]))
2. Array.includes
3. Array.find && Array.findIndex
4. Array.from
Array.from([1, 2, 3, 4], x => x * x)

// 类数组转成真数组
[...arguments]
Array.from(arguments)
Array.prototype.slice.call(arguments)

// 数组去重
Array.from(new Set(arr))
arr.filter((item, index, array) => array.indexOf(item) === index)
5. Array.of

1.9. 9. Promise

  • then()异步请求成功后

  • catch()异步请求错误的回调方法

  • finally()请求之后无论是什么状态都会执行

  • resolve()将现有对象转换为Promise对象

  • all()此方法用于将多个Promise实例包装成一个新的promise实例

  • race()也是将多个Promise实例包装成一个新的promise实例

  • reject()返回一个状态为Rejected的新Promise实例

/**
 * 1. Promise.all() 方法接收一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入。 —— 说明所传参数都具有Iterable,也就是可遍历。
 * 2. 并且只返回一个Promise实例。—— 说明最终返回是一个Promise对象。
 * 3. 那个输入的所有promise的resolve回调的结果是一个数组。—— 说明最终返回的结果是个数组,且数组内数据要与传参数据对应。
 * 4. 这个Promise的resolve回调执行是在所有输入的promise的resolve回调都结束,或者输入的iterable里没有promise了的时候。—— 说明最终返回时,要包含所有的结果的返回。
 * 5. 它的reject回调执行是,只要任何一个输入的promise的reject回调执行或者输入不合法的promise就会立即抛出错误,并且reject的是第一个抛出的错误信息。—— 说明只要一个报错,立马调用reject返回错误信息。
 */

const PromiseAll = (iterator) => {
  const promises = Array.from(iterator)
  const len = promises.length
  let index = 0, data = []
  return new Promise((resolve, reject) => {
    for (let [i, item] of promises.entries()) {
      item.then(res => {
        data[i] = res
        index ++
        console.log({ i })
        if (index === len) resolve(data)
      }).catch(err => {
        reject(err)
      })
    }
  })
}

const PromiseRace = (iterator) => {
  const promises = Array.from(iterator)
  return new Promise((resolve, reject) => {
    for (let item of promises) {
      item.then(res => {
        resolve(res)
      }).catch(err => {
        reject(err)
      })
    }
  })
}

const promise1 = Promise.resolve('promise1');
const promise2 = new Promise(function (resolve, reject) {
  setTimeout(resolve, 2000, 'promise2');
});
const promise3 = new Promise(function (resolve, reject) {
  setTimeout(resolve, 3000, 'promise3');
});

PromiseAll([promise1, promise2, promise3]).then(function(values) {
  console.log(values);
});

PromiseRace([promise2, promise3]).then(function(values) {
  console.log(values);
});

1.10. 10. async await yeild

function longTimeFn(time) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(time);
        }, time)
    })
}

function * generator() {
    const list = [1000, 2000, 3000];
    for (let i of list) {
        yield longTimeFn(i);
    }
}

const kick = () => {
    let g = generator();
    const next = () => {
        const { value, done } = g.next()
        if (done) return
        value.then(res => {
            console.log(res)
            next()
        })
    }
    next()
}

kick()

async 就相当于 generator 函数中的 *,await 相当于 yield。

async await 就是结合 promise 和 generator 的高级语法糖。

function longTimeFn(time) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(time);
        }, time);
    })
};

function asyncFunc(generator) {
    const iterator = generator(); // 接下来要执行next
    // data为第一次执行之后的返回结果,用于传给第二次执行
    const next = (data) => {
        const {
            value,
            done
        } = iterator.next(data); // 第二次执行,并接收第一次的请求结果 value 和 done

        if (done) return; // 执行完毕, 直接返回
        // 第一次执行next时,yield返回的 promise实例 赋值给了 value
        value.then(data => {
            next(data); // 当第一次value 执行完毕且成功时,执行下一步(并把第一次的结果传递下一步)
        });
    }
    next();
};

asyncFunc(function* () {
    let data = yield longTimeFn(1000);
    console.log(data);
    data = yield longTimeFn(2000);
    console.log(data);
    return data;
})

1.11. 11. proxy

对象的代理

  • get(target, propKey, receiver) => person.age

  • set(obj, prop, value, receiver) => person.age = 100

  • has(target, key) => '_age' in person 拦截 HasProperty 操作

let handler = {
  get: (target, propKey, receiver) => {
    if (propKey in target) {
      console.log({target, propKey, receiver})
      return target[propKey];
    } else {
      throw new ReferenceError("Prop name \"" + propKey + "\" does not exist.");
    }
  },
  set: (obj, prop, value, receiver) => {
    console.log({obj, prop, value, receiver})
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The age is not an integer');
      }
      if (value > 200) {
        throw new RangeError('The age seems invalid');
      }
    }

    obj[prop] = value;
    return true;
  },
  has: (target, key) => {
    if (key.startsWith('_')) {
      return false;
    }
    return key in target
  }
};

let person = new Proxy({}, handler);

person.age = 100;

console.log(person.age)

person._age = 100;

console.log('_age' in person) // false

函数的代理

  • apply(target, ctx, args) 参数说明:目标对象、目标对象的上下文对象(this)和目标对象的参数数组。
const handler = {
  apply (target, ctx, args) {
    // return Reflect.apply(...arguments) * 2;
    return target(...args) * 2
  }
};

const sum = (left, right) => {
  return left + right
};

const proxy = new Proxy(sum, handler)

console.log(proxy(1, 2)) // 6

proxy.call(null, 5, 6) // 22

proxy.apply(null, [7, 8]) // 30

proxy.bind(null, 50, 60)() // 220

其他(用得少,用的时候再学,知道有就行)

  • construct (target, args, newTarget) => 拦截 new 命令

  • deleteProperty (target, key) => 拦截 delete 操作

  • defineProperty (target, key, descriptor) => 拦截 Object.defineProperty() 操作

  • getOwnPropertyDescriptor (target, key) => 拦截 Object.getOwnPropertyDescriptor() 操作

  • getPrototypeOf(target) => 拦截获取对象原型,有点多:😁

    • Object.prototype.proto
    • Object.prototype.isPrototypeOf()
    • Object.getPrototypeOf()
    • Reflect.getPrototypeOf()
    • instanceof
  • isExtensible(target) => 拦截 Object.isExtensible() 操作

  • ownKeys(target) => 拦截对象自身属性的读取操作, 也有点多:😁

    • Object.getOwnPropertyNames()
    • Object.getOwnPropertySymbols()
    • Object.keys()
    • for...in循环
  • preventExtensions(target) => 拦截 Object.preventExtensions() 操作

  • setPrototypeOf (target, proto) => 拦截 Object.setPrototypeOf() 方法

1.12. 12. Reflect

Reflect是个什么东西?

  • 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法
  • 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。
  • Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。
name in obj => Reflect.has(obj, name)
delete obj[name] => Reflect.deleteProperty(obj, name)

1.13. 13 Set && WeakSet

A、Set类似于数组,但是成员的值都是唯一的,没有重复的值。[...new Set(array)]

const set = new Set([1, 2, 3, 4])
/** Set 基础属性和方法 */
set.add(5) // 添加某个值,返回 Set 结构本身
set.delete(5) // 删除某个值,返回一个布尔值,表示删除是否成功
set.has(5) // 返回一个布尔值,表示该值是否为Set的成员
set.size // 返回Set实例的成员总数
set.clear() // 清除所有成员,没有返回值

/** Set 遍历操作 */
set.keys():返回键名的遍历器
set.values():返回键值的遍历器
set.entries():返回键值对的遍历器
set.forEach():使用回调函数遍历每个成员
for (item of set) {
  console.log(item)
} // 类似于...set

B、WeakSet 与 Set 区别

  • WeakSet 的成员只能是对象,而不能是其他类型的值
  • WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。
  • WeakSet 不可遍历
  • WeakSet 没有size属性
  • WeakSet 没有clear、forEach方法

1.14. 14 Map && WeakMap

A、Map类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。

const map = new Map([
  ['name', '张三'],
  ['title', 'Author']
]);
const key = {'sex': 'male'}
/** Map 基础属性和方法 */
map.set(key, 'female').set(key, 'male') // 设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。set方法返回的是当前的Map对象,因此可以采用链式写法
map.delete(key) // 删除某个键,返回true。如果删除失败,返回false
map.get(key) // 读取key对应的键值,如果找不到key,返回undefined
map.has(key) // 返回一个布尔值,表示某个键是否在当前 Map 对象之中
map.size // 返回 Map 结构的成员总数
map.clear() // 清除所有成员,没有返回值

/** Map 遍历操作 */
map.keys():返回键名的遍历器
map.values():返回键值的遍历器
map.entries():返回键值对的遍历器
map.forEach():使用回调函数遍历每个成员
for (item of map) {
  console.log(item)
} // 类似于...set

B、Map 与 数组、对象、json 的相互转换

  • Map 与 数组
  • Map 与 对象
  • Map 与 json

C、WeakMap 与 Map 区别

  • WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
  • WeakMap的键名所指向的对象,不计入垃圾回收机制。注意:WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用
  • WeakMap 不可遍历
  • WeakMap 没有size属性
  • WeakMap 没有clear、forEach方法

1.15. 15 Symbol

  • Symbol('foo') 与 Symbol.for('foo')

  • let s3 = Symbol.for('foo') 对应 Symbol.keyFor(s3)=== 'foo'

  • s.description 比 s.toString() 好用直接返回描述信息 'foo'

  • Symbol 在对象中的表示 key 需要加上 []

  • Object.getOwnPropertySymbols 只便宜以 Symbol 作为 key 的属性

  • Reflect.ownKeys 返回所有的键名,包括 Symbol 属性

let s1 = Symbol('foo');
let s2 = Symbol('foo');

let s3 = Symbol.for('foo');
let s4 = Symbol.for('foo');

console.log(s3 === s4);

console.log(Symbol.keyFor(s3), Symbol.keyFor(s1))

console.log(s1.toString() === s2.toString());

console.log(s1.description, s2.description);

let a = {
  [s1]: 'Hello!',
  [s2]: 'World!'
};

console.log(a[s1], a[s2]);

Object.getOwnPropertySymbols(a).forEach(s => {
  console.log(s, a[s]);
})

Reflect.ownKeys(a).forEach(s => {
  console.log(s, a[s])
})

2. es7 新语法

2.1. 1.Array.prototype.includes()方法

2.2. 2.求幂运算符**

//在ES7中引入了指数运算符,具有与Math.pow()等效的计算结果
console.log(2**10); // 输出 1024
console.log(Math.pow(2, 10)) // 输出1 024

3. es8 新语法

3.1. 1.Async/Await

3.2. 2.Object.values(),Object.entries()

3.3. 3.String padding 字符串填充

'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'

'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"

3.4. 4.Object.getOwnPropertyDescriptors()

ES5 的 Object.getOwnPropertyDescriptor() 方法会返回某个对象属性的描述对象(descriptor)。ES8 引入了 Object.getOwnPropertyDescriptors() 方法,返回指定对象所有自身属性(非继承属性)的描述对象。

该方法的引入目的,主要是为了解决 Object.assign() 无法正确拷贝 get 属性和 set 属性的问题。我们来看个例子:

const source = {
  set foo (value) {
    console.log(value)
  },
  get bar () {
    return '浪里行舟'
  }
}
const target1 = {}
Object.assign(target1, source)
console.log(Object.getOwnPropertyDescriptor(target1, 'foo'))

4. es9 新特性

4.1. 1.for await of

function Gen (time) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve(time)
    }, time)
  })
}
async function test () {
  let arr = [Gen(2000), Gen(100), Gen(3000)]
  for await (let item of arr) {
    console.log(Date.now(), item)
  }
}
test()
// 1575536194608 2000
// 1575536194608 100
// 1575536195608 3000

4.2. 2.Object Rest Spread

const input = {
  a: 1,
  b: 2,
  c: 3
}
let { a, ...rest } = input
console.log(a, rest) // 1 {b: 2, c: 3}

4.3. 3.Promise.prototype.finally()

fetch('https://www.google.com')
  .then((response) => {
    console.log(response.status);
  })
  .catch((error) => { 
    console.log(error);
  })
  .finally(() => { 
    document.querySelector('#spinner').style.display = 'none';
  });

4.4. 4.新的正则表达式特性

  • s (dotAll) 标志

点(.)是一个特殊字符,代表任意的单个字符,但是有两个例外。一个是四个字节的 UTF-16 字符,这个可以用u修饰符解决;另一个是行终止符, 如换行符(\n)或回车符(\r):

console.log(/foo.bar/.test('foo\nbar')) // false
console.log(/foo.bar/s.test('foo\nbar')) // true
  • 命名捕获组
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = re.exec('2019-01-01');
console.log(match.groups);          // → {year: "2019", month: "01", day: "01"}
console.log(match.groups.year);     // → 2019
console.log(match.groups.month);    // → 01
console.log(match.groups.day);      // → 01
  • Lookbehind 后行断言
let test = 'world hello'
console.log(test.match(/(?<=world\s)hello/))
// ["hello", index: 6, input: "world hello", groups: undefined]
  • Unicode属性转义
const str = '㉛';
console.log(/\d/u.test(str));    // → false
console.log(/\p{Number}/u.test(str));     // → true

console.log(/\P{Number}/u.test('㉛'));    // → false
console.log(/\P{Number}/u.test('ض'));    // → true
console.log(/\P{Alphabetic}/u.test('㉛'));    // → true
console.log(/\P{Alphabetic}/u.test('ض'));    // → false

5. es10新特性

5.1. 1.Array.prototype.flat()

5.2. 2.Array.prototype.flatMap()

5.3. 3.Object.fromEntries()

5.4. 4.String.trimStart 和 String.trimEnd

5.5. 5.String.prototype.matchAll

5.6. 6.try…catch

try-catch 语句中的参数变为了一个可选项

5.7. 7.BigInt

5.8. 8.Symbol.prototype.description

5.9. 9.Function.prototype.toString()

5.10. 10.空值合并运算符(??)

空值合并运算符(??)是一个逻辑运算符。当左侧操作数为 null 或 undefined 时,其返回右侧的操作数。否则返回左侧的操作数。

const foo = null ?? 'default string';
console.log(foo);
// expected output: "default string"

const baz = 0 ?? 42;
console.log(baz);
// expected output: 0

5.11. 11.可选链式操作符(?.)

let person = {};
// 如果person对象不包含profile会报错
console.log(person.profile.name ?? "Anonymous"); // person.profile is undefined
// 下面的路径是可选的,如果person对象不包含profile属性直接返回"Anonymous"
console.log(person?.profile?.name ?? "Anonymous");
console.log(person?.profile?.age ?? 18);
Copyright © tomgou 2022 all right reserved,powered by Gitbook该文章修订时间: 2023-08-28 17:33:23

results matching ""

    No results matching ""