• 函数bind的实现


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

    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。

    开始实现这个函数

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

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

    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)来返回参数数组。

    args.concat(slice.call(arguments))

    把两个参数数组整合到一起

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

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

    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也一样,希望大神能解解惑!!!

  • 相关阅读:
    C# Array.Sort 省内排序
    Centos7开机启动tomcat8
    使用GeoWebCache发布ArcGIS切片地图(实现高清电子地图)
    获取经纬度之间距离的Java工具类
    centos7上安装rar解压软件
    GeoServer之发布Geotiff存在的问题
    $GPRMC解析
    如何在IDEA单元测试中使用Scanner获取输入内容
    GeoServer修改使用内存
    Github无法访问解决办法
  • 原文地址:https://www.cnblogs.com/kang-xjtu/p/5251459.html
Copyright © 2020-2023  润新知