前端-ES6

 1、ES6新增了哪些东西

  1. let
  2. 箭头函数
  3. 解构赋值
  4. (spread)扩展运算符和rest 参数。都是...
  5. 集合set与类
  6. 简化对象写法 {name:name2)
  7. Symbol
  8. 模板字符串

2、let var const 区别

3、箭头函数与普通函数的区别?

  • 箭头函数体内的 this 指向取决于外部代码块的 this。
  • 箭头函数不能当做构造函数,不能使用 new 。
  • 箭头函数没有原型对象。
  • 箭头函数无法使用 arguments 对象,取而代之的是 rest 参数
  • 箭头函数不可以当做 generator 函数,不能使用 yeild 关键字。

注意点:箭头函数的 this 永远指向其上下文的 this ,任何方法都改变了其指向,如: call() 、apply() 、apply()

如果 new 一个箭头函数会怎么样?

new 实现原理

  1. 首先创一个新的空对象。
  2. 根据原型链,设置空对象的 __proto__ 为构造函数的 prototype 。
  3. 构造函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)。
  4. 判断函数的返回值类型,如果是引用类型,就返回这个引用类型的对象。

我们说箭头函数不能当做构造函数,它没有 prototype ,也没有自己的 this 指向,不可以使用 arguments 参数,所以不能使用 new 。

new 箭头函数具体做了些什么?

  • 创建了一个空对象。
  • 将空对象的原型,指向构造函数的原型。
  • 将空对象作为构造函数的上下文。
  • 对构造函数的返回值进行处理判断,返回一个新的对象
  • 所以上面的 第二步和第三步,箭头函数是无法执行的。

4、扩展运算符(spread)剩余运算符(rest)都是三个点 (...) 

扩展运算符用三个点号表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值。

剩余运算符也是三个点号,不过其功能与扩展运算符恰好相反,把逗号隔开的值序列组合成一个数组。

var str = 'hello';
[...str] //  ['h','e','l','l','o']
// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']  // ['a', 'b', 'c', 'd']

//当函数参数个数不确定时,用 rest运算符
function f1(...args) {
  console.log(args); // [1,2,3]
}

5、赋值解构与模板字符串

let guest = "Jane";
let admin = "Pete";

// 让我们来交换变量的值:使得 guest = Pete,admin = Jane
[guest, admin] = [admin, guest];

alert(`${guest} ${admin}(成功交换!)`); // Pete Jane(成功交换!)

let a={res:{data:{name:'月明'}}}
let {res:{data}}=a;
console.log(data)  //name:'月明'

6、简化对象写法 

const name = "张三";
const age = 18;
const say = function () {}
 // es6新语法规定,如果对象的键和值是同一个名称,可以只指定一个
const obj = {
    name, 
    age, 
    say
}
console.log(obj);// { name:"张三",age:18,say:function(){} }

7、介绍下 Set、Map与对象的区别 ?

7.1、set

Set类似于数组,但是成员的值都是唯一的,没有重复的值。

创建Set  接受一个数组作为参数

set的方法与遍历

拥有类似于length的属性size

增删改查add delete has  clear

// 创建集合
let set = new Set([1, 2, 3, 4, 4]);
// 添加数据 5
let addSet = set.add(5);  // Set(5) {1, 2, 3, 4, 5}
 
// 删除数据 4s
let delSet = set.delete(4);
console.log(delSet); // true

// 查看是否存在数据 4
let hasSet = set.has(4);
console.log(hasSet); // false
// 清除所有数据
set.clear();
console.log(set); // Set(0) {}

遍历方法:

Set 提供了三个遍历器生成函数和一个遍历方法。
keys()     返回一个键名的遍历器
values()    返回一个键值的遍历器
entries()    返回一个键值对的遍历器
forEach()   使用回调函数遍历每个成员

