深入理解javascript的bind

时间:2022-12-31 16:42:08

bind有两个功能

  • 实现this绑定
  • 实现柯里化

1.实现this绑定

function f(){
return this.a;
}
var g = f.bind({a : "test"});
console.log(g()); // test
var o = {a : 37, f : f, g : g};
console.log(o.f(), o.g()); // 37, test

第一个bind将{a:”test”}对象绑定到f函数的this上。使得f函数返回了this.a即返回了{a:”test”}的a属性。

第二个调用o.g()时,g指针指向了var定义的g函数,由于g函数已经绑定了{a:”test”}对象,打印出test。

1.实现柯里化currying

什么是柯里化捏?

在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。这个技术由 Christopher Strachey 以逻辑学家 Haskell Curry 命名的,尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的。
var foo = {x: 888};
var bar = function () {
console.log(this.x);
}.bind(foo); // 绑定
bar(); // 888

下面是一个 bind 函数的模拟,testBind 创建并返回新的函数,在新的函数中将真正要执行业务的函数绑定到实参传入的上下文,延迟执行了。

Function.prototype.testBind = function (scope) {
var fn = this; //// this 指向的是调用 testBind 方法的一个函数,
return function () {
return fn.apply(scope);
}
};
var testBindBar = bar.testBind(foo); // 绑定 foo,延迟执行
console.log(testBindBar); // Function (可见,bind之后返回的是一个延迟执行的新函数)
testBindBar(); // 888

bind方法实现源码

先看一个案例

function foo() {
this.b = 100;
return this.a;
}
var func = foo.bind({a:1});
func(); // 1
new func(); // {b : 100}

new 一个func和直接执行func的区别是什么呢?
new一个func之后,会建立一个空对象,空对象的prototype是foo的prototype,空对象的b属性是100,会忽略掉return的a属性,把当前对象返回。所以返回的就是新建的这个对象{b:100}了。

所以说,new一个新对象之后,对于bind,返回值被覆盖了。

if (!Function.prototype.bind) {
//这里 oThis和作为bind函数后的第一个参数传入,上例中是{a:1}
Function.prototype.bind = function(oThis) {
//这里的this是调用bind方法的对象,上例中是foo。
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('What is trying to be bound is not callable');
}
//通过arguments获取多余的参数,即第二个参数及以后的参数。bind函数将多余的参数当作方法的形参
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
//fBound作为整个bind函数返回对象,上例中就是func对象
fBound = function() {
//这里的this是bind返回的对象实例指向的this,上例中就是func调用时指向的this。
return fToBind.apply(this instanceof fNOP? this : oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
/*下两句代码实现了object.create的作用。*/
//新建一个function 它的原型指向this的原型
fNOP.prototype = this.prototype;
//
fBound.prototype = new fNOP();
return fBound;
};
}

其中这段代码:

//这里的this是bind返回的对象实例指向的this,上例中就是func调用时指向的this。
return fToBind.apply(this instanceof fNOP? this : oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};

1.按照例子,如果说直接调用

func()

那么func 的this就指向了window对象,window对象肯定不在fNOP的原型链上,则返回oThis。即,指向var func = foo.bind({a:1});的{a:1}对象。

2.如果这样调用

new func(); 

我们知道new方法返回一个新对象,新对象的prototype属性指向构造器的prototype属性,即指向foo的prototype属性。

而在bind源码里,fNOP.prototype = this.prototype;这里this是指向的foo对象。那么这里的this也是指向的foo对象。那么this instanceof fNOP返回true,就这么成功的覆盖了bind最原始的返回对象啦。