call和apply的区别
ECAMScript3给Function的原型定义了两个方法,分别是Function.prototype.call和Function.prototype.apply,他们的作用一模一样,区别仅在于传参方式不同。
apply接收两个参数
第一个参数指定函数体内this对象的指向,第二个参数是数组或者类数组(例如arguments),是传入被调用函数的参数列表。
var func = function(a, b, c) {
alert([a,b,c]) // 输出[1,2,3]
}
func.apply(null,[1,2,3])
call参数数量不固定
与apply相同,第一个参数是指定函数体内this对象的指向,后面的是需要传入被调用函数的参数call实际上是包装在apply上的语法糖。如果我们需要明确的知道函数接收多少个参数,而且想一目了然的表达形参和实参的对应关系,可以用call。
var func = function(a, b, c) {
alert([a,b,c]) // 输出[1,2,3]
}
func.call(null,1,2,3)
调用apply和call时如果第一个参数为null,函数体内的this会指向默认的宿主对象。在浏览器中是window,在严格模式下还是null。
call和apply的用途
1.改变this指向
var obj1 = {
name:'sven'
}
var obj2 = {
name:'anne'
}
window.name = 'window'
var getName = function(){
alert(this.name)
}
getName() // 输出 window
getName.call(obj1) // 输出 sven
getName.call(obj2) // 输出 anne
2.实现bind方法
简单版本
Function.prototype.bind = function(context) {
var self = this; // 保存原函数
return function() { // 返回一个新函数
return self.apply(context, arguments); // 调用原函数并且将传入的context作为this传给原函数
}
}
var obj = {
name:'sven'
}
var func = function () {
alert(this.name) // sven
}.bind(obj)
func()
复杂版本,使bind的时候可以预填写一些参数
Function.prototype.bind = function() {
var self = this,// 保存原函数
context = [].shift.call(arguments),// 取出context
args = [].slice.call(arguments) // 将剩余的参数转换为数组
return function() { // 返回一个新函数
return self.apply(context, [].concat.call(args, [].slice.call(arguments))); // 调用原函数并且将传入的context作为this传给原函数
}
}
var obj = {
name:'sven'
}
var func = function (a,b,c,d) {
alert(this.name) // sven
alert([a,b,c,d]) // 1,2,3,4
}.bind(obj,1,2)
func(3,4)
3.借用其他对象的方法
1.借用构造函数,可以实现类似继承的效果
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('sven')
console.log(b.getName())
2.借用数组的方法操作类数组(arguments)
借用push方法:
(function(){
Array.prototype.push.call(arguments, 3) // 借助Array的方法向arguments中push一个3
console.log(arguments) // 输出[1,2,3]
})(1,2)
借用shift方法
(function(){
Array.prototype.shift.call(arguments) // 借助Array的方法向arguments中push一个3
console.log(arguments) // 输出[2]
})(1,2)