1、ES6新增了哪些东西
- let
- 箭头函数
- 解构赋值
- (spread)扩展运算符和rest 参数。都是...
- 集合set与类
- 简化对象写法 {name:name2)
- Symbol
- 模板字符串
2、let var const 区别
3、箭头函数与普通函数的区别?
- 箭头函数体内的 this 指向取决于外部代码块的 this。
- 箭头函数不能当做构造函数,不能使用 new 。
- 箭头函数没有原型对象。
- 箭头函数无法使用 arguments 对象,取而代之的是 rest 参数
- 箭头函数不可以当做 generator 函数,不能使用 yeild 关键字。
注意点:箭头函数的 this 永远指向其上下文的 this ,任何方法都改变了其指向,如: call() 、apply() 、apply()
如果 new 一个箭头函数会怎么样?
new 实现原理
- 首先创一个新的空对象。
- 根据原型链,设置空对象的 __proto__ 为构造函数的 prototype 。
- 构造函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)。
- 判断函数的返回值类型,如果是引用类型,就返回这个引用类型的对象。
我们说箭头函数不能当做构造函数,它没有 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由于没有格式限制,可以做数据存储
以下是补充
- JS所谓的“单线程”只是指主线程只有一个,并不是整个运行环境都是单线程
- JS的异步靠底层的多线程实现
- 不同的异步API对应不同的实现线程
- 异步线程与主线程通讯靠的是Event Loop
- 异步线程完成任务后将其放入任务队列
- 主线程不断轮询任务队列,拿出任务执行
- 任务队列有宏任务队列和微任务队列的区别,原理类似同步与异步任务执行顺序
- 微任务
先执行process.nextTick,再执行Promise.then
- Node.js的Event Loop跟浏览器的Event Loop不一样,他是分阶段的
宏任务:先执行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));
- promise是一个对象,对象和函数的区别就是对象可以保存状态,函数不可以。promise有三种状态: pending(等待态),fulfilled状态(成功态),rejected(失败态);状态一旦改变,就不会再变。
- Promise构造函数接收一个函数作为参数,该函数的两个参数是resolve,reject,它们由JavaScript引擎提供。当创建一个Promise对象时,会立即执行里面立即执行函数的代码,即一个具体的异步操作;异步执行成功了,promise是fulfilled状态,内部会调用成功的回调函数 resolve 把结果返回给调用者;异步执行失败了,promise是rejected状态,内部会调用成功的回调函数reject 把将操作报出的错误作为其参数传递出去
- 当方法被调用执行,当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语法糖,可以使得异步代码看起来更像是同步代码。