美文网首页
[underscore 源码学习] rest 参数 & 创建对象

[underscore 源码学习] rest 参数 & 创建对象

作者: 小黄人get徐先生 | 来源:发表于2020-01-27 20:58 被阅读0次

    rest 参数

    • 即自由参数、松散参数,自由和松散参数个数是随意的,与之对应的是固定参数。
    • ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用 arguments 对象。
    • rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
    _.restArguments

    underscore 的官方实现,它暴露了一个 _.restArguments 方法,通过给该方法传递一个函数,能够使得函数支持 rest 参数。

    // 源码
    var restArguments = function(func, startIndex) {
      startIndex = startIndex == null ? func.length -1 : +startIndex;
      return function() {
        var length = Math.max(arguments.length - startIndex, 0),
              rest = Array(length),
              index = 0;
        for (; index < length; index++) {
          rest[index] = arguments[index + startIndex];
        }
      };
      ...
    };
    

    Object.create polyfill

    Object.create()
    • Object.create() 方法创建一个新对象,使用现有的对象来作为新创建的对象的 __proto__
    • Object.create 不依赖构造函数,它内部已经维护了一个构造函数,并将该构造函数的 prototype 属性指向传入的对象,因此,它比 new 更灵活。
    Underscore 如何创建对象?
    • underscore 利用 baseCreate 创建对象时,会先检查当前环境是否已支持 Object.create,如不支持,会创建一个简易的 polyfill

    下面我们开始源码学习:

    restArguments 实现

    test.js

    (function(root) {
    
        const toString = Object.prototype.toString;
        const push = Array.prototype.push;
    
        const _ = function(obj) {
    
            if (obj instanceof _) {
                return obj;
            }
    
            if (!(this instanceof _)) {
                return new _(obj);
            }
            this._wrapped = obj;
        };
    
        // rest 参数
        _.restArguments = function(func) {
            // rest 参数位置
            const startIndex = func.length - 1;
            return function() {
                const length = arguments.length - startIndex;
                const rest = Array(length);
                // rest 数组中的成员
                for (let index = 0; index < length; index++) {
                    rest[index] = arguments[index + startIndex];
                }
                // 非 rest 参数成员的值一一对应
                const args = Array(startIndex + 1);
                for (let index = 0; index < startIndex; index++) {
                    args[index] = arguments[index];
                }
    
                args[startIndex] = rest;
                return func.apply(this, args);
            };
        };
    
    
        _.isFunction = function(obj) {
            return typeof obj === 'function';
        };
    
        const cb = function(iteratee, context, count) {
            if (iteratee === null) {
                return _.identity;
            }
    
            if (_.isFunction(iteratee)) {
                return optimizeCb(iteratee, context, count);
            }
        };
    
        const optimizeCb = function(func, context, count) {
            if (context === void 0) {
                return func;
            }
    
            switch (count == null ? 3 : count) {
                case 1:
                    return function(value) {
                        return func.call(context, value);
                    };
                case 3:
                    return function(value, index, obj) {
                        return func.call(context, value, index, obj);
                    };
            }
        };
    
        _.identity = function(value) {
            return value;
        };
    
        _.map = function(obj, iteratee, context) {
            // 生成不同功能迭代器
            const cbIteratee = cb(iteratee, context);
            const keys = !_.isArray(obj) && Object.keys(obj);
            const length = (keys || obj).length;
            const result = Array(length);
    
            for (let index = 0; index < length; index++) {
                const currentKey = keys? keys[index] : index;
                result[index] = cbIteratee(obj[currentKey], index, obj);
            }
    
            return result;
        };
    
        _.unique = function(obj, callback) {
            const res = [];
            for (let i = 0; i < obj.length; i++) {
                const val = callback? callback(obj[i]) : obj[i];
                if (res.indexOf(val) === -1) {
                    res.push(val);
                }
            }
            return res;
        };
    
        _.isArray = function(obj) {
            return toString.call(obj) === "[object Array]";
        };
    
        _.functions = function(obj) {
            const res = [];
            for (let key in obj) {
                res.push(key);
            }
            return res;
        };
    
        _.each = function(obj, callback) {
            if (_.isArray(obj)) {
                for (let i = 0;i < obj.length; i++) {
                    callback.call(obj, obj[i], i);
                }
            } else {
                for (let key in obj) {
                    callback.call(obj, key, obj[key]);
                }
            }
        };
    
        _.chain = function(obj) {
            const instance = _(obj);
            instance._chain = true;
            return instance;
        };
    
        const result = function(instance, obj) {
            return instance._chain? _(obj).chain() : obj;
        };
    
        _.prototype.value = function() {
            return this._wrapped;
        };
    
        _.mixin = function(obj) {
            _.each(_.functions(obj), (name) => {
                const func = obj[name];
    
                _.prototype[name] = function() {
                    let args = [this._wrapped];
                    push.apply(args, arguments);
                    return result(this, func.apply(this, args));
                };
            });
        };
    
        _.mixin(_);
    
        root._ = _;
    })(this);
    
    

    index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>underscore</title>
    </head>
    <body>
        <script src="./test.js"></script>
        <script>
            // ES6 引入 rest 参数(形式为 ... 变量名),用于获取函数的多余参数
            // function test(count, ...rest) {
            //     console.log(rest);
            // }
            // test(1,2,3,4);
    
            function test(count, rest) {
                console.log(count, rest);
            }
            // 包装器
            const restTest = _.restArguments(test);
            restTest(1,2,3,4);
        </script>
    </body>
    </html>
    
    

    显示结果如下:


    Object.create polyfill Object.create(object) baseCreate(object)
    const Ctor = function() {};
    
    const baseCreate = function(prototype) {
      if (!_.isObject(prototype)) return {};
      // 如果支持 Object.create,直接使用即可
      if (Object.create) return Object.create(prototype);
      Ctor.prototype = prototype;
      const result = new Ctor;
      Ctor.prototype = null;
      return result;
    }
    

    相关文章

      网友评论

          本文标题:[underscore 源码学习] rest 参数 & 创建对象

          本文链接:https://www.haomeiwen.com/subject/nciethtx.html