JavaScript面试题总结

前端面试题总结
JavaScript面试题总结
CSS面试题总结
VUE面试题总结

模拟new

new操作符做了这些事:

  • 它创建了一个全新的对象
  • 它会执行[[Prontotype]] (也就是proto)
  • 它使this指向新创建的对象
  • 通过new创建的对象将最终被[[Prototype]]链接到这个函数的prototype对象上
  • 如果函数没有返回对象类型Object(包含Function,Array,Date,RegExg,Error),那么new表达式中的函数调用将返回该对象引用
function objectFactory() {
  const obj = new Object(); // 用new Object() 的方式新建了一个对象 obj
  const Constructor = [].shift.call(arguments); // 取出第一个参数,就是我们要传入的构造函数。此外因为 shift 会修改原数组,所以 arguments 会被去除第一个参数
  obj.__proto__ = Constructor.prototype; // 将 obj 的原型指向构造函数,这样 obj 就可以访问到构造函数原型中的属性
  const ret = Constructor.apply(obj, arguments); // 使用 apply,改变构造函数 this 的指向到新建的对象,这样 obj 就可以访问到构造函数中的属性
  return typeof ret === "object" ? ret : obj; // 判断返回的值是不是一个对象,如果是一个对象,我们就返回这个对象,如果没有,我们该返回什么就返回什么。
}

bind、call、apply 区别

call 和 apply 都是为了解决改变 this 的指向。作用都是相同的,只是传参的方式不同。
除了第一个参数外,call 可以接收一个参数列表,apply 只接受一个参数数组。
bind 和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过 bind 实现柯里化。

let a = {
    value: 1
}
function getValue(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value)
}
getValue.call(a, 'vue', '24')
getValue.apply(a, ['vue', '24'])
getValue.bind(a, 'Vue', '24')()

bind的模拟实现

bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )

Function.prototype.bind2 = function (context) {

    if (typeof this !== "function") {
      throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);

    var fNOP = function () {};

    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
    }

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
}

箭头函数的特点

箭头函数其实是没有 this 的,这个函数中的 this 只取决于他外面的第一个不是箭头函数的函数的 this。并且 this 一旦绑定了上下文,就不会被任何代码改变。

闭包

闭包的定义很简单:函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。

function A() {
  let a = 1
  function B() {
      console.log(a)
  }
  return B
}

你是否会疑惑,为什么函数 A 已经弹出调用栈了,为什么函数 B 还能引用到函数 A 中的变量。因为函数 A 中的变量这时候是存储在堆上的。现在的 JS 引擎可以通过逃逸分析辨别出哪些变量需要存储在堆上,哪些需要存储在栈上。

经典面试题,循环中使用闭包解决 var 定义函数的问题
for (var i=1; i<=5; i++) {
    setTimeout( function timer() {
        console.log( i );
    }, i*1000 );
}

首先因为 setTimeout 是个异步函数,所有会先把循环全部执行完毕,这时候 i 就是 6 了,所以会输出一堆 6。
解决办法三种,第一种使用闭包

for (var i = 1; i <= 5; i++) {
  (function(j) {
    setTimeout(function timer() {
      console.log(j);
    }, j * 1000);
  })(i);
}

第二种就是使用 setTimeout 的第三个参数

for ( var i=1; i<=5; i++) {
    setTimeout( function timer(j) {
        console.log( j );
    }, i*1000, i);
}

第三种就是使用 let 定义 i 了

for ( let i=1; i<=5; i++) {
    setTimeout( function timer() {
        console.log( i );
    }, i*1000 );
}

你可能感兴趣的:(JavaScript面试题总结)