call------调用
apply------应用
bind----关联,绑定
call(),apply(),bind()都是用来重定义this这个对象的
使用call()
(或apply()
)来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。
每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象。例如:
function sum(num1, num2){ return num1 + num2; } function callSum1(num1, num2){ return sum.apply(this, arguments); // 传入arguments对象 } function callSum2(num1, num2){ return sum.apply(this, [num1, num2]); // 传入数组 } alert(callSum1(10,10)); //20 alert(callSum2(10,10)); //20
在上面这个例子中,callSum1()在执行sum()函数时传入了this作为this值(因为是在全局作用域中调用的,所以传入的就是window对象)和arguments对象。而callSum2同样也调用了sum()函数,但它传入的则是this和一个参数数组。这两个函数都会正常执行并返回正确的结果。
注意:在严格模式下,未指定环境对象而调用函数,则this值不会转型为window。除非明确把函数添加到某个对象或者调用apply()或call(),否则this值将是undefined。
call()
方法与apply()
方法的作用相同,它们的区别仅在于接收参数的方式不同。对于call()
方法而言,第一个参数是this
值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使用call()
方法时,传递给函数的参数必须逐个列举出来,如下面的例子所示。
function sum(num1, num2){ return num1 + num2; } function callSum(num1, num2){ return sum.call(this, num1, num2); } alert(callSum(10,10)); //20
在使用call()方法的情况下,callSum()必须明确地传入每一个参数。结果与使用apply()没有什么不同。至于是使用apply()还是call(),完全取决于你采取哪种给函数传递参数的方式最方便。如果你打算直接传入arguments对象,或者包含函数中先接收到的也是一个数组,那么使用apply()肯定更方便;否则,选择call()可能更合适。(在不给函数传递参数的情况下,使用哪个方法都无所谓。)
var each = function(array,fn){ for(var index in array){ fn.call(null,index,array[index]); } } each([4,20,3],function(index,ele){ document.write("第"+index+"个元素是"+ele+"<br />"); } )
在each对象中调用fn函数,但是这时候fn函数还没有确定呢,所以必须使用call()方法来调用。
call()方法 的语法格式是 函数引用.call(调用者,参数1,参数2,参数3)
调用者.函数(参数1,参数2,参数3) == 函数.call(调用者,参数1,参数2,参数3)
一个简单的使用示例
var foo = { x: 3; } var bar = function(){ console.log(this.x); } bar();//undefined var boundFunc = bar.bind(foo); boundFunc();//3
第一次bar()
执行的时候,console.log(this.x)
里面的this
指向的是全局作用域
而bind
方法执行的以后,把this
指向了foo
而不是全局作用域。
bind()用法示例
1.创建绑定函数
bind() 最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的 this 值。
this.x = 9; var module = { x: 81, getX: function() { return this.x; } }; module.getX(); // 返回 81 var retrieveX = module.getX; retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域 // 创建一个新函数,将"this"绑定到module对象 // 新手可能会被全局的x变量和module里的属性x所迷惑 var boundGetX = retrieveX.bind(module); boundGetX(); // 返回 81
2.偏函数
bind()
的另一个最简单的用法是使一个函数拥有预设的初始参数。这些参数(如果有的话)作为bind()
的第二个参数跟在this
(或其他对象)后面,
之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。
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]
3.配合setTimeout
在默认情况下,使用 window.setTimeout()
时,this
关键字会指向 window
(或全局)对象。当使用类的方法时,需要 this
引用类的实例,你可能需要显式地把 this
绑定到回调函数以便继续使用实例。
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(); // 一秒钟后, 调用'declare'方法
4.作为构造函数使用的绑定函数
这是bind()
的超前用法,在生产环境中请谨慎使用
自然而然地,绑定函数适用于用new
操作符 new
去构造一个由目标函数创建的新的实例。当一个绑定函数是用来构建一个值的,原来提供的 this
就会被忽略。
然而, 原先提供的那些参数仍然会被前置到构造函数调用的前面。
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*/); // 以下这行代码在 polyfill 不支持, // 在原生的bind方法运行没问题: //(译注:polyfill的bind方法如果加上把bind的第一个参数,即新绑定的this执行Object()来包装为对象,Object(null)则是{},那么也可以支持) 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
你知道不需要做特别的处理就可以用new
操作符 new
创建一个绑定函数。必然地,你需要知道不需要做特别处理就可以创建一个可以被直接调用的绑定函数,
即使你更希望绑定函数是用new
操作符 new
来调用。
// 这个例子可以直接在你的 javascript 控制台运行 // ...接着上面的代码继续(译注: // 仍然能作为一个普通函数来调用 // (即使通常来说这个不是被期望发生的) YAxisPoint(13); emptyObj.x + ',' + emptyObj.y; // '0,13'
5.快捷调用
在你想要为一个需要特定的 this
值的函数创建一个捷径(shortcut)的时候,bind()
方法也很好用。
你可以用 Array.prototype.slice
来将一个类似于数组的对象(array-like object)转换成一个真正的数组,就拿它来举例子吧。你可以创建这样一个捷径:
var slice = Array.prototype.slice; // ... slice.apply(arguments);
用 bind()
可以使这个过程变得简单。在下面这段代码里面,slice
是 Function.prototype
的 apply()
方法的绑定函数,并且将 Array.prototype
的 slice()
方法作为 this
的值。这意味着我们压根儿用不着上面那个 apply()
调用了。
// same as "slice" in the previous example var unboundSlice = Array.prototype.slice; var slice = Function.prototype.apply.bind(unboundSlice); // ... slice(arguments);