某人

此前素未谋面、此后遥遥无期

0%

函数apply-call-bind

Function.prototype.apply()

apply() 方法调用一个函数, 其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数。

语法:

1
fun.apply(thisArg, [argsArray])
  • thisArg: 在 fun 函数运行时指定的 this 值。需要注意的是,指定的 this 值并不一定是该函数执行时真正的 this 值,如果这个函数处于非严格模式下,则指定为 nullundefined 时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。

  • argsArray: 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。

ECMAScript 第5版开始,可以使用任何种类的类数组对象,就是说只要有一个 length 属性和[0...length) 范围的整数属性。例如现在可以使用 NodeList 或一个自己定义的类似 {'length': 2, '0': 'eat', '1': 'bananas'} 形式的对象。

注意:Chrome 14 以及 Internet Explorer 9 仍然不接受类数组对象。如果传入类数组对象,它们会抛出异常。

例子:

例1

1
2
3
var numbers = [5, 6, 2, 3, 7,+Infinity,-Infinity];
max = Math.max.apply(null, numbers); //Infinity
min = Math.min.apply(null, numbers); //-Infinity

例2

1
2
3
4
5
6
7
8
9
10
var cbj = {
lang:"汉语",
speak:function(){
console.log(this.lang);
}
}
var ebj = {
lang:"英语"
}
cbj.speak.apply(ebj)//英语

例3

1
2
3
4
5
6
7
8
9
function Person() {
this.lang = "谁知道你讲什么";
}
Person.prototype.speak = function(args1,args2) {
console.log("speak " + this.lang,args1,args2);
}
var pe = new Person();
pe.speak();//speak 谁知道你讲什么 undefined undefined
pe.speak.apply({lang:'汉语'},[1,2]);//speak 汉语 1 2

当心:如果用上面的方式调用 apply, 你很可能会遇到方法参数个数越界的问题. 当你对一个方法传入非常多的参数 (比如超过1W多个参数) 时, 就非常有可能会导致越界问题, 这个临界值是根据不同的 JavaScript 引擎而定的.

Function.prototype.call()

call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。

语法:

1
fun.call(thisArg[, arg1[, arg2[, ...]]])
  • thisArg:在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为nullundefinedthis值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
  • arg1, arg2, ...:指定的参数列表。

可以让call()中的对象调用当前对象所拥有的function。你可以使用call()来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)

例子:

使用 call 方法调用函数并且指定上下文的this

1
2
3
4
5
6
7
8
9
10
var cbj = {
lang:"汉语",
speak:function(){
console.log(this.lang);
}
}
var ebj = {
lang:"英语"
}
cbj.speak.call(ebj)//英语

使用call方法调用父构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function Product(name, price) {
this.name = name;
this.price = price;
this.getInfo = function(){
console.log(this.name+this.price+"块");
}
}

function Fruits(name, price) {
Product.call(this, name, price);
this.category = '水果';
this.getFruitsInfo = function(){
console.log(this.category+":"+this.name+this.price+"块");
};
}

function Vegetables(name, price) {
Product.call(this, name, price);
this.category = '蔬菜';
this.getVegetablesInfo = function(){
console.log(this.category+":"+this.name+this.price+"块");
};
}

var pro = new Product('红烧西瓜茄子', 10);
var fru = new Fruits('西瓜', 20);
var veg = new Vegetables('茄子', 30);

pro.getInfo();//红烧西瓜茄子10块

fru.getInfo();//西瓜20块
fru.getFruitsInfo();//水果:西瓜20块

veg.getInfo();//茄子30块
veg.getVegetablesInfo();//蔬菜:茄子30块

使用call方法调用匿名函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var animals = [
{color: '白色', name: '猴子'},
{color: '黑色', name: '老虎'}
];

for (var i = 0; i < animals.length; i++) {
(function (i) {
this.print = function () {
console.log('#' + i + ' ' + this.color + ': ' + this.name);
}
this.print();
}).call(animals[i], i);
}
//#0 白色: 猴子
//#1 黑色: 老虎

Function.prototype.bind()

bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。

语法

1
fun.bind(thisArg[, arg1[, arg2[, ...]]])
  • thisArg:当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
  • arg1, arg2, ...:当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。

bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。当新函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

例子:

创建绑定函数

bind() 最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的 this 值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
this.x = 9;
var obj = {
x: 81,
getX: function() { return this.x; }
};

obj.getX(); // 返回 81

var retrieveX = obj.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域

// 创建一个新函数,将"this"绑定到obj对象
var boundGetX = retrieveX.bind(obj);
boundGetX(); // 返回 81

用预设的初始参数创建函数

1
2
3
4
5
6
7
8
9
function List() {
return Array.prototype.slice.call(arguments);
}

// 用预设的初始参数创建函数
var clist = List.bind(undefined, 100);

clist(); //[100]
clist(1, 2, 3);//[100,1,2,3]

作为构造函数使用的绑定函数

绑定函数适用于用new操作符 new 去构造一个由目标函数创建的新的实例。

当一个绑定函数是用来构建一个值的,原来提供的 this 就会被忽略。

然而, 原先提供的那些参数仍然会被前置到构造函数调用的前面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Point(x, y) {
this.x = x;
this.y = y;
}

Point.prototype.print = function() {
console.log(this.x + ',' + this.y);
};

var obj = {};
var PointA= Point.bind(obj, 0);
var pa = new PointA(5);
PointA(5);//undefined
pa.print();//0,5
console.log(obj);//Object {x: 0, y: 5}

创建捷径

将一个类似于数组的对象(array-like object)转换成一个真正的数组

1
2
3
4
5
6
var a = {'1':'小明','2':'小黑','4':'小刚',length:6};
Array.prototype.slice.apply(a);
//[undefined × 1, "小明", "小黑", undefined × 1, "小刚", undefined × 1]

Array.prototype.slice.call(a);
// [undefined × 1, "小明", "小黑", undefined × 1, "小刚", undefined × 1]

bind() 可以使这个过程变得简单

sliceFunction.prototypecall() 方法的绑定函数.

并且将 Array.prototypeslice() 方法作为 this 的值

1
2
3
4
var a = {'1':'小明','2':'小黑','4':'小刚',length:6};
var slice = Function.prototype.call.bind(Array.prototype.slice);
slice(a);
// [undefined × 1, "小明", "小黑", undefined × 1, "小刚", undefined × 1]

最后

  • call()方法第二个参数是若干个参数的列表;而apply()方法的第二个参数是一个包含多个参数的数组;bind()方法第二个参数是若干个参数的列表,是创建函数的预设的初始参数 。
  • apply 、 call 、bind三个方法的第一个参数都是this要指向的对象,可以改变函数的this对象的指向。
  • bind方法 返回对应的创建函数,便于后续调用;apply 、call 则是立即执行 。

相关链接

  1. apply参数个数限制在65536)