let color = new Set(["red", "green", "blue"]);
for(let item of color.keys()){
 console.log(item);
}
// red
// green
// blue
for(let item of color.values()){
 console.log(item);
}
// red
// green
// blue
for(let item of color.entries()){
 console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
color.forEach((item) => {
 console.log(item)
})
// red
// green
// blue

7.2、map

当创建一个 Map 后,可以传入一个带有键值对的数组(或其它可迭代对象)来进行初始化。

  • 方法有 size、get、set、delete、has、clear
let map = new Map([
  ['1', 'str1'],
  [1, 'num1']
])
// 对每个键值对 (key, value) 运行 forEach 函数
recipeMap.forEach( (value, key, map) => {
  console.log(`${key}: ${value}`); 
});

遍历方法:

Set 提供了三个遍历器生成函数和一个遍历方法。
keys()     返回一个键名的遍历器
values()    返回一个键值的遍历器
entries()    返回一个键值对的遍历器
forEach()   使用回调函数遍历每个成员

7.3、区别

Objects和map的比较

1.Object:有一个原型链,可以去原型链查找本身不存在的值。map没有

2.键的类型:Map:任意值,Object:必须是 String 或Symbol

3.键的顺序:Map:有序,Object:无序

4.Size:Map:可直接获取长度,Object:不能直接获取长度

Map和Objects互相转化

//obj->map
let obj = {
  name: "John",
  age: 30
};

let map = new Map(Object.entries(obj));

console.log( map.get('name') ); // John

//map->obj
let prices = Object.fromEntries([ 
	 ['banana', 1],
     ['orange', 2]
]);

// 现在  prices = { banana: 1, orange: 2}
console.log(prices.orange); // 2

map与set比较

1.Map是键值对,Set是值的集合,当然键和值可以是任何的值;

2.Map可以通过get方法获取值,而set不能因为它只有值;

4.Set的值是唯一的可以做数组去重,Map由于没有格式限制,可以做数据存储

以下是补充

  1. JS所谓的“单线程”只是指主线程只有一个,并不是整个运行环境都是单线程
  2. JS的异步靠底层的多线程实现
  3. 不同的异步API对应不同的实现线程
  4. 异步线程与主线程通讯靠的是Event Loop
  5. 异步线程完成任务后将其放入任务队列
  6. 主线程不断轮询任务队列,拿出任务执行
  7. 任务队列有宏任务队列和微任务队列的区别,原理类似同步与异步任务执行顺序
  8. 微任务先执行process.nextTick,再执行Promise.then
  9. Node.js的Event Loop跟浏览器的Event Loop不一样,他是分阶段的
  10. 宏任务:先执行setImmediate,再setTimeout(fn, 0)
const syncFunc = (startTime) => {
  const time = new Date().getTime();
  while (true) {
    if (new Date().getTime() - time > 5000) {
      break;
    }
  }
  const offset = new Date().getTime() - startTime;
  console.log(`syncFunc run, time offset: ${offset}`);
};
const startTime = new Date().getTime();
syncFunc(startTime);

const promise = Promise.resolve();
setTimeout(() => {
  console.log("setTimeout");
}, 1);
setImmediate(() => {
  console.log("setImmediate");
});
promise.then(() => {
  console.log("promise");
});
process.nextTick(() => {
  console.log("nextTick");
});

syncFunc run, time offset: 5001
nextTick
promise
setTimeout
setImmediate

8、定时器

setInterval(fun,time)

setTimeout(fun,time)

setImmediate(fun,time)

9、promise

9.1、为什么会有promise对象?

1.   定时器执行时间不准确,谁先完成,就先调用谁,时间不准确。所以es6中提出的一种异步编程的解决方案--promise。它可以将异步操作以同步操作的流程表达出来,将异步操作队列化,按照期望的顺序执行,返回符合预期的结果。效果:能有效解决ajax中的串行,并行,异步编程中的回调地狱问题。

9.2、promise对象的理解

let pro = new Promise((resolve, reject) => {
//立即执行函数
  setTimeout(() => {
    console.log("pro");
  }, 1000);
  resolve("re");
});
pro.then((res) => console.log(res));
  1. promise是一个对象,对象和函数的区别就是对象可以保存状态,函数不可以。promise有三种状态: pending(等待态),fulfilled状态(成功态),rejected(失败态);状态一旦改变,就不会再变。
  2. Promise构造函数接收一个函数作为参数,该函数的两个参数是resolve,reject,它们由JavaScript引擎提供。当创建一个Promise对象时,会立即执行里面立即执行函数的代码,即一个具体的异步操作;异步执行成功了,promise是fulfilled状态,内部会调用成功的回调函数 resolve 把结果返回给调用者;异步执行失败了,promise是rejected状态,内部会调用成功的回调函数reject 把将操作报出的错误作为其参数传递出去
  3. 当方法被调用执行,当promise是fulfilled状态时,then方法是一个会执行接受resolve返回的数据的函数,第一个参数resolve的回调,当promise是rejected状态时,catch方法是一个会执行接受rejected返回的数据的函数

catch的理解

  • resolve的东西,一定会进入then的第一个回调,肯定不会进入catch
  • reject后的东西,一定会进入then中的第二个回调,如果then中没有写第二个回调,则进入catch
  • throw new Error 的情况和rej一样,但是他俩只会有一个发生
  • 另外,网络异常(比如断网),会直接进入catch而不会进入then的第二个回调**
  • 如果 try 里面没有抛出异常,catch 将被跳过。catch 语句定义当 try 语句发生错误时,捕获该错误并对错误进行处理。只有当 try 抛出了错误,才会执行。
  • finally 语句无论前面是否有异常都会执行。try 语句是必须的catch(err) 里面的参数是必须的; catch 和 finally 都是可选的。Error 有 name 和 message 两个属性
  • then链式调用时,当出现失败的情况时,then中没有第二个参数时,直接使用catch()中断;不在使用.then

可以通过 throw 语句产生错误并自定义抛出的异常

throw 'error!';
throw false;
throw new Error("error!")
Promise.reject().then(() => {
  console.log('1-1')
}).catch(() => {
  console.log('1-2')
  throw Error()
}).then(() => {
  console.log('1-3')
}).catch(() => {
  console.log('1-4')
})

Promise.resolve().then(() => {
  console.log('2-1')
}).catch(() => {
  console.log('2-2')
})

2-1、1-2、1-4

promise 错误捕获和try...catch一样会冒泡到外层。

try {
  try {
    throw new Error("oops");
  } catch (ex) {
    console.error("inner", ex.message);
    throw ex; //这里已经捕获到了异常,这个异常就失效了;如果这里不重新throw ex,外面的catch感知不到这里的异常的,即外层的catch{}不会执行
  } finally {
    console.log("finally");
  }
} catch (ex) {
  console.error("outer", ex.message); //捕获 throw ex;
}

// "inner" "oops"
// "finally"
// "outer" "oops"

3.promise的实现

参考:从如何使用到如何实现一个Promise - 掘金 (juejin.cn)

class Promise {
  constructor(executor) {
    // 默认状态是等待态
    this.status = "pending";
    this.value = undefined;
    this.reason = undefined;
    // 存放成功的回调
    this.onResolvedCallbacks = [];
    // 存放失败的回调
    this.onRejectedCallbacks = [];
    let resolve = (data) => {
      if (this.status === "pending") {
        this.value = data;
        this.status = "resolved";
        this.onResolvedCallbacks.forEach((fn) => fn());
      }
    };
    let reject = (reason) => {
      if (this.status === "pending") {
        this.reason = reason;
        this.status = "rejected";
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };
    try {
      // 执行时可能会发生异常
      executor(resolve, reject);
    } catch (e) {
      reject(e); // promise失败了
    }
  }
  then(onFulfilled, onRejected) {
    if (this.status === "resolved" && typeof onFulfilled === "function") {
      onFulfilled(this.value);
    }
    if (this.status === "rejected" && typeof onRejected === "function") {
      onRejected(this.reason);
    }
    if (this.status === "pending") {
      if (onFulfilled && typeof onFulfilled === "function") {
        this.resolveCallback.push(() =>
          // 这里我们用setTimeout来模拟实现then的微任务
          setTimeout(() => {
            onFulfilled(this.promiseRes);
          }, 0)
        );
      }
      if (onRejected && typeof onRejected === "function") {
        this.rejectCallback.push(() =>
          // 这里我们用setTimeout来模拟实现then的微任务
          setTimeout(() => {
            onRejected(this.promiseRes);
          }, 0)
        );
      }
    }
  }
}

let pro = new Promise((res, rej) => {
  console.log("cal");
  // res("res");
  rej("rej");
});
pro.then(
  (res) => console.log(res),
  (rej) => console.log(rej)
);

4.async

async函数,返回是一个promise对象。promise.then传入的参数就是async函数return的值。

async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。

参考:ES6 async await原则探索_await一旦-CSDN博客

ES6 async await原则探索_await一旦-CSDN博客

 

5. 补充:回调地狱

当我们写代码遇到异步回调时,我们想让异步代码按照我们想要的顺序执行,如果按照传统的嵌套方式,就会出现回调地狱,这样的代码不利于维护。

我们可以通过Promise对象进行链式编程来解决,Promise对象的then方法用来接收处理成功时响应的数据,catch方法用来接收处理失败时相应的数据。可以保证代码的执行顺序,前提是每一次在than做完处理后,一定要return一个Promise对象,这样才能在下一次then时接收到数据。

但是Promise最大的问题就是代码冗余,原来的异步任务被Promise封装一下,不管什么操作都用than,就会导致一眼看过去全是then…then…then…,这样也是不利于代码维护的。但是ES7给我们提供了更加舒适的async/await语法糖,可以使得异步代码看起来更像是同步代码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值