手写call、apply、bind

前言

大家都进入到手写代码的环节了,我想都明白它们各自的用法和含义了吧。

那么这里就不在啰嗦了,如果不明白的,可以去看看MDN,了解了之后再回来继续读。这样容易理解一些。

下面就开始进入正题!!!

实现 call() 方法

首先我们要知道,call方法实现的内容:

  1. 能够设置 this 的指向;
  2. 接收多个参数,可能一个或多个,也可能一个也没有;
  3. 需要把参数传递给函数;
  4. 操作完后要把函数给删除掉。
// 可以先看下面的 手写call 的内容
// 生成独一无二的函数名
function mySymbol(obj) {
// new Date().getTime() 获取现在时间到1970.1.1的时间戳
let uniq = (Math.random() + new Date().getTime()).toString(32).slice(0, 8)
if (obj.hasOwnProperty(uniq)) {
return mySymbol(obj) // 相同就再调用,直到生成不同的
} else {
return uniq
}
}

// 手写call
Function.prototype.myCall = function (context) {
// 如果没传入 this 指向,就将其指向 window
context = context || window
// 给 context 添加一个方法指向 this
// 这个方法要是独一无二的,防止已经被占用的可能
let func = mySymbol(context)
context[func] = this
// 取出传入的参数
// slice(1) 取出该数组中从下标1到末尾的所有元素
let args = [...arguments].slice(1)
context[func](...args) // 执行方法
delete context[func] // 删除方法
}

name = '李四'
let obj = {
name: '张三',
hh(age) {
console.log(`我叫${this.name},今年${age}`)
}
}
let obj1 = {
name: '王麻子'
}
obj.hh.myCall(obj1, 18) // 我叫王麻子,今年18
obj.hh.myCall(window, 20) // 我叫李四,今年20

实现 apply() 方法

首先我们要知道,apply 方法实现的内容:

  1. 能够设置 this 的指向;
  2. 接收多个参数,可能一个或多个,也可能一个也没有,其中参数是一个整体的数组;
  3. 需要把参数传递给函数;
  4. 操作完后要把函数给删除掉。
// 可以先看下面的 手写apply 的内容
// 生成独一无二的函数名
function mySymbol(obj) {
// new Date().getTime() 获取现在时间到1970.1.1的时间戳
let uniq = (Math.random() + new Date().getTime()).toString(32).slice(0, 8)
if (obj.hasOwnProperty(uniq)) {
return mySymbol(obj) // 相同就再调用,直到生成不同的
} else {
return uniq
}
}

// 手写apply
Function.prototype.myApply = function (context) {
context = context || window
let func = mySymbol(context)
context[func] = this
let args = [...arguments][1]
context[func](...args)
delete context[func]
}
obj.hh.myApply(obj1, [18]) // 我叫王麻子,今年18
obj.hh.myApply(window, [20]) // 我叫李四,今年20

实现 bind() 方法

首先我们要知道,bind 方法实现的内容:

  1. 能够设置 this 的指向;
  2. 接收多个参数,可能一个或多个,也可能一个也没有;
  3. 需要返回一个函数,所以需要在之前将 this 指向 和参数收集好;
  4. 调用这个函数的时候,有可能会传参,我们有需要收集好,然后同之前的参数进行合并;
  5. 这个时候就可以进行函数的调用和传参了。
// 手写bind
Function.prototype.myBind = function (context) {
console.log(context, this)
// 保存当前的 this 和 参数
let self = this,
args = [...arguments].slice(1)
// 返回一个绑定 this 的函数
return function () {
// 因为调用返回的函数,可能会传参
let newArgs = [...arguments]
// 合并两次的参数
args = args.concat(newArgs)
return self.myApply(context, args)
}
}
let obj2 = {
name: '张三',
hh(age, sex) {
console.log(`我叫${this.name},今年${age},性别${sex}`)
}
}
let obj3 = {
name: '王麻子'
}
obj2.hh.myBind(obj3, 18)('男') // 我叫王麻子,今年18,性别男
obj2.hh.myBind(window, 20)() // 我叫李四,今年20,性别undefined

