使用 call() 、apply() 和 bind()

目录

1、call() 和 apply()

2、使用 bind() 方法

3、call()、apply() 和 bind() 的区别

4、call() 和 apply() 性能比较


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等电脑当前性能不会有相同的情况,不同浏览器也会导致性能上的不同;可以多测几次。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值