这几天正努力的复习NoSQL的使用方法(多嘴说一句,NoSQL是指非关系型数据库,在互联网高并发的情况下,关系型数据库的执行效率实在是有点差,所以NoSQL就兴起了。)——因为自己的一个软件项目需要用到,经过斟酌之后,我选取了LowDB;它是基于JSON(JavaScript Object Notation)格式的一种键值对存储的数据库,这种数据的组成方式,也正是我们下面将要提起的对象(Object)的组成方式。
1 组成方式
在JavaScript当中,对象是最核心的概念,同时也是最重要的数据类型;它就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合,其标准的组成方式如下:
let obj = {
'key1': 'value1',
'key2': 'value2'
};
其中,obj是该对象的变量名称,大括号{}内部是对象的组成,key1以及key2是键名,value1和value2是分别对应key1和key2的键值,键名和键值之间使用冒号(:)分隔;不同的键值对之间使用逗号(,)分隔,最后一个键值对后可以加上逗号也可以不加。
在实际使用中,由于对象的键名始终是由字符串或者Symbol组成的,所以单引号或者双引号可以省略,写成下面的形式:
let obj = {
key1: 'value1',
key2: 'value2'
};
如果键名是数值,则会自动转换为字符串类型,如下:
上图当中所有看起来像是数值类型的键名,实际上已经转换为了字符串。
不过一旦键名不符合标识名的条件(比如第一个字符为数字,或者含有空格或运算符),且也不是数字,则必须加上引号,否则会报错:
对象的每一个键名又称为“属性”(property),它的“键值”可以是任何数据类型。如果一个属性的值为函数,通常把这个属性称为“方法”,它可以像函数那样调用。
简单说明一下上面的方法,虽然使用了x作为方法y的参数,但是由于并不是真的引用了testObj.x,所以这里的x和testObj.x是不同的两个变量。
如果属性的值还是一个对象,就形成了链式引用,如:
从上图也可以看出,属性是可是动态添加的,声明时不需要立刻添加属性:对象x在声明时,并没有任何属性,之后通过声明x.test属性时才动态添加了test属性,并将另一个对象y赋值给了该属性。
2 引用
如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。
此时,如果取消某一个变量对于原对象的引用,不会影响到另一个变量。
但是,这种引用只局限于对象,如果两个变量指向同一个原始类型的值。那么,变量这时都是值的拷贝,而不是指向同一个地址:
3 语句或表达式
前文曾经提到过,JavaScript引擎会默认判断大括号{}内为语句,如下:
这种情况下,如果行首是一个大括号,无法确定是对象还是代码块,JavaScript引擎会一律解释为代码块(语句)。
如果要解释为对象,最好在大括号前加上圆括号。因为圆括号的里面,只能是表达式,所以确保大括号只能解释为对象;这种差异在eval语句(作用是对字符串求值)中反映得最明显。
4 属性的操作
4.1 读取
读取对象属性,实际上有两种方法,一种是使用点.运算符,另一种是使用方括号[]运算符;其中使用[]的时候,属性需要使用引号括起来,否则会被认为是变量名:
由于方括号内支持使用变量,所以使用表达式也是可以的,譬如:
上图中,由于数值会被自动转换为字符串,所以[]内直接使用数值也是可行的。
但是数值类键名是不能直接使用.运算符调用的,因为会被认为是小数点而失去效果且报错:
4.2 赋值
.和[]运算符不仅仅可以读取属性,也可以对属性进行赋值:
4.3 查看
可以使用Object.keys来查看一个对象的属性:
4.4 删除
可以使用delete命令来删除一个对象的属性:
如上图,在使用delete命令删除对象obj的属性10之后,返回true表示删除成功。
但如果使用它来删除一个并不存在的属性,返回值也仍然是true,如下图:
故此,在删除一个属性之前,最好先判断一下该属性是否真的存在。
有且仅有一种情况下,delete会返回false,即:该属性存在,但不可删除。
另外,delete命令只能删除当前对象的属性,而非继承属性(后面会讲到原型和继承,此处仅举例):
如上图,虽然delete命令返回了true,但对象z所继承的toString()方法仍然可以正常使用,因为该方法是从Object对象继承来的。
4.5 判断属性是否存在
最简单的方法就是使用in运算符进行判断,一旦属性(键名)存在,则返回true,反之则返回false:
不过和delete相似的是,in无法区分属性是原生的还是继承而来的:
这个时候,可以使用hasOwnProperty()方法进行一下后续判断,是否为当前对象的原生属性:
4.6 遍历
通过循环命令for和in的组合(for (... in ...)),就可以对一个对象进行遍历了,它有一个固定的格式如下:
该遍历循环有两个使用注意点。
- 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。
- 它不仅遍历对象自身的属性,还遍历继承的属性。
举例来说,对象都继承了toString属性,但是该循环不会遍历到这个属性,因为该属性是不可遍历的。
如果只需要遍历当前对象的原生属性,仍然建议要使用hasOwnProperty()方法进行一下后续判断:
5 with语句
with语句的格式如下:
with (对象) {
语句;
}
它的作用是操作同一个对象的多个属性时,提供一些书写的方便。
注意,如果with区块内部有变量的赋值操作,必须是当前对象已经存在的属性,否则会创造一个当前作用域的全局变量。
上面代码中,对象obj并没有p4属性,对其赋值等于创造了一个全局变量p4。正确的写法应该是,先定义对象obj的p4属性,然后在with区块内操作它。
这是因为with区块没有改变作用域,它的内部依然是当前作用域。这造成了with语句的一个很大的弊病,就是绑定对象不明确。
with (obj) {
console.log(x);
}
单纯从上面的代码块,根本无法判断x到底是全局变量,还是对象obj的一个属性。这非常不利于代码的除错和模块化,编译器也无法对这段代码进行优化,只能留到运行时判断,这就拖慢了运行速度。因此,建议不要使用with语句,可以考虑用一个临时变量代替with,如:
写到这里,狭义上的对象就说明完毕了,接下来,就会是包含在广义对象当中的数组、函数这两个常用数据类型。
评论
发表评论