函数bind的实现

最近在看《MVC的JavaScript Web富应用开发》,其中出现了ES5新增的bind函数的实现代码,本人菜鸟一枚,花了一天才搞明白,代码先上:javascript

if (!Function.prototype.bind) {
    Function.prototype.bind = function (obj) {
        var slice = [].slice,
            args = slice.call(arguments, 1),
            self = this,
            nop = function () {},
            bound = function () {
                return self.apply( this instanceof nop ? this : (obj || {}),
                                   args.concat(slice.call(arguments)));
            };
        nop.prototype = self.prototype;
        bound.prototype = new nop();
        return bound;
    };
}

本文假设读者水平和我差很少,一步一步进行分析,高手勿喷,若是能够帮我解答一下最后的疑问,thx。java

开始实现这个函数面试

根据bind返回的函数的做用的不一样,这个函数有两种状况:(1)作普通函数(2)作构造函数数组

当新函数是普通函数时,代码为:markdown

Function.prototype.bind = function (obj) {
    var slice = [].slice,
        args = slice.call(arguments, 1),
        self = this,
        bound = function () {
            return self.apply( obj || {}, args.concat(slice.call(arguments)));
        };
    return bound;
};

代码中:闭包

[].slice.call(arguments, 1) 或者 Array.prototype.slice.call(argumens, 1)

由于arguments不是真的数组,不能改变,因此经过[].slice.call(arguments)来返回参数数组。app

args.concat(slice.call(arguments))

把两个参数数组整合到一块儿函数

另外,代码中 return bound; 涉及到闭包(或者函数柯里化),这里不展开,若是闭包的概念不知道能够搜索一下,面试常常问到。ui

当新函数用做构造函数时,上面的代码就不适用了,以下:this

var Book = function(name, price){
    this.name = name;
    this.price = price;
};
var NoteBook = Book.bind(null ,'notebook'),
    n = new NoteBook(13);
console.log(n) // 实际:{}, 预期:{name: 'notebook', price: 13}

说明上面的代码须要完善,在这以前,咱们先了解一下函数用new的实际过程(《JavaScript高程序设计》P145)

1.建立一个新对象

2.将构造函数的做用域赋给了this对象(所以this指向这个新对象)

3.执行构造函数中的代码

4.若是没有return,自动返回this,不然返回你返回的对象

如今解释为何会返回 {}:

这里写图片描述

经过上面的步骤,咱们能够知道关键就是self.apply调用时传入的对象不是实例对象,因此改变它就行了。

因此将obj || {}改为:this instanceof bound ? this : (obj || {}),这样就能够顺利返回实例,同时在执行Book函数时能保证this指向该实例,代码变成:

Function.prototype.bind = function (obj) {
    var slice = [].slice,
        args = slice.call(arguments, 1),
        self = this,
        bound = function () {
            return self.apply( this instanceof bound ? this : (obj || {}),
                               args.concat(slice.call(arguments)));
        };
    return bound;
};

到这里解决了一些问题,可是还不够,不要忘记了,咱们在Book的原型上可能定义了一下方法或者属性。

...... Book.prototype.say = function(){ console.log(this.name); }; ......

以上代码可没有办法作到继承到Book原型上的方法,因此应该bound应该继承Book的原型方法

Function.prototype.bind = function (obj) {
    var slice = [].slice,
        args = slice.call(arguments, 1),
        self = this,
        nop = function () {},
        bound = function () {
            return self.apply( this instanceof bound ? this : (obj || {}),
                               args.concat(slice.call(arguments)));
        };
    nop.prototype = self.prototype;
    bound.prototype = new nop();
    return bound;
};

到此和书上代码就基本同样了,除了书上用的是:

this instanceof nop ? this : (obj || {})

这个就是个人疑问了,个人感受就是这里nop换成bound也同样,但愿大神能解解惑!!!

相关文章
相关标签/搜索