call()、apply()、bind()详解

本文详细解析了JavaScript中this关键字的工作原理及其在不同上下文中的行为表现。包括在普通函数、严格模式、方法调用及构造函数中的作用,并介绍了call、apply、bind三个方法如何改变this的指向。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在 JavaScript 中,this 是指当前函数中正在执行的上下文环境;当你调用一个函数时,this总是作为一个(隐式)参数。

(1)、宽松模式下的普通函数:
this总是指向全局对象,(在浏览器中是window)

var a=123;
var A=function(){
   return this;  //Window 
}

(2)、严格模式下
使用严格模式,只需要将 ‘use strict’ 置于函数体的顶部。this总是undefined;

var a=123;
var A=function(){
   'use strict';
    return this;   //undefined
}

(3)方法
this指向调用方法的对象

  var qwe={
       method:function(){
           return this;
       }
   }
  console.log(qwe.method()===qwe) ;   //true

在讲call()之前先说一下,函数调用有4中类型:

函数调用 alert('Hello World!')

方法调用 console.log('Hello World!')

构造函数调用 new RegExp('\\d')

间接调用 alert.call(undefined, 'Hello World')

注意:this 是指当前函数中正在执行的上下文环境

陷阱:this 在内部函数中

一个常见的陷阱是理所应当的认为函数调用中的,内部函数中 this 等同于它的外部函数中的 this。

正确的理解是内部函数的上下文环境取决于调用环境,而不是外部函数的上下文环境。

为了获取到所期望的 this,应该利用间接调用修改内部函数的上下文环境,如使用 .call() 或者 .apply或者创建一个绑定函数 .bind()。

利用call、apply、bind这三个方法,可以改变this的指向,使它指向我们期望的对象。

一、call()

关于javascript中的call方法,总结网上的观点,call有两个妙用:

1: 修改函数运行时的this指针。

2: 继承。(不太喜欢这种继承方式。)

简单说明一下

var f=function(){}
f.call(o)

解释:一个简单的例子,在全局环境下创建一个变量f,在函数f里面的this指向全局,window;但是利用call方法,将this指向了对象o,在o的作用域中运行函数f。

1、call()的完整使用格式:

func.call(thisValue, arg1, arg2, ...)

第一个参数是this要重新指向的那个对象,后面的参数是调用时所需要的参数;

或者是:

Function.prototype.call(thisValue, arg1, arg2, ...)

说明 :
call 方法可以用来代替另一个对象调用一个方法。

obj1.method1.call(obj2,argument1,argument2)
如上,call的作用就是把obj1的方法method1放到obj2上使用,后面的argument1..这些做为参数传入。

简单例子如下:

   function one1(a,b){
        console.log(a+b);
    };
   function two1(a,b){
       console.log(a-b);
   };
   one1.call(two1,5,2);     //7

这个例子中的意思就是用 one1来替换 two1,所以运行结果为:7
// 注意:js 中的函数其实是对象,函数名是对 Function 对象的引用。

看个稍微复杂的例子:

   function duixiang1(){
       this.name="zyy";
       this.method=function(){
           return this.name;
       }
   };
   function duixiang2(){
       this.name="zxd";
   }
   var q1=new duixiang1();
   var q2=new duixiang2();
   console.log(q1.method());         //zyy
   console.log(q1.method.call(q2));  //zxd

解释:call 的意思是把 q1的方法放到q2上执行,原来q2是没有method() 方法,现在是把q1 的method()方法放到 q2 上来执行,所以this.name 应该是 zxd,执行的结果就是 :zxd;

有意思吧,可以让a对象来执行b对象的方法。还有更有趣的,可以用 call 来实现继承 :

接下来说明一下call()实现继承:

    function Class1(){
        this.name="abc";
        this.method=function(a){
            console.log("这是Class1的参数"+a);
        }
    };
    function Class2(){
        Class1.call(this);
       // this.name="qwe"
    }
    var newclass2=new Class2();
    newclass2.method("asd");   //这是Class1的参数asd

这样 Class2 就继承Class1了,Class1.call(this) 的 意思就是使用 this对象代替Class1对象调用Class1的方法,那么 Class2 中不就有Class1 的所有属性和方法了吗,Class1对象就能够直接调用Class1 的方法以及属性了,执行结果就是:alert(“cc”);

