call、apply、bind

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

posted on 2016-02-18 14:40  hobbycc  阅读(515)  评论(0编辑  收藏  举报

导航