跳至主要内容

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,如:


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

评论

此博客中的热门博文

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%" > ...

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' ,...

JavaScript从零开始——入门(1)

  正好这段时间在学习JavaScript全栈,顺便把自己到目前为止的知识重新整理一下,做个自己能够理解的学习路径出来。 先从入门开始,老生常谈一下5W1H——有很大一部分,尤其是历史是引用自网道的JavaScript教程,特此声明: 入门篇 ​ wangdoc.com What——什么是JavaScript? 各个网站、书籍、教程都写了无数次JavaScript的定义,最简单的一种说法就是: JavaScript是一种轻量级的脚本语言。 其实这个定义到2020的今天,有一点点不太一样了,毕竟它已经发展到一个前人无法想象的地步了,如今的JavaScript不再是只能用来编写控制其他大型应用程序的“脚本”,它还可以用来构建服务器(如最常用的Web服务器等),构造移动应用(主流的iOS或者Android),甚至可以直接编写PC端桌面应用(Windows,Mactintosh以及Linux),已经是不折不扣的“全栈”语言了。 当然其实它所提供的核心语法并不算很多,只能用来做一些数学和逻辑运算,也不提供任何与I/O相关的API,而是要靠宿主环境(host)提供,所以它其实算是 嵌入式(embedded)语言 ,现如今这诸多用途的开发,都是各大JavaScript框架的功劳:譬如服务器构建,用到了著名的Node.js;移动应用上常见的是React Native,PhoneGap/Cordova甚至jQuery Mobile;桌面应用的框架则主要是electron.js。 最后,从语法角度看,它是一种 “对象模型”语言 ,各种宿主环境通过这个模型,描述自己的功能和操作接口,从而通过JavaScript来控制对应功能;但JavaScript并不是纯粹的“面向对象的”语言,因为它还支持其他的编程范式——如函数式编程——这就导致几乎任何问题,它都可能有多种解决的方法。 Where——JavaScript可以用在哪里? 如前所述,JavaScript的核心语法其实很精简,只包括了: 基本的语法构造 :操作符、控制结构、语句等; 标准库 :Array(数组)、Date(日期时间)、Math(数学库)等等。 除此之外的所有其他特性,都是由宿主所提供的API,仅仅是通过JavaScript进行调用。 如浏览器,它额外提供了如下三大类API: 浏览器控制类: 操作浏览器 DOM(文档对...