Function.prototype.bind接口浅析
本文大部分内容翻译自 MDN内容, 翻译内容经过自己的理解。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Function.prototype.bind
Syntax
fun.bind(thisArg[, arg1[, arg2[, ...]]])Parameters
thisArg- The value to be passed as the
thisparameter to the target function when the bound function is called. The value is ignored if the bound function is constructed using thenewoperator.arg1, arg2, ...- Arguments to prepend to arguments provided to the bound function when invoking the target function.
bind函数,首先是所有函数的共有的属性,这个属性是一个函数,此函数为函数类(Function构造函数)的原型的一个属性(Function.prototype.xx),所以只有函数才能调用此接口,
故用法一般是定义一个函数,然后此函数调用bind接口
function XX(){}
XX.bind(thisArg[, arg1[, arg2[, ...]]])
此函数作用是,生成一个新的函数, 新函数具有和被绑定的函数具有相同功能(函数体)。
当新函数调用的时候,新函数中的this指代bind调用的第一个参数thisArg;
同时,如果bind调用时候有可选参数(arg1等),则这些参数则会作为被绑定函数默认前若干个参数。
为辅助理解,给出如下伪代码描述:
假设有函数
function XX(variable){ alert(this.variable=variable) }
则XX函数经过绑定后的效果
// var YY = XX.bind(widow, variable) 返回的函数 等价于如下代码段
function YY(Yvariable)
{
function XX(){ alert(widow.variable=variable) } //XX函数中的 this 替换为 window, 待绑定的目标
XX(variable, Yvariable); //XX调用,绑定的可选参数在前,YY的形参在后
}
函数绑定到一个对象
绑定之后的调用, 最后一行 boundGetX,虽然是被全局对象(window)调用,但是其中的this已经被替换为module,故其中this.XX 都会在module对象中查询。
this.x = 9;
var module = {
x: 81
};
var getX = function() { return this.x; }
getX(); // 9, because in this case, "this" refers to the global object // Create a new function with 'this' bound to module
var boundGetX = getX.bind(module);
boundGetX(); // 81
给函数预置前导参数值
如果有种需求,需要某个函数被调用的第一个参数,是一个恒值,则需要使用封装,朴素做法
function XX() {} // 有种需求, 需要XX第一个参数恒为 1
function YY(){
var xxArgs = [1];
xxArgs.concat(Array.prototype.slice.call(arguments)); // [1, YYarguments]
XX(xxArgs)
}
bind提供了另外一种简便方法
function list() {
return Array.prototype.slice.call(arguments);
} var list1 = list(1, 2, 3); // [1, 2, 3] // Create a function with a preset leading argument
var leadingThirtysevenList = list.bind(undefined, 37); var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
setTimeout触发函数中this保持绑定对象
默认 setTimeout触发函数执行时候, this是绑定全局环境window, 如果想让绑定固定对象,可以使用bind接口。
如下, 对象的declare函数需要在事件到达后执行, 执行时候, declare其中的this实际上是代指LateBloomer创建的对象,但是直接作为setTimout参数则this指代window。 绑定替换为对象方法:
function LateBloomer() {
this.petalCount = Math.ceil(Math.random() * 12) + 1;
} // Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this), 1000);
}; LateBloomer.prototype.declare = function() {
console.log('I am a beautiful flower with ' +
this.petalCount + ' petals!');
}; var flower = new LateBloomer();
flower.bloom(); // after 1 second, triggers the 'declare' method
一则定时器封装应用: https://github.com/fanqingsong/Timer_Javascript/blob/master/src/timer_core.js
绑定函数作为构造函数
Warning: This section demonstrates JavaScript capabilities and documents some edge cases of the bind() method. The methods shown below are not the best way to do things and probably should not be used in any production environment.
如下例子, 将原构造函数 Point绑定到一个空对象{}上, 并生成了一个新的构造函数YAxisPoint ,此构造函数具有两个特性:
1、 其原型和Point相同的原型,即bind生成的函数,继承了待绑定函数的原型(但是你直接使用 YAxisPoint.prototype打印是undfined,只能在new之后的对象上体现)。
2、 YAxisPoint 是特定对象绑定之后的构造函数,只在普通调用情况下(YAixPoint()) 才执行this指代绑定对象的逻辑。
但是在new操作来调用的情况下, 即new YAxisPoint(), 绑定失效, 即Point中的this不指代绑定的对象。
new操作符号将按照普通构造函数逻辑,将生成一个新的对象, 将构造函数代码执行一遍,构造函数中的this指代,新建立的对象。new Construtor() 执行逻辑:
var instance = {}
Constructor.apply(instance)
instance.__proto__ = Constructor.prototype;
3、虽然Point中this不指代YAxisPoint 绑定对象(YAxisPoint 绑定啥对象也没有关系了, 但是为了避免YAxisPoint ()造成污染其他变量,尽量使用null 和空对象), 但是绑定的参数还是按照bind的定义作为前导参数传递到Point构造函数中。
修改下开头绑定的为伪代码,添加new调用的情况:
假设有函数
function XX(variable){ alert(this.variable=variable) }
则XX函数经过绑定后的效果
// var YY = XX.bind(widow, variable) 返回的函数 等价于如下代码段
function YY(Yvariable)
{
if YY called classically then // YY普通调用模式, 由于XX已经绑定对象,需要对XX的this做替换
function XX(){ alert(widow.variable=variable) } //XX函数中的 this 替换为 window, 待绑定的目标
else if YY called by new operator then // new YY 情况, bind不影响创建新对象
function XX(){ alert(this.variable=variable) } //XX函数中的 this不替换,对应 被new调用的情况
end
XX(variable, Yvariable); //XX调用,绑定的可选参数在前,YY的形参在后
}
------ MDN 实例-----
function Point(x, y) {
this.x = x;
this.y = y;
} Point.prototype.toString = function() {
return this.x + ',' + this.y;
}; var p = new Point(1, 2);
p.toString(); // '1,2' var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0/*x*/);
// not supported in the polyfill below,
// works fine with native bind:
var YAxisPoint = Point.bind(null, 0/*x*/); var axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5' axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new Point(17, 42) instanceof YAxisPoint; // true
创建快捷方式
对于一些原型对象的函数, 如果需要引用, 往往需要些很长的一串代码, 如将array-like对象,转换为数组的 slice接口应用, 则需要如下写法:
Array.prototype.slice.apply(arguments);
进一步通过变量首先声明下slice接口, 然后调用,可以简洁点:
var slice = Array.prototype.slice; // ... slice.apply(arguments);
但是不能进一步将apply也放到 slice的声明中, 如果这样会有js报错, 原因为slice指向了apply函数,然后slice在window全局环境下执行, 效果相当于 window.apply,window是个对象, 不是函数,所以会有报错:
var slice = Array.prototype.slice.apply; // ... slice(arguments);
为了更简洁的写法,下面有请bind:
// same as "slice" in the previous example
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.apply.bind(unboundSlice); // ... slice(arguments);
现在给出进一步解释:
apply是任意函数的原型函数, 即 为 Function.prototype属性, 任意定义的一个函数 例如 function XX(){}
都可以调用apply, 即 XX.apply(obj), 此函数的意思是将 XX函数中的this替换为obj,并执行。 这里说的任意函数,当然也包括 上面例子的 Array.prototy.slice这也是函数。
function XX() { this.attr = 0}
XX(); // 由于在全局环境window中调用, this指代window对象
var obj = {}
XX.apply(obj) //将XX中的this替换为obj, 然后执行XX
// 上一行等价于, 先根据XX生成一个替换过this的函数YY, 然后再执行, 即apply有如下实现(伪代码):
function Function.prototype.apply(obj) //obj为XX函数中this替换的目标
var YYGenerator = function( caller) // caller为 apply调用对象, 即XX
return function() { caller.functionBody.replace( this, obj) } // XX函数体中 this 替换为 obj
end
var YY() = YYGenerator(this); // this 为apply调用者, 即为 XX函数
YY();
end
则apply函数经过绑定后的效果,构造过程包括三步,逐步填充slice函数体:
1、将Function.prototype.apply代码 复制到另一函数 boundapply
2、执行绑定动作, 将boundapply中 this 替换为 bind的第一个参数 unboundSlice
3、添加boundapply调用语句。
//var unboundSlice = Array.prototype.slice;
//var slice = Function.prototype.apply.bind(unboundSlice); 生成的函数等价于如下伪代码
function slice()
{
function boundapply(obj) //obj为unboundSlice函数中this替换的目标
var YYGenerator = function( caller) // caller为 apply调用对象, 即unboundSlice
return function() { caller.functionBody.replace( this, obj) } // unboundSlice函数体中 this 替换为 obj
end
var YY() = YYGenerator(unboundSlice); // this替换为unboundSlice为apply调用者, 即为 unboundSlice函数
YY();
end
boundapply(arguments); //arguments为unboundSlice函数中this替换的目标
}
slice(arguments)
此场景不太好理解, 故给出上文描述, 其中牵扯到两个易混淆点:
1、 bind 将apply函数中 this替换为 bind的入参 Array.prototype.slice, 这个在绑定的时刻即执行。
2、 apply生效,将Array.prototype.slice函数中 this替换为arguments,并执行, 这个在slice被调用阶段执行。
3、 例子当中的 bind之后的参数名称, slice为按照应用场景命名, 实际上此名称应该是 sliceBoundApply,即实际上它函数体还是Function.prototype.apply的代码,只不过 Function.prototype.apply代码中的this固定为一个函数 Array.prototype.slice, 那么 sliceBoundApply === Array.prototype.slice.apply (apply函数中的this指代Array.prototype.slice,因为是Array.prototype.slice对象调用了apply)
所以 sliceBoundApply(obj) === Array.prototype.slice.apply (obj)
通篇出现两种情况的绑定: 1、 被绑定函数中,将this作为Object使用, 则绑定的目标为Object,如”函数绑定到对象例子” 2、被绑定函数中,将this作为函数使用, 则绑定的目标为Function, 如“创建快捷方式”
绑定前后一图明义:

