1.call
我们知道,函数可通过调用call方法改变其内部的this指向,默认this指向window或global,下面来一段简单的代码:
function foo(){
return this.a;
}
var obj = {
a:1
};
console.log(foo.call(obj));// 1
从结果来看,foo函数中的this绑定了obj,并返回了obj中a属性的值。
2.apply
apply与call基本相同,掌握了call也就理解了apply,只不过apply方法的第二个参数是用了一个数组的形式进行传递。
function sum(b,c){
return this.a + b + c;
};
var obj = {
a:1
};
console.log(sum.apply(obj,[2,3]));// 6
以上两个是改变函数this指向最常用也是最简单的两个方法。
现在我想在每两秒输出一次count的值,count值不断递增,通常我们是这样做的
var obj = {
count:0,
timer:function(){
var _this = this;
setInterval(function(){
_this.count++;
console.log(obj.count);
},2000);
}
};
obj.timer();// 1, 2, 3,...
我们用_this引用上下文obj,以用于在setInterval回调函数中能访问到obj,这样写是没有错,但是我们现在有另外一种方式可以实现,es6中提供了箭头函数,使用箭头函数我们无须写function关键字,也不用担心this所在的作用域,请看
var obj = {
count:0,
timer:function(){
setInterval(()=>{
this.count++;
console.log(obj.count);
},2000);
}
};
obj.timer();// 1, 2, 3,...
这样写出了少写一行代码以外,也不用担心对this的困惑了。但是有一点,箭头函数虽好,但它是一个匿名函数,如果我们要在函数内部调用函数本身,该怎么办呢,
也许你会说,用callee,可是callee已将近被废弃,我们最好还是不要用的好。
如果你的代码要在函数内部调用自己时,可以给函数起一个名字,也就是用具名函数吧。但如果用具名函数,那this怎么办呢!这可真是一个鱼和熊掌不能兼得的问题呀!
不过你忘记了,如题,我们还有一个方法没有用到,那就是es5中的bind!这可是个好东西了,有了它,我们的代码可以这样
var obj = {
count:0,
timer:function(){
setInterval(function foo(){
this.count++;
console.log(obj.count);
}.bind(this),2000);
}
};
obj.timer();// 1, 2, 3,...
据说源码比较复杂,下面的代码是MDN提供的bind实现方式
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fBound && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
在Function的原型链上扩展的bind方法,返回一个新的函数。
this instanceof fBound && oThis? this: oThis;//用于判断函数是否被new调用,如果是this指向new出的实例,不是则使用bind中的第一个参数,为null则忽略 fNOP.prototype = this.prototype; //用一个空的fn继承this的原型 fBound.prototype = new fNOP(); //返回的函数与调用bind的函数共享原型链
我在chrome测试时,发现bind中如果传递null,输入结果不是我期望的,使用chrome默认的bind则不会,这说明上面贴出的代码与源码实现还是有很大差异的,如果要针对IE可再加上if(!Function.prototype.bind)判断,在IE兼容模式下7、8、11兼容性视图会输入undifined
下面是我的测试代码:
function test(something,a,b){
this.a = something+a+b;
};
var obj = {};
var newFn = test.bind(obj,1,2);
var res= new newFn(10);
console.log(res.a);// 13
而使用
var newFn = test.bind(null,1,2);// undefined