跳至主要内容

JavaScript从零开始——数据类型(4)

 这几天正努力的复习NoSQL的使用方法(多嘴说一句,NoSQL是指非关系型数据库,在互联网高并发的情况下,关系型数据库的执行效率实在是有点差,所以NoSQL就兴起了。)——因为自己的一个软件项目需要用到,经过斟酌之后,我选取了LowDB;它是基于JSON(JavaScript Object Notation)格式的一种键值对存储的数据库,这种数据的组成方式,也正是我们下面将要提起的对象(Object)的组成方式。


1 组成方式

在JavaScript当中,对象是最核心的概念,同时也是最重要的数据类型;它就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合,其标准的组成方式如下:

let obj = {
    'key1': 'value1',
    'key2': 'value2'
};

其中,obj是该对象的变量名称,大括号{}内部是对象的组成,key1以及key2是键名,value1value2是分别对应key1key2的键值,键名和键值之间使用冒号(:)分隔;不同的键值对之间使用逗号(,)分隔,最后一个键值对后可以加上逗号也可以不加。

在实际使用中,由于对象的键名始终是由字符串或者Symbol组成的,所以单引号或者双引号可以省略,写成下面的形式:

let obj = {
    key1: 'value1',
    key2: 'value2'
};

如果键名是数值,则会自动转换为字符串类型,如下:

上图当中所有看起来像是数值类型的键名,实际上已经转换为了字符串。

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

图中所示obj2的三个键名都不符合表示名条件,故而需要添加引号

对象的每一个键名又称为“属性”(property),它的“键值”可以是任何数据类型。如果一个属性的值为函数,通常把这个属性称为“方法”,它可以像函数那样调用。

简单说明一下上面的方法,虽然使用了x作为方法y的参数,但是由于并不是真的引用了testObj.x,所以这里的xtestObj.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 遍历

通过循环命令forin的组合(for (... in ...)),就可以对一个对象进行遍历了,它有一个固定的格式如下:

该遍历循环有两个使用注意点。

  • 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。
  • 它不仅遍历对象自身的属性,还遍历继承的属性。

举例来说,对象都继承了toString属性,但是该循环不会遍历到这个属性,因为该属性是不可遍历的。

如果只需要遍历当前对象的原生属性,仍然建议要使用hasOwnProperty()方法进行一下后续判断:

5 with语句

with语句的格式如下:

with (对象) {
  语句;
}

它的作用是操作同一个对象的多个属性时,提供一些书写的方便。

注意,如果with区块内部有变量的赋值操作,必须是当前对象已经存在的属性,否则会创造一个当前作用域的全局变量。

上面代码中,对象obj并没有p4属性,对其赋值等于创造了一个全局变量p4。正确的写法应该是,先定义对象objp4属性,然后在with区块内操作它。

这是因为with区块没有改变作用域,它的内部依然是当前作用域。这造成了with语句的一个很大的弊病,就是绑定对象不明确。

with (obj) {
  console.log(x);
}

单纯从上面的代码块,根本无法判断x到底是全局变量,还是对象obj的一个属性。这非常不利于代码的除错和模块化,编译器也无法对这段代码进行优化,只能留到运行时判断,这就拖慢了运行速度。因此,建议不要使用with语句,可以考虑用一个临时变量代替with,如:


写到这里,狭义上的对象就说明完毕了,接下来,就会是包含在广义对象当中的数组、函数这两个常用数据类型。

评论

此博客中的热门博文

Node.js从零开始——事件、系统和流

