在JavaScript中,call、apply和bind是Function对象自带的三个方法,本文将通过几个场景的应用,来详细理解三个方法。
- call()
call() 方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法。
当调用一个函数时,可以赋值一个不同的 this 对象。this 引用当前对象,即 call 方法的第一个参数。
通过 call 方法,你可以在一个对象上借用另一个对象上的方法,比如Object.prototype.toString.call([]),就是一个Array对象借用了Object对象上的方法。
语法 fun.call(thisArg[, arg1[, arg2[, ...]]])
thisArg
在fun函数运行时指定的this值。需要注意的是下面几种情况:
(1)不传,或者传null,undefined, 函数中的this指向window对象
(2)传递另一个函数的函数名,函数中的this指向这个函数的引用,并不一定是该函数执行时真正的this值
(3)值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象,如 String、Number、Boolean
(4)传递一个对象,函数中的this指向这个对象
1.1 例子
初级应用例子
function a(){ //输出函数a中的this对象 console.log(this); }
//定义函数b
function b(){} var obj = {name:'这是一个屌丝'}; //定义对象obj a.call(); //window a.call(null); //window a.call(undefined);//window a.call(1); //Number a.call(''); //String a.call(true); //Boolean a.call(b);// function b(){} a.call(obj); //Object
使用call方法调用匿名函数并且指定上下文的this
在下面的例子中,当调用 greet 方法的时候,该方法的 this 值会绑定到 i对象。
function greet() { var reply = [this.person, '是一个轻量的', this.role].join(' '); console.log(reply); }
var i = { person: 'JSLite.io', role: 'Javascript 库。' };
greet.call(i);
// JSLite.io 是一个轻量的 Javascript 库
使用call方法调用匿名函数
在下例中的for循环体内,我们创建了一个匿名函数,然后通过调用该函数的call方法,将每个数组元素作为指定的this值执行了那个匿名函数。这个匿名函数的主要目的是给每个数组元素对象添加一个print方法,这个print方法可以打印出各元素在数组中的正确索引号。当然,这里不是必须得让数组元素作为this值传入那个匿名函数(普通参数就可以),目的是为了演示call的用法。
var animals = [ {species: 'Lion', name: 'King'}, {species: 'Whale', name: 'Fail'} ];
for (var i = 0; i < animals.length; i++) { (function (i) { this.print = function () { console.log('#' + i + ' ' + this.species + ': ' + this.name); } this.print(); }).call(animals[i], i); }
//#0 Lion: King
//#1 Whale: Fail
使用call方法调用函数传参数
var a = { name:'JSLite.io', //定义a的属性 say:function(){ //定义a的方法 console.log("Hi,I'm function a!"); } };
function b(name){ console.log("Post params: "+ name); console.log("I'm "+ this.name); this.say(); }
b.call(a,'test'); //Post params: test //I'm JSLite.io //I'm function a!
- apply()
语法与 call() 方法的语法几乎完全相同,唯一的区别在于,apply的第二个参数必须是一个包含多个参数的数组(或类数组对象)。apply的这个特性很重要(因为这个特性可以做很多事情),
apply方法会调用一个函数,apply方法的第一个参数会作为被调用函数的this值,apply方法的第二个参数(一个数组,或类数组的对象)会作为被调用对象的arguments值,也就是说该数组的各个元素将会依次成为被调用函数的各个参数;
在调用一个存在的函数时,你可以为其指定一个 this 对象。 this 指当前对象,也就是正在调用这个函数的对象。 使用 apply, 你可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。
语法:fun.apply(thisArg[, argsArray])
注意:需要注意:Chrome 14 以及 Internet Explorer 9 仍然不接受类数组对象。如果传入类数组对象,它们会抛出异常。
2.1 参数
thisArg
同上call的thisArg参数。
argsArray
一个数组或者类数组对象,其中的数组元素将作为单独的参数传给fun 函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。
2.2 例子
function jsy(x,y,z){ console.log(x,y,z); }
jsy.apply(null,[1,2,3]); // 1 2 3
使用apply来链接构造器的例子
你可以使用apply来给一个对象链接构造器,类似于Java. 在接下来的例子中我们会创建一个叫做construct的全局的Function函数,来使你能够在构造器中使用一个类数组对象而非参数列表。
Function.prototype.construct = function(aArgs) { var fConstructor = this, fNewConstr = function() { fConstructor.apply(this, aArgs); }; fNewConstr.prototype = fConstructor.prototype; return new fNewConstr(); };
function MyConstructor () { for (var nProp = 0; nProp < arguments.length; nProp++) { console.log(arguments,this) this["property" + nProp] = arguments[nProp]; } }
var myArray = [4, "Hello world!", false];
var myInstance = MyConstructor.construct(myArray);
console.log(myInstance.property1); // logs "Hello world!" console.log(myInstance instanceof MyConstructor); // logs "true" console.log(myInstance.constructor); // logs "MyConstructor"