预备工作
JavaScript只有一种数据类型,它可以包含多个值:
对象
. 安
数组
是一种特殊形式的物体。
(普通)物体具有
{key: value, key: value, ...}
数组的形式是
[value, value, ...]
数组和对象都公开
key -> value
结构。数组中的键必须是数字的,而任何字符串都可以用作对象中的键。键值对也称为
“地产”
.
属性也可以使用
点表示法
const value = obj.someProperty;
或
括号记法
,如果属性名不是有效的javascript
identifier name
[spec]
,或者名称是变量的值:
// the space is not a valid character in identifier names
const value = obj["some Property"];
// property name as variable
const name = "some Property";
const value = obj[name];
出于这个原因,只能使用括号符号来访问数组元素:
const value = arr[5]; // arr.5 would be a syntax error
// property name / index as variable
const x = 5;
const value = arr[x];
等待。。。json呢?
json是数据的文本表示,就像xml、yaml、csv等。要处理这些数据,首先必须将其转换为JavaScript数据类型,即数组和对象(以及如何与刚才解释的那些数据一起工作)。问题中解释了如何解析json
Parse JSON in JavaScript?
.
进一步阅读材料
如何访问数组和对象是基本的javascript知识,因此最好阅读
MDN JavaScript Guide
,尤其是部分
访问嵌套数据结构
嵌套数据结构是指引用其他数组或对象的数组或对象,即其值是数组或对象。这样的结构可以通过连续地应用点或括号符号来访问。
下面是一个例子:
const data = {
code: 42,
items: [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}]
};
假设我们想访问
name
第二个项目。
以下是我们如何一步一步地做这件事:
正如我们所看到的
data
是一个对象,因此我们可以使用点标记来访问它的属性。这个
items
属性的访问方式如下:
data.items
值是一个数组,要访问它的第二个元素,必须使用括号表示法:
data.items[1]
这个值是一个对象,我们再次使用点符号来访问
名称
财产。所以我们最终得到:
const item_name = data.items[1].name;
或者,我们可以对任何属性使用括号表示法,特别是如果名称包含的字符可能会使其对点表示法无效:
const item_name = data['items'][1]['name'];
我想进入一处房产,但我只能
undefined
回来?
大多数时候当你得到
未定义
,对象/数组没有具有该名称的属性。
const foo = {bar: {baz: 42}};
console.log(foo.baz); // undefined
使用
console.log
或
console.dir
检查对象/数组的结构。您试图访问的属性可能实际上是在嵌套对象/数组上定义的。
console.log(foo.bar.baz); // 42
如果属性名称是动态的,而我事先不知道它们怎么办?
如果属性名称未知或我们想访问数组的对象/元素的所有属性,则可以使用
for...in
[MDN]
对象和
for
[MDN]
循环数组以迭代所有属性/元素。
物体
遍历的所有属性
数据
我们可以迭代
对象
像这样:
for (const prop in data) {
// `prop` contains the name of each property, i.e. `'code'` or `'items'`
// consequently, `data[prop]` refers to the value of each property, i.e.
// either `42` or the array
}
根据对象来自何处(以及您想要做什么),您可能需要在每次迭代中测试该属性到底是对象的属性,还是继承的属性。你可以这样做
Object#hasOwnProperty
[MDN]
.
作为替代
为了……
具有
hasOwnProperty
,你可以使用
Object.keys
[MDN]
得到一个
属性名称数组
:
Object.keys(data).forEach(function(prop) {
// `prop` is the property name
// `data[prop]` is the property value
});
数组
遍历
data.items
数组
我们使用
对于
循环:
for(let i = 0, l = data.items.length; i < l; i++) {
// `i` will take on the values `0`, `1`, `2`,..., i.e. in each iteration
// we can access the next element in the array with `data.items[i]`, example:
//
// var obj = data.items[i];
//
// Since each element is an object (in our example),
// we can now access the objects properties with `obj.id` and `obj.name`.
// We could also use `data.items[i].id`.
}
你也可以用
为……
要在数组上迭代,但有一些原因需要避免:
Why is 'for(var item in list)' with arrays considered bad practice in JavaScript?
.
随着ecmascript 5浏览器支持的增加,array方法
forEach
[MDN]
也成为一个有趣的选择:
data.items.forEach(function(value, index, array) {
// The callback is executed for each element in the array.
// `value` is the element itself (equivalent to `array[index]`)
// `index` will be the index of the element in the array
// `array` is a reference to the array itself (i.e. `data.items` in this case)
});
在支持ES2015(ES6)的环境中,还可以使用
for...of
[MDN]
循环,它不仅适用于数组,而且适用于任何
iterable
:
for (const item of data.items) {
// `item` is the array element, **not** the index
}
在每次迭代中,
为了……
直接给了我们iterable的下一个元素,没有“索引”可以访问或使用。
如果我不知道数据结构的“深度”呢?
除了未知键之外,数据结构的“深度”(即有多少嵌套对象)也可能未知。如何访问深度嵌套的属性通常取决于确切的数据结构。
但是,如果数据结构包含重复模式,例如二叉树的表示,则解决方案通常包括
recursively
[Wikipedia]
访问数据结构的每个级别。
下面是获取二叉树的第一个叶节点的示例:
function getLeaf(node) {
if (node.leftChild) {
return getLeaf(node.leftChild); // <- recursive call
}
else if (node.rightChild) {
return getLeaf(node.rightChild); // <- recursive call
}
else { // node must be a leaf node
return node;
}
}
const first_leaf = getLeaf(root);
const root = {
leftChild: {
leftChild: {
leftChild: null,
rightChild: null,
data: 42
},
rightChild: {
leftChild: null,
rightChild: null,
data: 5
}
},
rightChild: {
leftChild: {
leftChild: null,
rightChild: null,
data: 6
},
rightChild: {
leftChild: null,
rightChild: null,
data: 7
}
}
};
function getLeaf(node) {
if (node.leftChild) {
return getLeaf(node.leftChild);
} else if (node.rightChild) {
return getLeaf(node.rightChild);
} else { // node must be a leaf node
return node;
}
}
console.log(getLeaf(root).data);
访问具有未知键和深度的嵌套数据结构的更通用的方法是测试值的类型并相应地执行操作。
下面是一个示例,它将嵌套数据结构中的所有基元值添加到数组中(假设它不包含任何函数)。如果我们遇到一个对象(或数组),我们只需调用
toArray
再次调用该值(递归调用)。
function toArray(obj) {
const result = [];
for (const prop in obj) {
const value = obj[prop];
if (typeof value === 'object') {
result.push(toArray(value)); // <- recursive call
}
else {
result.push(value);
}
}
return result;
}
const data = {
code: 42,
items: [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}]
};
function toArray(obj) {
const result = [];
for (const prop in obj) {
const value = obj[prop];
if (typeof value === 'object') {
result.push(toArray(value));
} else {
result.push(value);
}
}
return result;
}
console.log(toArray(data));
帮手
由于复杂对象或数组的结构不一定是显而易见的,所以我们可以检查每个步骤的值来决定如何进一步移动。
console.log
[MDN]
和
console.dir
[MDN]
帮我们做这个。例如(Chrome控制台的输出):
> console.log(data.items)
[ Object, Object ]
我们看到了
数据项目
是一个包含两个元素的数组,这两个元素都是对象。在Chrome控制台中,对象甚至可以立即展开和检查。
> console.log(data.items[1])
Object
id: 2
name: "bar"
__proto__: Object
这告诉我们
data.items[1]
是一个物体,展开后我们看到它有三个性质,
id
,
名称
和
__proto__
. 后者是用于对象的原型链的内部属性。不过,原型链和继承超出了这个答案的范围。