1 基本概念及语法
对象(Object)是JavaScript最重要的数据类型。
简单地说,对象就是一组“键值对”的集合,而且它是一个无序的复合数据集合。
1.1 基本语法
如下代码所示:大括号就定义了一个对象,并被赋值给一个变量obj
。该对象内部有两个键值对(或叫成员),键和值之间用冒号分隔,两个键值对之间用逗号分隔。
注:最后一个键值对后面加不加逗号都可以,建议省略。
var obj={
foo:'Hello',
bar:'World'
}
上述obj
对象的键名默认都是字符串(ES6中的Symbol
类型也可以用作键名),即便为数值,也会被自动转为字符串,因此加不加引号都可以。
注:若键名不符合标识符的命名条件,则必须加上引号才可以,否则会报错。
1.2 对象的属性
键名又被称为属性,而其对应的键值可以是任意数据类型;若键值(即属性的值)为函数,则该属性又被称作方法,它可以像函数那样调用。
var obj={
p:function(x){
return 2*x;
}
}
obj.p(1);//结果为2
1.3 链式引用
若键值(即属性的值)为一个对象,就形成了链式引用。
如下例子:o1
的属性foo
指向对象o2
,这样就可以链式引用o2
的属性。
var o1={};
var o2={bar:'hello'};
o1.foo=o2;
o1.foo.bar //输出为“hello”
上述例子中o1
的属性并没有在声明时就指定,因为属性可以被动态创建。
2 对象的引用
如果多个不同的变量名指向同一个对象,那么这些变量名则指向相同的内存地址,当修改其中一个变量时,会影响到其他所有变量。
var o1={};
var o2=o1;
o1.a=1;
o2.a //值也为1
o2.b=2;
o1.b //值同样为2
若取消某一个变量对于原对象的引用,则不会影响到其他的变量。
var o1={};
var o2=o1;
o1=1; //给o1赋新值则可取消o1对原对象的引用
o2 //此时o2仍指向原来的对象{}
上述的引用只局限于对象,若两个变量指向同一个原始类型的值,那么变量都是值的拷贝。
如下代码:当x
的值发生变化,y
值并没有改变,这表明x
和y
并没有指向同一个内存地址。
var x=1;
y=x;
x=2;
y //此时y值仍为1
3 属性的操作
3.1 属性的读取
方法1:使用点运算符:语法为:对象.属性
,例如obj.prop
。
方法2:使用方括号运算符:语法为对象.['属性']
,例如obj['prop']
。
举例:
-
若使用方括号运算符,属性名(或键名)需要放在引号中,否则会被看作变量来处理。
var foo = 'bar'; var obj = { foo: 1, bar: 2 }; obj.foo // 1 obj[foo] // obj[foo]即为obj['bar'],结果显然为2
-
方括号内部可以使用表达式(仍是上面的例子):
obj['b'+'a'+'r'] //即obj['bar'],值显然为2
-
若属性为数字,则在读取时可以不加引号,因为它会被自动转为字符串,但此时需要用方括号取值。若此时用点运算符则会报错,因为点运算符会被认为是小数点。
var obj={ 123:'hello world' }; obj.123 //报错 obj[123] //相当于obj['123'],正确,输出值为“hello world”
3.2 属性的赋值
属性的赋值方法和第一小节的读取方法一样,也是使用点运算符和方括号运算符。例如:
var obj={};
obj.foo='hello';
obj['bar']='world';
3.3 属性的查看
查看一个对象本身所有属性,可以使用Object.keys()
方法,例如:
var obj={
key1:1,
key2:2
}
Object.keys(obj); //输出结果为["key1", "key2"]
3.4 属性的删除
delete
命令用于删除对象的属性,删除成功后返回true
。
具体测试如下:
从上面的测试也容易看出:由于删除一个不存在的属性仍返回true,因此不能根据delete命令的结果,来判断某个属性是存在的。
那么什么时候delete
命令会返回false
呢?
答案是只有一种情况:若将对象的configurable
属性设置为false
,即不可配置,此时该属性将无法删除,delete
命令此时会返回false
。具体请参考:Object.defineProperty() | MDN (mozilla.org),这里不再赘述。
3.5 判断属性是否存在
3.5.1 in运算符
in
运算符用于检查对象是否包含某个属性(检查的是键名而非键值),若包含则返回true
,否则返回false
。语法为:字符串 in 对象
。
3.5.2 hasOwnProperty()方法
在JavaScript中,像toString
属性每个对象都会有(因为这个属性是继承来的),因此在使用in
运算符判断都会返回true
,这时,可以借助hasOwnProperty()
方法来判断是否为对象自身的属性。
var obj={};
if('toString' in obj){
console.log(obj.hasOwnProperty('toString')) //显然输出为:false
}
3.6 属性的遍历
for...in
循环用来遍历一个对象的全部属性。
举个例子好理解:
var obj={a:1,b:2,c:3};
for (var i in obj){
console.log('键名:',i);
console.log('键值:',obj[i]);
}
输出结果为:
键名: a
键值: 1
键名: b
键值: 2
键名: c
键值: 3
for...in
循环输出的项需满足以下两个条件:
- 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性;
- 它不仅遍历自身的属性,还遍历继承的属性;
问题来了:那为何上述例子没有输出继承属性toString
呢?
那是因为该属性默认是不可遍历的,由于不满足上面的第一个条件,因此没有被输出。
技巧:在使用
for...in
循环时可以结合hasOwnProperty()
方法,首先在循环内部判断该属性是否为自有属性。
4 with语句
with
语句可以更方便地操作同一个对象的多个属性。语法如下:
with(对象){
语句;
}
例1:我们可以利用with语句一次为对象修改多个属性:
var obj={p1:1,p2:2};
with(obj){
p1=4;
p2=5;
}
上面等同于obj.p1=4;obj.p2=5;
例2:可以同时读取对象的多个属性:
with(document.links[0]){
console.log(href);
console.log(title);
console.log(style);
}
等同如下代码:
console.log(document,links[0].href);
console.log(document,links[0].title);
console.log(document,links[0].style);
需要注意的是:with
区块内部有变量的赋值操作,必须是当前对象已经存在的属性,否则会创建一个当前作用域的全局变量。如下测试所示:由于对象被创建时没有p1
属性,因此输出obj.p1
时会返回undefined
,对p1
和p2
赋值等于创造了2个全局变量p1
和p2
。
综上来看,with
语句会造成绑定对象不明确的问题,因此非必须情况下尽量避免使用with
语句。