总的代码

有注释的代码

// 生成独一无二的函数名
function mySymbol(obj) {
// new Date().getTime() 获取现在时间到1970.1.1的时间戳
let uniq = (Math.random() + new Date().getTime()).toString(32).slice(0, 8)
if (obj.hasOwnProperty(uniq)) {
return mySymbol(obj) // 相同就再调用,直到生成不同的
} else {
return uniq
}
}

// 手写call
Function.prototype.myCall = function (context) {
// 如果没传入 this 指向,就将其指向 window
context = context || window
// 给 context 添加一个方法指向 this
// 这个方法要是独一无二的,防止已经被占用的可能
let func = mySymbol(context)
context[func] = this
// 取出传入的参数
// slice(1) 取出该数组中从下标1到末尾的所有元素
let args = [...arguments].slice(1)
context[func](...args) // 执行方法
delete context[func] // 删除方法
}

name = '李四'
let obj = {
name: '张三',
hh(age) {
console.log(`我叫${this.name},今年${age}`)
}
}
let obj1 = {
name: '王麻子'
}
obj.hh.myCall(obj1, 18) // 我叫王麻子,今年18
obj.hh.myCall(window, 20) // 我叫李四,今年20

// 手写apply
Function.prototype.myApply = function (context) {
context = context || window
let func = mySymbol(context)
context[func] = this
let args = [...arguments][1]
context[func](...args)
delete context[func]
}
obj.hh.myApply(obj1, [18]) // 我叫王麻子,今年18
obj.hh.myApply(window, [20]) // 我叫李四,今年20

// 手写bind
Function.prototype.myBind = function (context) {
// 保存当前的 this 和 参数
let self = this,
args = [...arguments].slice(1)
// 返回一个绑定 this 的函数
return function () {
// 因为调用返回的函数,可能会传参
let newArgs = [...arguments]
// 合并两次的参数
args = args.concat(newArgs)
return self.myApply(context, args)
}
}
let obj2 = {
name: '张三',
hh(age, sex) {
console.log(`我叫${this.name},今年${age},性别${sex}`)
}
}
let obj3 = {
name: '王麻子'
}
obj2.hh.myBind(obj3, 18)('男') // 我叫王麻子,今年18,性别男
obj2.hh.myBind(window, 20)() // 我叫李四,今年20,性别undefined

无注释的代码(检验)

function mySymbol(obj) {
let uniq = (Math.random() * new Date().getTime()).toString(32).slice(0, 8)
if (obj.hasOwnProperty(uniq)) {
return mySymbol(obj)
} else {
return uniq
}
}
Function.prototype.myCall = function (context) {
context = context || window
let fn = mySymbol(context)
context[fn] = this
let args = [...arguments].slice(1)
context[fn](...args)
delete context[fn]
}
Function.prototype.myApply = function (context) {
context = context || window
let fn = mySymbol(context)
context[fn] = this
let args = [...arguments][1]
context[fn](...args)
delete context[fn]
}
Function.prototype.myBind = function (context) {
context = context || window
let self = this,
args = [...arguments].slice(1)
return function () {
let newArgs = [...arguments]
args = args.concat(newArgs)
return self.apply(context, args)
}
}
name = "张三"
let obj = {
getPerson(age) {
console.log(`我叫${this.name},今年${age}`)
}
}
let obj1 = {
name: "李四"
}

obj.getPerson.myCall(obj1, 18) // 我叫李四,今年18
obj.getPerson.myCall(window, 19) // 我叫张三,今年19
obj.getPerson.myApply(obj1, [20]) // 我叫李四,今年20
obj.getPerson.myApply(window, [21]) // 我叫张三,今年21
obj.getPerson.myBind(obj1, 22)() // 我叫李四,今年22
obj.getPerson.myBind(window, 23)() // 我叫张三,今年23