Function.prototype.bind接口浅析的更多相关文章
- 浅析 JavaScript 中的 Function.prototype.bind() 方法
Function.prototype.bind()方法 bind() 方法的主要作用就是将函数绑定至某个对象,bind() 方法会创建一个函数,函数体内this对象的值会被绑定到传入bind() 函数 ...
- 一起Polyfill系列:Function.prototype.bind的四个阶段
昨天边参考es5-shim边自己实现Function.prototype.bind,发现有不少以前忽视了的地方,这里就作为一个小总结吧. 一.Function.prototype.bind的作用 其实 ...
- JavaScript 函数绑定 Function.prototype.bind
ECMAScript Edition5 IE9+支持原生,作用为将一个对象的方法绑定到另一个对象上执行. Function.prototype.bind = Function.prototype.bi ...
- Function.prototype.bind
解析Function.prototype.bind 简介 对于一个给定的函数,创造一个绑定对象的新函数,这个函数和之前的函数功能一样,this值是它的第一个参数,其它参数,作为新的函数的给定参数. b ...
- 解析Function.prototype.bind
简介 对于一个给定的函数,创造一个绑定对象的新函数,这个函数和之前的函数功能一样,this值是它的第一个参数,其它参数,作为新的函数的给定参数. bind的作用 bind最直接的作用就是改变this的 ...
- javascript Function.prototype.bind
语法: fn.bind(obj,arg1,arg2,arg3...) bind是es5新增的方法,顾名思义,它的作用是将函数绑定到某个对象上,就像是某个对象调用方法一样.其本质还是改变了该函数的上下文 ...
- 理解javascript中的Function.prototype.bind
在初学Javascript时,我们也许不需要担心函数绑定的问题,但是当我们需要在另一个函数中保持上下文对象this时,就会遇到相应的问题了,我见过很多人处理这种问题都是先将this赋值给一个变量(比如 ...
- 理解 JavaScript 中的 Function.prototype.bind
函数绑定(Function binding)很有可能是你在开始使用JavaScript时最少关注的一点,但是当你意识到你需要一个解决方案来解决如何在另一个函数中保持this上下文的时候,你真正需要的其 ...
- prototype.js中Function.prototype.bind方法浅解
prototype.js中的Function.prototype.bind方法: Function.prototype.bind = function() { var __method = this; ...
随机推荐
- [ZZ] GTX760首测
再一次让AMD难做!NVIDIA新主力GTX760首测 1又见短板高端显卡,GTX760外观对比回顶部 [PConline评测]NVIDIA迅速的步伐真让人吃惊,短时间内拿出GTX780.GTX770 ...
- Twitter Storm中Bolt消息传递路径之源码解读
本文初次发表于storm-cn的google groups中,现以blog的方式再次发表,表明本人徽沪一郎确实读过这些代码,:). Bolt作为task被executor执行,而executor是一个 ...
- PHP 开发 APP 接口 学习笔记与总结 - Redis 缓存
Redis 可以定期将数据备份到磁盘中(持久化),同时不仅仅支持简单的key/value 类型的数据,同时还提供list,set,hash等数据结构的存储:Memcache 只是简单的key/valu ...
- InnoDB , MyISAM :MySQL 5.7 Supported Storage Engines
http://dev.mysql.com/doc/refman/5.7/en/storage-engines.html https://en.wikipedia.org/wiki/ACID https ...
- Java中调用c/c++语言出现Exception in thread "main" java.lang.UnsatisfiedLinkError: Test.testPrint(Ljava/lang/String;)V...错误
错误: Exception in thread "main" java.lang.UnsatisfiedLinkError: Test.testPrint(Ljava/lang/S ...
- 【转】C# 解析 json
C# 解析 json JSON(全称为JavaScript Object Notation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集. JSON采用完全独立于语言的 ...
- CentOS 6.5 源码安装MySQL5.6.26
1:下载安装cmake (mysql5.5以后是通过cmake来编译的) 2:创建mysql的安装目录及数据库存放目录 #mkdir /usr/mysql //安装my ...
- (转)freemakeer初入门
在web开发过程中,尤其是后台管理系统的开发中,少不了增删改成的基础操作,原来我自己的做法是一份一份的拷贝粘贴,然后修改其中的不同,然而这样既枯燥无味又浪费了大量的时间,所以根据自己项目结构的特点写了 ...
- TOMCAT源码分析(启动框架)
建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, 是不那么容易掌握TOMCAT的框架的. 所以得实践.实践.再实践. 建议下载一份TOMCAT的源码, 调试通过, 然后单步跟踪其启动 ...
- css背景图片定位练习(二): background-position的百分比
background-position:x y; 百分比定位并不能直观的看出来,需要通过计算. background-position百分比计算公式: (容器宽度—背景图片的宽度)*x%=xpx(容器 ...