《前端口试手记》之ES6重难点整顿

👇 内容速览 👇

  • let和const
  • Set和Map
  • Generator和yield
  • Promise、async/await引见
  • Proxy代办器

🔍检察悉数教程 / 浏览原文🔍

let和const

ES6新增了letconst,它们声明的变量,都处于“块级作用域”。而且不存在“变量提拔”,不允许反复声明。

同时,const声明的变量所指向的内存地点保留的数据不得转变:

  • 关于简朴范例的数据(数值、字符串、布尔值),值就保留在变量指向的谁人内存地点,因而等同于常量。
  • 关于复合范例的数据(主假如对象和数组),变量指向的内存地点,保留的只是一个指向现实数据的指针,const只能保证这个指针是牢固的(即老是指向另一个牢固的地点),不能保证指向的数据结构不可变。

假如要保证指向的数据结构也不可变,须要自行封装:

/**
 * 凝结对象
 * @param {Object} obj 
 * @return {Object}
 */
function constantize(obj) {
  if(Object.isFrozen(obj)) {
    return obj
  }

  Reflect.ownKeys(obj).forEach(key => {
    // 假如属性是对象,递归凝结
    typeof obj[key] === 'object' && (obj[key] = constantize(obj[key]))
  });

  return Object.freeze(obj)
}

/********测试代码 **********/

const obj = {
  a: 1,
  b: {
    c: 2,
    d: {
      a: 1
    }
  },
  d: [
    1,
    2
  ]
}

const fronzenObj = constantize(obj)
try {
  fronzenObj.d = []
  fronzenObj.b.c = 3
} catch(error) {
  console.log(error.message)
}

Set和Map

问题:诠释下
Set
Map

  • Set元素不允许反复
  • Map相似对象,然则它的键(key)可所以恣意数据范例

①Set经常运用要领

// 实例化一个set
const set = new Set([1, 2, 3, 4]);

// 遍历set
for (let item of set) {
  console.log(item);
}

// 增添元素,返回Set自身
set.add(5).add(6);

// Set大小
console.log(set.size);

// 搜检元素存在
console.log(set.has(0));

// 删除指定元素,返回bool
let success = set.delete(1);
console.log(success);

set.clear();

其他遍历要领:由于没有键名,values()keys()返回一样效果。

for (let item of set.keys()) {
  console.log(item);
}

for (let item of set.values()) {
  console.log(item);
}

for (let item of set.entries()) {
  console.log(item);
}

②Map经常运用要领

Map接口基础和Set一致。差别的是增添新元素的API是:set(key, value)

const map = new Map();

// 以恣意对象为 Key 值
// 这里以 Date 对象为例
let key = new Date(); 
map.set(key, "today");

console.log(map.get(key));

Generator与yield

generator函数是es6供应的新特征,它的最大特性是:掌握函数的实行。让我们从网上最火的一个例子来看:

function* foo(x) {
  var y = 2 * (yield x + 1);
  var z = yield y / 3;
  return x + y + z;
}

var b = foo(5);
b.next(); // { value:6, done:false }
b.next(12); // { value:8, done:false }
b.next(13); // { value:42, done:true }

浅显的诠释下为何会有这类输出:

  1. 给函数foo传入参数5,但由于它是generator,所以实行到第一个yield前就住手了。
  2. 第一次挪用next(),此次传入的参数会被疏忽停息**。
  3. 第二次挪用next(12),传入的参数会被看成上一个yield表达式的返回值。因而,y = 2 * 12 = 24。实行到第二个yield,返回厥后的表达式的值 24 / 3 = 8。然后函数在此处停息。
  4. 第三次挪用next(13),没有yield,只剩return了,根据一般函数那样返回return的表达式的值,而且donetrue

难点:在于为何末了的value是42呢?

起首,x的值是刚开始挪用foo函数传入的5。而末了传入的13被看成第二个yield的返回值,所以z的值是13。关于y的值,我们在前面第三步中已计算出来了,就是24。

所以,x + y + z = 5 + 24 + 13 = 42

看懂了上面的剖析,再看下面这段代码就很好理解了:

function* foo(x) {
  var y = 2 * (yield x + 1);
  var z = yield y / 3;
  return x + y + z;
}

var a = foo(5);
a.next(); // Object{value:6, done:false}
a.next(); // Object{value:NaN, done:false}
a.next(); // Object{value:NaN, done:true}

只要第一次挪用next函数的时刻,输出的value是6。其他时刻由于没有给next传入参数,因而yield的返回值都是undefined,举行运算后自然是NaN

Promise引见

简朴归结下 Promise:三个状况、两个历程、一个要领

  • 三个状况:pendingfulfilledrejected
  • 两个历程(单向不可逆):

    • pending->fulfilled
    • pending->rejected
  • 一个要领thenPromise本质上只要一个要领,catchall要领都是基于then要领完成的。

请看下面这段代码:

// 组织 Promise 时刻, 内部函数马上实行
new Promise((resolve, reject) => {
  console.log("new Promise");
  resolve("success");
});
console.log("finifsh");