毕竟不是一个真正的教程,这里主要还是以普及和介绍为主,所以这一部分就是 Node.js 的其他部分介绍了,主要也就是事件触发、操作系统以及流的知识。 1 事件触发器 因为我们之前在浏览器中使用 JavaScript ,所以知道 JS 通过事件处理了许多用户的交互:鼠标的单击、键盘按钮的按下、对鼠标移动的反应等等。 在后端, Node.js 也提供了使用 events 模块 构建类似系统的选项。 具体上,此模块提供了 EventEmitter 类,用于处理事件。 使用以下代码进行初始化: const EventEmitter = require ( 'events' ); const eventEmitter = new EventEmitter (); 该对象公开了 on 和 emit 方法: emit 用于触发事件 on 用于添加回调函数(会在事件被触发时执行) 例如,创建 start 事件,并提供一个示例,通过记录到控制台进行交互: eventEmitter . on ( 'start' , () => { console . log ( '开始' ); }); 当运行以下代码时: eventEmitter . emit ( 'start' ); 事件处理函数会被触发,且获得控制台日志。 可以通过将参数作为额外参数传给 emit() 来将参数传给事件处理程序: eventEmitter . on ( 'start' , number => { console . log ( `开始 ${ number } ` ); }); eventEmitter . emit ( 'start' , 23 ); 多个参数: eventEmitter . on ( 'start' , ( start , end ) => { console . log ( `从 ${ start } 到 ${ end } ` ); }); eventEmitter . emit ( 'start' ,

Node.js从零开始——HTTP 服务器

其实 Node.js 最初的目的,就是实现一个完全可以由 JavaScript 来进行开发的服务器端,所以归根到底,它的后端能力之一就是实现一个 HTTP 服务器,这里我们来看看它。 1 搭建 HTTP 服务器 其实前面我们已经看过了一个例子,不过这里再来看一个 HTTP web 服务器的例子: const http = require ( 'http' ); const port = 3000 ; const server = http . createServer (( req , res ) => { res . statusCode = 200 ; res . setHeader ( 'Content-Type' , 'text/plain' ); res . end ( '你好世界\n' ); }) server . listen ( port , () => { console . log ( `服务器运行在 http:// ${ hostname } : ${ port } /` ); }); 简要分析一下: 这里引入了 ref=" http:// nodejs.cn/api/http.html ">http 模块:使用该模块来创建 HTTP 服务器 服务器被设置为在指定的 3000 端口上进行监听, 当服务器就绪时,则 listen 回调函数会被调用 传入的回调函数会在每次接收到请求时被执行, 每当接收到新的请求时, "http://nodejs.cn/api/http.html#http_event_request">request 事件 会被调用,并提供两个对象:一个请求( http.IncomingMessage 对象)和一个响应( http.ServerResponse 对象) request 提供了请求的详细信息, 通过它可以访问请求头和请求的数据, response 用于构造要返回给客户端的数据;在此示例中: res . statusCode = 200 ; 设置 status

Web API从零开始——SVG

SVG 是我基本没有用过的知识块,所以这里也是边分享边学习,尽量在我自己理解的基础上来分享。 1 概念 SVG 是一种基于 XML 语法的图像格式,全称是可缩放矢量图(Scalable Vector Graphics);其他图像格式都是基于像素处理的, SVG 则是属于对图像的形状描述,所以它本质上是文本文件,体积较小,且不管放大多少倍都不会失真。 SVG 文件可以直接插入网页,成为 DOM 的一部分,然后用 JavaScript 和 CSS 进行操作。 上面是 SVG 代码直接插入网页的例子。 SVG 代码也可以写在一个独立文件中,然后用 、 、 、 等标签插入网页: < img src = "circle.svg" > < object id = "object" data = "circle.svg" type = "image/svg+xml" ></</span> object > < embed id = "embed" src = "icon.svg" type = "image/svg+xml" > < iframe id = "iframe" src = "icon.svg" ></</span> iframe > CSS 也可以使用 SVG 文件: . logo { background : url ( icon.svg ); } SVG 文件还可以转为 BASE64 编码,然后作为 Data URI 写入网页: < img src = "data:image/svg+xml;base64,[data]" > 2 语法 2.1 标签 我们可以把 SVG 代码都放在顶层标签 之中,下面是一个例子: < svg width = "100%" height = "100%" >