call
// 原生的call
var foo = { value: 1 };
function bar(...args) {
console.log("this", this.value, args);
}
bar.call(foo)
// call 改变了bar的this指向
// bar函数执行了
// 等价于
// var foo = {
// name: "tengzhu",
// sex: "man",
// bar() {
// console.log("this", this);
// },
// };
- 手动实现call
var foo = { value: 1 };
function bar(...args) {
console.log("this", this.value, args);
}
// 1. 将函数设置为对象的属性
// 2. 执行该函数
// 3. 删除该函数
// 4. 接受多个参数
Function.prototype.myCall = function (context, ...args) {
// this参数为null 的时候 ,指向window
// context === foo
// args 为 bar的入参
const ctx = context || window;
const symbol = Symbol();
ctx[symbol] = this;
// const args = [...arguments].slice(1);
const result = ctx[symbol](...args);
delete ctx[symbol];
return result;
};
bar.myCall(foo, 1, 2, 3);
- 手动实现apply
var foo = { value: 1 };
function bar(...args) {
console.log("this", this.value, args);
}
// 1. 将函数设置为对象的属性
// 2. 执行该函数
// 3. 删除该函数
// 4. 接受两个入参,第一个为要指向的对象,数组
Function.prototype.myApply = function (context, args) {
// this参数为null 的时候 ,指向window
// context === foo
// args 为 bar的入参
const ctx = context || window;
const symbol = Symbol();
ctx[symbol] = this;
// const args = [...arguments].slice(1);
const result = ctx[symbol](...args);
delete ctx[symbol];
return result;
};
bar.myApply(foo, [1, 2, 3]);
- 手动实现bind
基础版
// 1. 返回该函数
// 2. 接收多个入参,并且主动调用时的入参也生效
Function.prototype.myBind = function (context, ...args) {
const _self = this; // bar
return function (...newArgs) {
return _self.myApply(context, [...args, ...newArgs]);
};
};
const bindFoo = bar.myBind(foo, 1, 2, 3);
bindFoo(4, 5, 6);
通过new bindFoo()使用
const foo = { value: 1 };
function bar(name, age) {
this.habbit = "basketball";
console.log("this", name, age);
}
bar.prototype.friend = "xiaoming";
// 手写bind
// 1. 一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数
// * 当 bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效
Function.prototype.myBind = function (context, ...args) {
// 判断是否是undefined 和 null
if (typeof context === "undefined" || context === null) {
context = window;
}
const _self = this; // bar
const middleFn = function () {}; // 中转空函数
const returnFn = function (...newArgs) {
// 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值
// 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context
const ctx = this instanceof returnFn ? this : context; // 判断this是否失效
return _self.myApply(ctx, [...args, ...newArgs]);
};
// 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承绑定函数的原型中的值
// 方法一:
// 原型链共享 影响父层原型(即修改returnFn.prototype上的属性会影响父级(bar)的原型):bar.prototype.friend 会变成xiaohong
// returnFn.prototype = _self.prototype;
// returnFn.prototype.friend = "xiaohong";
// 方法二:
// 通过一个空函数中转,构造函数的实例指向构造函数的prototype,但是不会修改构造函数原型上的属性,相当于实例=>构造函数原型为只读状态
middleFn.prototype = _self.prototype;
returnFn.prototype = new middleFn();
return returnFn;
};
// const test = bar.myBind(foo, "xiaoshuai", 19);
// test(4, 5, 7);
const bindFoo = bar.myBind(foo, "xiaoshuai", 19);
const test = new bindFoo(9);
console.log("test", test, test.friend, bar.prototype.friend);