0%

手写call,apply,bind方法

这是老生常谈的手写了,今天想自己试着实现一下,做个笔记。

call 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Function.prototype.myCall = function (context) {
if (context === undefined || context === null) {
context = window // 指定为 null 和 undefined 的 this 值会自动指向全局对象
} else {
context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
}

const fn = Symbol('uniqueFn') // 用 Symbol 是防止跟上下文的原属性冲突
context[fn] = this
let arg = [...arguments].slice(1)
let result = context[fn](...arg)
delete context[fn]
return result
}

判断函数上下文网上也有写法是:

1
2
context = context ? Object(context) : window
context = context || window

但不是很严谨,因为遇到空字符串,0false时,context也会被判断为window

apply 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Function.prototype.myApply = function (context) {
if (context === undefined || context === null) {
context = window
} else {
context = Object(context)
}

// 判断是否为类数组对象
function isArrayLike(o) {
if (
o && // o 不是null、undefined等
typeof o === 'object' && // o是对象
isFinite(o.length) && // o.length是有限数值
o.length >= 0 && // o.length为非负值
o.length === Math.floor(o.length) && // o.length是整数
o.length < Math.pow(2, 32)
) {
return true
}
return false
}
const fn = Symbol('uniqueFn')
context[fn] = this
let args = arguments[1]
let result
if (!Array.isArray(args) && !isArrayLike(args)) {
throw new Error('CreateListFromArrayLike called on non-object') // 第二个参数不为数组且不为类对象数组
} else {
args = Array.from(args) // 转为数组
result = context[fn](...args)
}
delete context[fn]
return result
}

bind 方法

实现 bind 要额外考虑一个问题:方法一个绑定函数也能使用 new 操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new Error('Expected this is a function')
}
let self = this
let args = [...arguments].slice(1)
let fn = function (...innerArg) {
const isNew = this instanceof fn // 返回的fn是否通过new调用
return self.apply(isNew ? this : Object(context), args.concat(innerArg)) // new调用就绑定到this上,否则就绑定到传入的context上
}
// 复制原函数的prototype给fn,一些情况下函数没有prototype,比如箭头函数
if (self.prototype) {
fn.prototype = Object.create(self.prototype)
}
return fn // 返回拷贝的函数
}

-------------本文结束感谢您的阅读-------------