简介:apply()和call()都是属于Function.prototype的一个方法属性,它是JavaScript引擎内在实现的方法,因为属于Function.prototype,所以每个Function实例,也就是每个方法都能使用apply和call方法。
作用:call 和 apply 都是为了改变某个函数运行时的 context 即上下文而存在的,换句话说,就是为了改变函数体内部 this 的指向。因为 JavaScript 的函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念。(需要理解JavaScript的执行环境和作用域的概念)
介绍完这两个方法后,说下它们的异同点:
相同点:这两个方法都是劫持另外一个对象的方法,继承另外一个对象的属性. 怎样理解这句话呢?代码如下:
例子 一:
function people(){ /*this.name="无名氏";*/ this.sayName=function(){ alert("您的姓名是:"+this.name); } } function xiaohua(){ this.name="小花"; people.call(this); //这里使用call()方法的作用是,当前this对象(xiaohua对象),劫持了people对象,所以people中的this指向了xiaohua对象,所以xiaohua对象拥有了 //所有people对象的方法和属性 } var aa=new xiaohua(); aa.sayName(); //输出: 您的姓名是:小花
例子二:
function cat() { } cat.prototype={ food:"fish", say:function(){ alert("I love "+this.food) } } var blackcat=new cat(); blackcat.say(); //输出:I love fish
这里做个假设我们有一个对象whiteDog={food:"bone"},我不想对它重新定义say方法,因为这个say方法对需求来说完全适用!这个使用就需要使用call方法了!代码如下:
function cat() { } cat.prototype={ food:"fish", say:function(){ alert("I love "+this.food) } } var blackcat=new cat(); //这里做个假设我们有一个对象whiteDog={food:"bone"},我不想对它重新定义say方法,因为这个say方法对需求来说完全适用 var whiteDog={food:"bone"}; blackcat.say.call(whiteDog); //这句代码的意思是blackcat对象实例里面的say方法属性里面的this指向whiteDog对象,所以say方法里面的food属性就被whiteDog里面的food属性替换掉了 whiteDog.say();//输出I love bone
例子三:
下面这个例子据说是网易的前端面试题,来look,look
var testA = function (b) { return this.a + b; }; var obj = {a: 2}; var testB = testA.myBind(obj, 1); alert(testB);
问题是:怎样实现myBind()方法,才能使testB的值3;
先一步步分析,
(1)我们发现myBind()方法是通过testA()方法调用的,我们知道在JavaScript中所有的方法都是一个对象,而所有的方法都继承自Function对象,所以所有在Function.prototype的方法和属性,将被所有的方法实例共享,比如call,apply。所有的方法实例都能通过fun1.(Function.prototype的中定义方法和属性)的形式调用。
而这里的myBind也是通过方法实例调用的方法,这种情况只有两种可能:
1:是上面所分析的是Function.prototype的方法实例,被所有的方法所共享的方法
2:内嵌函数,嵌套在testA函数中的函数
但是分析testA方法的形参和返回值,排除了第二种可能,那只能是第一种情况,代码如下:
Function.prototype.myBind=function(object,extra){ //当哪个方法调用myBind方法,其this指针就指向该方法 return this.call(object,extra); //传入的object对象劫持了testA对象,所以testA对象中的this指向objetc对象 } var testA = function (b) { return this.a + b;//这里的this指向object对象,所以this.a=2,b=1; }; var obj = {a: 2}; var testB = testA.myBind(obj, 1); alert(testB);//输出:3
例子四:
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
这样domNodes就可以应用Array下的所有方法了。
<script> function add(para1,para2) { alert(this); //输出:function substract(para1,para2) {return para1-para2;} return para1+para2; } function substract(para1,para2) { return para1-para2; } alert(window.add.call(window.substract,1,2));//输出:3 // 这里注意:这里的上下文只和属性和方法有关,也就是说add方法劫持了substract方法的上下文对象,能使用其内部的属性和方法 //但是其return语句并不会被覆盖,还是输出return para1+para2 //首先先分析下上面这段语句的执行过程,首先将add的上下文对象替换成substract方法的对象, // 然后调用add方法并传入1和2两个参数 </script>
例子六:使用Call()方法实现单继承
<script> function Animal(name) { /* console.log(this);//输出Cat对象*/ this.name=name; this.sayName=function () { alert(this.name); } } function Cat(name) { Animal.call(this,name); //首先Animal劫持了this(Cat)对象,将Animal中的上下文替换成了Cat的上下文,更具上面的输出可以看出,然后调用Animal方法并传入 //name参数给Aniaml方法,我个人觉得应该是js引擎在做完上面的操作后返回一个Animal和Cat的结合的实体引用回去 //所以我改变上面的代码加了一个 return,发现没有影响,也印证了我的猜测 } var cat=new Cat("cat"); cat.sayName();//输出:cat,根据输出得知,call方法可以实现oop的继承功能 </script>
例子七:使用call实现多继承,上面的单继承如果理解了的话,那么多继承也就很简单了
<script> function Add() { this.a=1; this.b=2; this.add=function(a,b) { return a+b; } } function Substract() { this.sub=function(a,b) { return a-b; } } function Operation(a,b) { Add.call(this,a,b); Substract.call(this,a,b); } var op=new Operation(); alert(op.add(1,2));//输出:3 alert(op.sub(1,2));//输出:-1 //终极版分析: //分析上面方法的执行过程,首先初始化Operation对象,然后Add劫持Operation,Add内部的this指向Operation //其实这个劫持过程可以理解为在初始化Operation对象的时候,将Add对象初始化为自己的内部属性,方便调用 //这个分析我是更具在chrome中的js运行过程看出来的,纯属我个人的观点 </script>