您的位置:首页 > 移动开发

关于call(),apply(),bind()函数的理解

2017-03-17 14:35 615 查看
在Javascript中,我们比较难以理解的东西就是this,它表示在当前作用域中的上下文。有时候我们需要在使用过程中改变this的指向,那么就需要通过这三种方法:call(),apply(),bind()。

具体什么意思呢?说的太官方可能还是不懂,大致意思就是,有两个对象,其中一个对象想要使用另一个对象中的方法,这个时候就要用到这个三个方法。

举个例子:有两个人张三和李四,张三会说话并说的很好,李四是个哑巴,说不出来,这时候李四特别的想说话,憋得满脸通红说不出来,这时候他知道张三会说话,所以李四就把张三拉过来,用手比划给张三,让张三替他说话。

说了这么一大堆,还是想说一下概念,call(),apply(),bind()我为了改变某个函数的上下文来使用的。

函数其实就是对象,当每个函数在创建时,我们都知道,在函数内部会自动创建arguments和this两个对象,用来代表函数所接收的参数和一个运行环境的上下文this.

但与此同时,函数上还自带几个属性和方法如:



从图中我们可以看到,比较熟悉的如arguments,name.length,hasOwnProperty等属性都在,而且还有apply,call,bind这三个,他们都是方法,都是自带的,所以使用起来也是没问题的。

下面就举例子来看看这三个函数是怎么用的。

var person1 = {
name:'张三',
say:function(city){
console.log(this.name + '说你好,非常欢迎您来到' + city + '!');
}
};

var person2 = {
name:'李四'
};


上面列出了两个人,张三和李四,张三有个say()的功能,李四却没有。

person1.say('北京');//张三说你好,非常欢迎您来到北京!
person2.say('北京');//person2.say is not a function


可以看到,person2.say()提示了错误,但是我们现在想说话,而且又不想加新的属性该怎么办呢?这个时候我们的call,apply,bind就可以派上用场了。

person1.say.call(person2,'北京');//李四说你好,非常欢迎您来到北京!
person1.say.apply(person2,['北京']);//李四说你好,非常欢迎您来到北京!
person1.say.bind(person2)('北京');//李四说你好,非常欢迎您来到北京!


可以看到,通过上面三句代码,都可以让我们的李四开口说话,并使用张三的方法。

这三个函数第一个参数都代表原函数的上下文应该更改成这个对象。

很显然从上面代码可以看出来三个方法的用法是不同的。

1 call()和apply()方法都是直接对方法的调用,而bind()执行过后返回一个函数。

2 call()方法从第二个参数开始依次向函数中传参,而apply()方法第二个参数是一个参数数组,不过他也是按照在数组中的顺序依次向函数中传参的。

3 bind()函数倒是没有规范传参的方式,在bind()函数中第二个参数也行,这里放一个数组也行,在外面调用的时候传参同样可以,不过要注意的是bind()函数返回的是一个函数,需要调用这个函数才会执行,而且,bind()方法只能使用一次,连续多个bind()函数是不管用的

var foo = {
foo:2,
sum:function(x,y){
return this.foo + x + y;
}
};

var bar = {
foo:5
};

var aa = {
foo:12
};
console.log(foo.sum(5,6));//13
console.log(foo.sum.call(bar,5,6));//16
console.log(foo.sum.apply(bar,[5,6]));//16
console.log(foo.sum.bind(bar)(5,6));//16
console.log(foo.sum.bind(bar,5,6)());//16
console.log(foo.sum.bind(bar,5)(6));//16
console.log(foo.sum.bind(bar,5).bind(aa)(6));//16


从上面可以看到,bind()函数的传参方式可以在bind函数体内,也可以在调用函数括号内,但是不管怎么样,他都是从bind()函数第二个参数开始算起的,bind()函数的传参方式和call()方法是一样的,不能传参数数组。最后一句话我们可以看到,在bind()了一个bar对象之后,又进行了一次bind(),这次bind()到了aa,但是打印出来的结果仍然是16,看来,bind()函数只能使用一次,多的是不管用的。

如果第一个参数是null,那么上下文就会指向全局,将会是在全局作用域中使用这个函数。

var color = 'white';
var obj = {
color:'yellow',
say:function(){
console.log('The color is ' + this.color);
}
};

var green = {
color:'green'
};

obj.say();//The color is yellow
obj.say.call(green);//The color is green
obj.say.call(null);//The color is white


第三句代码中call()方法传入了null,因此他将指向全局作用域,我们在全局作用域中也定义了color,所以他自然打印出了white.

通过这些特性,我们衍生出了很多巧妙的用法:

1 类的继承:

function Parent(name,age){

this.name = name;
this.age = age;

}

function Child(name,age,height){
Parent.call(this,name,age);
this.height = height;
}


2 数组的合并:

var arr1 = [1,33,5,3,23];
var arr2 = [55,43,12,<
b778
span class="hljs-number">56];
var newArr = Array.prototype.push.apply(arr1,arr2);

console.log(arr1);//[1, 33, 5, 3, 23, 55, 43, 12, 56]


我们知道push()方法貌似一次只能想数组中添加一个元素,如果使用apply()方法,apply的参数是一个数组,因此使用apply()之后一次就可以添加整个数组了。

3 数组中的大小值

var arr1 = [1,33,5,3,23];

var max = Math.max.apply(Math,arr1);
var min = Math.min.apply(Math,arr1);
console.log(max);//33
console.log(min);//1


Math.max(arg1,arg2,arg3,…),使用apply之后就可以将数组参数放入其中,就可以计算出我们想要的值。

总之:call(),apply()和bind()方法就是用来改变函数体内上下文的,这三个方法用的非常的多,作为一名FE,都应该掌握其用法。上面有许多不对的地方还请大家指正,欢迎批评!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  javascript