最开始对于这三者的概念不是很理解,通过查阅资料总算有点理解了,所以在这里记录一下,有什么不对的地方欢迎指出。
1 定义:call()和apply()其实就是改变函数的执行上下文(this的值),可以劫持另外一个对象的方法,继承另外一个对象的属性。他们两是Function对象的方法,所以每个函数都能调用他们。
例如:Function.apply(obj,args)可以说是在obj里面去执行Function里面的内容。也就是Function的执行主体变成了obj,改变函数的执行上下文(this的值)。
2 区别:call()和apply()的意思是一样的,不同的是参数列表不一样,call的第二部分参数要一个一个传,apply要把这些参数放到数组中。这就是他们的区别。
Function.apply(obj,args),Function.call(obj,[param1[,param2[,…[,paramN]]]])
obj:这个对象将代替Function类里this对象
params:这个是一个参数列表
apply的一点小扩展:
①实现数组最大最小的一项
因为Math.max 参数里面不支持Math.max([param1,param2]) 也就是数组
所以可以使用Math.max.apply(null,array)或Math.min.apply(null,array)得到数组最大最小的一项
②可以实现两个数组合并
vararr1=new Array("1","2","3");
vararr2=new Array("4","5","6");
Array.prototype.push.apply(arr1,arr2);
③一个数组插入另一个数组的指定位置
var arr1 = ['a', 'b', 'c'];
var arr2 = ['1', '2', '3'];
arr2.unshift(2, 0);
Array.prototype.splice.apply(arr1, arr2);
console.log(arr1);
3、下面来讲bind()函数,bind()是es5中的方法,它也是用来实现上下文绑定,但与bind()和call与apply不同。bind是新创建一个函数,然后把它的上下文绑定到bind()括号中的参数上,然后将它返回。所以,bind后函数不会执行,而只是返回一个改变了上下文的函数副本,而call和apply是直接执行函数。
下面代码可以反映出这点,而且也显示了bind的用法(后面的代码皆取自张鑫旭大神的博客)
var button = document.getElementById("button"), text = document.getElementById("text");button.onclick = function() { alert(this.id);// 弹出text}.bind(text);
但由于ie6~ie8不支持该方法,所以若想在这几个浏览器中使用,我们就要模拟该方法,这也是面试常考的问题,模拟的代码如下:
if (!function() {}.bind) {
Function.prototype.bind = function(context) {
var self = this
, args = Array.prototype.slice.call(arguments);
return function() {
return self.apply(context, args.slice(1));
}
};
}
就是这段代码,纠正了我长久以来的一个误区。下面来讲一下这段代码
首先,我们判断是否存在bind方法,然后,若不存在,向Function对象的原型中添加自定义的bind方法。
这里面var self = this这段代码让我很困扰,按理说,prototype是一个对象,对象的this应该指向对象本身,也就是prototype,但真的是这样吗。看看下面的代码:
function a(){};
a.prototype.testThis = function(){console.log(a.prototype == this);};
var b = new a();
b.testThis();//false
显然,this不指向prototype,而经过测试,它也不指向a,而指向b。所以原型中的this值就明朗了。指向调用它的对象。
Array.prototype.slice.call(arguments);
接下来就是上面这段代码,它会将一个类数组形式的变量转化为真正的数组。为啥呢,其实书上并没有说slice还有这样的用法,也不知道是谁发明的。slice的用法可以顺便上网查一下,就能查到。但要更正一点,网上的介绍说slice有两个参数,第一个参数不能省略。然而我不知道是我理解的问题还是咋地,上面这段代码tmd不就是典型的没传参数吗!!!arguments是传给call的那个上下文,前面讲过,不要弄混(由于arguments自己没有slice方法,这里属于借用Array原型的slice方法)。而且经过测试,若果你不给slice传参数,那就等于传了个0给它,结果就是返回一个和原来数组一模一样的副本。
这之后的代码就很好理解,返回一个函数,该函数把传给bind的第一个参数当做执行上下文,由于args已经是一个数组,排除第一项,将之后的部分作为第二部分参数传给apply,前面讲过apply的用法。
如此,我们自己的这个bind函数的行为就同es5中的bind一样了。
总结:call和apply都是改变上下文中的this并立即执行这个函数,bind方法可以让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加,这是它们的区别,根据自己的实际情况来选择使用。