//  then 中 运用了 return,那末 return 的值会被 Promise.resolve() 包装
Promise.resolve(1)
  .then(res => {
    console.log(res); // => 1
    return 2; // 包装成 Promise.resolve(2)
  })
  .then(res => {
    console.log(res); // => 2
  });

async/await引见

async函数返回一个Promise对象,能够运用then要领增添回调函数。

当函数实行的时刻,一旦碰到await就会先返回,比及异步操纵完成,再接着实行函数体内背面的语句。

这也是它最受欢迎的处所:能让异步代码写起来像同步代码,而且轻易掌握递次

能够利用它完成一个sleep函数壅塞历程:

function sleep(millisecond) {
  return new Promise(resolve => {
    setTimeout(() => resolve, millisecond)
  })
}

/**
 * 以下是测试代码
 */
async function test() {
  console.log('start')
  await sleep(1000) // 就寝1秒
  console.log('end')
}

test() // 实行测试函数

虽然轻易,然则它也不能庖代Promise,尤其是我们能够很轻易地用Promise.all()来完成并发,而async/await只能完成串行。

function sleep(second) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(Math.random());
      resolve();
    }, second);
  });
}

async function chuanXingDemo() {
  await sleep(1000);
  await sleep(1000);
  await sleep(1000);
}


async function bingXingDemo() {
  var tasks = [];
  for (let i = 0; i < 3; ++i) {
    tasks.push(sleep(1000));
  }

  await Promise.all(tasks);
}

运转bingXingDemo(),险些同时输出,它是并发实行;运转chuanXingDemo(),每一个输出距离1s,它是串行实行。

ES6对象和ES5对象

问题:es6 class 的new实例和es5的new实例有什么区分?

ES6中(和ES5比拟),classnew实例有以下特性:

  • class的组织参数必需是new来挪用,不能够将其作为一般函数实行
  • es6class不存在变量提拔
  • 最主要的是:es6内部要领不能够罗列。es5的prototype上的要领能够罗列。

为此我做了以下测试代码举行考证:

console.log(ES5Class()) // es5:能够直接作为函数运转
// console.log(new ES6Class()) // 会报错:不存在变量提拔

function ES5Class(){
  console.log("hello")
}

ES5Class.prototype.func = function(){ console.log("Hello world") }

class ES6Class{
  constructor(){}
  func(){
    console.log("Hello world")
  }
}

let es5 = new ES5Class()
let es6 = new ES6Class()

// 引荐在轮回对象属性的时刻,运用for...in
// 在遍历数组的时刻的时刻,运用for...of
console.log("ES5 :")
for(let _ in es5){
  console.log(_)
}

// es6:不可罗列
console.log("ES6 :")
for(let _ in es6){
  console.log(_)
}

参考/引荐《JavaScript建立对象—从es5到es6》

Proxy代办器

他能够完成js中的“元编程”:在目的对象之前架设阻拦,能够过滤和修正外部的接见。

它支撑多达13种阻拦操纵,比方下面代码展现的setget要领,离别能够在设置对象属性和接见对象属性时刻举行阻拦。

const handler = {
  // receiver 指向 proxy 实例
  get(target, property, receiver) {
    console.log(`GET: target is ${target}, property is ${property}`)
    return Reflect.get(target, property, receiver)
  },
  set(target, property, value, receiver) {
    console.log(`SET: target is ${target}, property is ${property}`)
    return Reflect.set(target, property, value)
  }
}

const obj = { a: 1 , b: {c: 0, d: {e: -1}}}
const newObj = new Proxy(obj, handler)

/**
 * 以下是测试代码
 */

newObj.a // output: GET...
newObj.b.c // output: GET...

newObj.a = 123 // output: SET...
newObj.b.c = -1 // output: GET...

运转这段代码,会发明末了一行的输出是 GET ...。也就是说它触发的是get阻拦器,而不是希冀的set阻拦器。这是由于关于对象的深层属性,须要专门对其设置Proxy

更多请见《阮一峰ES6入门:Proxy》

EsModule和CommonJS的比较

现在js社区有4种模块治理范例:AMD、CMD、CommonJS和EsModule。 ES Module 是原生完成的模块化计划,与 CommonJS 有以下几个区分:

  • CommonJS 支撑动态导入,也就是 require(${path}/xx.js),后者现在不支撑,然则已有提案:import(xxx)
  • CommonJS 是同步导入,由于用于服务端,文件都在当地,同步导入纵然卡住主线程影响也不大。而后者是异步导入,由于用于浏览器,须要下载文件,假如也采纳同步导入会对衬着有很大影响
  • commonJs输出的是值的浅拷贝,esModule输出值的援用
  • ES Module 会编译成 require/exports 来实行的

更多系列文章

⭐在GitHub上珍藏/定阅⭐

《前端学问系统》

《设想形式手册》

《Webpack4渐进式教程》

⭐在GitHub上珍藏/定阅⭐

    原文作者:心谭
    原文地址: https://segmentfault.com/a/1190000018927601
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