对象的概念和java一样,就是一种抽象的概念,万物皆对象
通常有三种方法
直接使用大括号生成( {}
)
var o = {
p1: 'Hello',
p2: 'World'
};
大括号就定义了一个对象,包含了两个键值对的对象,该对象被赋值给变量 o
。
p
是“键名”(成员的名称),字符串 Hello World
是“键值”(成员的值)
new
命令生成一个Object对象的实例 使用 Object.create
方法生成。
var o1 = {};
var o2 = new Object();
var o3 = Object.create(null);
上面三行语句是等价的
第三种写法一般用在需要对象继承的场合
对象的所有键名都是字符串,所以加不加引号都可以
var o = {
'p': 'Hello World'
};
如果键名是数值,会被自动转为字符串:
var o ={
1: 'a',
3.2: 'b',
1e2: true,
1e-2: true,
.234: true,
0xFF: true,
};
o
// Object {
// 1: "a",
// 100: true,
// 255: true,
// 3.2: "b",
// 0.01: true,
// 0.234: true
// }
不合法的键名必须加上引号
键名如果既不符合标识名的条件(比如第一个字符为数字,或者含有空格或运算符),也不是数字,则必须加上引号,否则会报错
var o = {
'1p': "Hello World",
'h w': "Hello World",
'p+q': "Hello World"
};
对象的每一个“键名”又称为“属性”(property)
“键值”可以是任何数据类型
如果一个属性的值为函数,通常把这个属性称为“方法”,它可以像函数那样调用
var o = {
p: function (x) {
return 2 * x;
}
};
o.p(1)
// 2
对象的属性之间用逗号分隔,最后一个属性后面可以加逗号(trailing comma),也可以不加
属性可以动态创建,不必在对象声明时就指定
var obj = {};
obj.foo = 123;
obj.foo // 123
上面代码中,直接对 obj
对象的 foo
属性赋值,结果就是在运行时创建了 foo
属性。
name属性
由于对象的方法就是函数,因此也有 name
属性
var obj = {
m1: function m1() {},
m2: function () {}
};
obj.m1.name // m1
obj.m2.name // undefined
和java类似
指向同一个对象的不同变量指向同一个内存地址,修改其中一个变量,会影响到其他所有变量
var o1 = {};
var o2 = o1;
o1.a = 1;
o2.a // 1
o2.b = 2;
o1.b // 2
如果取消某一个变量对于原对象的引用,不会影响到另一个变量。
var o1 = {};
var o2 = o1;
o1 = 1;
o2 // {}
上面代码中, o1
和 o2
指向同一个对象,然后 o1
的值变为1,这时不会对 o2
产生影响, o2
还是指向原来的那个对象。
原始类型数据的赋值和对象引用不同,是值传递
var x = 1;
var y = x;
x = 2;
y // 1
上面的代码中,当 x
的值发生变化后, y
的值并不变,这就表示 y
和 x
并不是指向同一个内存地址。
{ foo: 1 }
由于对象采用大括号表示,所以如果行首是一个大括号,会产生歧义
foo
属性的对象; foo
,指向表达式 123
。 为了避免这种情况,JavaScript规定,如果行首是大括号,一律解释为语句(即代码块)。如果要解释为表达式(即对象),必须在大括号前加上圆括号:
({ foo: 1})
读取对象的属性,有两种方法
如果使用方括号运算符,键名必须放在引号里面,否则会被当作变量处理。但数字键可以不加引号,因为会被当作字符串处理。
var o = {
p: 'Hello World'
};
o.p // "Hello World"
o['p'] // "Hello World"
如果使用方括号运算符,键名必须放在引号里面,否则会被当作变量处理。由于数字键会被当作字符串处理,可以不加引号。
var o = {
0.7: 'Hello World'
};
o['0.7'] // "Hello World"
o[0.7] // "Hello World"
方括号运算符内部可以使用表达式
o['hello' + ' world']
o[3 + 3]
数值键名不能使用点运算符(因为会被当成小数点),只能使用方括号运算符。
obj.0xFF
// SyntaxError: Unexpected token
obj[0xFF]
// true
如果读取一个不存在的键,会返回 undefined
,而不是报错。可以利用这一点,来检查一个全局变量是否被声明。
// 检查a变量是否被声明
if (a) {...} // 报错
if (window.a) {...} // 不报错
if (window['a']) {...} // 不报错
上面的后二种写法之所以不报错,是因为在浏览器环境,所有全局变量都是 window
对象的属性。 window.a
的含义就是读取 window
对象的 a
属性,如果该属性不存在,就返回 undefined
,并不会报错。
需要注意的是,后二种写法有漏洞,如果 a
属性是一个空字符串(或其他对应的布尔值为 false
的情况),则无法起到检查变量是否声明的作用。正确的做法是可以采用下面的写法。
// 写法一
if (window.a === undefined) {
// ...
}
// 写法二
if ('a' in window) {
// ...
}
点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值。
o.p = 'abc';
o['p'] = 'abc';
查看一个 对象本身 的所有属性,可以使用 Object.keys
方法。
var o = {
key1: 1,
key2: 2
};
Object.keys(o);
// ['key1', 'key2']
删除一个属性,需要使用 delete
命令。
一旦使用 delete
命令删除某个属性,再读取该属性就会返回 undefined
,而且 Object.keys
方法返回的该对象的所有属性中,也将不再包括该属性:
var o = {p: 1};
Object.keys(o) // ["p"]
delete o.p // true
o.p // undefined
Object.keys(o) // []
删除不存在的属性,仍未true。因此,delete命令只能用来保证某个属性的值为undefined,而无法保证该属性是否真的存在
var o = {};
delete o.p // true
只有一种情况, delete
命令会返回 false
,那就是该属性存在,且不得删除。
可以通过 configurable: false 来配置属性不可修改
delete
命令只能删除对象本身的属性,不能删除继承的属性 in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值),如果包含就返回 true
,否则返回 false
。
in
运算符不能识别对象继承的属性。如果使用它来判断继承的属性,也会返回true:
var o = new Object();
o.hasOwnProperty('toString') // false
'toString' in o // true
上面代码中, toString
方法不是对象 o
自身的属性,而是继承的属性, hasOwnProperty
方法可以说明这一点。但是, in
运算符不能识别,对继承的属性也返回 true
。
待确定,、、、、、、是否任意属性都会返回true??????
继承的属性怎么理解??是新创建的对象属性?还是java中继承某个类
for...in
循环用来遍历一个对象的全部属性,包括自身的和继承的
var o = {a: 1, b: 2, c: 3};
for (var i in o) {
console.log(o[i]);
}
// 1
// 2
// 3
// name 是 Person 本身的属性
function Person(name) {
this.name = name;
}
// describe是Person.prototype的属性
Person.prototype.describe = function () {
return 'Name: '+this.name;
};
var person = new Person('Jane');
// for...in循环会遍历实例自身的属性(name),
// 以及继承的属性(describe)
for (var key in person) {
console.log(key);
}
// name
// describe
如果只想遍历对象本身的属性,可以使用hasOwnProperty方法,在循环内部做一个判断:
for (var key in person) {
if (person.hasOwnProperty(key)) {
console.log(key);
}
}
// name
或者新建一个继承 null
的对象。由于 null
没有任何属性,所以新对象也就不会有继承的属性了。
with
语句的格式如下:
它的作用是操作同一个对象的多个属性时,提供一些书写的方便。
with
区块没有改变作用域,它的内部依然是当前作用域
因此 with
区块内部的变量,必须是当前对象已经存在的属性,否则会创造一个当前作用域的全局变量:
var o = {};
with (o) {
x = "abc";
}
o.x // undefined
x // "abc"
上面代码中,对象 o
没有属性 x
,所以 with
区块内部对 x
的操作,等于创造了一个全局变量 x
。正确的写法应该是,先定义对象 o
的属性 x
,然后在 with
区块内操作它:
var o = {};
o.x = 1;
with (o) {
x = 2;
}
o.x // 2
with
语句的一个很大的弊病,就是绑定对象不明确
with (o) {
console.log(x);
}
单纯从上面的代码块,根本无法判断 x
到底是全局变量,还是 o
对象的一个属性。这非常不利于代码的除错和模块化,编译器也无法对这段代码进行优化,只能留到运行时判断,这就拖慢了运行速度。因此,建议不要使用 with
语句,可以考虑用一个临时变量代替 with
:
with(o1.o2.o3) {
console.log(p1 + p2);
}
// 可以写成
var temp = o1.o2.o3;
console.log(temp.p1 + temp.p2);