Underscore 对象函数1
对象函数(Object Functions)
1. _.keys
_.keys(object) 检索object拥有的所有可枚举属性的名称。
var ObjProto = Object.prototype;
// keys函数在小于IE9系统中不能使用`for key in ...`方法遍历对象属性。
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.
// 如果list包含指定的value则返回true(愚人码头注:使用===检测)。
// 如果list 是数组,内部使用indexOf判断。使用fromIndex来给定开始检索的索引位置。
// _.contains([1, 2, 3], 3); => true
var prop = 'constructor';
if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
while (nonEnumIdx--) {
prop = nonEnumerableProps[nonEnumIdx];
if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
keys.push(prop);
}
}
};
var nativeKeys = Object.keys;
_.keys = function(obj) {
if (!_.isObject(obj)) return [];
if (nativeKeys) return nativeKeys(obj);
var keys = [];
// _.has(object, key) 对象是否包含给定的键吗?
// 等同于object.hasOwnProperty(key),
// 但是使用hasOwnProperty 函数的一个安全引用,以防意外覆盖。
for (var key in obj) if (_.has(obj, key)) keys.push(key);
// Ahem, IE < 9.
if (hasEnumBug) collectNonEnumProps(obj, keys);
return keys;
};
1、 检测obj是否为对象类型,若不是,则返回空数组。
2、 判断是否存在函数nativeKeys(Object.keys),若存在,则直接返回该对象obj的所有可枚举自身属性的属性名。
3、 若都不满足上面的两个条件,则继续往下运行。
4、 定义变量keys,用于存储空数组。
5、 运行for key in …循环函数,判断若key存在于obj内,那么把key属性名push到keys中。
7、 为了兼容IE9以下的系统,则需要判段hasEnumBug是否为真。若为真,则运行collectNonEnumProps()函数。
注:
1) 由于hasEnumBug等于函数{toString: null}.propertyIsEnumerable(‘toString’)的假检测。
2) 而函数propertyIsEnumerable()的作用是用来检测属性是否属于某个对象的,如果检测到了,返回true,否则返回false。
3) 但函数propertyIsEnumerable()要返回true,还要满足两个条件:
a) 这个属性必须属于实例的,并且不属于原型.
b) 这个属性必须是可枚举的,也就是自定义的属性,可以通过for..in循环出来的.
8、 由于IE9以下的系统不能通过for key in …循环遍历对象属性。故hasEnumBug为true。
9、 运行collectNonEnumProps(obj, keys)函数。
1) 定义变量nonEnumIdx,用于存储nonEnumerableProps的长度。
2) 定义变量constructor,用于存储obj的构造函数obj.constructor。
3) 定义变量proto,用于存储对象的原型对象prototype。
4) 定义变量prop,用于存储需要运行的js原生函数名。
5) 当prop等于constructor时,判断prop是否存在与obj对象,且keys中包含props,则把prop的属性名push到keys中。
6) 运行while循环,从新赋值prop为nonEnumerableProps[nonEnumIdx],并满足于判断句后,把prop的属性名push到keys中。
例:
var test = _.keys({one: 1, two: 2, three: 3});
console.log(test);
// ["one", "two", "three"]
console.log('--------------------------');
var test2 = _.keys({1: 'a', 2: 'b', 3: 'c', length:3});
console.log(test2);
// ["1", "2", "3", "length"]
2. _.allKeys
_.allKeys(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;
};
1、 检测obj是否为对象类型,若不是,则返回空数组。
2、 定义变量keys,用于存储空数组。
3、 运行for key in …循环函数,把key属性名push到keys中。
4、 为了兼容IE9以下的系统,则需要判段hasEnumBug是否为真。若为真,则运行collectNonEnumProps()函数。
例:
function Stooge(name) {
this.name = name;
}
Stooge.prototype.silly = true;
var test = _.allKeys(new Stooge("Moe"));
console.log(test);
// ["name", "silly"]
3. _.values
_.values(object)
返回object对象所有的属性值。
_.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;
};
1、 运行_.keys()函数,把obj中得到的所有属性名称的集合赋值给keys。
2、 定义变量length,存储keys的长度值。
3、 定义空数组values。
4、 运行for循环函数,得出obj的各个值,并赋值给values[i]。
5、 返回values的值。
var test = _.values({one: 1, two: 2, three: 3});
console.log(test);
// [1, 2, 3]
4. _.mapObject
_.mapObject(object, iteratee, [context]) 它类似于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;
};
1、 运行cb()函数,得到新的iteratee函数。
2、 运行_.keys()函数,得到obj属性的集合。
3、 定义length存在keys的长度值,定义results空对象。
4、 运行for循环,把keys对应的index值(obj的属性名)赋值给currentKey,并运行iteratee函数,并把新计算的结果返回给results[currentKey]。
5、 返回results的最终结果。
例:
var test = _.mapObject({start: 5, end: 12}, function(val, key) {
return val + 5;
});
console.log(test);
// {start: 10, end: 17}
5. _.pairs
_.pairs(object) 把一个对象转变为一个[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;
};
1、 运行_.keys()函数,得到obj属性的集合。
2、 定义length存在keys的长度值,定义pairs空数组。
3、 运行for循环,把obj的键和值组合为一个新的数组赋值给pairs对应的索引位置中。
4、 返回pairs的最终结果。
例:
var test = _.pairs({one: 1, two: 2, three: 3});
console.log(test);
// [["one", 1], ["two", 2], ["three", 3]]
6. _.invert
_.invert(object) 返回一个object副本,使其键(keys)和值(values)对换。对于这个操作,必须确保object里所有的值都是唯一的且可以序列号成字符串。
_.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;
};
1、 定义空对象result。
2、 运行_.keys()函数,得到obj属性的集合。
3、 运行for函数,以obj的键名作为result的值,obj的值作为result的键名,把obj的键名赋值给result。
4、 返回results的最终结果。
例:
var test = _.invert({Moe: "Moses", Larry: "Louis", Curly: "Jerome"});
console.log(test);
// {Moses: "Moe", Louis: "Larry", Jerome: "Curly"};
7. _.functions
_.functions(object)(.methods) 返回一个对象里所有的方法名, 而且是已经排序的 — 也就是说, 对象里每个方法(属性值是一个函数)的名称.
_.functions = _.methods = function(obj) {
var names = [];
for (var key in obj) {
if (_.isFunction(obj[key])) names.push(key);
}
return names.sort();
};
1、 定义空数组names。
2、 运行for in函数,遍历对象中的各属性。
3、 运行_.isFunction()函数,判断obj的值是否为函数类型,若是,则把obj对应的键名push到names中。
4、 运行sort()函数,对names数组的元素进行排序。并返回结果。
例:
var obj = {
all:function(){},
one:'one',
bindAll:function(){},
bind:function(){},
any:function(){}
};
var test = _.functions(obj);
console.log(test);
// ["all", "any", "bind", "bindAll"]
8. _.extend
var createAssigner = function(keysFunc, defaults) {
return function(obj) {
var length = arguments.length;
if (defaults) obj = Object(obj);
if (length < 2 || obj == null) return obj;
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];
}
}
return obj;
};
};
_.extend = createAssigner(_.allKeys);
1、 运行createAssigner()函数,传入参数_.allKeys。
2、 运行function(obj)函数,定义length,用于存储传入参数的长度。
3、 判断defaults是否存在,若存在,则返回与obj对应类型的对象,并赋值给obj,相当于new一个新的对象出来。
4、 判断length传入参数的长度是否小于2或者obj为空,那么直接返回结果obj。
5、 运行for循环函数,注意index是从1开始:
1) 定义source,用于存储第index个传入的参数数据。
2) 运行keysFunc(source)函数,即这里运行的函数为 _.allKeys(source),把所有的属性名,包括原型中的属性名都组成一个数组传入keys中。
3) 定义变量l,用于存储keys的长度。
4) 运行第二级的for循环
a) 把数组keys中的每个key值遍历一次并赋值给变量key。
b) 判断defaults不存在,或obj中key的值为undefined时,则把source中对应的键值对添加到obj中。
6、 返回最终结果obj。
例:
function baseCar(){
this.color = 'red';
this.size = 'big';
this.name = 'lily';
}
function extendCar(){
this.number = 'aa';
}
extendCar.prototype = new baseCar();
var instance = new extendCar();
var test = _.extend({name: 'moe'}, instance);
console.log(test);
// Object {name: "lily", number: "aa", color: "red", size: "big"}
9. _.extendOwn
_.extendOwn(destination, *sources) Alias: assign 类似于 extend,但只复制自己的属性覆盖到目标对象。(愚人码头注:不包括继承过来的属性)
_.extendOwn = _.assign = createAssigner(_.keys);
1、 运行createAssigner()函数,传入参数_.keys。
2、 运行function(obj)函数,定义length,用于存储传入参数的长度。
3、 判断defaults是否存在,若存在,则返回与obj对应类型的对象,并赋值给obj,相当于new一个新的对象出来。
4、 判断length传入参数的长度是否小于2或者obj为空,那么直接返回结果obj。
5、 运行for循环函数,注意index是从1开始:
1) 定义source,用于存储第index个传入的参数数据。
2) 运行keysFunc(source)函数,即这里运行的函数为 _.keys(source),把所有属于自己的属性名,其中不包括原型中的属性名,组成一个数组传入keys中。此处是与 _.extend()函数的最大区别。
3) 定义变量l,用于存储keys的长度。
4) 运行第二级的for循环
a) 把数组keys中的每个key值遍历一次并赋值给变量key。
b) 判断defaults不存在,或obj中key的值为undefined时,则把source中对应的键值对添加到obj中。
6、 返回最终结果obj。
例:
function baseCar(){
this.color = 'red';
this.size = 'big';
}
function extendCar(){
this.number = 'aa';
}
extendCar.prototype = new baseCar();
var instance = new extendCar();
var test = _.extendOwn({name: 'moe'}, instance);
console.log(test);
// Object {name: "moe", number: "aa"}
10. _.defaults
_.defaults(object, *defaults) 用defaults对象填充object 中的undefined属性。 并且返回这个object。一旦这个属性被填充,再使用defaults方法将不会有任何效果。
_.defaults = createAssigner(_.allKeys, true);
1、 运行的方式与_.extend函数一样,唯一的不同是 _.defaults函数会传入第二个参数true。
2、 此参数的作用是:当obj对象和source对象存在相同的key时,那么source对象中的key值将不会对obj对象相应的key值作出任何改变。
3、 但是,在_.extend函数中,若obj对象与source对象存在相同的key,那么source对象中的key值将会覆盖obj对象的key值。
例:
var iceCream = {flavor: "chocolate"};
var test = _.defaults(iceCream, {flavor: "vanilla", sprinkles: "lots"});
console.log(test);
// {flavor: "chocolate", sprinkles: "lots"}
11. _.findKey
_.findKey(object, predicate, [context]) 它类似于 _.findIndex,但是这用于检测对象的键。当predicate通过真检查时,返回第一个键名;否则返回undefined。
_.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;
}
};
1、 运行cb()函数,得到迭代函数predicate()的值
predicate=optimizeCb(predicate,context);
=function(value, index, collection) {
return predicate.call(context, value, index, collection);
};
2、 运行 _.keys(obj)函数,把obj的属性名组成数组传给keys。
3、 运行for循环,判断predicate()函数返回的结果是否为真,若为真,则返回此属性名。
例:
var obj = {
one:'a',
two:'b',
three:'c',
four:'b',
five:'d',
six:'e'
};
var test = _.findKey(obj,function(value,key){
return value == 'b';
});
console.log(test);
// two
12. _.pick
_.pick(object, *keys) 返回一个object副本,只过滤出keys(有效的键组成的数组)参数指定的属性值。或者接受一个判断函数,指定挑选哪个key。
var keyInObj = function(value, key, obj) {
return key in obj;
};
_.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]);
keys = _.allKeys(obj);
} else {
iteratee = 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;
}
return result;
});
1、 运行restArgs()函数,由于function传入了两个参数:obj、keys,故restArgs()函数中的参数startIndex=1。所以运行返回的函数:
func.call(this, arguments[0], rest);
2、 定义空数组result,定义变量iteratee,初始值为rest的第一个值keys[0]。
3、 判断obj是否为空,若为空则直接返回result。
4、 判断iteratee是否为函数类型,若是则
1) 判断数组keys的长度是否大于1,若是则运行optimizeCb()函数。
2) 运行 _.allKeys(obj)函数,把obj的所有属性名,包括原型中的属性全部组成一个数组赋值给keys。
5、 若iteratee不是函数类型,则
1) 把iteratee设置为keyInObj函数,此函数作用是判断属性key是否存在与obj对象中。
2) 运行flatten()函数,把keys数组减少到一维数组,且保留非数组类型的数据。
3) 返回与obj对应类型的对象,并赋值给obj。
6、 运行for函数:
1) 遍历数组keys,并把相应索引i的值赋值给key
2) 定义变量存储obj对应key的值。
3) 判断iteratee()函数返回的结果是否为真,若为真,则更新result的值。
7、 返回最终结果result。
例:
var obj = {name: 'moe', age: 50, userid: 'moe1'};
var test = _.pick(obj, 'name', 'age');
console.log(test);
// {name: 'moe', age: 50}
var test2 = _.pick(obj, function(value, key, object) {
return _.isNumber(value);
});
console.log(test2);
// {age: 50}
13. _.omit
_.omit(object, *keys) 返回一个object副本,只过滤出除去keys(有效的键组成的数组)参数指定的属性值。 或者接受一个判断函数,指定忽略哪个key。
_.omit = restArgs(function(obj, keys) {
var iteratee = keys[0], context;
if (_.isFunction(iteratee)) {
// _.negate(predicate)
// 返回一个新的predicate函数的否定版本。
iteratee = _.negate(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);
});
1、 运行restArgs()函数,由于function传入了两个参数:obj、keys,故restArgs()函数中的参数startIndex=1。所以运行返回的函数:
func.call(this, arguments[0], rest);
2、 判断iteratee是否为函数类型,若是则
1) 运行_.negate(iteratee)函数,把iteratee的运行结果取反后重新赋值给iteratee。
2) 判断数组keys的长度是否大于1,若是则把keys[1]的值赋值给context。
3、 若iteratee不是函数类型,则
1) 运行map()遍历函数,先运行flatten()函数,把keys数组减少到一维数组,且保留非数组类型的数据。再遍历keys数组,把保留字符串类型的数据并组成新的数组赋值给keys。
2) 定义函数iteratee,检测在keys中不包含key的数据,并返回布尔变量。
3) 运行_.pick()函数。并返回最终结果。
例:
var obj = {name: 'moe', age: 50, userid: 'moe1'};
var test = _.omit(obj, 'name', 'age');
console.log(test);
// {userid: 'moe1'}
var test2 = _.omit(obj, function(value, key, object) {
return _.isNumber(value);
});
console.log(test2);
// {name: 'moe', userid: 'moe1'}
14. _.create
_.create(prototype, props) 创建具有给定原型的新对象, 可选附加props 作为 own的属性。 基本上,和Object.create一样, 但是没有所有的属性描述符。
var Ctor = function(){};
var nativeCreate = Object.create;
var baseCreate = function(prototype) {
if (!_.isObject(prototype)) return {};
if (nativeCreate) return nativeCreate(prototype);
Ctor.prototype = prototype;
var result = new Ctor;
Ctor.prototype = null;
return result;
};
_.create = function(prototype, props) {
var result = baseCreate(prototype);
if (props) _.extendOwn(result, props);
return result;
};
1、 运行baseCreate()函数,传入参数prototype。
1) 若prototype不是对象类型,则直接返回空对象。
2) 若nativeCreate存在,即Object.create存在,则返回nativeCreate(prototype)对象。
其中Object.create(prototype, descriptors)函数是创建一个具有指定原型且可选择性地包含指定属性的对象。第一个参数是要继承的原型,如果不是一个子函数,可以传一个null,第二个参数是对象的属性描述符,这个参数是可选的。
3)若上面两个添加都不满足,则往下运行,定义Ctor的原型为传入参数prototype。定义result为新的Ctor函数。返回result。
2、 此处由于存在nativeCreate为函数function create() { [native code] },故
result = nativeCreate(prototype)
= Object.create(prototype)
= {}
3、 若有传入第二个参数props,则运行_.extendOwn(),把props对象扩展到result中。
4、 返回最终结果result。
例:
function baseCar(){
this.color = 'red';
this.size = 'big';
}
function extendCar(){
this.number = 'aa';
}
extendCar.prototype = new baseCar();
var test = _.create(extendCar.prototype, {name: "Moe"});
console.log(test); // baseCar {name: "Moe"}
console.log(test.name); // Moe
15. _.clone
_.clone(object) 创建 一个浅复制(浅拷贝)的克隆object,即副本。任何嵌套的对象或数组都通过引用拷贝,而不是复制原对象。
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
1、 若obj不是对象类型,则直接返回obj对象。
2、 若obj为数组类型,则通过slice()函数,返回obj数组。若obj不为数组类型,则运行_.extend()函数,把obj对象扩展到空对象中。
例:
var test = _.clone({name: 'moe'});
console.log(test);
// {name: 'moe'}
var test2 = _.clone([1, 2, 3, 4, 5]);
console.log(test2);
// [1, 2, 3, 4, 5]
16. _.tap
_.tap(object, interceptor) 用 object作为参数来调用函数interceptor,然后返回object。这种方法的主要意图是作为函数链式调用 的一环, 为了对此对象执行操作并返回对象本身。
_.tap = function(obj, interceptor) {
interceptor(obj);
return obj;
};
1、 以obj作为为参数,运行interceptor函数,并返回obj本身。
例:
var test = _.chain([1,2,3,200])
.filter(function(num) { return num % 2 == 0; })
.tap(alert) // [2, 200] (alerted)
.map(function(num) { return num * num })
.value();
console.log(test);
//[4, 40000]
var test2 = _.tap({one:'a',two:'b'},_.keys);
console.log(test2);
//{one:'a',two:'b'}
17. _.has
_.has(object, key) 对象是否包含给定的键?等同于object.hasOwnProperty(key),但是使用hasOwnProperty 函数的一个安全引用,以防意外覆盖。
var ObjProto = Object.prototype;
var hasOwnProperty = ObjProto.hasOwnProperty;
_.has = function(obj, key) {
return obj != null && hasOwnProperty.call(obj, key);
};
1、 hasOwnProperty函数方法是返回一个布尔值,指出一个对象是否具有指定名称的属性。
使用方法:
object.hasOwnProperty(proName
其中参数object是必选项。一个对象的实例。proName是必选项。一个属性名称的字符串值。此方法无法检查该对象的原型链中是否具有该属性;该属性必须是对象本身的一个成员。
2、 判断obj对象不为空且使用hasOwnProperty.call方法,检测key属性存在于obj对象中,则返回true。
3、 若不满足这两个条件中的任一个条件,则返回false。
例:
var test = _.has({a: 1, b: 2, c: 3}, "b");
console.log(test);
//true
18. _.property
_.property(key) 返回一个函数,这个函数返回任何传入的对象key的属性值。
var property = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};
_.property = property;
1、 判断obj为null时,返回undefined,若不为空,则返回obj对应的key值。
例:
var obj = {name: 'moe'};
var test = _.property('name')(obj)
console.log('moe' === test);
//true
19. _.propertyOf
_.propertyOf(object) 和 _.property相反。需要一个对象,并返回一个函数,这个函数将返回一个提供的属性的值。
_.propertyOf = function(obj) {
return obj == null ? function(){} : function(key) {
return obj[key];
};
};
1、 判断obj是否为null,若是则返回空的函数,若否则运行function并返回obj对应的key值。
例:
var stooge = {name: 'moe'};
var test = _.propertyOf(stooge)('name');
console.log(test);
//'moe'
20. _.matcher
_.matcher(attrs) 返回一个断言函数,这个函数会给你一个断言可以用来辨别给定的对象是否匹配attrs指定键/值属性。
_.matcher = _.matches = function(attrs) {
attrs = _.extendOwn({}, attrs);
return function(obj) {
// _.isMatch(object, properties)
// 告诉你properties中的键和值是否包含在object中。
return _.isMatch(obj, attrs);
};
};
1、 运行 _.extendOwn()函数,把arrts对象扩展到空对象中。并重新赋值给attrs。
2、 运行 _.isMatch()函数,判断attrs是否存在与obj中。若存在则返回true,否则返回false。
例:
var list = [
{selected: true, visible: true},
{selected: true, visible: true, unable: false},
{selected: false, visible: true, unable: false},
];
var obj = {selected: true, visible: true, unable: false}
var ready = _.matcher({selected: true, visible: true});
var test = _.matcher({selected: true, visible: true})(obj);
var readyToGoList = _.filter(list, ready);
console.log(test); //true
console.log(readyToGoList);
/*
[
{selected: true, visible: true},
{selected: true, visible: true, unable: false},
]
*/