重拾JavaScript(四)-对象

对象

定义

本质上是一种无序的数据集合,由若干个“键值对”(又称为:成员)(key-value)构成。其中,键值对,包括键名(key,即成员的名称)、键值(value,即成员的值)。

1
2
3
var obj = {
p: 'Hello World'
};

上面代码中,大括号就定义了一个对象,它被赋值给变量obj。这个对象内部包含一个键值对(又称为“成员”),p是“键名”(成员的名称),字符串Hello World是“键值”(成员的值)。键名与键值之间用冒号分隔。如果对象内部包含多个键值对,每个键值对之间用逗号分隔。


基本使用

首先要定义一个对象

1、一般写法:

1
2
3
4
5
6
7
8
var company = {
name:'tennnat'
}
//undefined

/* 调用属性name,输出值:"tennant" */
company
// {name: "tennant"}

或者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var company = {
name: '世界你好',
age: 3,
sayHello: function(){
console.log('hello world')
}
}
// undefined

/* 分别调用各属性和值 */
company.name
// "世界你好"
company.age
// 3
company.sayHello()
// hello world

2、对象里属性值的获取写法:

1
2
方法一:console.log(company.name)//变量名.属性名    以此来调用值
方法二:console.log(company['name'])//变量名['字符串']

3、新增属性,直接赋值

1
2
3
4
5
6
company.addr = '宁波市'
//"杭州市"

/* 验证:调用变量,输出:{属性:值} */
company
//{name: "世界你好", age: 3, sayHello: ƒ, addr: "宁波市"}

或者

1
2
3
4
company['business'] = '学习'
//"学习"
company
// {name: "世界你好", age: 3, sayHello: ƒ, addr: "宁波市", business: "学习"}

细节

键名

对象的所有键名都是字符串,所以加不加引号都可以。

1
2
3
var o = {
'p': 'Hello World'
};

如果键名是数值,会被自动转为字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var o ={
1: 'a',
3.2: 'b',
1e2: true,
1e-2: true,
.234: true,
0xFF: true
};

o
// Object {
// 1: "a",
// 3.2: "b",
// 100: true,
// 0.01: true,
// 0.234: true,
// 255: true
// }

如果键名不符合标识名的条件(比如第一个字符为数字,或者含有空格或运算符),也不是数字,则必须加上引号,否则会报错。

1
2
3
4
5
var o = {
'1p': "Hello World",
'h w': "Hello World",
'p+q': "Hello World"
};

上面对象的三个键名,都不符合标识名的条件,所以必须加上引号。

注意,JavaScript的保留字可以不加引号当作键名。

1
2
3
4
var obj = {
for: 1,
class: 2
};

对象的引用

如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。

1
2
3
4
5
6
7
8
var o1 = {};
var o2 = o1;

o1.a = 1;
o2.a // 1

o2.b = 2;
o1.b // 2

上面代码中,o1o2指向同一个对象,因此为其中任何一个变量添加属性,另一个变量都可以读写该属性。

此时,如果取消某一个变量对于原对象的引用,不会影响到另一个变量。

1
2
3
4
5
var o1 = {};
var o2 = o1;

o1 = 1;
o2 // {}

上面代码中,o1o2指向同一个对象,然后o1的值变为1,这时不会对o2产生影响,o2还是指向原来的那个对象。

但是,这种引用只局限于对象,如果两个变量指向同一个原始类型的值。那么,变量这时都是值的拷贝。

1
2
3
4
5
var x = 1;
var y = x;

x = 2;
y // 1

上面的代码中,当x的值发生变化后,y的值并不变,这就表示yx并不是指向同一个内存地址。

表达式还是语句

对象采用大括号表示,这导致了一个问题:如果行首是一个大括号,它到底是表达式还是语句?

1
{ foo: 123 }

JavaScript 引擎读到上面这行代码,会发现可能有两种含义。第一种可能是,这是一个表达式,表示一个包含foo属性的对象;第二种可能是,这是一个语句,表示一个代码区块,里面有一个标签foo,指向表达式123

为了避免这种歧义,V8 引擎规定,如果行首是大括号,一律解释为对象。不过,为了避免歧义,最好还是在大括号前加上圆括号。

1
({ foo: 123})

读取属性

读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符。

1
2
3
4
5
6
var o = {
p: 'Hello World'
};

o.p // "Hello World"
o['p'] // "Hello World"

上面代码分别采用点运算符和方括号运算符,读取属性p

请注意,如果使用方括号运算符,键名必须放在引号里面,否则会被当作变量处理。但是,数字键可以不加引号,因为会被当作字符串处理。

1
2
3
4
5
6
var o = {
0.7: 'Hello World'
};

o['0.7'] // "Hello World"
o[0.7] // "Hello World"

方括号运算符内部可以使用表达式。

1
2
o['hello' + ' world']
o[3 + 3]

数值键名不能使用点运算符(因为会被当成小数点),只能使用方括号运算符。

1
2
3
4
obj.0xFF
// SyntaxError: Unexpected token
obj[0xFF]
// true

上面代码的第一个表达式,对数值键名0xFF使用点运算符,结果报错。第二个表达式使用方括号运算符,结果就是正确的。

查看所有属性

查看对象本身的所有属性,可用Object.key 或使用for。。。in循环遍历

(1)方法一:Object.keys

1
2
3
4
5
6
7
var o = {
key1: 1,
key2: 2
};

Object.keys(o);
-->(2) ["key1", "key2"]

(2)方法二:for。。。in循环遍历

1
2
3
4
5
6
7
8
var obj = {a: 1, b: 2, c: 3};

for (var i in obj) {
console.log(obj[i]);
}
// 1
// 2
// 3

delete命令

(1)删除一个存在的属性:

两种情况:

只能删除属性

1
2
3
4
5
6
7
8
9
var o = {p: 1};
Object.keys(o)
// ["p"]

delete o.p
->o.p

Object.keys(o)
// []

不能删除var命令声明的变量

var声明的全局变量都是顶层对象的属性,而且默认不得删除。

1
2
3
var p = 1;
delete p // false
delete window.p // false

(2)删除一个不存在的属性:

delete不报错,而且返回true

不能根据delete命令的结果,认定某个属性是存在的,只能保证读取这个属性肯定得到undefined

1
2
var o = {};
delete o.p // true