实现bind

174 阅读2分钟

Function.prototype.bind()

bind()将函数绑定到某个对象,bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()第一个参数的值,剩余的就是其它参数类似apply传入。 例:fn.bind(obj),可以理解为obj.fn(),fn中this就指向obj;

第一版

Function.prototype.myBind = function(context, ...args1) {
	let _self = this; // this指向调用的function
    return function(...args2) {
    	return _self.apply(context, args1.concat(args2));
    }
}

function test(a, b, c) {
	console.log(this.value + ' ' + a + ' ' + b + ' ' + c);
}

var obj = {
	value: 'ccf'
};
let hello = test.myBind(obj,1,2,3);
hello(); // ccf 1 2 3
// arguments 类数组转数组的方法
// es6
// es6 Array.from(arguments) [...arguments];
// es5 [].slice.call(arguments)  等价于Array.property.slice.call(arguments);

第二版

bind绑定后, 作为构造函数new新对象,bind的obj会丢失


let stuff  = test.bind(obj,1,2,3);
stuff(); // ccf 1 2 3
new stuff(); // undefined 1 2 3

改造下

Function.prototype.myBind = function(context, ...args1) {
	let _self = this; // this指向调用的function
    let fn = function(...args2) {
    	if(this instanceof fn) {
        	return _self.apply(_self, args1.concat(args2));
        } else {
        	return _self.apply(context, args1.concat(args2));
        }
    }
    return fn
}
let hello = test.myBind(obj,1,2,3);
new Hello() // ccf 1 2 3 new操作,构造函数返回值是否是个对象,如果是对象,就返回这个对象,否则返回临时对象
hello(); // ccf 1 2 3

第三版

添加原有原型链

test.prototype.hello = 'my friend';  // 在原型链上添加一个属性
let stuff  = test.bind(obj,1,2,3);
stuff(); // ccf 1 2 3
let bbq = new stuff(); // undefined 1 2 3
console.log(bbq.hello) //  undefined

原有的属性值丢失

Function.prototype.myBind = function(context, ...args1) {
	let _self = this; // this指向调用的function
    let fn = function(...args2) {
    	if(this instanceof fn) {
        	return _self.apply(_self, args1.concat(args2));
        } else {
        	return _self.apply(context, args1.concat(args2));
        }
    }
    fn.prototype = _self.prototype;
    return fn
}
let test = function(a,b,c) {
	console.log(this.value + ' ' + a + ' ' + b + ' ' + c);
}
test.prototype.hello = 'my friend';
let hello = test.myBind({value: 'ccf'},1,2,3);
let go = new hello();
go.hello // 'my friend'


第四版

实例化的对象,可以修改原有原型链

 	let go = new hello();
    go.hello // 'my friend'
    go.__proto__.hello = 'my boy';
    test.prototype.hello // 'my boy' // 修改原有构造函数的原型
 	Function.prototype.myBind = function(context, ...args1) {
	let _self = this; // this指向调用的function
    let fn = function(...args2) {
    	if(this instanceof fn) {
        	return _self.apply(_self, args1.concat(args2));
        } else {
        	return _self.apply(context, args1.concat(args2));
        }
    }
    fn.prototype = Object.create(_self.prototype);
    //Object.create 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
    return fn
}

收工!