一、this的理解
在call,apply,bind的应用中,都会出现this,因此我们需要先来了解一下this的具体分类和意思。
JavaScript的this总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。
(一)This的指向(四种):
1、作为对象的方法调用:此时this指向该对象
var obj1 = {
name : 'a',
getName : function () {
console.log(this === obj1); //true
return this.name; //'a'
}
}
obj1.getName();
2、最为普通函数调用:此时this指向全局对象(在浏览器中,全局对象为window)
window.name = 'windowName';
var getName = function () {
console.log('this = ' + this); ///this = [object Window]
return this.name; //"windowName"
}
getName();
3、构造器调用:此时this指向返回的对象
var Animal = function (name) {
this.name = name;
}
var cat = new Animal('cat');
console.log(cat.name); //'cat'
4、Function.prototype.call或Function.prototype.apply调用:此时this指向传入的第一个参数对象
var obj1 = {
name : 'a',
getName : function () {
console.log(this === obj1); //false
return this.name; //'b'
}
};
var obj2 = {
name : 'b'
};
obj1.getName.call(obj2);
(二)丢失的this
var obj1 = {
name : 'a',
getName : function () {
return this.name;
}
};
var getName2 = obj1.getName;
getName2(); // 'windowName',此时的this指向window,由于之前已经设置window.name= 'windowName'
要想实现预期效果,需要对this进行修正
方法一:
var obj1 = {
name : 'a',
getName : function (self) {
return self.name;
}
};
var getName2 = obj1.getName;
getName2(obj1); // 'a'
方法二:
var obj1 = {
name : 'a',
getName : function () {
return this.name;
}
};
obj1.getName = (function (func) {
return function () {
return func.apply(obj1);
}
})(obj1.getName);
var getName2 = obj1.getName;
getName2(); //'a'
二、call、apply、bind
call和apply是为了动态改变this而出现的,当一个object没有某个方法,但是其他的有,我们可以借助call或apply用其它对象的方法来操作。
(一)call的应用
Function.call(this.obj,arg1,args,……)
Call中的第一个参数是对象,参数的传入需要一个个列举,返回的是调用函数的返回值。
例如,为了让字符串有pop的方法,字符串可以调用数组的slice方法,这样就返回一个数组,因为slice的返回值就是数组。
Array.prototype.slice.call('123456');//返回值[1,2,3,4,5,6]
(二)apply的应用
Function.apply(this.obj,[arg1,arg2,……])
Apply的第一个参数是对象,第二个参数则是数组,返回的是调用函数的返回值。
function add (a,b) {
return a+b;
}
var obj1 = {
name:'haha'
};
add.apply(obj1,[4,5]);
(三)bind的应用
Bind则是函数绑定到一个对象,返回一个新函数,通过可选的指定参数,作为指定对象的方法调用该方法。
Function.bind(this.obj,arg1,arg2,……);//返回一个函数
利用call和apply实现bind方法:
Function.prototype.bind = function() {
var self = this, //保存原来的函数function
content = [].shift.call(arguments),//bind中传入的函数,提取出第一个this参数
args = [].slice.call(arguments); //保存bind中的其他参数
return function () { //返回一个函数
return self.apply(content, [].concat.call(args,[].slice.call(arguments))); //返回执行原来function的结果。Arguments为新函数的传参。
}
}
var func2 = function (a,b,c,d) {
console.log(this.name);
console.log(a,b,c,d);
}
var obj1 = {
name: 'zsx'
};
var func1 = func2.bind(obj1, 1, 2);
func1(3,4); //'zsx', 1,2,3,4
三、具体使用场景区分
如果只是简单的调用对象本身没有的方法,则使用call或者apply。当你的参数是明确知道数量时,用 call,而不确定的时候,用 apply,然后把参数 push 进数组传递进去。当参数数量不确定时,函数内部也可以通过 arguments 这个数组来遍历所有的参数。
如果需要一个本身对象没有,且之后还经常使用的,那就使用bind,在其他函数的基础上,绑定一个新函数,之后传入相应的参数即可多次使用。
注意:
当使用call或者apply时,如果我们传入的第一个参数是null,函数体内的this会指向默认的宿主对象,在浏览器中就是window;但如果是在严格模式下,函数体内的this还是为null。
参考:
call,apply && bind,currying
《JavaScript设计模式与开发实践》--曾探
图片引用,侵权立删