Call
Function.prototype.myCall = function(thisArg, ...args) {
thisArg = thisArg || window;
thisArg.func = this;
const result = thisArg.func(...args); // args 为数组
delete thisArg.func;
return result;
}
Function.prototype.myCall = function(thisArg, ...args) {
thisArg = thisArg || window;
const func = Symbol(); // Symbol
thisArg[func] = this;
const result = thisArg[func](...args);
Reflect.deleteProperty(thisArg, func); // Reflect
return result;
}
测试
const obj = {};
obj.hasOwnProperty('toString') // false
// 覆盖掉继承的 hasOwnProperty 方法
obj.hasOwnProperty = function () {
return true;
};
obj.hasOwnProperty('toString') // true
Object.prototype.hasOwnProperty.call(obj, "toString") // false
Object.prototype.hasOwnProperty.myCall(obj, "toString") // false
apply
Function.prototype.myApply = function(thisArg, arr) {
thisArg = thisArg || window;
arr = arr ? Array.from(arr) : []; // 考虑null和undefined
thisArg.func = this;
const result = thisArg.func(...arr);
delete thisArg.func;
return result;
}
Function.prototype.myApply = function(thisArg, arr) {
thisArg = thisArg || window;
arr = arr ? Array.from(arr) : [];
const func = Symbol(); // Symbol
thisArg[func] = this;
const result = thisArg[func](...arr);
Reflect.deleteProperty(thisArg, func); // Reflect
return result;
}
测试
const a = [10, 2, 4, 15, 9];
Math.max.apply(null, a) // 15
Math.max.myApply(null, a) // 15
Array.prototype.slice.apply({0: 1}) // []
Array.prototype.slice.apply({0: 1, length: 2}) // [1, empty]
Array.prototype.slice.myApply({0: 1}) // []
Array.prototype.slice.myApply({0: 1, length: 2}) // [1, empty]
补充测试
Math.max.apply(null, {}) // -Infinity
Math.max.myApply(null, {}) // -Infinity
bind
补充特性
一个绑定函数也能使用 new 操作符创建对象:这种行为就像把原函数当成构造器,提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
Function.prototype.myBind = function (thisArg, ...args) {
thisArg = thisArg || window;
const self = this;
const fBound = function (...rest) {
if (this instanceof self) {
return new self(...args.concat(rest));
}
return self.apply(thisArg, args.concat(rest));
}
fBound.prototype = Object.create(self.prototype);
fBound.prototype.constructor = fBound;
return fBound;
}
// 或者
Function.prototype.myBind = function(thisArg, ...args) {
thisArg = thisArg || window;
const self = this;
return function fBound(...rest) {
if (this instanceof fBound) {
return new self(args.concat(rest));
}
return self.apply(obj, args.concat(rest));
}
}
测试1
const counter = {
count: 0,
inc: function () {
this.count++;
}
};
const func = counter.inc.myBind(counter);
func();
counter.count // 1
测试2
let value = 2;
let foo = {
value: 1
};
function bar(name, age) {
this.habit = 'shopping';
console.log(this.value);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'kevin';
// let bindFoo = bar.bind(foo, 'Jack');
let bindFoo = bar.myBind(foo, 'Jack');
let obj = new bindFoo(20);
// undefined
// Jack
// 20
obj.habit
// shopping
obj.friend
// kevin
补充测试(无法正常实现)
// const push = Function.prototype.call.myBind(Array.prototype.push);
const push = Function.prototype.call.bind(Array.prototype.push);
const a = [1 ,2 ,3];
push(a, 4)
a // [1,2,3,4]
const d = new Date();
d.getTime() // 1656683945486
const print = d.getTime.bind(d)()
print() // 1656683945486
// d.getTime.myBind(d)()
// Uncaught TypeError: Object prototype may only be an Object or null: undefined