除了像number和string这样的原始数据类型之外,JavaScript 中还有一种重要的数据类型,叫做object。
对象是键值对的集合。
因此,它们可以充当其他语言中常说的映射或字典。
在其他语言里,映射中的所有值通常必须是同一种数据类型。 而在 JavaScript 中,只有键的类型受到限制:它必须是字符串。 同一个对象里的值可以有不同的类型。 它们可以是数字这样的原始类型,也可以是数组、其他对象,甚至函数。 这让对象非常灵活,因此它也是 JavaScript 中面向对象编程(OOP)的核心概念。
接下来,我们会把重点放在作为集合或映射的对象上。对象的其他用法会在其他概念中介绍,比如类。
使用大括号可以创建对象字面量。 你也可以直接在里面写一些条目(键值对)。 写法是先写键,然后是一个冒号和值。
const emptyObject = {};
const obj = {
favoriteNumber: 42,
greeting: 'Hello World',
useGreeting: true,
address: {
street: 'Trincomalee Highway',
city: 'Batticaloa',
},
fruits: ['melon', 'papaya'],
addNumbers: function (a, b) {
return a + b;
},
};
在 JavaScript 中,最后一项后面的尾随逗号是可选的。 你可以在这里了解使用它的好处。
你可能会奇怪,既然键应该是字符串,为什么没有用引号括起来。 这是一种简写形式。 如果键符合 JavaScript 标识符的命名规则,就可以省略引号。 如果键名里含有特殊字符,就需要使用常规的字符串写法。
const obj = {
'1keyStartsWithNumber': '...',
'key/with/slashes': '...',
'key-with-dashes': '...',
'key with spaces': '...',
'#&()[]{}èä樹keyWithSpecialChars': '...',
};
声明对象时,你常常想把已有的变量加进去。
这通常会写成name: name这样的键值对。
JavaScript 有一种简写形式,允许只写name。
在对象里定义函数也有简写形式。
这时可以省略冒号和function关键字。
const x = 1;
const y = 2;
const obj = {
x: x,
y: y,
calcSum: function (a, b) {
return a + b;
},
};
// Can be shortened to ...
const obj = {
x,
y,
calcSum(a, b) {
return a + b;
},
};
如果想创建对象字面量,而键名保存在变量里或由表达式得出,就需要把这个变量或表达式用方括号括起来。
const key = 'greeting';
const obj = {
[key]: 'hello world',
};
console.log(obj);
// => { greeting: 'hello world' }
为给定的键读取值有两种方式:点表示法和方括号表示法。
const obj = { greeting: 'hello world' };
obj.greeting;
// => hello world
obj['greeting'];
// => hello world
// Bracket notation also works with variables.
const key = 'greeting';
obj[key];
// => hello world
把点表示法当作简写,限制和省略引号一样。 只有当键符合标识符的命名规则时才能这样写。
如果尝试读取对象里不存在的键,JavaScript 会返回undefined。
更多细节请见 Null 与 Undefined。
如果想从嵌套对象里读取值,可以逐级串联键。
const obj = {
address: {
street: 'Trincomalee Highway',
city: 'Batticaloa',
},
};
obj.address.city;
// => 'Batticaloa'
obj['address']['city'];
// => 'Batticaloa'
// Notations can also be mixed.
obj.address['city'];
// => 'Batticaloa'
可以使用赋值运算符=来添加或修改值。
同样,点表示法和方括号表示法都可以用。
const obj = { greeting: 'hello world' };
obj.greeting = 'Hi there!';
obj['greeting'] = 'Welcome.';
obj.newKey1 = 'new value 1';
obj['new key 2'] = 'new value 2';
const key = 'new key 3';
obj[key] = 'new value 3';
可以使用delete关键字从对象里删除一个键值对。
const obj = {
key1: 'value1',
key2: 'value2',
};
delete obj.key1;
delete obj['key2'];
注意,虽然读取不存在的键会返回undefined,但把某个键设为undefined并不会删除这个条目。
这时应该始终使用delete。
可以用hasOwnProperty方法检查对象里是否存在某个键。
const obj = { greeting: 'hello world' };
obj.hasOwnProperty('greeting');
// => true
obj.hasOwnProperty('age');
// => false
对于继承来的键,hasOwnProperty会返回false。这通常正是你想要的行为。
如果想让存在性检查也包括继承来的键,可以改用 in运算符。
有一种特殊的for...in循环,可以迭代对象的所有键。
const obj = {
name: 'Ali',
age: 65,
};
for (let key in obj) {
console.log(key, obj[key]);
}
// name Ali
// age 65
看起来for...in似乎总是按键出现或被添加到对象里的顺序(插入顺序)访问它们。
不过,你不应该依赖这一点,因为在某些特殊情况下,顺序会和插入顺序不同。
写代码时始终假设顺序是任意的。
如果需要可靠的顺序,请使用Map或嵌套的[数组][concept-arrays],而不是对象。
如果对象有继承来的键,for…in循环就会有一个坑。
for…in循环也会访问这些键。
通常你希望避免这种情况,所以需要像下面这样跳过它们。
for (let key in obj) {
if (!obj.hasOwnProperty(key)) {
continue;
}
// ...
}
JavaScript 还有一种for...of循环,例如用来迭代数组。
需要注意的是,for...in和for...of不能互换使用,详情请见 MDN 文档。
内置对象Object提供了辅助方法,能把给定对象的所有键、值或条目以数组的形式取出。
const obj = {
name: 'Ali',
age: 65,
};
Object.keys(obj);
// => [ 'name', 'age' ]
Object.values(obj);
// => [ 'Ali', 65 ]
Object.entries(obj);
// => [ [ 'name', 'Ali' ], [ 'age', 65 ] ]
你可能已经注意到,JavaScript 里的空对象并不是完全空的。
比如,它包含hasOwnProperty方法,以及toString等其他方法。
通常这不会带来任何问题,但如果你确实需要创建一个真正空的对象,可以使用通过Object.create(null)创建的空对象。