call,apply,bind原理分析

1,099 阅读3分钟

以前只是知道这三者的大概用法,却不是很了解其内部是怎么实现的,今天来研究一下。

一.call的模拟实现

要想深入连接call的内部原理,就必须知道它的用法。call的作用是改变调用它的函数的this指向,指向传入的第一个参数,如果第一个参数为undefined,null时,非严格模式默认为window,严格模式下为undefined。

call的内部具体做了如下几件事

1.将调用call的函数作为call第一个参数this代表的对象的属性

2.向此函数传递参数并调用

3.在对象中删除此函数

4.判断第一个参数是否存在或者为null那么直接将context置为window

5.返回函数返回值

Function.prototype.call2 = function (context, ...args) {
  var context = context || window //先判断传入的执行上下文是否为空,如果为空指向window
  context.fn = this; // 将调用call的函数作为传入的执行上下文的一个属性
  var res = context.fn(...args) // 调用这个函数并且传入call的第二个参数也就是那些参数
  delete context.fn //删除这个属性
  return res //如果有返回值就直接fanhui
}

二.apply的模拟实现

apply的内部原理与call基本相同,不同的是,它们的第二个参数不同,apply的为数组,call的为一个一个的参数

实现如下

Function.prototype.apply2 = function (context,args) {
  var context = context || window;
  context.fn = this;
  let result =  context.fn(...args) //这里不同
  delete context.fn
  return result;
}

三.bind的模拟实现

调用时机的不同

bind的用法与前两周有有所不同,call和apply是调用后,函数会立即执行,而bind则会返回一个以及被改变了this执行的原函数,让程序员自己选择在何时调用。

传递参数的次数不同

call与apply传递参数是在被调用时传递只需传递一次即可,而bind除了可以在第一次被调用时传递参数还可以在之后的返回了改变了this指向的函数中继续传递。

bind的内部具体做了如下几件事

1.改变函数的this指向但是不立即执行而是返回一个函数让程序员自己调用,

2.将第一次传递的参数与之后传递的参数合并作为函数的参数统一传递给函数

3.当将bind返回的函数作为构造函数时返回的函数中的this指向构造函数创建的实例,否则指向指定的执行上下文

4.当传入的this不是函数时直接报错。

Function.prototype.bind2 = function (context, ...args1) {
 if (typeof this !== "function") {
      throw new Error("Function.prototype.bind - what is trying to be   bound is not callable");
    }
    let self = this;
    let args1 = [...args1]
 let fBound = function (...args2) {
    let args2 = [...args2]
    return self.apply(this instanceof fBound ? this : context, [...args1, ...args2])
// 改变this指向并向返回函数中传递参数
}
fBound.prototype = Object.create(this.prototype);
// 如果返回的函数是作为构造函数,则被构造的实例的原型需要指向返回函数的原型对象
return fBound   // 返回改变了this执行的函数让程序员自己调用
}

四.new的模拟实现

用new实例化对象时,new做了什么

new的内部具体做了如下几件事

1.创建一个新对象(实例)

2.将此对象的隐式原型指向构造函数的显示原型

3.将传入的构造函数的this指向实例并向内传递参数

4.判断构造函数返回的是对象还是基本类型,如果是对象就返回对象,如果不是则返回实例

function _new(fn, ...args) {
    const obj = {}  // 创建一个空对象
    obj.__proto__ = fn.prototype //让这个空对象的原型指向构造函数的原型对象
    let res = fn.call(obj, ...args) //将构造函数中的this指向这个空对象
    return typeof res === object ? res : obj  //判断执行后的返回值是否为对象,如果是,返回此对象,如果不是,返回这个空对象
}