Function.prototype.bind、call与apply方法简介

前言

前段时间面试遇见一题,题目内容大概是

function Parent() {
    this.prop = 'parent';
}

Parent.prototype.get = function() {
    alert(this.prop);
};

Parent.prototype.show = function() {
    setTimeout(this.get, 100);
};

var child = new Parent();
child.show(); // ?

分析

上述题目考察的是this的指向性这个经典问题。
众所周知,setTimeout是window对象的一个属性,主要起到延迟给定函数执行的作用。setTimeout(fn, delay),因此this.get的这个this指向的是window对象,但是我们并没用在window对象上定义相应的get函数,所以会报错,而不是调用构造函数Parent的原型中的get方法。

怎么改?

经过上面的分析,我们知道setTimeout(this.get, 100)会报错,但是如果我们想正常调用并且alert出正确的值,应该怎么改?
改动的原理很明确,就是将setTimeout中的this指向Parent.prototype,但是单纯的将this.get改成Parent.prototype.get仍然没有alert出真正的parent,而是undefined,原理仍然是原型中的get方法在通过实例对象child调用时内部的this仍然指向window对象。所以我们只能换种思路解决了

ps: 这个当时卡壳了,没答上来,汗。。。

思路1

Parent.prototype.show = function (){
    var that = this;
    setTimeout(this.get.call(that), 100);
}

这种方法在很多框架中经常使用,即先存储this默认指向的作用域,然后改变函数内部绑定的作用域来实现。ps: 不通过that存储,直接传递this也可

思路2

ES5中对函数方面唯一扩展是新增了一个bind函数,用于劫持函数作用域,并预先添加更多参数,然后返回绑定了新作用域的函数。而现在我们也可以使用它,其实这个思路在MDN上有相应的介绍使用,哎,只怪自己的年少无知。。。

Parent.prototype.show = function() {
    setTimeout(this.get.bind(this), 100);
};

bind/call/apply

三者都是用于绑定函数作用域,区别如下:

  • call 是obj.method()到method(obj)的变换,返回函数调用结果,所需参数依次用逗号分割添加至obj尾部。
  • apply 功能同call,区别是传递参数的方式不是call的那种参数列表形式,而是以数组或类数组形式传递。
  • bind 返回绑定作用域后的一个新函数,不会执行函数

我们可以通过apply方法实现bind函数:

if(!function() {}.bind) {
    Function.prototype.bind = function(context) {
        if (arguments.length < 2 && context == void 0)
            return this;
        var _self = this,
            args = [].slice.call(arguments, 1);
        return function() {
            _self.apply(context, args.concat.apply(args, arguments));
        }
    }
}

另外,我们可以利用bind修复IE事件绑定attachEvent回调中的this问题,它总是指向window对象,而标准浏览器的addEventListener中的this则为其调用对象。

function(el, type, fn) {
    el.attachEvent('on' + type, fn.bind(el, event));
}
posted @ 2015-08-31 20:45  qingguoing  阅读(1062)  评论(0编辑  收藏  举报