javascript中关于对call()、apply()和bind()的一点理解

最开始对于这三者的概念不是很理解,通过查阅资料总算有点理解了,所以在这里记录一下,有什么不对的地方欢迎指出。

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方法可以让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加,这是它们的区别,根据自己的实际情况来选择使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容