示例代码
var thisArg = {};
function targetFunc( arg1, arg2, arg3 ) {
console.log( this === thisArg );
}
var boundFunc = targetFunc.bind( thisArg, 1, 2 );
// thisArg
boundFunc(); // true
// function name
console.log( targetFunc.name ); // targetFunc
console.log( boundFunc.name ); // bound targetFunc
// the typical number of arguments expected by the function
console.log( targetFunc.length ); // 3
console.log( boundFunc.length ); // 1
以上述代码为示例,boundFunc 和 targetFunc 的关系:
基础
- 调用 boundFunc 函数就像调用 targetFunc 函数一样,只是 boundFunc 内部的 this 指向 thisArg。
- boundFunc 的名字是 targetFunc 函数的名字加上前缀:bound。
- boundFunc 形参数量(boundFunc.length)是 targetFunc 形参数量减去调用 targetFunc.bind( thisArg, 1, 2 ) 时候输入的参数的数量。如果调用 bind 时输入的参数数量大于 targetFunc 的形参数量,则 boundFunc 的形参数量为 0(boundFunc.length === 0)。
function tf( arg1, arg2, arg3 ) {
}
var bf = tf.bind( null, 1, 2, 3, 4, 5 );
console.log( bf.length ); // 0
高级
bind 返回的 boundFunc 其实不是一般的对象(在 js 中,函数也是一个对象),在规范中称之为 exotic object,而一般的函数在规范中称之为 ordinary object。区别在于 exotic object 可能没有 ordinary object 一些通用的内部属性(internal slot)。譬如:一般的函数对象有一个内部属性[[Strict]]
,标识这个函数是否运行在严格模式下,而 boundFunc 没有。但是 boundFunc 有如下一些特殊的内部属性:
-
[[BoundTargetFunction]]
:被包装的函数,即上例中的 targetFunc。 -
[[BoundThis]]
:targetFunc 每次被调用时,this 指向的那个值,即上例中的 thisArg。 -
[[BoundArguments]]
:一个列表,当 targetFunc 被调用时候先传入的参数们,即上例中的 1,2。
然后运行 boundFunc( argumentsList ) 的过程基本上概括为:
// 假设 argumentsList 是 boundFunc 被调用时传入的参数列表
var argumentsList;
var target = boundFunc.[[BoundTargetFunction]];
var boundThis = boundFunc.[[BoundThis]];
var boundArgs = boundFunc.[[BoundArguments]];
// boundArgs 和 argumentsList 都不是 js array,但是这里作为示例伪代码 就懒得做转换了。
var args = boundArgs.concat( argumentsList );
target.apply( boundThis, args );
后记
看了一遍 Function.prototype.bind 的规范,发现没有什么特别的边际条件,倒是又引出了两个主题:函数作为普通函数和构造函数分别是如何运行。(接下来准备为这两个主题分别总结下,等写完会 link 到这里的后记里)