目录
1、call() 和 apply()
call() 和 apply() 是 Function 对象的原型方法,它们能够将特定函数当作一个方法绑定到指定对象上并进行调用。业界俗称:修改函数的 this 指向。具体用法如下所示:
function.call(thisobj,arg1,arg2...);
function.apply(thisobj,[args]);
其中参数 thisobj 表示指定的对象,参数 args 表示要传递给被调用函数的参数。call() 方法只能接收多个参数列表,而 apply() 只能接收一个数组或者伪类数组,数组元素将作为参数传递给被调用的函数。
当函数被绑定到指定对象上之后,将利用传递的参数执行函数,并返回函数的返回值。
function fun(x,y){ //定义一个简单的函数
return x + y;
}
function obj(a,b){ //定义一个函数结构的伪对象
return a * b;
}
alert(fun.call(obj,3,4)) //返回 7 -----> 实际上等价于下面这三句
obj.m = fun; //为对象 obj 定义一个方法 m,该方法将调用函数 fun
alert(obj.m(3,4)); //返回 7
delete obj.m; //删除对象 obj 的方法 m
在上面示例中,fun 是一个简单的函数,而 obj 是一个构造函数对象。通过 call() 方法把函数 fun 绑定到对象身上,变为它的一个方法,然后动态调用函数 fun ,同时把参数 3 和 4 传递给函数 fun,则调用函数 fun 后返回值为 7。
apply() 与 call() 方法功能和用法都相同,唯一的区别是它们传递给参数的方式不同。其中 apply() 是以数组形式传递参数,而 call() 方法以多个值的形式传递参数。针对上面示例,使用 apply() 方法来调用函数 fun,则设计代码如下所示:
function fun(x,y){ //定义一个简单的函数
return x + y;
}
function obj(a,b){ //定义一个函数结构的伪对象
return a * b;
}
alert(fun.apply(obj,[3,4])) //返回 7
设计把一个数组或伪类数组的所有元素作为参数进行传递时,使用 apply() 方法就非常非常遍历。
function max(){
var m = Number.NEGATIVE_INEINITY; //声明一个负无穷大的数值
for(var i=0;i<arguments.length;i++){ //遍历函数所有的实参
if(argquments[i]>m) //如果实参值大于变量m
m = arguments[]; //则把该实参值赋值给m
}
return m; //返回最大值
}
var a = [23,45,2,46,62,45,56,63]; //声明并初始化数组
var m = max.apply(Object,a); //把函数max绑定为Object对象的方法,并动态调用
alert(m); //返回63
在上面示例中,设计定义一个函数 max() ,用来计算所有参数中最大值参数。首先通过 apply() 方法,动态调用 max() 函数,然后把它绑定为 Object 对象的一个方法,并把包含多个值的数组传递给它,最后返回经过 max() 计算后的最大数组元素。
也可以把数组元素通过 apply() 方法传递给 Math 的 max() 方法来计算数组的最大值元素。
var a = [23,45,2,46,62,45,56,63]; //声明并初始化数组
var m = Math.max.apply(Object,a); //调用系统函数max
alert(m); //返回63
使用 call() 和 apply() 方法可以把一个函数转换为指定对象的方法,并在这个对象上调用该方法,这种行为只是临时的,函数实际上并没有作为对象的方法而存在,当函数被动态调用之后,这个对象的临时方法也会自动被注销。
function fun(){ //定义空函数
}
fun.call(Object); //把函数fun绑定为Object对象的方法
Object.fun(); //再次调用该方法,则返回编译错误
call() 和 apply() 方法能够动态改变函数内 this 指代的对象,这在面向对象编程中是非常有用的,以下示例使用 call() 方法不断改变函数内 this 指代对象,主要通过变换 call() 方法的第一个参数值来实现。
var x = "o"; //定义全局变量x,初始化为字符。
function a(){ this.x = "a"; } //定义函数类结构a,并定义函数内部变量x并初始化为字符
function b(){ this.x = "b";} //定义函数类结构b,定义函数内局部变量x,初始化为字符b
function c(){ alert(x);} //定义普通函数,提示变量x的值
function fun(){ alert(this.x);} //定义普通函数,提示当前指针所包含的变量x的值
fun(); //返回字符o,即全局变量x的值。this此时指向window对象
fun.call(window); //返回字符o,即全局变量x的值。this此时指向window对象 注意:如果不写参数,默认是window
fun.call(new a()); //返回字符a,即函数a内部的局部变量x的值、this 此时指向函数a
fun.call(new b()); //返回字符b,即函数b内部的局部变量x的值,this此时指向函数b
fun.call(c); //返回undefined,即函数c内部的局部变量x的值。
//但是该函数并没有定义x变量,所以返回undefined,this此时指向函数c
在函数体内,call() 和 apply() 方法的第一个参数就是调用函数内 this 的值。
function fun(){ //定义函数类结构
this.a = "a"; //定义成员a并赋值,a为属性
this.b = function(){ //定义成员b并赋值,b为方法
alert("b");
}
}
function e(){ //定义函数
fun.call(this); //在函数体内动态调用函数fun,this指代函数e
alert(a); //显示变量a的值
}
e(); //返回字符串 a
2、使用 bind() 方法
ECMAScript 5 为 Function 增加了一个原型方法 bind(Function.prototype.bind),用来把函数绑定到指定对象上。从本质上讲,它允许在其他对象链中执行一个函数。
也就是说,对于给定函数,创建具有与原始函数相同的主体的绑定函数,在绑定函数中,this 对象将解析为传入的对象。绑定函数具有指定的初始参数。具体用法如下:
function.bind(thisArg[,arg1[,axg2[,argN]]])
//function:必需参数,一个函数对象。
//thisArg:必需参数,this关键字可在新函数中引用的对象。
//arg1[,arg2[,argN]:可选参数,要传递到新函数的参数的列表。
bind 方法将返回与 function 函数相同的新函数,并不会直接该执行函数。
下面例子是定义原始函数 checkNumbericRange,用来检测传入的参数值是否在一个指定范围内,范围下限和上限根据当前实例对象的 minimum 和 maximum 属性决定。然后使用 bind 方法把 checkNumbericRange 函数绑定到对象 range 身上。如果再次调用这个新绑定后的函数 boundCheckNumericRange 后、就可以根据该对象的属性 minmum 和 maximum 来确定调用函数时传入值是否在指定的范围内。
var checkNumericRange = function(value){
if(typeof value !== 'number')
return false;
else
return value >= this.minimum && value <= this.maximum;
}
var range = {minimum:10, maximum:20};
var boundCheckNumericRange = checkNumericRange.bind(range); //返回一个新函数
var result = boundCheckNumericRange(12); //新函数的调用并传参
document.write(result); //true
3、call()、apply() 和 bind() 的区别
它们在功能上是没有区别的,都是改变 this 的指向,它们的区别主要是在于方法的实现形式和参数传递上的不同。
函数.call(对象,arg1,arg2....)
函数.apply(对象,[arg1,arg2,...])
函数.bind(对象,arg1,arg2,....)() //bind返回的是一个函数体,并不会直接执行函数
function show(sex){
console.log("姓名为" + this.yourname + ",性别为" + sex);
}
var person = {
name:"张三",
age:14
};
show.call(person,"男"); //姓名为张三,性别为男
show.apply(person,['女']); //姓名为张三,性别为女
show.bind(person,"未知")();//姓名为张三,性别为未知
call()、apply() 和 bind() 三者的区别,是函数传递的方式不同以及 bind 方法的实现方式不同。
4、call() 和 apply() 性能比较
经过测试,call() 和 apply() 方法的性能和传递的参数有关,当传递的参数在 3 个或 3 个以内的时候,二者的性能几乎相同,但是当传递的参数大于 3 个的时候,call() 方法的性能更好,推荐优先使用 call() 方法。
在日常开发中如果需要用到性能测试,可以简单的通过代码运行时间来判断。
//第一种性能检测方式,只能精确到毫秒
let t1 = new Date();
for(let i=0;i<10000;i++){
}
console.log(new Date() - t1);
//第二种性能检测方法,精确度更高
console.time('A');
for(leti=e;i<10000;i++){
}
console.timeEnd('A');
//第三种性能检测方法,精确度比第二种还要高
//console.profile()
//在火狐浏览器中安装FireBug,可以更精准的获取到程序每一个步骤所消耗的时间
自己实现性能测试:任何的代码性能测试都是和测试的环境有关系的,例如CPU、内存、GPU等电脑当前性能不会有相同的情况,不同浏览器也会导致性能上的不同;可以多测几次。