深入理解 JavaScript 之手写 call, apply, bind 方法
首先我们先学习下,在ECMAScript 5中,函数内部存在两个特殊的对象:arguments和this
- arguments:它是一个类数组对象,包含调用函数时传入的所有参数。这个对象只有以function关键字定义函数(相对于使用箭头语法创建函数)时才会有。
- this: 在标准函数中,this引用的是把函数当成方法调用的上下文对象,这时候通常称其为this值(在网页的全局上下文中调用函数时,this指向windows)。 这是老生常谈的手写了,今天想自己试着实现一下,做个笔记。
call 方法
javascript
Function.prototype.myCall = function (proxyObj) {
if (proxyObj === undefined || proxyObj === null) {
proxyObj = window // null 和 undefined this 值指向全局对象
} else {
proxyObj = Object(proxyObj) // 值为原始值的 this 会指向该原始值的实例对象
}
const funName = Symbol('uniqueName') // 用 Symbol 是防止跟上下文的原属性冲突
proxyObj[funName] = this
let arg = [...arguments].slice(1)
let result = proxyObj[funName](...arg)
delete proxyObj[funName]
return result
}
apply 方法
javascript
Function.prototype.myApply = function (proxyObj) {
if (proxyObj === undefined || proxyObj === null) {
proxyObj = window
} else {
proxyObj = Object(proxyObj)
}
const fn = Symbol('uniqueName')
proxyObj[fn] = this
let args = arguments[1]
let result
if (!Array.isArray(args)) {
throw new Error('参数错误') // 第二个参数必须是数组
} else {
args = Array.from(args) // 转为数组
result = proxyObj[fn](...args)
}
delete proxyObj[fn]
return result
}
bind 方法
javascript
Function.prototype.myBind = function (proxyObj) {
if (typeof this !== 'function') {
throw new Error('参数错误')
}
let self = this
let args = [...arguments].slice(1)
let fn = function (...innerArg) {
const isNew = this instanceof fn
return self.apply(isNew ? this : Object(proxyObj), args.concat(innerArg))
}
if (self.prototype) {
fn.prototype = Object.create(self.prototype)
}
return fn
}