_.assign(object, [sources])
191
_.assign(object, [sources])
_.assign将源对象的自身可枚举的字符串键属性分配到目标对象身上。应用源对象的时候从左到右(arguments参数列表)。参数列表里随后的源对象的属性会覆盖前面的源对象的同名属性。
参数
object (Object): 目标对象
[sources] (...Object): 源对象
返回值
(Object): 返回处理好的目标对象
例子
function Foo() { this.a = 1; } function Bar() { this.c = 3; } Foo.prototype.b = 2; Bar.prototype.d = 4; _.assign({ 'a': 0 }, new Foo, new Bar); // => { 'a': 1, 'c': 3 }
源代码
/** * lodash (Custom Build) <https://lodash.com/> * Build: `lodash modularize exports="npm" -o ./` * Copyright jQuery Foundation and other contributors <https://jquery.org/> * Released under MIT license <https://lodash.com/license> * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors */ /** Used as references for various `Number` constants. */ var MAX_SAFE_INTEGER = 9007199254740991; /** `Object#toString` result references. */ var argsTag = '[object Arguments]', funcTag = '[object Function]', genTag = '[object GeneratorFunction]'; /** Used to detect unsigned integer values. */ var reIsUint = /^(?:0|[1-9]\d*)$/; /** * A faster alternative to `Function#apply`, this function invokes `func` * with the `this` binding of `thisArg` and the arguments of `args`. * * @private * @param {Function} func The function to invoke. * @param {*} thisArg The `this` binding of `func`. * @param {Array} args The arguments to invoke `func` with. * @returns {*} Returns the result of `func`. */ //一个速度更快的apply方法 function apply(func, thisArg, args) { switch (args.length) { case 0: return func.call(thisArg); case 1: return func.call(thisArg, args[0]); case 2: return func.call(thisArg, args[0], args[1]); case 3: return func.call(thisArg, args[0], args[1], args[2]); } return func.apply(thisArg, args); } /** * The base implementation of `_.times` without support for iteratee shorthands * or max array length checks. * * @private * @param {number} n The number of times to invoke `iteratee`. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns the array of results. */ //_times的基础实现,返回一个长度为n的数组,元素值是遍历器处理过的index //n是循环调用遍历器的次数,iteratee是遍历器 function baseTimes(n, iteratee) { var index = -1,//循环索引 result = Array(n);//结果数组 while (++index < n) { result[index] = iteratee(index); } return result; } /** * Creates a unary function that invokes `func` with its argument transformed. * * @private * @param {Function} func The function to wrap. * @param {Function} transform The argument transform. * @returns {Function} Returns the new function. */ //创建一个一元方法,来处理函数的参数 function overArg(func, transform) { return function(arg) { return func(transform(arg)); }; } /** Used for built-in method references. */ var objectProto = Object.prototype; /** Used to check objects for own properties. */ var hasOwnProperty = objectProto.hasOwnProperty; /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) * of values. */ var objectToString = objectProto.toString;//用来获取一个值的 toStringTag来判断数据类型 /** Built-in value references. */ //Object.prototype.propertyIsEnumerable() 返回一个布尔值,表示指定的属性是否可枚举。 var propertyIsEnumerable = objectProto.propertyIsEnumerable; /* Built-in method references for those with the same name as other `lodash` methods. */ var nativeKeys = overArg(Object.keys, Object), nativeMax = Math.max; /** Detect if properties shadowing those on `Object.prototype` are non-enumerable. */ //查明是否蒙蔽了prototype上属性的同名属性是否是不可枚举的 var nonEnumShadows = !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf'); /** * Creates an array of the enumerable property names of the array-like `value`. * * @private * @param {*} value The value to query. * @param {boolean} inherited Specify returning inherited property names. * @returns {Array} Returns the array of property names. */ //创建一个array-like对象的可枚举的属性名组成的数组 //inherited布尔值,是否指定需要返回继承来的属性 function arrayLikeKeys(value, inherited) { // Safari 8.1 makes `arguments.callee` enumerable in strict mode. // Safari 9 makes `arguments.length` enumerable in strict mode. var result = (isArray(value) || isArguments(value)) ? baseTimes(value.length, String) : []; //结果数组初始化,如果value是数组或者arguments对象,使用baseTimes处理,baseTimes返回的数组的元素是索引的字符串值组成的数组,否则为空数组 var length = result.length,//结果数组长度 skipIndexes = !!length;//是否需要跳过数字索引 for (var key in value) {//for in循环value的可枚举属性 if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && (key == 'length' || isIndex(key, length)))) { //如果需要返回继承来的属性值,或者当前key是value自身属性 //并且判断是否是不是length属性或者数字索引属性,因为这两种属性需要跳过 result.push(key); } } return result; } /** * Assigns `value` to `key` of `object` if the existing value is not equivalent * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. * * @private * @param {Object} object The object to modify. * @param {string} key The key of the property to assign. * @param {*} value The value to assign. */ //给object上指定key赋值value,使用SameValueZero规则比较值是否相等 function assignValue(object, key, value) { var objValue = object[key];//赋值前object上key属性的值 if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || (value === undefined && !(key in object))) { //如果object上有key键,但是对应值和新值不相等,或者object上没有key键,直接赋值 object[key] = value; } } /** * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. */ //_.keys的基础实现,将对象的可枚举属性的key组成一个数组返回 function baseKeys(object) { if (!isPrototype(object)) {//如果不是prototype对象,使用原生Object.keys方法 return nativeKeys(object); } var result = [];//结果数组 for (var key in Object(object)) { if (hasOwnProperty.call(object, key) && key != 'constructor') { //不等于constructor的自身属性push入结果数组 result.push(key); } } return result; } /** * The base implementation of `_.rest` which doesn't validate or coerce arguments. * * @private * @param {Function} func The function to apply a rest parameter to. * @param {number} [start=func.length-1] The start position of the rest parameter. * @returns {Function} Returns the new function. */ //_.rest方法的基础实现,为函数实现ES6的rest参数 //rest参数的起始位置,默认是形参个数减去1,func.length - 1 function baseRest(func, start) { start = nativeMax(start === undefined ? (func.length - 1) : start, 0); //处理rest参数的起始位置,如果没传递start,就用函数形参个数减一,如果传递了,就在start和0之间选一个最大值 return function() { var args = arguments,//函数接收到的参数数组 index = -1,//循环索引 length = nativeMax(args.length - start, 0),//rest参数的长度 array = Array(length);//创建一个长度为rest参数长度的数组 while (++index < length) {//循环rest参数长度,将rest参数全部存入array变量中 array[index] = args[start + index]; } index = -1;//循环参数 var otherArgs = Array(start + 1);//创建一个数组存放除了rest参数以外的其他参数 while (++index < start) {//循环获取其他参数存入otherArgs中 otherArgs[index] = args[index]; } otherArgs[start] = array;//otherArgs的最后一个参数就是rest参数数组 return apply(func, this, otherArgs);//调用func,参数已处理成带有rest参数的形式 }; } /** * Copies properties of `source` to `object`. * * @private * @param {Object} source The object to copy properties from. * @param {Array} props The property identifiers to copy. * @param {Object} [object={}] The object to copy properties to. * @param {Function} [customizer] The function to customize copied values. * @returns {Object} Returns `object`. */ //复制source对象的属性到object对象上 function copyObject(source, props, object, customizer) { object || (object = {});//如果没有传递object,就新建一个空对象 var index = -1, length = props.length; while (++index < length) {//遍历属性key组成的数组 var key = props[index];//每一个key var newValue = customizer ? customizer(object[key], source[key], key, object, source) : undefined;//如果传入了自定义方法,就用自定义方法获取到键对应值 assignValue(object, key, newValue === undefined ? source[key] : newValue); //调用assignValue来复制值 } return object; } /** * Creates a function like `_.assign`. * * @private * @param {Function} assigner The function to assign values. * @returns {Function} Returns the new assigner function. */ //创建一个类似 _.assign 的方法 function createAssigner(assigner) { return baseRest(function(object, sources) {//sources已被处理成rest参数数组 var index = -1, length = sources.length,//rest参数长度 customizer = length > 1 ? sources[length - 1] : undefined,//rest参数最后一个参数可能是是自定义方法 guard = length > 2 ? sources[2] : undefined; customizer = (assigner.length > 3 && typeof customizer == 'function') ? (length--, customizer) : undefined; //判断是否有自定义方法 if (guard && isIterateeCall(sources[0], sources[1], guard)) { customizer = length < 3 ? undefined : customizer; length = 1; } object = Object(object); while (++index < length) { var source = sources[index]; if (source) { assigner(object, source, index, customizer);//调用assigner从source上复制属性到object上 } } return object; }); } /** * Checks if `value` is a valid array-like index. * * @private * @param {*} value The value to check. * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. */ //检查一个值是否是一个有效的array-like的索引 function isIndex(value, length) { length = length == null ? MAX_SAFE_INTEGER : length;//如果length是空就换成最大安全数字 return !!length && (typeof value == 'number' || reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length); //length存在 //value是number值或者value符合reIsUint = /^(?:0|[1-9]\d*)$/正则 //value是正数,value是整数,value小于length } /** * Checks if the given arguments are from an iteratee call. * * @private * @param {*} value The potential iteratee value argument. * @param {*} index The potential iteratee index or key argument. * @param {*} object The potential iteratee object argument. * @returns {boolean} Returns `true` if the arguments are from an iteratee call, * else `false`. */ //判断参数是否来自一个迭代器调用的参数 function isIterateeCall(value, index, object) { if (!isObject(object)) {//如果object不是对象,返回false return false; } var type = typeof index;//index的typeof类型 if (type == 'number' ? (isArrayLike(object) && isIndex(index, object.length)) : (type == 'string' && index in object) ) { //如果index的类型是数字,说明遍历的是数组,如果是字符串并且是object的属性,说明遍历的是对象 return eq(object[index], value); //判断object[index]是否和value相等,返回比较结果 } return false; } /** * Checks if `value` is likely a prototype object. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. */ //检查一个值是否类似一个prototype对象 function isPrototype(value) { var Ctor = value && value.constructor, proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; //获取这个值的constructor属性,也就是构造函数,通过构造函数获取它的prototype,如果没有就用Object.prototype代替 //判断它和它的prototype是否是同一个对象 return value === proto; } /** * Performs a * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * comparison between two values to determine if they are equivalent. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * * var object = { 'a': 1 }; * var other = { 'a': 1 }; * * _.eq(object, object); * // => true * * _.eq(object, other); * // => false * * _.eq('a', 'a'); * // => true * * _.eq('a', Object('a')); * // => false * * _.eq(NaN, NaN); * // => true */ //用SameValueZero规则判断两个值是否相等 function eq(value, other) { return value === other || (value !== value && other !== other); } /** * Checks if `value` is likely an `arguments` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an `arguments` object, * else `false`. * @example * * _.isArguments(function() { return arguments; }()); * // => true * * _.isArguments([1, 2, 3]); * // => false */ //判断一个值是否是一个arguments对象 function isArguments(value) { // Safari 8.1 makes `arguments.callee` enumerable in strict mode. return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') && (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag); //value是一个array-like对象,value有名为callee的自身属性,callee属性不可枚举,对象的toString标签是'[object Arguments]' } /** * Checks if `value` is classified as an `Array` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array, else `false`. * @example * * _.isArray([1, 2, 3]); * // => true * * _.isArray(document.body.children); * // => false * * _.isArray('abc'); * // => false * * _.isArray(_.noop); * // => false */ //判断一个值是否是数组 var isArray = Array.isArray; /** * Checks if `value` is array-like. A value is considered array-like if it's * not a function and has a `value.length` that's an integer greater than or * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is array-like, else `false`. * @example * * _.isArrayLike([1, 2, 3]); * // => true * * _.isArrayLike(document.body.children); * // => true * * _.isArrayLike('abc'); * // => true * * _.isArrayLike(_.noop); * // => false */ //判断一个值是否是一个array-like //规则:不等于null,不是function类型,并且有length属性,length是大于0小于Number.MAX_SAFE_INTEGER的整数 function isArrayLike(value) { return value != null && isLength(value.length) && !isFunction(value); } /** * This method is like `_.isArrayLike` except that it also checks if `value` * is an object. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array-like object, * else `false`. * @example * * _.isArrayLikeObject([1, 2, 3]); * // => true * * _.isArrayLikeObject(document.body.children); * // => true * * _.isArrayLikeObject('abc'); * // => false * * _.isArrayLikeObject(_.noop); * // => false */ //判断一个值是不是一个array-like对象 //isObjectLike判断一个值是否是一个object-like,规则是:typeof返回object,并且不是null //isArrayLike判断一个值是否是一个array-like,规则:不等于null,不是function类型,并且有length属性,length是大于0小于Number.MAX_SAFE_INTEGER的整数 function isArrayLikeObject(value) { return isObjectLike(value) && isArrayLike(value); } /** * Checks if `value` is classified as a `Function` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a function, else `false`. * @example * * _.isFunction(_); * // => true * * _.isFunction(/abc/); * // => false */ //判断一个值是否是一个function对象 function isFunction(value) { // The use of `Object#toString` avoids issues with the `typeof` operator // in Safari 8-9 which returns 'object' for typed array and other constructors. var tag = isObject(value) ? objectToString.call(value) : '';//如果是object就获取toString tag return tag == funcTag || tag == genTag;//toString tag 等于[object Function]或者[object GeneratorFunction] } /** * Checks if `value` is a valid array-like length. * * **Note:** This method is loosely based on * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. * @example * * _.isLength(3); * // => true * * _.isLength(Number.MIN_VALUE); * // => false * * _.isLength(Infinity); * // => false * * _.isLength('3'); * // => false */ //判断一个值是否是一个有效的array-like对象的length属性 function isLength(value) { return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; } /** * Checks if `value` is the * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an object, else `false`. * @example * * _.isObject({}); * // => true * * _.isObject([1, 2, 3]); * // => true * * _.isObject(_.noop); * // => true * * _.isObject(null); * // => false */ //判断一个值是否是对象,object或者function类型 function isObject(value) { var type = typeof value; return !!value && (type == 'object' || type == 'function'); } /** * Checks if `value` is object-like. A value is object-like if it's not `null` * and has a `typeof` result of "object". * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is object-like, else `false`. * @example * * _.isObjectLike({}); * // => true * * _.isObjectLike([1, 2, 3]); * // => true * * _.isObjectLike(_.noop); * // => false * * _.isObjectLike(null); * // => false */ //判断一个值是否是一个object-like,规则是:typeof返回object,并且不是null function isObjectLike(value) { return !!value && typeof value == 'object'; } /** * Assigns own enumerable string keyed properties of source objects to the * destination object. Source objects are applied from left to right. * Subsequent sources overwrite property assignments of previous sources. * * **Note:** This method mutates `object` and is loosely based on * [`Object.assign`](https://mdn.io/Object/assign). * * @static * @memberOf _ * @since 0.10.0 * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @returns {Object} Returns `object`. * @see _.assignIn * @example * * function Foo() { * this.a = 1; * } * * function Bar() { * this.c = 3; * } * * Foo.prototype.b = 2; * Bar.prototype.d = 4; * * _.assign({ 'a': 0 }, new Foo, new Bar); * // => { 'a': 1, 'c': 3 } */ //将源对象的自身可枚举的字符串键属性分配到目标对象身上。应用源对象的时候从左到右(arguments参数列表)。参数列表里随后的源对象的属性会覆盖前面的源对象的同名属性。 var assign = createAssigner(function(object, source) { //nonEnumShadows 查明是否蒙蔽了prototype上属性的同名属性是否是不可枚举的 //isPrototype(source) 判断source是否是一个prototype对象 //isArrayLike(source) 判断source是否是一个array-like if (nonEnumShadows || isPrototype(source) || isArrayLike(source)) { //总之这里就判断source对象身上的属性有没有那种需要复制但是是不可枚举的,这时候就借助copyObject方法将其复制到目标对象上 copyObject(source, keys(source), object); return; } //如果source对象身上属性都是可枚举的,不需要用特殊方法复制,那么for in循环, for (var key in source) { if (hasOwnProperty.call(source, key)) {//Object.prototype.hasOwnProperty()判断source自身属性中是否含有此key assignValue(object, key, source[key]);//调用assignValue将属性复制 } } }); /** * Creates an array of the own enumerable property names of `object`. * * **Note:** Non-object values are coerced to objects. See the * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) * for more details. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.keys(new Foo); * // => ['a', 'b'] (iteration order is not guaranteed) * * _.keys('hi'); * // => ['0', '1'] */ //创建一个对象的自身可枚举属性名组成的数组,类似原生的Object.keys() //如果object是array-like对象,调用arrayLikeKeys处理,否则使用baseKeys处理 function keys(object) { return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); } module.exports = assign;