在我们日常开发中,对象的使用频率很高,我们计算数组的长度是非常方便的,但是如何计算对象的长度呢?
假如我们有一个图书馆的项目,项目中有一组图书和作者,像下面这样:
var bookAuthors = {
"Farmer Giles of Ham": "J.R.R. Tolkien",
"Out of the Silent Planet": "C.S. Lewis",
"The Place of the Lion": "Charles Williams",
"Poetic Diction": "Owen Barfield"
};
我们分析现在的需求,我们给一个API发送数据,但是书的长度不能超过100,因此我们需要在发送数据之前计算在一个对象中总共有多少本书。那么我们总怎么做呢?我们可能会这样做:function countProperties (obj) {
var count = 0;
for (var property in obj) {
if (Object.prototype.hasOwnProperty.call(obj, property)) {
count++;
}
}
return count;
}
var bookCount = countProperties(bookAuthors);
// Outputs: 4
console.log(bookCount);
这是可以实现的,幸运的是Javascript提供了一个更改的方法来计算对象的长度:
var bookAuthors = {
"Farmer Giles of Ham": "J.R.R. Tolkien",
"Out of the Silent Planet": "C.S. Lewis",
"The Place of the Lion": "Charles Williams",
"Poetic Diction": "Owen Barfield"
};
var arr = Object.keys(bookAuthors);
//Outputs: Array [ "Farmer Giles of Ham", "Out of the Silent Planet", "The Place of the Lion", "Poetic Diction" ]
console.log(arr);
//Outputs: 4
console.log(arr.length);
下面我们来对数组使用keys方法:
var arr = ["zuojj", "benjamin", "www.zuojj.com"];
//Outputs: ["0", "1", "2"]
console.log(Object.keys(arr));
//Outputs: 3
console.log(arr.length);
Object.keys() 方法会返回一个由给定对象的所有可枚举自身属性的属性名组成的数组,数组中属性名的排列顺序和使用for-in循环遍历该对象时返回的顺序一致(两者的主要区别是 for-in 还会遍历出一个对象从其原型链上继承到的可枚举属性)。方法的兼容性及详情请戳这里。
Object.keys()方法,只能使用在现代浏览器,IE8及以下是不支持的,如果想支持IE低版本,可以使用es5-shim来兼容。其中代码如下:
// ES5 15.2.3.14
// http://es5.github.com/#x15.2.3.14
// http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
var hasDontEnumBug = !({'toString': null}).propertyIsEnumerable('toString'),
hasProtoEnumBug = (function () {}).propertyIsEnumerable('prototype'),
dontEnums = [
"toString",
"toLocaleString",
"valueOf",
"hasOwnProperty",
"isPrototypeOf",
"propertyIsEnumerable",
"constructor"
],
dontEnumsLength = dontEnums.length;
defineProperties(Object, {
keys: function keys(object) {
var isFn = isFunction(object),
isArgs = isArguments(object),
isObject = object !== null && typeof object === 'object',
isStr = isObject && isString(object);
if (!isObject && !isFn && !isArgs) {
throw new TypeError("Object.keys called on a non-object");
}
var theKeys = [];
var skipProto = hasProtoEnumBug && isFn;
if (isStr || isArgs) {
for (var i = 0; i < object.length; ++i) {
theKeys.push(String(i));
}
} else {
for (var name in object) {
if (!(skipProto && name === 'prototype') && owns(object, name)) {
theKeys.push(String(name));
}
}
}
if (hasDontEnumBug) {
var ctor = object.constructor,
skipConstructor = ctor && ctor.prototype === object;
for (var j = 0; j < dontEnumsLength; j++) {
var dontEnum = dontEnums[j];
if (!(skipConstructor && dontEnum === 'constructor') && owns(object, dontEnum)) {
theKeys.push(dontEnum);
}
}
}
return theKeys;
}
});
var keysWorksWithArguments = Object.keys && (function () {
// Safari 5.0 bug
return Object.keys(arguments).length === 2;
}(1, 2));
var originalKeys = Object.keys;
defineProperties(Object, {
keys: function keys(object) {
if (isArguments(object)) {
return originalKeys(ArrayPrototype.slice.call(object));
} else {
return originalKeys(object);
}
}
}, !keysWorksWithArguments);