对的,就是这样,这就是 javaScript 如何来模拟面向对象中的继承的,还可以实现多重继承。

    function Ff1(){
        this.methodadd=function(a,b){
            console.log(a+b)
        }
    };
    function Ff2(){
        this.methodjian=function(a,b){
            console.log(a-b);
        }
    };
    function Ff3(){
        Ff1.call(this);
        Ff2.call(this);
    };
    var shili=new Ff3();
    shili.methodadd(2,2);  //4
    shili.methodjian(5,4); //1

很简单,使用两个 call 就实现多重继承了 。

二、apply()

跟call()的用途是一样的,都是在特定的作用域中调用函数。 只是用法不同于call();
接收参数方面不同,apply()接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
call()方法第一个参数与apply()方法相同,但传递给函数的参数必须列举出来。

1、语法:
Function.apply(obj,args)方法能接收两个参数
obj:这个对象将代替Function类里this对象
args:这个是数组,它将作为参数传给Function(args–>arguments)

 function Student(name,age,grand){
       this.name=name;
       this.age=age;
       this.grand=grand;
   };
   function Student2(name,age,grand){
      Student.apply(this,arguments);
     //  Student.call(this,name,age,grand);
   };
    var lili=new Student2("lili",12,"043");
    console.log(lili.name);     //lili
    console.log(lili.age);      //12
    console.log(lili.grand);    //043

apply的一些其他巧妙用法:

Math.min()和Math.max()用法相似。

两个方法用来获取给定的一组数值中的最大值或最小值,但是却不接受数组作为参数。

因为Math.max 参数里面不支持Math.max([param1,param2]) 也就是数组
但是它支持Math.max(param1,param2,param3…),所以可以根据刚才apply的那个特点来解

有两个快捷的方法可以接受数组类型参数:

1 . Math.min.apply(null, arr)

唉?不是不能接收数组类型的参数吗?这是apply方法的特性,apply方法第二个参数为参数的数组,明白了吧,虽然我们传入的是数组参数,但是apply会将数组拆分并传入调用的函数。可以说是比较巧的用法了。

    alert(Math.max(2,4,6,9));   //9
    alert(Math.max([7,9,9,78]));//NaN
    alert(Math.max.apply(null,[7,9,9,78]));//78

三、call()与apply()的区别:

apply和call功能一样,只是传入的参数列表形式不同:如 func.call(func1,var1,var2,var3)
对应的apply写法为:func.apply(func1,[var1,var2,var3])

也就是说:call调用的为单个,apply调用的参数为数组。

四、bind()

自己总结:可以正确的提取方法

正确的提取方法:

 var proto1={
        name:"abc",
        describe:function(){
            return "name:"+this.name;
        }
    };

var func=proto1.describe.bind(proto1);
console.log(func());    //name: abc

由于我们把proto1.describe的值作为函数来调用,,因此现在的this是全局对象window,,而window.describe不存在,,此时就需要用到bind(),,,在调用方法时后面绑定bind(方法的对象),,这样就保证了describe(方法)与对象(proto1)不会失去联系。

五、方法中的this会被掩盖

嵌套函数中:一个方法包含一个普通的函数,如果想在后者的内部访问前者,方法的this却被后者的普通函数this掩盖。。。。

var obj={
        name:'zs',
        friends:["jone","grace"],
        loop:function(){
            "use strict"
            this.friends.forEach(function(friend){
                console.log(this.name+"的朋友是:"+friend);//(1)
            })
        }
    };
obj.loop();

(1)出的this.name中的this是指loop后的function函数。并非obj,,,

解决方案:

方案一:that=this

loop:function(){
            "use strict"
            var that=this;
            this.friends.forEach(function(friend){
                console.log(that.name+"的朋友是:"+friend);
            })
 }

执行结果:

zs的朋友是:jone
zs的朋友是:grace

方案二:bind()

使用bind()给里面变化的this函数绑定一个你想要的,this固定值,,即外面obj对象的this

 loop:function(){
            "use strict"
            this.friends.forEach(function(friend){
                console.log(this.name+"的朋友是:"+friend);
            }.bind(this));
        }

输出结果:

zs的朋友是:jone
zs的朋友是:grace

方案三:forEach()的thisValue

这个方案特定于forEach(),,,里面的第二个参数成为这个函数的第二个参数。

loop:function(){
            "use strict"
            this.friends.forEach(function(friend){
                console.log(this.name+"的朋友是:"+friend);
            },this)
        }

输出结果:

zs的朋友是:jone
zs的朋友是:grace
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值