第一题
学过JavaScript都知道函数有2种调用方式,怎么判断它是用哪种方式调用的?例:
function Fn(){}
Fn() //第一种
new Fn() //第二种
答案: new.target
在普通的函数调用中,new.target的值是undefined,否则其值为当前函数。可以用来判断一个构造函数是否用new关键字调用。
function Fn(){ console.log(new.target); }
Fn();//undefined
new Fn;//[Function:Fn]
第二题
学过JavaScript都知道Promise,怎么判断对象是一个Promise?
错误事例:鸭子类型。 看起来像鸭子,走起来像鸭子,尝起来像鸭子的,就是鸭子。
const isPromise = (val) => {
return isObject(val) && isFunction(val.then) && isFunction(val.catch); //错误
};
答案:检查原型 | 转换Promise
//第一种
function isPromise(obj) {
if (Promise && Promise.resolve) {
return Promise.resolve(obj) == obj
} else {
throw new Error('当前环境不支持 Promise !')
}
}
//第二种
function isPromise(obj) {
return obj && Object.prototype.toString.call(obj) === '[object Promise]'
}
第三题
页面上有2个相同类型相同内容的字符串,为什么它们不相等,例:
答案:零宽字符
零宽字符是一种字节宽度为0的不可打印的Unicode字符, 在浏览器和一般的文本编辑器中是不可见, 但是真是存在, 获取字符串长度时也会占位置, 表示某一种控制功能的字符。
let a = 'ABC\u200d'
let b = 'ABC'
console.log(a === b) //false
第四题
怎么让for of能遍历对象?
let json = { a: 1, b: 2, c: 3 }
for (let {k,v} of json) {
console.log(k,v) //报错:Uncaught TypeError: json is not iterable
}
答案:给对象加一个迭代器
let json = { a: 1, b: 2, c: 3 }
json[Symbol.iterator] = function () {
let keys = Object.keys(json);
let len = keys.length;
let n = 0;
return {
next() {
return n < len ? {
value: { k: keys[n], v: json[keys[n++]] },
done: false
} : {
done: true
}
}
}
}
for (let { k, v } of json) {
console.log(k, v)
}
如果有同学觉得自己写迭代器有点麻烦,就写一个生成器吧! 效果与上面是一样的~
let json = { a: 1, b: 2, c: 3 }
json[Symbol.iterator] = function* a() {
yield* Object.entries(json)
}
for (let [k, v] of json) {
console.log(k, v)
}
第五题
学过ES6模板字符串,大家都知道非常好用。但是如果模板字符串里面混合了逻辑后,是否代码还优雅呢?
let xm = 'XM'
let zy = 'ZY'
let cz = 'CZ'
let tpl = `My name is ${xm.toUpperCase()},My name is ${zy.toUpperCase()},My name is ${cz.toUpperCase()}`
答案:使用标记 只需在模板字符串前面加一个函数名
let xm = 'xm'
let zy = 'zy'
let cz = 'cz'
let tpl = tag`My name is ${xm},My name is ${zy},My name is ${cz}`
function tag(strings, ...values) { //抽离一个方法
let str = '';
for (let i = 0; i < strings.length; i++) {
str += strings[i] + (values[i] || '').toUpperCase();
}
return str;
}
第六题
变量交换怎么做才最优雅?
let num,a=123,b=456;
num=a;
a=b;
b=num;
console.log(a,b);
答案:ES6解构 | 位运算符
let a = 123,b = 456;
a = (b ^= a ^= b) ^ a; //位运算符
console.log(a, b);
let a = 123,b = 456;
[a,b] = [b,a]; //ES6解构
console.log(a, b);
终极炸弹
讲一下为什么Promise.resolve(4)刚好插入了 5 的前面?
Promise.resolve().then(() => {
console.log(0)
return Promise.resolve(4)
}).then((res) => {
console.log(res)
})
Promise.resolve().then(() => {
console.log(1)
}).then(res => {
console.log(2)
}).then(res => {
console.log(3)
}).then(res => {
console.log(5)
}).then(res => {
console.log(6)
})
//打印结果:0 1 2 3 4 5 6
答案:Promise的then方法是要根据上一个then执行完的的值返回。因为多了一层解析,解析完成才会放入微任务队列。 看一个案例
Promise.resolve().then(() => {
console.log(0)
}).then((res) => {
console.log(2)
})
Promise.resolve().then(() => {
console.log(1)
}).then(res => {
console.log(3)
})
//打印结果:0 1 2 3
会发现上面的结果是交叉执行。为什么会产生这种现象? 是因为第一个Promise.resolve()与第二个Promise.resolve()属于同步代码。从上到下按顺序把then推入微任务队列。这时候队列里面就是0,1 0这个then执行完后,马上发现后面跟着then。这时候2进入队列。1这个then执行完后。也发现后面跟着then。3进入队列。最后按照顺序0,1,2,3
Promise.resolve().then(() => {
console.log(0)
Promise.resolve().then(() => {
console.log(2)
})
})
Promise.resolve().then(() => {
console.log(1)
}).then(res => {
console.log(3)
})
//打印结果:0 1 2 3
这种写法与上面效果也是一样!
Promise.resolve().then(() => {
console.log(0)
return Promise.resolve()
}).then(res=>{
console.log(4)
})
Promise.resolve().then(() => {
console.log(1)
}).then(res => {
console.log(2)
}).then(res => {
console.log(3)
})
//打印结果:0 1 2 3 4
这就不一样了。 这个就和最开始的题目是一模一样了。
首先0,1进队列。执行完0这个then马上发现这里是return Promise进队列排队吧。此时队列里面就是
【0,1,promise】
此时到了2,但是会看到0,1都执行完了,在栈中清空了。【promise,2】。2会看前面的promise就问 你这吊毛执行不执行啊? 不执行我先来。交换位置【2,promise】
【promise.then(),3】此时到了3。3会看前面的promise马上要变状态了就问 你这吊毛执行不执行啊? 不执行我先来。交换位置【3,promise.then()】
最后到了这里【4】 3也执行完了。4终于出来了。 出来了最好就执行呗。。所以依次输出 0 1 2 3 4
兄弟们。。本次分享全部讲完了。。 大家收获了知识就给我点个赞吧!