• underscore.js源码解析【对象】


    // Object Functions
      // ----------------
    
      // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
      /*
        ie9以下版本中,对象中的key是不能被遍历的
      */
      var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
      var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
                          'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];// 不能被遍历的属性
    
      var collectNonEnumProps = function(obj, keys) {
        var nonEnumIdx = nonEnumerableProps.length;
        var constructor = obj.constructor;
        var proto = _.isFunction(constructor) && constructor.prototype || ObjProto;// 这是访问实例原型的一种方法!!!!!!!!
    
        // Constructor is a special case.
        // Constructor是特殊的属性,单独处理一下
        var prop = 'constructor';
        // _.has()只访问自身属性,不行沿作用域链查找;_.contains()是用for in 查找,所以找不到不可遍历的属性
        // 'constructor'在自身,但是for in 遍历不到
        if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
    
        while (nonEnumIdx--) {
          prop = nonEnumerableProps[nonEnumIdx];
          // prop在obj上(也有可能在原型上),并且obj[prop]与原型上的值不一样,并且用for in找不到prop
          if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
            keys.push(prop);
          }
        }
      };
    
      // Retrieve the names of an object's own properties.
      // Delegates to **ECMAScript 5**'s native `Object.keys`.
      /*
        获取对象中的所有属性
      */
      _.keys = function(obj) {
        if (!_.isObject(obj)) return [];
        if (nativeKeys) return nativeKeys(obj);// Object.keys(obj)
        var keys = [];
        for (var key in obj) if (_.has(obj, key)) keys.push(key);// _.has()会判断对象属性是否在本身对象里
        // Ahem, IE < 9.
        if (hasEnumBug) collectNonEnumProps(obj, keys);// 将不可遍历的属性也加进去
        return keys;
      };
    
      // Retrieve all the property names of an object.
      /*
        检索object拥有的和继承的所有属性的名称
      */
      _.allKeys = function(obj) {
        if (!_.isObject(obj)) return [];
        var keys = [];
        for (var key in obj) keys.push(key);
        // Ahem, IE < 9.
        if (hasEnumBug) collectNonEnumProps(obj, keys);
        return keys;
      };
    
      // Retrieve the values of an object's properties.
      /*
        通过_.keys()获取所有value
      */
      _.values = function(obj) {
        var keys = _.keys(obj);
        var length = keys.length;
        var values = Array(length);
        for (var i = 0; i < length; i++) {
          values[i] = obj[keys[i]];
        }
        return values;
      };
    
      // Returns the results of applying the iteratee to each element of the object.
      // In contrast to _.map it returns an object.
      /*
        它类似于map,但是这用于对象。转换每个属性的值。
      */
      _.mapObject = function(obj, iteratee, context) {
        iteratee = cb(iteratee, context);
        var keys = _.keys(obj),
            length = keys.length,
            results = {};
        for (var index = 0; index < length; index++) {
          var currentKey = keys[index];
          results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
        }
        return results;// 返回新对象
      };
    
      // Convert an object into a list of `[key, value]` pairs.
      /*
        把一个对象转变为一个[key, value]形式的数组
      */
      _.pairs = function(obj) {
        var keys = _.keys(obj);
        var length = keys.length;
        var pairs = Array(length);
        for (var i = 0; i < length; i++) {
          pairs[i] = [keys[i], obj[keys[i]]];
        }
        return pairs;
      };
    
      // Invert the keys and values of an object. The values must be serializable.
      /*
        返回一个object副本,使其键(keys)和值(values)对换
      */
      _.invert = function(obj) {
        var result = {};
        var keys = _.keys(obj);
        for (var i = 0, length = keys.length; i < length; i++) {
          result[obj[keys[i]]] = keys[i]; // 交换键和值
        }
        return result;
      };
    
      // Return a sorted list of the function names available on the object.
      // Aliased as `methods`.
      /*
        返回一个对象里所有的方法名, 而且是已经排序的 — 也就是说, 对象里每个方法(属性值是一个函数)的名称
      */
      _.functions = _.methods = function(obj) {
        var names = [];
        for (var key in obj) {
          if (_.isFunction(obj[key])) names.push(key);
        }
        return names.sort();
      };
    
      // An internal function for creating assigner functions.
      var createAssigner = function(keysFunc, defaults) {
        return function(obj) {
          var length = arguments.length;
          if (defaults) obj = Object(obj);
          if (length < 2 || obj == null) return obj;// 如果参数个数小于2,或者obj为null,返回null
          for (var index = 1; index < length; index++) {// 第二个参数开始
            var source = arguments[index],
                keys = keysFunc(source),
                l = keys.length;
            for (var i = 0; i < l; i++) {
              var key = keys[i];
              if (!defaults || obj[key] === void 0) obj[key] = source[key];// 若source中没有key属性才会复制,相同属性key不会覆盖
            }
          }
          return obj;
        };
      };
    
      // Extend a given object with all the properties in passed-in object(s).
      /*
        复制source对象中的所有属性覆盖到destination对象上,并且返回 destination 对象. 不会覆盖
      */
      _.extend = createAssigner(_.allKeys);
    
      // Assigns a given object with all the own properties in the passed-in object(s).
      // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
      /*
        类似于 extend, 但只复制自己的属性覆盖到目标对象。
      */
      _.extendOwn = _.assign = createAssigner(_.keys);
    
      // Returns the first key on an object that passes a predicate test.
      /*
        类似findIndex(),返回符合predicate的key
      */
      _.findKey = function(obj, predicate, context) {
        predicate = cb(predicate, context);
        var keys = _.keys(obj), key;
        for (var i = 0, length = keys.length; i < length; i++) {
          key = keys[i];
          if (predicate(obj[key], key, obj)) return key;
        }
      };
    
      // Internal pick helper function to determine if `obj` has key `key`.
      /*
        判断key是obj的自身属性(或继承属性)
      */
      var keyInObj = function(value, key, obj) {
        return key in obj;
      };
    
      // Return a copy of the object only containing the whitelisted properties.
      /*
        返回一个object副本,只过滤出keys(有效的键组成的数组)参数指定的属性值。或者接受一个判断函数,指定挑选哪个key。、
        restArgs()把keys及后面的参数变为一个数组
      */
      _.pick = restArgs(function(obj, keys) {
        var result = {}, iteratee = keys[0];
        if (obj == null) return result;
        if (_.isFunction(iteratee)) {// 如果此函数的第二个参数是一个函数
          if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]);// 如果后面还有第三个参数,作为itetratee的参数传入,变成一个高阶函数
          keys = _.allKeys(obj);
        } else {// 第二个参数不是一个函数
          iteratee = keyInObj;// 就把上面的keyInObj传给他
          keys = flatten(keys, false, false);
          obj = Object(obj);
        }
        for (var i = 0, length = keys.length; i < length; i++) {
          var key = keys[i];
          var value = obj[key];
          if (iteratee(value, key, obj)) result[key] = value;// 如果是函数,则执行函数;不是函数,执行 key in obj
        }
        return result;
      });
    
      // Return a copy of the object without the blacklisted properties.
      /*
        返回一个object副本,只过滤出除去keys(有效的键组成的数组)参数指定的属性值。 或者接受一个判断函数,指定忽略哪个key。
      */
      _.omit = restArgs(function(obj, keys) {
        var iteratee = keys[0], context;
        if (_.isFunction(iteratee)) {
          iteratee = _.negate(iteratee);// 返回iteratee的否定版本
          if (keys.length > 1) context = keys[1];
        } else {
          keys = _.map(flatten(keys, false, false), String);
          iteratee = function(value, key) {
            return !_.contains(keys, key);
          };
        }
        return _.pick(obj, iteratee, context);
      });
    
      // Fill in a given object with default properties.
      _.defaults = createAssigner(_.allKeys, true);
    
      // Creates an object that inherits from the given prototype object.
      // If additional properties are provided then they will be added to the
      // created object.
      /*
        创建具有给定原型的新对象, 可选附加props 作为 own的属性。 基本上,和Object.create一样, 但是没有所有的属性描述符。
      */
      _.create = function(prototype, props) {
        var result = baseCreate(prototype);// 原型继承
        if (props) _.extendOwn(result, props);
        return result;
      };
    
      // Create a (shallow-cloned) duplicate of an object.
      /*
        创建 一个浅复制(浅拷贝)的克隆object。任何嵌套的对象或数组都通过引用拷贝,不会复制。
      */
      _.clone = function(obj) {
        if (!_.isObject(obj)) return obj;
        return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
      };
    
      // Invokes interceptor with the obj, and then returns obj.
      // The primary purpose of this method is to "tap into" a method chain, in
      // order to perform operations on intermediate results within the chain.
      /*
        用 object作为参数来调用函数interceptor,然后返回object。这种方法的主要意图是作为函数链式调用 的一环, 为了对此对象执行操作并返回对象本身。
      */
      _.tap = function(obj, interceptor) {
        interceptor(obj);
        return obj;
      };
    
      // Returns whether an object has a given set of `key:value` pairs.
      /*
        告诉你properties中的键和值是否包含在object中。
      */
      _.isMatch = function(object, attrs) {
        var keys = _.keys(attrs), length = keys.length;
        if (object == null) return !length;
        var obj = Object(object);
        for (var i = 0; i < length; i++) {
          var key = keys[i];
          if (attrs[key] !== obj[key] || !(key in obj)) return false;
        }
        return true;
      };
    
    
      // Internal recursive comparison function for `isEqual`.
      /*
        内部比较函数,判断a,b是否相等??????????????????????????????????????????????????????????????????????????????
      */
      var eq, deepEq;
      eq = function(a, b, aStack, bStack) {
        // Identical objects are equal. `0 === -0`, but they aren't identical.
        // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
        if (a === b) return a !== 0 || 1 / a === 1 / b;// 0 === -0
        // A strict comparison is necessary because `null == undefined`.
        // 针对null == undefined ,"==="是必须的
        if (a == null || b == null) return a === b;
        // `NaN`s are equivalent, but non-reflexive.
        // NaN
        if (a !== a) return b !== b;
        // Exhaust primitive checks
        var type = typeof a;
        if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
        return deepEq(a, b, aStack, bStack);
      };
    
      // Internal recursive comparison function for `isEqual`.
      deepEq = function(a, b, aStack, bStack) {
        // Unwrap any wrapped objects.
        if (a instanceof _) a = a._wrapped;
        if (b instanceof _) b = b._wrapped;
        // Compare `[[Class]]` names.
        // 判断a,b的类型,并且a,b类型一定相等
        var className = toString.call(a);
        if (className !== toString.call(b)) return false;
        switch (className) {
          // Strings, numbers, regular expressions, dates, and booleans are compared by value.
          case '[object RegExp]':
          // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
          case '[object String]':
            // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
            // equivalent to `new String("5")`.
            return '' + a === '' + b;
          case '[object Number]':
            // `NaN`s are equivalent, but non-reflexive.
            // Object(NaN) is equivalent to NaN.
            if (+a !== +a) return +b !== +b;
            // An `egal` comparison is performed for other numeric values.
            return +a === 0 ? 1 / +a === 1 / b : +a === +b;
          case '[object Date]':
          case '[object Boolean]':
            // Coerce dates and booleans to numeric primitive values. Dates are compared by their
            // millisecond representations. Note that invalid dates with millisecond representations
            // of `NaN` are not equivalent.
            return +a === +b;
          case '[object Symbol]':
            return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
        }
    
        var areArrays = className === '[object Array]';
        if (!areArrays) {// 如果不是数组
          if (typeof a != 'object' || typeof b != 'object') return false;
    
          // Objects with different constructors are not equivalent, but `Object`s or `Array`s
          // from different frames are.
          var aCtor = a.constructor, bCtor = b.constructor;
          if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
                                   _.isFunction(bCtor) && bCtor instanceof bCtor)
                              && ('constructor' in a && 'constructor' in b)) {
            return false;
          }
        }
        // Assume equality for cyclic structures. The algorithm for detecting cyclic
        // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
    
        // Initializing stack of traversed objects.
        // It's done here since we only need them for objects and arrays comparison.
        aStack = aStack || [];
        bStack = bStack || [];
        var length = aStack.length;
        while (length--) {
          // Linear search. Performance is inversely proportional to the number of
          // unique nested structures.
          if (aStack[length] === a) return bStack[length] === b;
        }
    
        // Add the first object to the stack of traversed objects.
        aStack.push(a);
        bStack.push(b);
    
        // Recursively compare objects and arrays.
        if (areArrays) {// 如果是数组
          // Compare array lengths to determine if a deep comparison is necessary.
          length = a.length;
          if (length !== b.length) return false;// 如果长度不等,直接pass
          // Deep compare the contents, ignoring non-numeric properties.
          while (length--) {
            if (!eq(a[length], b[length], aStack, bStack)) return false;// 比较数组中的每一项
          }
        } else {// 如果不是,深度比较对象
          // Deep compare objects.
          var keys = _.keys(a), key;
          length = keys.length;
          // Ensure that both objects contain the same number of properties before comparing deep equality.
          if (_.keys(b).length !== length) return false;// 如果key的长度不等,直接pass
          while (length--) {
            // Deep compare each member
            key = keys[length];
            if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
          }
        }
        // Remove the first object from the stack of traversed objects.
        aStack.pop();
        bStack.pop();
        return true;
      };
    
      // Perform a deep comparison to check if two objects are equal.
      /*
        执行两个对象之间的优化深度比较,确定他们是否应被视为相等。
      */
      _.isEqual = function(a, b) {
        return eq(a, b);
      };
    
      // Is a given array, string, or object empty?
      // An "empty" object has no enumerable own-properties.
      /*
        判断是否为空
      */
      _.isEmpty = function(obj) {
        if (obj == null) return true;
        // 有length的类型,直接判断length属性是否为0
        if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
        return _.keys(obj).length === 0;
      };
    
      // Is a given value a DOM element?
      /*
        如果object是一个DOM元素,返回true。
      */
      _.isElement = function(obj) {
        return !!(obj && obj.nodeType === 1);
      };
    
      // Is a given value an array?
      // Delegates to ECMA5's native Array.isArray
      /*
        判断是否是数组类型
      */
      _.isArray = nativeIsArray || function(obj) {
        return toString.call(obj) === '[object Array]';
      };
    
      // Is a given variable an object?
      /*
        判断是不是对象
      */
      _.isObject = function(obj) {
        var type = typeof obj;
        return type === 'function' || type === 'object' && !!obj;// !!排除null
      };
    
      // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet, isWeakSet.
      /*
        定义各个类型判断的函数
      */
      _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) {
        _['is' + name] = function(obj) {
          return toString.call(obj) === '[object ' + name + ']';
        };
      });
    
      // Define a fallback version of the method in browsers (ahem, IE < 9), where
      // there isn't any inspectable "Arguments" type.
      /*
        判断是否是arguments
      */
      if (!_.isArguments(arguments)) {
        _.isArguments = function(obj) {
          return _.has(obj, 'callee');
        };
      }
    
      // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
      // IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236).
      var nodelist = root.document && root.document.childNodes;
      if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') {
        _.isFunction = function(obj) {
          return typeof obj == 'function' || false;
        };
      }
    
      // Is a given object a finite number?
      /*
        判断是否有限
      */
      _.isFinite = function(obj) {
        return !_.isSymbol(obj) && isFinite(obj) && !isNaN(parseFloat(obj));
      };
    
      // Is the given value `NaN`?
      _.isNaN = function(obj) {
        return _.isNumber(obj) && isNaN(obj);
      };
    
      // Is a given value a boolean?
      _.isBoolean = function(obj) {
        return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
      };
    
      // Is a given value equal to null?
      _.isNull = function(obj) {
        return obj === null;
      };
    
      // Is a given variable undefined?
      _.isUndefined = function(obj) {
        return obj === void 0;
      };
    
      // Shortcut function for checking if an object has a given property directly
      // on itself (in other words, not on a prototype).
      /*
        判断obj是否自身而不是在原型链中拥有key
      */
      _.has = function(obj, key) {
        return obj != null && hasOwnProperty.call(obj, key);
      };

    小结

    1.访问实例原型

    var constructor = obj.constructor;
    var proto = _.isFunction(constructor) && constructor.prototype || ObjProto;

    2.判断是否有限

    _.isFinite = function(obj) {
    return !_.isSymbol(obj) && isFinite(obj) && !isNaN(parseFloat(obj));
    };
  • 相关阅读:
    格式化输出函数(1): Format
    ini 文件操作记要(2): 使用 TMemIniFile
    文本文件读写
    格式化输出函数(3): FormatFloat
    Delphi 中的哈希表(2): TStringHash
    格式化输出函数(2): FormatDateTime
    Delphi 中的哈希表(1): THashedStringList
    调用系统关于对话框
    在Ubuntu上安装Thrift并调通一个例子
    rpm2html/rpmfind
  • 原文地址:https://www.cnblogs.com/shytong/p/5835480.html
Copyright © 2020-2023  润新知