这不是 JavaScript 中的数组
虽然 PHP 将其称为 关联数组,但它在 JavaScript 中只是一个 object(也称为 hash),并且是一个 hash table或 散列图 在理论计算机科学中。
在...中使用
话虽如此,for ... in 指令迭代对象的键,而不是其值:
var clients = {
clientid1: { name: "John Doe", age: 42 },
clientid2: { name: "Jane Doe", age: 34 }
};
for (var key in clients) {
console.log(key);
}
Console output:
"clientid1"
"clientid2"
因此,要访问客户端哈希的 值,例如 socket 属性,请像这样迭代它:
for (var serial in clients) {
clients[serial].socket.sendSomethingViaTheSocket(...);
}
好吧,for ... in 似乎可行,为什么还有其他解决方案存在?
由于 JavaScript 是一种具有 prototypal inheritance 的非常动态的语言,您可以添加、更改和删除几乎每个 JavaScript 对象的属性,并向对象原型添加新的属性和方法(哪些方法和属性的“蓝图”某个“类”的对象具有)。
这意味着这是完全有效的 JavaScript 代码:
HTMLElement.prototype.isParagraph = false;
HTMLParagraphElement.prototype.isParagraph = true;
var myElement = document.querySelector(...);
if (myElement.isParagraph) {
// that's possible in javascript!
}
由于 JavaScript 中的大多数对象都允许扩展原型,因此 Object 原型也是可能的,它是所有非值对象的祖先。假设您想定义自己的方法来向对象添加属性(为什么会决定这样做):
Object.prototype.addProperty = function (name, value) {
this[name] = value;
};
var exampleCustomer = { name: "John Doe" };
exampleCustomer.addProperty("age", 42);
console.log(exampleCustomer.age); // logs 42 on the console
但这意味着从现在开始,JavaScript 中的每个 对象都有一个addProperty 方法,因此当你使用for ... in 时,你不会得到你期望的结果:
for (var key in exampleCustomer) {
console.log('key ', key, ' equals ', exampleCustomer[key]);
}
Console output:
key name equals John Doe
key age equals 42
key addProperty equals function (name, value) { ... }
^ oops!
您现在会发现for ... in 还会迭代在您要迭代的对象的原型链上设置的属性。这意味着使用for ... in 很好当且仅当,您可以确定没有人更改您的原型。
ES5:Object.defineProperty 和 Object.defineProperties
从 ES5 开始,有两种方法可以缓解这个问题。认识Object.defineProperty 和Object.defineProperties。如果您需要在原型上定义方法,请使用以下方法:
Object.defineProperty(
Object.prototype, // the object the property should be defined on
"addProperty", // name of the property
{
enumerable: false, // should the property show up in for ... in / Object.keys()?
writable: true, // can the value of this property be changed with obj.addProperty = ..?
configurable: true, // can this property be changed with another defineProperty call?
value: function (name, value) { this[name] = value; }
}
);
当您使用configurable: false 定义属性时,它不会显示在for ... in 循环中。
解决原型问题的解决方案(原生 JavaScript)
如果您不想使用库来解决上述问题,您可以使用原生 JavaScript 中包含的一些方法。
Object.keys 和Array.prototype.forEach:
Object.keys(clients).forEach(function (key) {
clients[key].socket.sendSomething();
});
// will NOT iterate over prototype-inherited properties
ECMAScript2015/ECMAScript6 for ... of:(适用于 node 4+ 和新浏览器,无 IE)
for (let clientid of clients) {
clients[clientid].socket.sendSomething();
}
// 也不会迭代原型继承的属性
您还可以在 node 4+ 中使用Array.prototype.map 和 箭头函数 以非常可读的方式获取所有客户端的套接字:
let clientSockets = Object.keys(clients).map(k => clients[k].socket);
// clientSockets contains the socket of all clients now
解决原型问题(库)的解决方案
下划线/lodash:
_.each(clients, function (client) {
client.socket.sendSomething();
});
jQuery:
$.each(clients, function (id, client) {
client.socket.something();
});
AngularJS:
angular.forEach(clients, function (client, id) { // order is different than with jQuery!
client.socket.something();
});