JS重写call、apply、bind和new方法

407 阅读1分钟

重写call、apply、bind

在重写之前首先要了解这三个方法:

  • 它们都是Function.prototype上的方法,都接收两个参数([this的指向],[传递的参数])
  • 它们的共同点都是改变this的指向
  • 不同点:
    • 传递参数不同:apply区别于call和bind是apply的传递参数必须是数组

    • 执行时间不同:bind区别于call和apply是call和apply调用不仅会改变this指向还会立即执行绑定的函数,而bind调用则只是将this改变和传递参数,并不会执行绑定函数,因为它返回的是一个新的函数,新函数的this就是bind的第一个参数。

具体使用:


let obj = {
  name: 'zz',
  age: 12
}

let sayHello = function (x, y) {
  console.log('Hello ' + x + ' and ' + y)
}
// 现在想使用 obj.sayHello() 来调用sayHello

// call
sayHello.call(obj, 'Lucy', 'Mike') // Hello Lucy and Mike

// apply
sayHello.apply(obj, ['Lucy', 'Mike']) // Hello Lucy and Mike

// bind
console.log(sayHello.bind(obj, 'Lucy', 'Mike')) 
// ƒ (x, y) {console.log('Hello' + x + 'and' + y)}

我们已经知道了它们的基本原理,那么现在让我们来实现以下

// call

/**
* 当然我们还要注意一点就是call,apply如不指定第一个参数
* 那么默认为window,且第一个参数不管是什么类型都会呗转换成引用类型
*/
Function.prototype._call = function (obj, ...params) {
  obj == null ? (obj = window) : null
  !/^(object|function)$/.test(typeof obj) ? Object(obj) : null
  let key = Symbol('key')
  obj[key] = this
  let res = obj[key](...params)
  delete obj[key]
  return res
}


// bind 就是一个compose函数

Function.prototype._bind = function (obj, ...params) {
  let that = this
  return function (...args) {
    params = params.concat(args)
    return that.call(obj, ...params)
  }
}

重写new方法

在来复习一下new具体做了些什么?

  1. 首先它创建了一个空对象
  2. 将空对象的__proto__指向构造函数的prototype
  3. 将构造函数的this指向空对象

上代码

function _new(Ctor, ...params) {
  // 创建一个空对象
  // let obj = {}

  // 空对象原型链继承构造函数的原型
  // obj.__proto__ = Ctor.prototype

  let obj = Object.create(Ctor.prototype)

  // 将构造函数的this指向obj
  
  let res = Ctor.call(obj, ...params)

  // 观察函数执行的返回值, 如果没有返回值或者返回的基础数据类型,
  // 则返回 实例对象,引用数据类型则返回自己的数据

  if (/^(object|function)$/.test(typeof res)) return res

  return obj
}
    

来来来顺便就把Object.create()学了

Object.create = function (proto, properties = {}) {
  let newObj = {}
  newObj.__proto__ = proto
  Object.defineProperties(newObj, properties)
  return newObj
}