在上一篇文章中我们对js中的this指向问题进行了介绍,其中涉及到call()和apply()方法,在这篇文章中,我们将对call()和apply()进行探讨;
首先我们来说说这两种方法的共同点:
1. 每个函数都包含两个非继承而来的方法:call()方法和apply()方法。
2.这两个方法的作用是一样的。 都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。
在JS中this总是用来指向某个方法的对象,而call()和apply()的作用就是用来改变this的指向。
call()和apply()作用完全一样,只是接受参数的方式不一样。
call的用法:
下面的代码演示了this指向问题,call()改变this指向,以及call()实现继承
//下面这段代码主要演示了this指向的问题
window.color = 'red';
document.color = 'yellow';
var s1 = {color: 'blue' };
changeColor(){
console.log(this.color);
}
changeColor.call(); //red (默认传递参数)
changeColor.call(window); //red
changeColor.call(document); //yellow
changeColor.call(this); //red
changeColor.call(s1); //blue
//下面我们将看看call是怎样改变this指向的
var Pet = {
words : '...',
speak : function (say) {
console.log(say + ''+ this.words)
}
}
Pet.speak('Speak'); // 结果为:Speak...
var Dog = {
words:'Wang'
} //将this的指向改变成了Dog
Pet.speak.call(Dog, 'Speak'); //结果为: SpeakWang
//下面这段代码我们再看看使用call()实现继承
function Animal(name){
this.name = name;
this.showName = function(){
console.log(this.name);
}
}
function Cat(name){
Animal.call(this, name);
}
var cat = new Cat("Black Cat");
cat.showName(); //结果为: Black Cat
apply()与call()用法类似,可参考上面的例子
下面我们看看不同点,即传入参数不同:
var func = function(arg1, arg2) {
};
//调用该函数的两种不同方式
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
其中 this 是你想指定的上下文,他可以是任何一个 JavaScript 对象(JavaScript 中一切皆对象),call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。
我们已经看了call()和apply()的基本用法,下面我们用一些实例巩固一下:
1.数组的追加
var array1 = [1,2,3];
var array2 = [a,b,c];
Array.prototype.push.apply(array1, array2);
// array1 值为 [1,2,3,a,b,c]
Array.prototype.push可以实现数组的追加
2.获取数组中的最大最小值
var numbers = [5, 458 , 120 , -215 ];
var maxInNumbers = Math.max.apply(Math, numbers), //458
maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458
number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。
3.验证是否为数据类型
console.log(Object.prototype.toString.call(123)) //[object Number]
console.log(Object.prototype.toString.call('123')) //[object String]
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]
console.log(Object.prototype.toString.call(true)) //[object Boolean]
console.log(Object.prototype.toString.call({})) //[object Object]
console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call(function(){})) //[object Function]
4.类数组使用数组方法
首先我们要明白什么是类数组:
1.具有length属性
2.按索引方式存储数据
3.不具有数组的push,pop等方法
常见的类数组有:arguments,NodeList
(function(){
Array.prototype.push.call(arguments,4);
console.log(arguments);//[1, 2, 3, 4]
})(1,2,3)
Array.prototype.slice.call(document.getElementsByTagName("*"));
Javascript中存在一种名为伪数组的对象结构。比较特别的是 arguments 对象,还有像调用 getElementsByTagName , document.childNodes 之类的,它们返回NodeList对象都属于伪数组。不能应用 Array下的 push , pop 等方法。
但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象,这样 domNodes 就可以应用 Array 下的所有方法了。
下面是一个经典的面试题:
定义一个 log 方法,让它可以代理 console.log 方法,常见的解决方法是:
1:
function log(msg) {
console.log(msg);
}
log(1); //1
log(1,2); //1
2:上面方法可以解决最基本的需求,但是当传入参数的个数是不确定的时候,上面的方法就失效了,这个时候就可以考虑使用 apply 或者 call,注意这里传入多少个参数是不确定的
,所以使用apply是最好的,方法如下:
function log(){
console.log.apply(console, arguments);
};
log(1); //1
log(1,2); //1 2
3.接下来的要求是给每一个 log 消息添加一个"(app)"的前辍,比如:
log("hello world"); //(app)hello world
该怎么做比较优雅呢?这个时候需要想到arguments参数是个伪数组,通过 Array.prototype.slice.call 转化为标准数组,再使用数组方法unshift,像这样:
function log(){
var args = Array.prototype.slice.call(arguments);
args.unshift('(app)');
console.log.apply(console, args);
};
log('hello world') //(app)hello world
以上是关于apply()和call()的一些用法,不尽详细,欢迎指正