原文地址:https://wangdoc.com/javascript/
概述
简单的说,对象就是一组键值对的集合。
键名
对象的所有键名都是字符串(ES6又引入了Symbol值也可以作为键名),所以加不加引号都可以。但是如果键名不符合标识符的条件,必须要加上引号,否则会报错。属性值还可以是对象,就形成了链式引用。
var object1 = {};
var object2 = {bar : "hello"};
object1.foo = object2;
object1.foo.bar // "hello"
上面的代码直接对object2对象的foo属性赋值,结果就在运行时创建了foo属性。
对象的引用
如果不同的变量指向同一个对象,那么它们都是这个对象的引用,也就是说指向了同一块内存。修改其中一个变量,会影响到其他的所有变量,如果取消一个变量对原对象的引用,不会影响到其他的变量。
var object1 = {};
var object2 = object1;
object1.a = 1;
object2.a // 1
object1 = 1;
object2 // {}
但是这种引用只局限于对象,如果两个变量指向同一个原始类型的值。那么,变量是值得拷贝。
var x = 1;
var y = x;
x = 2;
y // 1
表达式还是语句
对象采用大括号表示,这就导致了一个问题,如果行首是一个大括号,它到底是表达式还是语句?
{ foo: 123 }
JavaScript引擎读到上面的这行代码,会发现可能有两种含义。
- 这是一个表达式,表示一个包含foo属性的对象;
- 这是一个代码块,label为foo,指向表达式123。
为了避免这种歧义,V8引擎规定,如果行首是大括号,一律解释为对象。不过为了避免歧义,最好加上小括号。这种差异在eval语句中反应的最为明显。
({ foo: 123 })
eval("{foo: 123}") // 123 代码块
eval("({foo: 123})") // {foo: 123} 对象
属性的操作
属性的访问
有两种方式
- 点运算符
- 方括号运算符
var obj = {
p: "Hello World!"
};
obj.p // "Hello World!"
obj["p"] // "Hello World!"
注意使用方括号运算符时,属性名必须加引号,否则会当做变量处理。
var foo = "bar";
var obj = {
foo: 1,
bar: 2
};
obj.foo // 1
obj.[foo] // 2
方括号运算符内部可以使用表达式,纯数字键可以不加引号,会自动转成字符串,但是纯数字键名不能使用点运算符,只能使用方括号运算符。
obj["hello" + "world"]
var obj2 = {
0.7: "hello"
}
obj2[0.7] // "hello"
obj2["0.7"] // "hello"
属性的赋值
点运算符和方括号运算符不仅可以用来访问,还可以用来赋值。
JavaScript允许属性的后绑定,也就是说,你可以在任意时刻新增属性,没必要在定义对象的时候,就定义好属性。
var obj = { p : 1};
// 等价于
var obj = {};
obj.p = 1;
### 属性的查看
查看一个对象所有的属性,可以使用<font color=red>Object.keys()</font>方法。
var obj = {
key1: 1,
key2: 2
};
Object.keys(obj); // ["key1", "key2"]
### 属性的删除:delete关键字
<font color=red>delete</font>关键字用于删除对象的属性,删除成功后返回<font color=red>true</font>。**注意,删除一个不存在的属性,<font color=red>delete</font>不报错,而且返回<font color=red>true</font>,只有一种情况,<font color=red>delete</font>会返回false,那就是这个属性存在,且不能删除。
```js
var obj = { p: 1 };
Object.keys(obj); // ["p"]
delete obj.p; // true
obj.p // undefined
Object.keys(obj); // []
delete obj.o // true
var obj = Object.defineProperty({}, "p", {
value: 123,
configurable: false
});
obj.p // 123
delete obj.p // false
另外,需要注意的是,delete只能删除对象本身的属性,无法删除继承的属性。
var obj = {};
delete obj.toString // true
obj.toString // function toString() { [native code] }
上面代码中,toString是obj继承的属性,虽然返回true,但该属性并没有被删除。说明**即使delete返回true,该属性依然可能读到值。
属性是否存在:in预算符
in运算符用于检查对象是否包含某个属性,包含返回true,否则返回false。in运算符的一个问题是,它不能识别哪些属性是自身的,哪些是继承的。这时,可以用对象的hasOwnProperty方法判断一下。
var obj = {p: 1};
"p" in obj // true
"toString" in obj // true
if ("toString" in obj) {
console.log(obj.hasOwnProperty("toString")) // false
}
属性的遍历: for ... in循环
var obj = {a: 1, b: 2, c: 3};
for (var i in obj) {
console.log("key: " + i);
console.log("value: " + obj[i]); // 注意是obj[i]
}
注意点:
* 它遍历的是所有可遍历的属性,会跳过不可遍历的属性
* 它不仅遍历对象自身的属性,还遍历继承的属性
举例来说,对象都继承了toString属性,但是<font color=red>for...in</font>循环不会遍历这个属性。
一般情况下,都是只想遍历自身的属性,所以使用<font color=red>for...in</font>的时候,应该结合使用<font color=red>hasOwnProperty</font>方法。
```js
var person = {...}
for (var key in person) {
if (person.hasOwnProperty(key)) {
console.log(key);
}
}
with语句
with语句格式如下
with (对象) {}
语句;
}
它的作用是操作同一个对象的多个属性时,提供一些书写的方便。
var obj = {
p1: 1,
p2: 2,
};
with (obj) {
p1 = 4;
p2 = 5;
}
// 等同于
obj.p1 = 4;
obj.p2 = 5;
注意,**如果<font color=red>with</font>区块内部有变量的赋值操作,必须是当前对象已经存在的属性,否则会创造一个当前作用域的全局变量。
```js
var obj = {};
with (obj) {
p1 = 4;
}
obj.p1 // undefined
p1 // 4
这是因为with区块没有改变作用域,它的内部依然是当前作用域。这造成with语句一个很大的弊病,就是绑定对象不明确。
with (obj) {
console.log(x);
}
单纯从上面的代码块,根本无法判断x到底是全局变量,还是对象obj的一个属性。建议不要使用with语句,可以考虑用一个临时变量代替。
with (obj1.obj2.obj3) {
console.log(p1 + p2);
}
// 写成
var temp = obj1.obj2.obj3;
console.log(temp.p1 + temp.p2);