本文详细解析 JavaScript 中 Function.prototype.bind 方法的作用与原理。通过对比 var that = this 的传统做法,阐述 bind 如何更清晰地绑定函数上下文。文章包含多个代码示例,说明 bind 在对象方法、回调函数中的应用,并提供 MDN 提供的 IE8 及以下版本兼容实现方案。
理解 Function.prototype.bind 方法
在初学 JavaScript 时,我们通常不会过多关注函数绑定的问题。然而,当需要在另一个函数内部保持上下文对象 this 的指向时,这个问题便会出现。许多开发者处理此类问题的方式是先将 this 赋值给一个变量(例如 self、_this 或 that 等),其中 var that = this 是最常见的做法,以便在环境改变后仍能通过该变量访问原对象。虽然这种方法可行,但还有一种更专业且专有的方法,即使用 Function.prototype.bind,下文将进行详尽讲解。
需要解决的问题
第一部分:需要解决的问题
首先观察以下代码示例。
var myObj = {
specialFunction: function () {
},
anotherSpecialFunction: function () {
},
getAsyncData: function (cb) {
cb();
},
render: function () {
this.getAsyncData(function () {
this.specialFunction();
this.anotherSpecialFunction();
});
}
};
myObj.render();
这里我们创建一个对象,其中包含前两个普通方法;第三个方法接收一个函数作为参数并立即执行;最后一个方法调用 myObj 对象的 getAsyncData 方法,此处使用了 this。在 getAsyncData 方法中传入的函数继续调用该对象的前两个方法,同样使用了 this。此时,许多人已经能看出潜在问题:将上述代码输入控制台,会得到如下结果:
TypeError: this.specialFunction is not a function
问题剖析
第二部分:问题剖析
在对象 render 方法中,this 确实指向 myObj 对象,因此我们可以通过 this.getAsyncData 调用该对象中的函数。然而,当我们把函数作为参数传递时,这里的 this 指向了全局环境 window。由于全局环境中不存在对象的前两个方法,因此会报错。
解决问题的几种方式
第三部分:解决问题的几种方式
因此,我们需要正确调用对象中的前两个方法。许多人采用的方法是在对象环境中获取 this 并赋值给另一个变量,从而在后续环境中调用,如下所示:
render: function () {
var that = this;
this.getAsyncData(function () {
that.specialFunction();
that.anotherSpecialFunction();
});
}
虽然这种方法可行,但使用 Function.prototype.bind() 会使代码更清晰、易懂,如下所示:
render: function () {
this.getAsyncData(function () {
this.specialFunction();
this.anotherSpecialFunction();
}.bind(this));
}
这里我们就成功地把 this 绑定到了环境中。
下面是另外一个简单的例子:
var foo = {
x: 3
}
var bar = function(){
console.log(this.x);
}
bar(); // undefined
var boundFunc = bar.bind(foo);
boundFunc(); // 3
下面的例子也是常见的:
this.x = 9; // this refers to global "window" object here in the browser
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 81
var retrieveX = module.getX;
retrieveX();
// returns 9 - The function gets invoked at the global scope
// Create a new function with 'this' bound to module
// New programmers might confuse the
// global var x with module's property x
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81
浏览器支持
第四部分:浏览器支持
但是这个方法在 IE8 及以下是不被支持的,所以我们可以使用 MDN 提供的方法来使得 IE 低版本支持 .bind() 方法:
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。