call
首先需要了解不同传入参数的返回
- 如果传入原始类型,this则是包装类型的对象,如传入 number 则
this
为 包装类型Number对象。
- 如果传入
null
或undefined
,this 为全局对象。
开始封装,如果传入 null
或 undefined
,this 为全局对象;如果传入原始类型,则通过 Object()
包装为对象。
Object.prototype.myCall = function(ctx,...args){
ctx = ctx === null || ctx === undefined ? globalThis : Object(ctx)
}
继续完善,使用 Symbol
确保 key 的唯一性
Function.prototype.myCall = function (ctx, ...args) {
ctx = ctx === null || ctx === undefined ? globalThis : Object(ctx)
const key = Symbol()
ctx[key] = this
const r = ctx[key](...args)
}
但此时对象中会多出属性 Symbol
将其变为不可枚举属性,且运行完成后删除该属性即可
Function.prototype.myCall = function (ctx, ...args) {
ctx = ctx === null || ctx === undefined ? globalThis : Object(ctx)
const key = Symbol()
Object.defineProperty(ctx, key, {
value: this,
enumerable: false,
})
const r = ctx[key](...args)
delete ctx[key]
return r
}
apply
基本同 call
,区别在于需要将参数作为数组传入
Function.prototype.myApply = function (ctx, argsArray) {
ctx = ctx === null || ctx === undefined ? globalThis : Object(ctx);
const key = Symbol();
Object.defineProperty(ctx, key, {
value: this, enumerable: false })
const r = argsArray ? ctx[key](...argsArray) : ctx[key]();
delete ctx[key];
return r;
}
bind
基本内容如下:
Function.prototype.myBind = function (context, ...args1) {
const self = this
return function (...args2) {
return self.apply(context, [...args1, ...args2])
}
}
需要考虑构造函数情况
但是目前的实现无法处理构造函数
继续优化,使用 new.target
判断是否通过 new
调用
Function.prototype.myBind = function (context, ...args1) {
const self = this
return function (...args2) {
const allArgs = [...args1, ...args2]
if (new.target) {
return new self(...allArgs)
} else {
return self.apply(context, allArgs)
}
}
}