手写javascript的bind函数
核心:需要考虑作为构造函数时,绑定的是实例this而不是传入的obj对象,以及需要考虑参数合并(类似函数柯里化)
Function.prototype.myBind = function (context, ...args) {
if (!context || context === null) {
context = window;
}
let fn = Symbol();
const _this = this;
let result = function (...innerArgs) {
// 特殊情况:若是将bind绑定之后的函数当作构造函数,通过new操作符使用,则不绑定传入的this,而是将this指向实例化出来的对象
// 此时由于new操作符作用,this指向result实例对象 而result又继承传入的_this 由原型链可知
// this.__proto__ === result.prototype // this instanceOf result === true
// this.__proto__.__proto__ === result.prototype.__proto__ === _this.prototype // this instanceOf _this === true
if (this instanceof _this) {
// 把上下文指向this实例
context = this;
}
context[fn] = _this;
// 参数合并
context[fn](...args, ...innerArgs);
delete context[fn];
};
// 如果绑定的是构造函数 那么需要继承构造函数原型属性和方法
// 实现继承的方式:使用Oject.create
result.prototype = Object.create(this.prototype);
return result;
};
//用法如下
function Person(name, age) {
console.log(name); //'我是参数传进来的name'
console.log(age); //'我是参数传进来的age'
console.log(this); //构造函数this指向实例对象
}
// 构造函数原型的方法
Person.prototype.say = function () {
console.log(123);
};
let obj = {
objName: "我是obj传进来的name",
objAge: "我是obj传进来的age",
};
// 普通函数
function normalFun(name, age) {
console.log(name); //'我是参数传进来的name'
console.log(age); //'我是参数传进来的age'
console.log(this); //普通函数this指向绑定bind的第一个参数 也就是例子中的obj
console.log(this.objName); //'我是obj传进来的name'
console.log(this.objAge); //'我是obj传进来的age'
}
// 先测试作为构造函数调用;
let bindFun = Person.myBind(obj, "我是参数传进来的name");
let a = new bindFun("我是参数传进来的age");
a.say(); //123
// 再测试作为普通函数调用;
let bindNormalFun = normalFun.myBind(obj, "我是参数传进来的name");
bindNormalFun("我是参数传进来的age");