在ES3中给Function的原型定义了2个方法,分别是Function.prototype.call和Function.prototype.apply,在我们的项目开发中,尤其是在一些函数式风格的代码编写过程中,call和apply的作用显得格外重要。下面就让我们来看看这两个方法的作用吧! call和apply的区别 Function.prototype.call和Function.prototype.apply这两个方法都是非常常用的,它们的作用是一模一样的,唯一的区别在于参数传递的形式不一样。 apply接收两个参数,第一个参数指定了函数体内this的指向,第二个参数是一个集合,可以是一个数组,也可以是一个类数组,apply将这个集合作为参数传递给被调用的函数。 var fun = function(a, b, c, d) { alert([a, b, c, d]); // [1, 2, 3, 4] } fun.apply(null, [1, 2, 3, 4]); call接受的参数是不固定的,第一个参数和apply一样,不同的是从第二个参数开始之后,依次传递给被调用的函数。 var fun = function(a, b, c, d) { alert([a, b, c, d]); // [1, 2, 3, 4] } fun.apply(null, 1, 2, 3, 4); 当我们在调用一个函数时,其JavaScript解释器并不在意形参和实参在数量、类型和顺序上的区别,因为JavaScript的参数在内部其实就是一个数组。从这种意义上来说,apply就会比call的效率更高,因为我们不必在担心需要传递参数的数量,直接使用apply往里推就是了,当然,如果想要很清楚的查看形参和实参的对应关系,也是可以使用call的。 在使用call和apply的时候,如果我们传入的第一个参数为null,则this会指向默认的宿主对象,在我们的浏览器中也就是指向window。 var fun = function(){ console.log(this === window); // true }; fun.apply(null, [1, 2, 3]); 但是在ES5的严格模式下,函数体内的this依然为null。 var fun = function(){ "use strict"; console.log(this === null); // true }; fun.apply(null, [1, 2, 3]); 其实有的时候我们使用call或者apply并不是为了指定this的指向,而是为了借用其他对象的方法。 Math.max.apply(null, [1, 5, 4, 3, 3]) // 5 call和apply的用途 下面就让我们来看看call和apply的一些用途 1. 改变this的指向 我们在使用call和apply最常用的例子就是来改变this的指向,来看看下面的例子: window.name = 'iFuhang'; var obj1 = { name : 'smile' }; var obj2 = { name : 'tom' }; var getName = function(){ console.log(this.name) } getName(); // iFuhang (this指向window) getName.call(obj1); // smile (this指向obj1) getName.call(obj2); // tom (this指向obj2) 当我们在执行getName.call(obj1)的时候,getName内部的this已经指向了obj1,所以最后输出了smile。 在实际的项目开发中,有时会不经意间改变了this的指向,这时我们就可以利用call或者apply来修正this的指向。其实我们在上一章节JavaScript中this关键字的使用中已经看到过类似的例子了,这里我就不在赘述了,感兴趣的小伙伴可以去看看。 2. Function.prototype.bind方法的实现 现在有大部分高级浏览器已经实现了Function.prototype.bind方法的使用,用来指定函数内部this的指向,但是为了兼容那些还没有实现这个方法的浏览器,我们可以模拟出一个Function.prototype.bind方法来。 Function.prototype.bind = function(context) { var self = this; // 保存原函数,也就是调用bind方法的函数 return function(){ return self.apply(context, arguments); // context指最后需要指向的那个对象,此案例中表示obj } } var obj = { name : 'iFuhang' } var getName = function(){ console.log(this.name); // iFuhang }.bind(obj); getName(); 其实这里我们通过Function.prototype.bind来”包装“了getName函数,同时传入了一个context参数,这个参数就是我们最后需要给this指定的对象。 在Function.prototype.bind函数内部,我们首先保存了原函数,然后返回了一个新函数,在我们执行getName函数时,其实首先执行的时刚刚返回的新函数,然后在返回的新函数内部,self.apply(context, arguments)这句话才是我们最后执行的getName 函数,同时也将this的指向绑定到了context上了,也就是案例中的obj上面。 其实上面还只是一个简单的模拟,我们还可以是实现一个稍微复杂一些的bind方法。 Function.prototype.bind = function(){ var self = this; var context = [].shift.call(arguments), // 从参数列表中取出第一个参数,也就是最后this需要指向的那个对象 args = [].slice.call(arguments); // 取出参数列表中剩余的参数并转换为数组 return function(){ return self.apply(context, [].concat.call(args, [].slice.call(arguments))); // 合并从bind方法中去除的数组args和self函数中传进来的数组 } } var obj = { name : 'iFuhang' } var getName = function(a, b, c, d){ console.log(this.name); // iFuhang console.log([a, b, c, d]); // [2, 3, 4, 5] }.bind(obj, 2, 3); getName(4, 5); 3. 借用其他对象的方法 我们还可以通过call和apply来借用其他对象的方法。 借用方法的第一种场景就是”借用构造函数“,通过这种方式,我们可以实现类似继承的效果: var A = function(name){ this.name = name; } var B = function(){ A.apply(this, arguments); } B.prototype.getName = function(){ return this.name; } var b= new B('iFuhang'); console.log(b.getName()); // iFuhang 下面这种借用方法我想大家会经常用到。 众所周知,函数的参数列表是一个类数组对象,虽然它也有索引下标和length属性,但他并且是一个正真的数组,所以它没有像数组中有那么多的API可以使用,这时我们就可以借用Array.prototype上的一些方法提供给arguments使用。 function args() { Array.prototype.push.apply(arguments, [1, 2, 3]); console.log(arguments); // [1, 2, 3] } args() 其实在操作arguments时,有很多的方法都可以去借用Array.prototype中的方法的,这里就不一一举例了。
在ES3中给Function的原型定义了2个方法,分别是Function.prototype.call和Function.prototype.apply,在我们的项目开发中,尤其是在一些函数式风格的代码编写过程中,call和apply的作用显得格外重要。下面就让我们来看看这两个方法的作用吧!
call和apply的区别
Function.prototype.call和Function.prototype.apply这两个方法都是非常常用的,它们的作用是一模一样的,唯一的区别在于参数传递的形式不一样。
apply接收两个参数,第一个参数指定了函数体内this的指向,第二个参数是一个集合,可以是一个数组,也可以是一个类数组,apply将这个集合作为参数传递给被调用的函数。
call接受的参数是不固定的,第一个参数和apply一样,不同的是从第二个参数开始之后,依次传递给被调用的函数。
当我们在调用一个函数时,其JavaScript解释器并不在意形参和实参在数量、类型和顺序上的区别,因为JavaScript的参数在内部其实就是一个数组。从这种意义上来说,apply就会比call的效率更高,因为我们不必在担心需要传递参数的数量,直接使用apply往里推就是了,当然,如果想要很清楚的查看形参和实参的对应关系,也是可以使用call的。
在使用call和apply的时候,如果我们传入的第一个参数为null,则this会指向默认的宿主对象,在我们的浏览器中也就是指向window。
但是在ES5的严格模式下,函数体内的this依然为null。
其实有的时候我们使用call或者apply并不是为了指定this的指向,而是为了借用其他对象的方法。
call和apply的用途
下面就让我们来看看call和apply的一些用途
1. 改变this的指向
我们在使用call和apply最常用的例子就是来改变this的指向,来看看下面的例子:
当我们在执行getName.call(obj1)的时候,getName内部的this已经指向了obj1,所以最后输出了smile。
在实际的项目开发中,有时会不经意间改变了this的指向,这时我们就可以利用call或者apply来修正this的指向。其实我们在上一章节JavaScript中this关键字的使用中已经看到过类似的例子了,这里我就不在赘述了,感兴趣的小伙伴可以去看看。
2. Function.prototype.bind方法的实现
现在有大部分高级浏览器已经实现了Function.prototype.bind方法的使用,用来指定函数内部this的指向,但是为了兼容那些还没有实现这个方法的浏览器,我们可以模拟出一个Function.prototype.bind方法来。
其实这里我们通过Function.prototype.bind来”包装“了getName函数,同时传入了一个context参数,这个参数就是我们最后需要给this指定的对象。
在Function.prototype.bind函数内部,我们首先保存了原函数,然后返回了一个新函数,在我们执行getName函数时,其实首先执行的时刚刚返回的新函数,然后在返回的新函数内部,self.apply(context, arguments)这句话才是我们最后执行的getName 函数,同时也将this的指向绑定到了context上了,也就是案例中的obj上面。
其实上面还只是一个简单的模拟,我们还可以是实现一个稍微复杂一些的bind方法。
3. 借用其他对象的方法
我们还可以通过call和apply来借用其他对象的方法。
借用方法的第一种场景就是”借用构造函数“,通过这种方式,我们可以实现类似继承的效果:
下面这种借用方法我想大家会经常用到。
众所周知,函数的参数列表是一个类数组对象,虽然它也有索引下标和length属性,但他并且是一个正真的数组,所以它没有像数组中有那么多的API可以使用,这时我们就可以借用Array.prototype上的一些方法提供给arguments使用。
其实在操作arguments时,有很多的方法都可以去借用Array.prototype中的方法的,这里就不一一举例了。