即使你工作了5年,你也不一定知道JavaScript里面这些知识!

168 阅读3分钟

第一题

学过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个相同类型相同内容的字符串,为什么它们不相等,例:

微信截图_20230606154953.png

答案:零宽字符

零宽字符是一种字节宽度为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

兄弟们。。本次分享全部讲完了。。 大家收获了知识就给我点个赞吧!