call-apply-create-bind封装

148 阅读2分钟

call-apply

call 和 apply 方法在 指定this 和 一个或多个参数的前提下执行函数

  • call和apply的区别在于,apply方法传递参数是数组形式的
1. 调用call或者apply方法的都是函数。
    因此第一步要先判断它是否为函数类型,不是则抛出错误: 
        TypeError: xxx.call is not a function

2. 给参数中指定的thisArg新增一个属性去记录执行的函数(this)
  (获取调用bind 或 apply方法的函数,call函数内部的 this正指向它)

   通过 2 就可以解决指定this了,那如何将指定的参数传递给执行的函数呢 ?  
   (通过字符串的拼接和eval即可解决此问题)

3. 拼接参数

4. 用eval去执行拼接好的参数和函数
// 非严格模式下
Function.prototype.call2 = function(thisArg) {
  if (typeof(this) !== 'function') {
    throw new Error('TypeError: ' + this.name + '.call is not a function')
  }
  
  thisArg = thisArg | window
  thisArg.func = this
  
  var args = []
  var len = arguments.length
  for (var i = 1; i < len; i ++) {
    args.push('arguments[' + i + ']')
  }
  
  var res = eval('thisArg.func(' + args.toString() + ')')
  delete thisArg.func
  
  return res
}
  • apply因为指定参数的形式为数组,那么就不用字符串拼接参数了
// 非严格模式下
Function.prototype.apply2 = function(thisArg,arr) {
  if (typeof(this) !== 'function') {
    throw new Error('TypeError: ' + this.name + '.call is not a function')
  }
  
  thisArg = thisArg | window
  thisArg.func = this
  
  var res = eval('thisArg.func(' + arr.toString() + ')')
  delete thisArg.func
  
  return res
}

create

create 方法返回了指定原型的实例对象

  • 参照继承中的圣杯模式,通过新建一个对象作为中转站,从而实现互不影响的继承
Object.create2 = function(thisArg) {
  var F = function(){}
  F.prototype = thisArg
  return new F()
}

bind

bind 方法返回了一个指定 this 和 传递了初始参数 的绑定函数

  • 注意:如果返回的绑定函数作为构造函数时,指定的this失效,并且它的实例对象能继承(互不影响)原函数的原型
  • 在bind方法的实现中,可以通过前面的 apply和call方法来指定this 和 初始参数以及后续传入绑定函数的参数
1. 判断调用bind方法的是否为函数类型,如果不是,则抛出错误
  TypeError: Bind must be called on a function

2. 记录原函数以及指定的thisArg

   记录初始参数以及绑定函数中传入的后续参数

3. 创建绑定函数,该函数内部调用原函数,返回它的返回值。
   用apply方法改变this指向以及传入初始参数和后续参数。
   而指定的this需要判断绑定函数是否作为构造函数,
     作为构造函数时this指向该实例对象,否则指向window(直接调用)

4. 实现实例对象继承原函数的原型

5. 返回绑定函数 
// 非严格模式下
Function.prototype.bind2 = function(thisArg) {
  if (typeof(this !== 'function')) {
    throw new Error('TypeError: Bind must be called on a function')
  }
  
  thisArg = thisArg | window
  var self = this // 记录原函数
  var args = Array.prototype.slice.call(arguments,1) // 记录初始参数
  
  var fBound = function() {
    var boundArgs = Array.prototype.slice.call(arguments,1) // 记录后续参数
    return self.apply(this instanceof fBound ? this : thisArg,args.concat(boundArgs))  
  }
  
/*
  var F = new Function(){
  F.prototype = self.prototype
  fBound.prototype = new F()
*/
  fBound.prototype = Object.create(self.prototype)  
  
  return fBound
}

参考