1、call和apply的区别
call和apply唯一的区别是传入参数的形式不同。
apply接受两个参数,第一个参数指定了函数体内this对象的指向,第二个参数为一个带下标的集合,可以是数组,也可以是类数组,apply方法会把集合中的元素作为参数传递给被调用的函数。
var func = function (a, b, c) {console.log ([a, b, c]); // 输出 [1, 2, 3]};func.apply(null, [1, 2, 3]); // 数组中的1,2,3分别对应参数列表中的a,b,c
复制代码
call跟apply相同的是,第一个参数也是代表函数体内的this指向,第二个参数开始往后,参数依次被传入函数内,传入的参数数量不固定。
var func = function (a, b, c) {console.log ([a, b, c]); //输出:[1, 2, 3]}func.call (null, 1, 2, 3);
复制代码
当调用函数时,Javascript解释器并不会计较形参和实参的数量、类型及顺序,在Javascript内部统一都是用数组表示。所以call其实就是包装在apply上的一颗语法糖。 在使用call和apply的时候,如果传入的第一个参数是null,函数体内的this会默认指向宿主对象,在浏览器环境里,就是window。
var func = function (a, b, c) {console.log (this === window); //输出:true
};
func.apply(null, [1, 2, 3]);
复制代码
如果是严格模式下,函数体内的this还是null
var func = function (a, b, c) {'use strict';console.log (this === null); //输出:true
};
func.apply(null, [1, 2, 3]);
复制代码
有时候call和apply还可以借用其他对象的方法,我们可以传入null来替代某个具体对象:
Math.min.apply(null, [5, 2, 1, 3, 4]); //输出:1
复制代码
2、call和apply的用途
(1) 可以改变this指向
call和apply最常见的用法就是用来改变函数内部的this指向。
var obj1 = {name: 'lq'
};
var obj2 = {name: 'xiaoming'
};
window.name = 'angelababy';
var getName = function () {console.log (this.name);
};
getName(); // 输出:angelababy
getName.call(obj1); // 输出:lq
getName.call(obj2); // 输出:xiaoming
复制代码
(2) Function.prototype.bind
几乎所有的高级浏览器都内置了Function.prototype.bind方法用来指定函数内部this指向问题。如果不支持原生的Function.prototype.bind,我们可以自己实现一个:
Function.prototype.diyBind = function (context) {var _this = this; // 保存原函数return function () { // 返回一个新的函数return _this.apply(context, arguments); // 执行新的函数时,会将之前传入的context当作新函数体内的this}
};
var obj = {name: 'lq'
}
var func = function () {console.log (this.name);
}.diyBind(obj);func();
复制代码
上面是简化版的,下面是稍微复杂点的版本:
Function.prototype.binds = function(){var _this = this, // 保存原来的函数context = [].shift.call(arguments), //截取第一个参数,即是绑定的this上下文args = [].slice.call(arguments); //剩余的参数转成数组return function(){ //返回一个新的函数return _this.apply(context, [].concat.call(args,[].slice.call(arguments))); // 执行新函数的时候,会将之前传入的context最为新函数体内的this,在此例中就是obj。用concat合并两次传入的参数,最为新函数的参数}
}
var obj = {name: 'lq'
}
var func = function(a, b, c, d){console.log(this.name); // 输出:lqconsole.log([a, b, c, d]); // 输出:[1, 2, 3, 4]
}.binds(obj,1, 2);func(3,4);
复制代码
(3)借用其他对象的方法
借用的第一种场景是“借用构造函数”:
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('xiaoming');
console.log (b.getName()); //输出:xiaoming
复制代码
借用的第二种场景是“借用Array.prototype对象上的方法”,比如:
(function(){Array.prototype.push.call(arguments, 3);console.log (arguments); // 输出:[1, 2, 3]
})(1, 2)
复制代码
在操作arguments时我们经常会借用Array.prototype对象上的各种方法。比如想把arguments转成真正数组时,可以借用Array.prototype.slice方法。想截去arguments列表中的第一个元素,可以借用Array.prototype.shift方法。