- JavaScript编程思想:从ES5到ES9
- 柯霖廷
- 13681字
- 2021-03-24 02:19:28
第2章 表达式与运算符
本章内容主要介绍构成表达式的要素,包括常量、变量、子表达式、函数的返回值等形式的操作数,以及多种不同的运算符。
2.1 操作数
编程语言中的表达式主要由操作数(operand)和运算符(operator)构成。其中,操作数可以是常量(constant)、变量(variable)、子表达式(subsidiary expression)或是特定函数(function)的返回值(return value)。
2.1.1 常量(ES6)
在各种编程语言中,常量(constant)是指固定而明确的数据。例如:
(1)整数(integer)常量:25、770、-110。
(2)浮点数(floating-point number)常量:25.625、-23.71、Math.PI、Math.LN2、Math.SQRT2。
(3)字符串(string)的字面量(literal):'Alex'、"Happily Ever After"、 '=.=+'。
(4)正则表达式(regular expression)的字面量:/\w+\s*.(\w\d+){1,3}/g、/^\w{2}\d{5}\s*$/。
(5)布尔(boolean)常量:true、false。
(6)原始(primitive)常量:NaN、null、undefined。
(7)编程人员在源代码中,自行声明(declare / declaration)的常量,可简称为自声明常量。例如在源代码片段【const num01 = 157, num02 = 268;】中,num01和num02即是自声明常量。
关于自声明常量,可参考如下示例。
【2-1-1-constants.js】
const E = 299792458 ; console.log('Speed of light is greater than 2.9E8?', E > 2.9e8) ;
【相关说明】
const E = 299792458 ;
声明数值为299792458的常量E。
console.log('Speed of light is greater than 2.9E8?', E > 2.9e8) ;
在浏览器调试工具的【Console】面板里显示出【Speed of light is greater than 2.9E8? true】的信息。其中,2.9e8代表2.9×108,也就是290000000。所以,【E > 2.9e8】等价于【E > 290000000】,会返回布尔值true,这是因为自声明常量E的数值为299792458,大于290000000。
在上述简短的源代码中,常量E刻意被声明成整数常量299792458,因此常量E在源代码的运行期间,不可被变更为其他数据。
2.1.2 变量(ES6)
在各种编程语言中,变量(variable)的标识符(identifier)/名称(name)用来识别特定存储位置中的可变数据。所谓的可变,是指在运行期间,其数据是可被变更的。
在JavaScript语言中,变量的作用范围(scope)大致可如下区分:
• 全局(global)范围
• 局部(local)范围
• 块(block)范围
下面通过示例,介绍如何运用全局范围和局部范围的变量。
【2-1-2-e1-global-and-local-scope-variables.js】
let start = 123 ; const STEP = 3 ; var result = 0 ; function some_steps(step_count = 1) { var output ; // or let output ; output = start + STEP * step_count ; console.log(`in function: start = ${start}, STEP = ${STEP}, result = ${result}`) ; console.log(`in function: output = ${output}\n\n`) ; return output ; } result = some_steps(5) ; console.log(`outer: start = ${start}, step = ${STEP}, result = ${result}`) ; console.log(`outer: output = ${output}`) ;
【相关说明】
let start = 123 ; const STEP = 3 ; var result = 0 ;
声明全局范围的变量start与result,以及全局范围的自声明常量STEP。既然是全局范围的变量和常量,即可让后续的任意表达式,加以访问。STEP是一个常量,所以在后续的源代码里,无法被赋予新数据。
function some_steps(step_count = 1) { var output ; // or let output ; output = start + STEP * step_count ; console.log(`in function: start = ${start}, step= ${STEP}, result = ${result}`) ; console.log(`in function: output = ${output}\n\n`) ; return output ; }
定义函数some_steps(),并在内部声明局部范围的变量output。既然是局部范围的,变量output只能在函数some_steps()内部被访问。变量output接着被赋予【带有全局范围变量start和常量STEP】的表达式的结果值。这个函数内部的多条语句,访问了全局范围的变量start、常量STEP,以及局部范围的变量output。
result = some_steps(5) ; console.log(`outer: start = ${start}, step= ${STEP}, result = ${result}`) ;
访问全局范围的变量start、result与常量STEP。
console.log(`outer: output = ${output}`) ;
因为访问了函数some_steps()内的局部范围的变量output,所以会显示出错误信息【Uncaught ReferenceError: output is not defined】。
关于块范围的理解,可参考如下示例。
【2-1-2-e2-block-scope-variables.js】
// different heights measured in meters. { // height of the building. let height = 100 ; { // height of one floor. let height = 4 ; { // height of a room. let height = 3 ; { // height of a desk. let height = 1 ; console.log('height of a desk =', height) ; } console.log('height of a room =', height) ; } console.log('height of one floor =', height) ; } console.log('height of the building =', height) ; } console.log('') ; for (var m = 1; m < 10; m++) { for(var n = 1; n < 10; n++) { console.log(`kernel count = (${m},${n})`) ; } console.log(`inner count = (${m},${n})\n\n`) ; } console.log(`outer count = (${m},${n})`) ; console.log('') ; for (let i = 1; i < 10; i++) { for(let j = 1; j < 10; j++) { console.log(`kernel count = (${i},${j})`) ; } console.log(`inner count = (${i},${j})\n\n`) ; } // console.log(`outer count = (${i},${j})`) ;
【相关说明】
// different heights measured in meters. { // height of the building. let height = 100 ;
此为第1层大括号内的块范围变量height,所以第1层大括号之外的语句是访问不到的。声明块范围的变量,必须通过关键字let才行;由关键字var声明的变量,只会成为全局范围或局部范围的变量。在此,可将此层块范围的变量height视为建筑物的高度。
{ // height of one floor. let height = 4 ;
此为第2层大括号内的块范围变量height,因此第2层大括号之外的语句是访问不到的。在此,可将此层块范围的变量height,视为建筑物特定楼层的高度。
{ // height of a room. let height = 3 ;
此为第3层大括号内的块范围变量height,因此第3层大括号之外的语句是访问不到的。在此,可将此层块范围的变量height,视为特定楼层中特定房间的高度。
{ // height of a desk. let height = 1 ;
此为第4层大括号内的块范围变量height,因此第4层大括号之外的语句是访问不到的。在此,可将此层块范围的变量height,视为特定房间中特定桌子的高度。
console.log('height of a desk =', height) ; } console.log('height of a room =', height) ; } console.log('height of one floor =', height) ; } console.log('height of the building =', height) ; }
通过此源代码片段,可供读者调试出每层块范围变量height的对应值。
console.log('') ; for (var m = 1; m < 10; m++) { for(var n = 1; n < 10; n++) { console.log(`kernel count = (${m},${n})`) ; } console.log(`inner count = (${m},${n})\n\n`) ; } console.log(`outer count = (${m},${n})`) ;
测试此源代码片段,可看出通过关键字var声明的变量m与n,在带有大括号的for循环语句结束之后,依然可以被访问到。这也就意味着,关键字var声明的,无法成为块范围的变量。
console.log('') ; for (let i = 1; i < 10; i++) { for(let j = 1; j < 10; j++) { console.log(`kernel count = (${i},${j})`) ; } console.log(`inner count = (${i},${j})\n\n`) ; } // console.log(`outer count = (${i},${j})`) ;
测试此源代码片段,可看出通过关键字let声明的变量m与n,只能被访问于for循环语句大括号内的块范围里。这也就意味着,关键字let声明的,可以成为块范围的变量。复原最后一行成为注释的语句,并进行测试之后,可在网页浏览器的调试工具【Console】面板中,看到因为访问不到块范围变量i与j的错误信息。
关于特殊变量的理解,可参考如下示例。
【2-1-2-e3-special-variables.js】
document.body.style.backgroundColor = 'RoyalBlue' ; console.log(document.body.style.backgroundColor) ; document.body.style.fontSize = '3em' ; console.log(document.body.style.fontSize) ; file_selector.style.color = 'Gold' ; console.log(file_selector.style.color) ; file_selector.style.zoom = 2 ;
【相关说明】
file_selector.style.zoom = 2 ;
此语句中的子属性zoom,因为可被设置新数据,所以算是变量的一种;然而,此语句中的子属性style,则无法被设置新数据,也因此算是常量的一种。
通过JavaScript语法,可访问文档对象模型(DOM, document object model)中的特定元素实例(element instance)。元素实例可内含常量属性,只可被读取其数据,而不允许被写入新数据。下面举出4个元素实例中的常量属性:
• file_selector.style
• document.body.style
• document.body
• window.document(可被简写为document)
以下可变更数据的是字符串(string)类型的属性,亦是网页浏览器内置的,所以可认为是网页程序在运行时的特殊变量:
• document.body.style.backgroundColor
• document.body.style.fontSize
• file_selector.style.color
• file_selector.style.zoom
2.1.3 子表达式
一般而言,特定表达式可再分割成为子表达式(subsidiary expression)。关于子表达式的运用,请参考如下示例。
【2-1-3-subsidiary-expressions.js】
var r = 10 ; var volume = 4 / 3 * Math.PI * Math.pow(r, 3) ; circumference = r => 2 * Math.PI * r ; circle_area = r => Math.PI * Math.pow(r, 2) ; sphere_volume = r => 4 / 3 * Math.PI * Math.pow(r, 3) ; cylinder_volume = (r, h) => circle_area(r) * h ; cylinder_surface_area = (r, h) => 2 * circle_area(r) + circumference(r) * h ; rounded_circle_area = circle_area(10).toFixed(3) ; rounded_cylinder_volume = cylinder_volume(10, 20).toFixed(3) ; rounded_cylinder_surface_area = cylinder_surface_area(10, 20).toFixed(3) ; console.log(rounded_circle_area) ; console.log(rounded_cylinder_volume) ; console.log(rounded_cylinder_surface_area) ;
【相关说明】
var r = 10 ;
此语句内含左侧表达式。
var volume = 4 / 3 * Math.PI * Math.pow(r, 3) ;
此主要表达式内含如下子表达式:
• 左侧表达式:【var volume =】和【Math.】
• 算术表达式:【4 / 3 * Math.PI * Math.pow(r, 3)】
• 主要表达式:【Math.PI】和【Math.pow(r, 3)】
circumference = r => 2 * Math.PI * r ;
此主要表达式内含如下子表达式:
• 左侧表达式:【circumference =】和【Math.】
• 箭头函数表达式:【r => 2 * Math.PI * r】
• 算术表达式:【2 * Math.PI * r】
• 主要表达式:【Math.PI】
circle_area = r => Math.PI * Math.pow(r, 2) ;
此表达式内含如下子表达式:
• 左侧表达式:【circle_area =】和【Math.】
• 箭头函数表达式:【r => Math.PI * Math.pow(r, 2)】
• 算术表达式:【Math.PI * Math.pow(r, 2)】
• 主要表达式:【Math.PI】和【Math.pow(r, 2)】
sphere_volume = r => 4 / 3 * Math.PI * Math.pow(r, 3) ;
此表达式内含如下子表达式:
• 左侧表达式:【sphere_volume =】和【Math.】
• 箭头函数表达式:【r => 4 / 3 * Math.PI * Math.pow(r, 3)】
• 算术表达式:【4 / 3 * Math.PI * Math.pow(r, 3)】
• 主要表达式:【Math.PI】和【Math.pow(r, 3)】
cylinder_volume = (r, h) => circle_area(r) * h ;
此表达式内含如下子表达式:
• 左侧表达式:【cylinder_volume =】
• 箭头函数表达式:【(r, h) => circle_area(r) * h】
• 算术表达式:【circle_area(r) * h】
• 主要表达式:【circle_area(r)】
cylinder_surface_area = (r, h) => 2 * circle_area(r) + circumference(r) * h ;
此表达式内含如下子表达式:
• 左侧表达式:【cylinder_surface_area =】
• 箭头函数表达式:【(r, h) => 2 * circle_area(r) + circumference(r) * h】
• 算术表达式:【2 * circle_area(r) + circumference(r) * h】
• 主要表达式:【circle_area(r)】和【circumference(r)】
rounded_circle_area = circle_area(10).toFixed(3) ;
此表达式内含如下子表达式:
• 左侧表达式:【rounded_circle_area =】和【circle_area(10).】
• 主要表达式:【circle_area(10).toFixed(3)】、【circle_area(10)】和【toFixed(3)】
rounded_cylinder_volume = cylinder_volume(10, 20).toFixed(3) ;
此表达式内含如下子表达式:
• 左侧表达式:【rounded_cylinder_volume =】和【cylinder_volume(10, 20).】
• 主要表达式:【cylinder_volume(10, 20).toFixed(3)】、【cylinder_volume(10, 20)】和【toFixed(3)】
rounded_cylinder_surface_area = cylinder_surface_area(10, 20).toFixed(3) ;
此表达式内含如下子表达式:
• 左侧表达式:【rounded_cylinder_surface_area =】和【cylinder_surface_area(10, 20).】
• 主要表达式:【cylinder_surface_area(10, 20).toFixed(3)】、【cylinder_surface_area(10, 20)】和【toFixed(3)】
console.log(rounded_circle_area) ; console.log(rounded_cylinder_volume) ; console.log(rounded_cylinder_surface_area) ;
如上3个语句排除分号【;】的其余部分,皆为主要表达式。
2.1.4 函数的返回值
在多种编程语言中,关键字return开头的语句,除了会终止当前函数内源代码的执行,并返回调用函数的子表达式之外,亦会使得前述子表达式,被取代成为返回值(return value),成为原始表达式的操作数。关于返回值的理解,可参考如下示例。
【2-1-4-function-return-values.js】
// cv stands for "cubic volume". function cv01(l, w, h) { let cubic_volume = l * w * h ; console.log(`cubic volume of (${l}, ${w}, ${h}) = ${cubic_volume}`) ; // return ; } function cv02(l, w, h) { let cubic_volume = l * w * h ; return cubic_volume ; } let result01 = cv01(3, 5, 10) ; let result02 = cv02(3, 5, 10) ; let result03 = cv02(1, 3, 5) + cv02(2, 4, 6) ; console.log(`result01 = ${result01}`) ; console.log(`result02 = ${result02}`) ; console.log(`result03 = ${result03}`) ;
【相关说明】
// cv stands for "cubic volume". function cv01(l, w, h) { let cubic_volume = l * w * h ; console.log(`cubic volume of (${l}, ${w}, ${h}) = ${cubic_volume}`) ; // return ; }
• 在上述函数内的末尾处,并未放置return语句,或是return语句仅仅衔接分号【;】。所以,此段源代码定义了未带返回值的函数cv01()。
• 函数cv01()可在网页浏览器的调试工具【Console】面板里,显示出长、宽、高可能不同的立方体积。
function cv02(l, w, h) { let cubic_volume = l * w * h ; return cubic_volume ; }
• 此源代码片段定义了具有返回值的函数cv02(),因为在上述函数内的末尾处,放置了语句【return cubic_volume ;】。
• 函数cv02()亦可计算出长、宽、高可能不同的立方体积。
let result01 = cv01(3, 5, 10) ;
• 此语句调用了函数cv01(3, 5, 10),显示出长、宽、高各为3、5、10的立方体积。
• 因为函数cv01()并无返回值,所以变量result01会被赋予原始常量undefined。
let result02 = cv02(3, 5, 10) ;
此语句调用了函数cv02(3, 5, 10),并将长、宽、高各为3、5、10的立方体积结果值,返回到此语句,成为新的操作数150,使得此语句等价于【let result02 = 150 ;】,进而让变量result02的数值变成150。
let result03 = cv02(1, 3, 5) + cv02(2, 4, 6) ;
此语句调用了函数cv02(1, 3, 5)和cv02(2, 4, 6),并将长、宽、高各为1、3、5与2、4、6的个别立方体积结果值,返回到此语句,成为新的操作数15与48,使得此语句等价于【let result02 = 15+ 48 ;】,进而让变量result02的数值变成63。
console.log(`result01 = ${result01}`) ;
此语句显示出变量result01的数据为undefined。
console.log(`result02 = ${result02}`) ; console.log(`result03 = ${result03}`) ;
这两个语句分别显示出变量result02的数值150,以及变量result03的数值63。
2.2 运算符
各种计算机编程语言均支持一系列的运算符,并和参与其中的操作数,构成特定形态的表达式,以满足计算机各种复杂的演算、分析与归纳。
2.2.1 算术运算符(ES7)
JavaScript语言的算术运算符如表2-1所示。
表2-1 JavaScript的运算符

下面通过示例介绍各种算术运算符的运用。
【2-2-1-arithmetic-operators.js】
let num01 = 125, num02 = 10, num03 = 5 ; let result = 0 ; result = num01 % num02 ; console.log(result) ; result = num01 / num02 ; console.log(result) ; result = num01 * num02 ; console.log(result) ; result = num01 + num02 - num03 ; console.log(result) ; result = ++num01 ; console.log(result, num01) ; result = num01++ ; console.log(result, num01) ; ++num01 ; num01++ ; console.log(num01) ; num01 += 1 ; console.log(num01) ; num01 = num01 + 1 ; console.log(num01) ; result = --num02 ; console.log(result, num02) ; result = num02-- ; console.log(result, num02) ; --num02 ; num02-- ; console.log(num02) ; num02 -= 1 ; num02 = num02 - 1 ; console.log(num02) ; result = num03 ** 3 ** 2 ; console.log(result) ; result = num03 ** (3 ** 2) ; console.log(result) ; result = (num03 ** 3) ** 2 ; console.log(result) ;
【相关说明】
let num01 = 125, num02 = 10, num03 = 5 ; let result = 0 ;
这两个语句声明了具有初始数据的变量num01、num02、num03与result。
result = num01 % num02 ;
此语句使得变量result,被赋予了【变量num01的数值除以变量num02的数值】的余数。
result = num01 / num02 ;
此语句使得变量result,被赋予了【变量num01的数值除以变量num02的数值】的结果值。
result = num01 * num02 ;
此语句使得变量result,被赋予了【变量num01的数值乘以变量num02的数值】的结果值。
result = num01 + num02 - num03 ;
此语句使得变量result,被赋予了【变量num01的数值加上变量num02的数值,再减去变量num03的数值】的结果值。
result = ++num01 ;
此语句被执行之前,变量num01的数值为125。
因为运算符++出现在变量num01的左侧,也就意味着num01的数值要先递增为126,再执行【result = num01】。
所以,result的数值最终为126。
result = num01++ ;
此语句被执行之前,变量num01的数值已经变成126。
因为运算符++出现在变量num01的右侧,也就意味着要先执行【result = num01】,之后num01的数值才递增为127。
所以,result的数值最终仍然为126。
++num01 ; num01++ ;
这两个语句被执行之前,变量num01的数值已经变成127。因为表达式++num01与num01++均单独出现在语句中,所以可简单视为变量num01的数值,被进行了2次递增,变成129。
num01 += 1 ;
此语句被执行之前,变量num01的数值已经变成129。此语句等同于递增变量num01的数值,使得num01的数值成为130。
num01 = num01 + 1 ;
此语句被执行之前,变量num01的数值已经变成130。此语句亦等同于递增变量num01的数值,成为131。
result = --num02 ;
此语句被执行之前,变量num02的数值为10。因为运算符--出现在变量num02的左侧,也就意味着num02的数值要先递减为9,再执行【result = num02】。所以,result的数值最终为9。
result = num02-- ;
此语句被执行之前,变量num02的数值已经变成9。因为运算符--出现在变量num02的右侧,也就意味着要先执行【result = num02】,之后num02的数值才递减为8。所以,result的数值最终仍然为9。
--num02 ; num02-- ;
这两个语句被执行之前,变量num02的数值已经变成8。因为运算符--num02与num02--均单独出现在语句中,所以可简单视为变量num01的数值,被进行了两次递减,变成6。
num02 -= 1 ;
此语句被执行之前,变量num02的数值已经变成6。此语句等同于递减变量num02的数值,成为5。
num02 = num02 - 1 ;
此语句被执行之前,变量num02的数值已经变成5。此语句亦等同于递减变量num02的数值,成为4。
result = num03 ** 3 ** 2 ;
在此,变量num03的数值是5。因为求幂运算符**具有右结合的特征,所以【3 ** 2】要先被评估成为32,也就是9,然后再评估【num03 ** 9】,结果值为59,也就是1953125。
result = num03 ** (3 ** 2) ;
在此,借助小括号运算符()的辅助,可明确得知【(3 ** 2)】会优先被评估成为32,也就是9,然后评估【num03 ** 9】的结果值为59,也就是1953125。
result = (num03 ** 3) ** 2 ;
在此,使用小括号运算符()的辅助,可明确得知【(num03 ** 3)】会优先被评估成为53,也就是125,然后再评估【125** 2】的结果值为1252,也就是15625。
2.2.2 赋值运算符
JavaScript编程语言的赋值运算符(assignment operator)存在基本形式的等号【=】,以及如表2-2所示的复合形式。
表2-2 JavaScript的赋值运算符

关于赋值运算符的运用,可参考如下示例。
【2-2-2-assignment-operators.js】
let a = 18, b = 5, c = 2 ; let result = 0 ; result = a + b + c ; console.log(result) ; // a = a + b ; a += b ; console.log(a) ; // a = a - b ; a -= b ; console.log(a) ; // a = a * b ; a *= b ; console.log(a) ; // a = a / b ; a /= b ; console.log(a) ; // a = a % b ; a %= b ; console.log(a) ; // b = b ** c ; b **= c ; console.log(b) ; // b = b << c ; b <<= c ; console.log(b) ; // b = b >> c ; b >>= c ; console.log(b) ; // b = b >>> c ; b >>>= c ; console.log(b) ; // b = b & c ; b &= c ; console.log(b) ; // b = b ^ c ; b ^= c ; console.log(b) ; // b = b | c ; b |= c ; console.log(b) ;
【相关说明】
let a = 18, b = 5, c = 2 ; let result = 0 ;
这两个语句声明了具有初始数据的变量a、b、c与result。
result = a + b + c ;
此语句里的赋值运算符【=】,使得变量result的数值,成为变量a、b、c各数值的总和。
// a = a + b ; a += b ;
此语句里的赋值运算符【+=】,使得变量a的数值,成为【变量a本身的数值加上变量b的数值】的结果值。
// a = a - b ; a -= b ;
此语句里的赋值运算符【-=】,使得变量a的数值,成为【变量a本身的数值减去变量b的数值】的结果值。
// a = a * b ; a *= b ;
此语句里的赋值运算符【*=】,使得变量a的数值,成为【变量a本身的数值乘以变量b的数值】的结果值。
// a = a / b ; a /= b ;
此语句里的赋值运算符【/=】,使得变量a的数值,成为【变量a本身的数值除以变量b的数值】的结果值。
// a = a % b ; a %= b ;
此语句里的赋值运算符【%=】,使得变量a的数值,成为【变量a本身的数值除以变量b的数值】的余数。
// b = b ** c ; b **= c ;
此语句里的赋值运算符【**=】,使得变量b的数值,成为【变量b本身数值的c幂次】(bc)的结果值。
// b = b << c ; b <<= c ;
此语句里的赋值运算符【<<=】,使得变量b的二进制数值,向左偏移变量c所代表的比特位(bit)个数。
// b = b >> c ; b >>= c ;
此语句里的赋值运算符【>>=】,使得变量b的二进制数值,在保留正负号的前提下,向右偏移变量c所代表的比特位(bit)个数。所谓的保留正负号就是:
• 若该二进制数值为负值,则其最左侧的符号位(sign bit)是1,并在向右偏移的同时,保持其符号位为1。
• 若该二进制数值为正值,则其最左侧的符号位是0,并在向右偏移的同时,保持其符号位为0。
// b = b >>> c ; b >>>= c ;
此语句里的赋值运算符【>>>=】,使得变量b的数值,成为【变量b的二进制数值,向右偏移变量c所代表的比特位(bit)个数,并在其左侧补上相同个数的二进制0】之后的结果值。
在其左侧补上相同个数的二进制0,也就意味着,一开始无论其最左侧的符号位是0(正值)或是1(负值),后续皆在向右偏移的同时,保持符号位成为0。
// b = b & c ; b &= c ;
此语句里的赋值运算符【&=】,使得变量b的数值,成为【变量b本身的二进制数值与变量c的二进制数值,进行按位与(bitwise and)运算】之后的结果值。
// b = b ^ c ; b ^= c ;
此语句里的赋值运算符【^=】,使得变量b的数值,成为【变量b的二进制数值与变量c的二进制数值,进行按位异或(bitwise exclusive or)运算】之后的结果值。
// b = b | c ; b |= c ;
此语句里的赋值运算符【^=】,使得变量b的数值,成为【变量b的二进制数值与变量c的二进制数值。进行按位或(bitwise or)运算】之后的结果值。
2.2.3 比较运算符
在各编程语言中,比较运算符(comparison operator)是用来决定其两侧操作数相等或不相等的关系。JavaScript编程语言的比较运算符如表2-3所示。
表2-3 JavaScript语言的比较运算符

关于比较运算符的综合运用,可参考如下示例。
【2-2-3-comparison-operators.js】
let v01 = 100 , v02 = 250, v03 = 500 ; let s01 = '100', s02 = '250', s03 = '500' ; console.log(v01 == s01) ; console.log(v01 === s01) ; console.log(v02 > s01) ; console.log(v02 < s03) ; console.log(v03 >= s03) ; console.log(v03 <= s03) ;
【相关说明】
let v01 = 100 , v02 = 250, v03 = 500 ;
此语句声明了初始数值为整数常量的变量v01、v02和v03。
let s01 = '100', s02 = '250', s03 = '500' ;
此语句声明了初始数据为字符串字面量的变量s01、s02与s03。
console.log(v01 == s01) ;
判断变量v01与s01的数据是否相同,并显示判断为真的返回值true,于浏览器的调试工具【Console】面板中。
console.log(v01 === s01) ;
判断变量v01与s01的数据是否相同,以及其数据类型是否也相同,并显示判断为假的返回值false,于浏览器的调试工具【Console】面板中。
console.log(v02 > s01) ;
判断变量v02的数值是否大于变量s01的数据,并显示判断为真的返回值true,于浏览器的调试工具【Console】面板中。
console.log(v02 < s03) ;
判断变量v02的数值是否小于变量s03的数据,并显示判断为真的返回值true,于浏览器的调试工具【Console】面板中。
console.log(v03 >= s03) ;
判断变量v03的数值是否大于或者等于变量s03的数据,并显示判断为真的返回值true于浏览器调试工具的【Console】面板中。
console.log(v03 <= s03) ;
判断变量v03的数值是否小于或者等于变量s03的数据,并显示判断为真的返回值true,于浏览器的调试工具【Console】面板中。
2.2.4 逻辑运算符
在各种编程语言中,逻辑运算符(logical operator)主要用来串联带有比较含义的表达式。JavaScript编程语言的逻辑运算符如表2-4所示。
表2-4 JavaScript语言的逻辑运算符

关于逻辑运算符的综合运用,可参考如下示例。
【2-2-4-logical-operators.js】
let v01 = 10, v02 = 50, v03 = 60 ; let values = [10, 20, 30, 40, 50] ; let person = {name: 'Gary', gender: 'male', age: '25'} ; // result = true && true ; result = v01 < v02 && v02 < v03 ; console.log(result) ; // result = false || true ; result = v01 > v02 || v03 > v02 ; console.log(result) ; // result = ! false ; result = ! (v01 > v02) ; console.log(result) ; result = 2 in values ; console.log(result) ; result = 'length' in values ; console.log(result) ; result = 'round' in Math ; console.log(result) ; result = 'gender' in person ; console.log(result) ;
【相关说明】
let v01 = 10, v02 = 50, v03 = 60 ;
此语句声明了初始数值为整数常量的变量v01、v02与v03。
let values = [10, 20, 30, 40, 50] ;
此语句声明了初始数据为数组实例的变量values。
let person = {name: 'Gary', gender: 'male', age: '25'} ;
此语句声明了初始数据为对象实例的变量person。
// result = true && true ; result = v01 < v02 && v02 < v03 ;
【v01 < v02】为真,所以返回true;【v02 < v03】为真,所以返回true。因此,【v01 < v02 &&v02 < v03】亦为真而返回true,使得变量result的数据成为布尔值true。
// result = false || true ; result = v01 > v02 || v03 > v02 ;
【v01 > v02】为假,所以返回false;【v03 > v02】为真,所以返回true。因此,【v01 > v02 ||v03 > v02】亦为真而返回true,使得变量result的数据成为布尔值true。
// result = ! false ; result = ! (v01 > v02) ;
【v01 > v02】为假,所以返回false。因此,【! (v01 > v02)】为真而返回true,使得变量result的数据成为布尔值true。
result = 2 in values ;
变量values的数据是数组实例[10, 20, 30, 40, 50],所以values[0]可访问到数组实例的元素值10;values[4]可访问到数组实例的元素值50。
在数组实例名称values右侧的中括号[]里面,例如values[3],存在作为索引值(index value)的整数值3,以访问其索引值3所代表的特定元素40。
在此,因为索引值2(第3个)对应到values[2]所代表的第3个元素值30,所以【2 in values】为真而返回true,使得变量result的数据成为布尔值true。
result = 'length' in values ;
变量values的数据是数组实例,因此变量values具有属性length,可用来获取values.length所代表元素个数的整数值5。所以,【'length' in values】为真而返回true,使得变量result的数据成为布尔值true。
result = 'round' in Math ;
内置的对象Math具有函数round(),所以【'round' in Math】为真而返回true,进而使得变量result的数据成为布尔值true。
result = 'gender' in person ;
变量person的数据是对象实例{name: 'Gary', gender: 'male', age: '25'},所以属性gender可用来访问到person.gender所代表属性的数据'male'。所以,【'gender' in person】为真而返回true,使得变量result的数据成为布尔值true。
2.2.5 条件运算符
从C语言开始,许多后继的编程语言,均支持条件运算符(conditional operator)/三元运算符(ternary operator),并用来简化特定形式的if语句。关于条件运算符的运用,可参考如下示例。
【2-2-5-conditional-operator.js】
let score, passed ; score = 58 ; passed = score >= 60 ? 'yes' : 'no' ; console.log(passed) ; score = 77 ; passed = score >= 60 ? 'yes' : 'no' ; console.log(passed) ; score = 60 ; if (score >= 60) passed = 'yes' ; else passed = 'no' ; console.log(passed) ;
【相关说明】
let score, passed ;
此语句声明了变量score与passed。
score = 58 ;
此语句使得变量score,被赋予整数值58。
passed = score >= 60 ? 'yes' : 'no' ;
对于表达式【score >= 60 ? 'yes' : 'no'】而言,若【score >= 60】为真,则返回字符串'yes';否则返回字符串'no'。在此,其为假,所以返回字符串'no',进而使得变量passed的数据,成为字符串'no'。
score = 77 ;
此语句使得变量score,被赋予整数值77。
passed = score >= 60 ? 'yes' : 'no' ;
在此,表达式【score >= 60 ? 'yes' : 'no'】返回字符串'yes',进而使得变量passed的数据,成为字符串'yes'。
score = 60 ;
此语句使得变量score,被赋予整数值60。
if (score >= 60) passed = 'yes' ; else passed = 'no' ;
对于此条件语句而言,若【score >= 60】为真,则变量passed会被赋予字符串'yes';否则会被赋予字符串'no'。在此,其为真,所以返回字符串'yes',进而使得变量passed,被赋予字符串'yes'。
2.2.6 类型运算符
类型运算符(typeof operator)用来返回特定操作数(operand)的数据类型(data type)。关于类型运算符的综合运用,可参考如下示例。
【2-2-6-typeof-operator.js】
let num01 = 33, num02 = 1.414 ; console.log(typeof num01) ; console.log(typeof 33) ; console.log(typeof num02) ; console.log(typeof 1.414) ; console.log('') ; console.log(typeof Math.PI) ; console.log(typeof NaN) ; console.log(typeof Infinity) ; console.log('') ; console.log(typeof '') ; console.log(typeof "") ; console.log(typeof "Hello, Earth!") ; console.log('') ; console.log(typeof true) ; console.log(typeof false) ; console.log(typeof (num01 > num02)) ; console.log('') ; console.log(typeof undefined) ; console.log(typeof num03) ; console.log('') ; console.log(typeof function() {}) ; console.log(typeof Array.isArray) ; console.log('') ; console.log(typeof Object()) ; console.log(typeof new Object()) ; console.log(typeof {}) ; console.log('') ; console.log(typeof Array()) ; console.log(typeof new Array()) ; console.log(typeof []) ; console.log('') ; console.log(typeof null) ; console.log('') ; console.log(typeof String('test')) ; console.log(typeof new String('test')) ; console.log('') ; console.log(typeof Number(123)) ; console.log(typeof new Number(123)) ; console.log('') ; console.log(typeof Date()) ; console.log(typeof new Date()) ;
【相关说明】
let num01 = 33, num02 = 1.414 ;
此语句声明了具有初始数值的变量num01与num02。
console.log(typeof num01) ;
表达式【typeof num01】会得出变量num01的数据类型名称,在此为字符串'number'。
console.log(typeof 33) ;
表达式【typeof 33】会得出常量33的数据类型名称,在此为字符串'number'。
console.log(typeof num02) ;
表达式【typeof num02】会得出变量num02的数据类型名称,在此为字符串'number'。
console.log(typeof 1.414) ;
表达式【typeof 1.414】会得出常量1.414的数据类型名称,在此为字符串'number'。
console.log(typeof Math.PI) ;
表达式【typeof Math.PI】会得出内置对象Math的常量属性PI的数据类型名称,在此为字符串'number'。
console.log(typeof NaN) ;
表达式【typeof NaN】会得出原始常量NaN的数据类型名称,在此为字符串'number'。
console.log(typeof Infinity) ;
表达式【typeof Infinity】会得出内置常量Infinity的数据类型名称,在此为字符串'number'。
console.log(typeof '') ;
表达式【typeof''】会得出空字符串''的数据类型名称,在此为字符串'string'。
console.log(typeof "") ;
表达式【typeof ""】会得出空字符串""的数据类型名称,在此为字符串'string'。
console.log(typeof "Hello, Earth!") ;
表达式【typeof "Hello, Earth!"】会得出字符串"Hello, Earth!"的数据类型名称,在此为字符串'string'。
console.log(typeof true) ;
表达式【typeof true】会得出布尔值true的数据类型名称,在此为字符串'boolean'。
console.log(typeof false) ;
表达式【typeof false】会得出布尔值false的数据类型名称,在此为字符串'boolean'。
console.log(typeof (num01 > num02)) ;
表达式【typeof (num01 > num02)】会得出子表达式【num01 > num02】返回值的数据类型名称,在此为字符串'boolean'。
console.log(typeof undefined) ;
值得关注的是,原始常量undefined的数据类型亦是undefined。表达式【typeof undefined】会得出原始常量undefined的数据类型名称,在此为字符串'undefined'。
console.log(typeof num03) ;
在此,num03未被声明为变量或函数名称,所以num03处于未定义(undefined)的状态。表达式【typeof num03】会得出未被声明的num03的数据类型名称,在此为字符串'undefined'。
console.log(typeof function() {}) ;
表达式【typeof function() {}】会得出匿名函数的数据类型名称,在此为字符串'function'。
console.log(typeof Array.isArray) ;
表达式【typeof Array.isArray】会得出内置对象Array的函数isArray()的数据类型名称,在此为字符串'function'。
console.log(typeof Object()) ;
因为Object对象的构造函数Object(),会返回Object对象的实例;也因此表达式【typeof Object()】会得出Object对象实例的数据类型名称,在此为字符串'object'。
console.log(typeof new Object()) ;
表达式【typeof new Object()】也会得出Object对象实例的数据类型名称,在此亦为字符串'object'。此外,JavaScript语言尚未界定【Object()】与【new Object()】的明显区别。
console.log(typeof {}) ;
表达式【typeof {}】会得出空对象实例{}的数据类型名称,在此亦为字符串'object'。而在JavaScript编程语言中,如下3个语句是等价的:
• obj = {} ;
• obj = new Object() ;
• obj = Object() ;
console.log(typeof Array()) ;
因为Array对象的构造函数Array(),会返回Array对象的实例;也因此表达式【typeof Array()】会得出Array对象实例的数据类型名称,在此竟然也为字符串'object'。
console.log(typeof new Array()) ;
表达式【typeof new Array()】亦会得出Array对象实例的数据类型名称,在此亦为字符串'object'。此外,JavaScript编程语言尚未界定【Array()】与【new Array()】的明显区别。
console.log(typeof []) ;
表达式【typeof []】会得出空数组实例[]的数据类型名称,在此为字符串'object'。而在JavaScript语言中,如下3个语句是等价的:
• arr = [] ;
• arr = new Array() ;
• arr = Array() ;
在JavaScript语言里,将数组实例(array instance)的数据类型视为object。
console.log(typeof null) ;
【typeof null】表达式会得出原始常量null的数据类型名称,在此为字符串'object'。
console.log(typeof String('test')) ;
内置对象String的构造函数String('test'),只会返回字符串'test'。因此,表达式【typeof String('test')】等同于获得字符串'test'的数据类型名称,在此为字符串'string'。
console.log(typeof new String('test')) ;
内置对象String的构造函数String('test')会返回字符串'test'。再经过new关键字的处理,会返回内含字符串'test'的String对象实例。因此,表达式【typeof new String('test')】等同于获得String对象实例的数据类型名称,在此为字符串'object'。
console.log(typeof Number(123)) ;
内置对象Number的构造函数Number(123),会返回整数常量123。因此,【typeof Number(123)】表达式等同于获得整数常量123的数据类型名称,在此为字符串'number'。
console.log(typeof new Number(123)) ;
内置对象Number的构造函数Number(123),会返回整数常量123。再经过new关键字的处理,会返回内含整数常量123的Number对象实例。因此,表达式【typeof new Number(123)】等同于获得Number对象实例的数据类型名称,在此为字符串'object'。
console.log(typeof Date()) ;
内置Date对象的构造函数Date(),会返回当前日期与时间的字符串。因此,表达式【typeof Date()】等同于获得当前日期与时间的字符串的数据类型名称,在此为字符串'string'。
console.log(typeof new Date()) ;
内置Date对象的构造函数Date(),会返回当前日期与时间的字符串。再经过new关键字的处理,会返回内含当前日期与时间的Date对象实例。因此,表达式【typeof new Date()】等同于获得Date对象实例的数据类型名称,在此为字符串'object'。
2.2.7 按位运算符
JavaScript语言的按位运算符(bitwise operator),在被运算之前,其两侧操作数(operand)的数据,会被转换成为32个比特位的数据。关于按位运算符的综合运用,可参考如下示例。
【2-2-7-bitwise-operators.js】
let num01 = 56, num02 = 77 ; let num03 = 124, num04 = -3 ; console.log(num01.toString(2)) ; console.log(num02.toString(2)) ; console.log(num03.toString(2)) ; console.log('') ; console.log(num01 == 0b111000) ; console.log(num01 == 0b00111000) ; console.log('') ; console.log(num02 == 0b1001101) ; console.log(num02 == 0b01001101) ; console.log('') ; console.log(num03 == 0b1111100) ; console.log(num03 == 0b01111100) ; console.log('') ; /* 00111000 &01001101 ---------- 00001000 */ result = num01 & num02 ; console.log(result) ; console.log(result.toString(2)) ; console.log('') ; /* 00111000 |01001101 ---------- 01111101 */ result = num01 | num02 ; console.log(result) ; console.log(result.toString(2)) ; console.log('') ; /* 00111000 ^01001101 ---------- 01110101 */ result = num01 ^ num02 ; console.log(result) ; console.log(result.toString(2)) ; console.log('') ; /* ~ 01111100 ------------ 10000011 (negative value) = -01111100 + 00000001 (because of 2's complement) ------------ -01111101 */ result = ~ num03 ; console.log(result) ; console.log(result.toString(2)) ; console.log('') ; /* << 001111100 ------------ 011111000 */ result = num03 << 1 ; console.log(result) ; console.log(result.toString(2)) ; console.log('') ; /* >> 001111100 ------------ 000111110 */ result = num03 >> 1 ; console.log(result) ; console.log(result.toString(2)) ; console.log('') ; /* but -3 actually is 11111111111111111111111111111101 in 32-bit 2's complement. So, >>> 11111111111111111111111111111101 -------------------------------------- 01111111111111111111111111111110 */ result = num04 >>> 1 ; console.log(num04.toString(2)) ; console.log(result) ; console.log((2 ** 31 - 1) - 1) ; console.log(result.toString(2)) ;
【相关说明】
let num01 = 56, num02 = 77 ; let num03 = 124, num04 = -3 ;
这两个语句声明了初始数据为整数值的变量num01、num02、num03与num04。
console.log(num01.toString(2)) ; console.log(num02.toString(2)) ; console.log(num03.toString(2)) ;
这3个语句中的【.toString(2)】,可分别将变量num01、num02与num03的整数值,转换成为字符串类型的二进制数码。
console.log(num01 == 0b111000) ;
【num01 == 0b111000】可用来判断变量num01的数值,是否等于二进制整数【111000】。在此,其返回true。加上【0b】在二进制数码的左侧,可使得此代码被视为二进制数值。
在此亦可得知,浏览器中的JavaScript引擎,仍然将最左比特位(left-most bit)为1的数值,视为正值(positive value)。因此,若要变更成为负值(negative value),则修改为【- 0b111000】。
console.log(num01 == 0b00111000) ;
【num01 == 0b00111000】可用来判断变量num01的数值,是否等于二进制整数【00111000】。在此,其返回true。
在此可得知,浏览器中的JavaScript引擎将【0b00111000】与【0b111000】,视为相同的二进制数值。
console.log(num02 == 0b1001101) ; console.log(num02 == 0b01001101) ;
【num02 == 0b1001101】可用来判断变量num02的数值,是否等于二进制整数【1001101】。在此,其返回true。
【num02 == 0b01001101】可用来判断变量num02的数值,是否等于二进制整数【01001101】。在此,其返回true。
在此可得知,JavaScript引擎将【0b1001101】与【0b01001101】,视为相同的二进制数值。
console.log(num03 == 0b1111100) ; console.log(num03 == 0b01111100) ;
【num03 == 0b1111100】可用来判断变量num03的数值,是否等于二进制整数【0b1111100】。在此,其返回true。
【num03 == 0b01111100】可用来判断变量num03的数值,是否等于二进制整数【0b01111100】。在此,其返回true。
由此可得知,JavaScript引擎将【0b1111100】与【0b01111100】,视为相同的二进制数值。
/* 00111000 &01001101 ---------- 00001000 */ result = num01 & num02 ;
【num01 & num02】可得到【变量num01与num02的二进制数据,进行按位和(bitwise and)运算】之后的结果值。
/* 00111000 |01001101 ---------- 01111101 */ result = num01 | num02 ;
【num01 | num02】可得到【变量num01与num02的二进制数据,进行按位或(bitwise or)运算】之后的结果值。
/* 00111000 ^01001101 ---------- 01110101 */ result = num01 ^ num02 ;
【num01 ^ num02】可得到【变量num01与num02的二进制数据,进行按位异或(bitwise exclusive or)运算】之后的结果值。
/* ~ 01111100 ------------ 10000011 (negative value) = -01111100 + 00000001 (because of 2's complement) ------------ -01111101 */ result = ~ num03 ;
【~ num03】会得到【变量num03的二进制数据,进行2的补码(two's complement)】之后的结果值。
/* << 001111100 ------------ 011111000 */ result = num03 << 1 ;
【num03 << 1】会得出【变量num03的2进位数据,向左偏移1个比特位】之后的结果值。
/* >> 001111100 ------------ 000111110 */ result = num03 >> 1 ;
【num03 >> 1】会得出【变量num03的2进位数据,向右偏移1个比特位】之后的结果值。
/* but -3 actually is 11111111111111111111111111111101 in 32-bit 2's complement. So, >>> 11111111111111111111111111111101 -------------------------------------- 01111111111111111111111111111110 */ result = num04 >>> 1 ;
【num04 >>> 1】会得出【变量num04的2进位数据,向右偏移1个比特位的同时,在其最左侧的符号位(sign bit),填入0】之后的结果值。
此语句被执行之前,变量num04的数值为负整数-3。此语句被执行之后,变量num04的数值却变成非常大的正整数2147483646,可见其符号位被填入了0。
console.log((2 ** 31 - 1) - 1) ;
【(2 ** 31 - 1) - 1】会评估出(231 - 1) - 1,也就是正整数2147483646。
2.2.8 括号运算符
括号运算符包含:
• 小括号/圆括号(parentheses, round brackets)运算符():除了用于变更特定表达式的运算优先级之外,亦被用于if、switch、for、while等语句和函数的调用。
• 中括号/方括号(brackets, square brackets)运算符[]:主要用于数组(array)或字符串(string)相关的定义和访问。
• 大括号/花括号(braces, curly brackets)运算符{}:用于语句的分组(grouping)、函数的主体构造,以及对象(object)的定义。
关于括号运算符的综合运用,可参考如下示例。
【2-2-8-brackets-operators.js】
let a = 10, b = 5, c = 3 ; let result = 0 ; result = a - b * c ; console.log(result) ; result = (a - b) * c ; console.log(result) ; result = a ** c ** 2 ; console.log(result) ; result = (a ** c) ** 2 ; console.log(result) ; console.log('') ; /// let now = new Date() ; console.log(now) ; console.log(now.toLocaleString()) ; console.log('') ; let obj = new Object() ; obj.name = 'Jasper' ; obj.gender = 'male' ; obj.age = 28 ; console.log(obj) ; console.log('') ; /// function display(choice, message) { if (choice == 1) alert(message) ; else if (choice == 2) confirm(message) ; } display(1, 'Hello, Earth!') ; display(2, 'Hello, are you human?') ; /// let fruits = ['apple', 'banana', 'cherry', 'durian'] ; console.log(fruits[1]) ;
【相关说明】
let a = 10, b = 5, c = 3 ; let result = 0 ;
这两个语句声明了具有初始数值的变量a、b、c和result。
result = a - b * c ;
表达式【a - b * c】会先被计算出【b * c】的结果值,再计算出整个表达式的结果值。
result = (a - b) * c ;
因为小括号运算符的缘故,表达式【(a - b) * c】会先被计算出【a - b】的结果值,再计算出整个表达式的结果值。
result = a ** c ** 2 ;
因为求幂运算符**具有右结合的特征,所以表达式【a ** c ** 2】会先被计算出【c ** 2】的结果值,再计算整个表达式的结果值。
result = (a ** c) ** 2 ;
因为小括号运算符的缘故,表达式【(a ** c) ** 2】会先被计算出【a ** c】的结果值,再被计算出整个表达式的结果值。
let now = new Date() ;
此语句使得变量now,具有初始数据为【内含当前日期与时间】的Date对象实例。
console.log(now) ;
借助此语句,可在网页浏览器的调试工具【Console】面板中,显示出标准格式的日期与时间。例如:【Fri Dec 29 2017 01:16:16 GMT+XX00 (XX标准时间)】。
console.log(now.toLocaleString()) ;
【now.toLocalString()】可将日期与时间,以精简的本地格式,显示出来。例如:【2017/12/29上午1:16:16】。
let obj = new Object() ;
因为小括号运算符的缘故,使得Object对象的构造函数Object()被调用。此语句使得变量obj的初始数据,成为Object对象的空实例(empty instance)。
obj.name = 'Jasper' ; obj.gender = 'male' ; obj.age = 28 ;
这3个语句分别设置了变量obj的新属性name、gender、age和其个别数据'Jasper'、'male'、28。
console.log(obj) ;
此语句可显示出变量obj的数据,也就是对象实例{name: "Jasper", gender: "male", age: 28}。
function display(choice, message)
因为小括号运算符的缘故,网页浏览器或其他软件中的JavaScript引擎,可认出关键字function开头的此源代码片段,即是用来定义【带有参数choice和message,而且其名称为display】的函数。
{ if (choice == 1) alert(message) ; else if (choice == 2) confirm(message) ; }
借助大括号运算符{},JavaScript引擎可认出大括号{ ... }里的源代码,即是函数display()的主体结构。
在函数display()的主体结构里,仍然可以看到小括号运算符,出现在if语句中。另外,亦可看到小括号运算符,出现在内置函数alert()与confirm()的调用语法中。
display(1, 'Hello, Earth!') ; display(2, 'Hello, are you human?') ;
这两个语句,通过传入不同参数值,而重复调用了函数display()。
let fruits = ['apple', 'banana', 'cherry', 'durian'] ;
借助中括号运算符[],在此语句里的等号右侧,即是内含4个字符串元素的数组实例。
console.log(fruits[1]) ;
此语句通过数组变量的名称fruits,以及中括号运算符里的元素索引值1,访问到fruits[1]所代表的字符串元素'banana'。
2.2.9 扩展运算符(ES6)
简单来说,由3个英文句点【...】构成的扩展运算符(spread operator),可被用来【卸除】特定数组的中括号或者特定对象的大括号。关于扩展运算符的综合运用,可参考如下示例。
【2-2-9-spread-operator.js】
var greetings = ['Hi', 'Howdy', 'Hey, man', 'G\'day mate'] ; var extended_greetings = ['Long time no see', 'Nice to see you', 'Hiya' , ... greetings] ; console.log(extended_greetings) ; console.log('') ; var number_texts = ['one', 'two', 'three'] var number_digits = '123' ; var numbers = [... number_texts, ... number_digits] console.log(numbers) ; console.log('') ; /// let birthday = new Date(1999, 11, 25, 20, 30) ; let now = new Date() ; console.log(birthday.toLocaleString()) ; console.log(now.toLocaleString()) ; console.log('') ; /// let arr01 = [1, 2, 3] ; let arr02 = [10, 20, 30] ; let arr03 = [... arr01, ... arr02, 100, 200, 300] ; let obj01 = {name: 'orange', amount: 10} ; let obj02 = {name: 'durian', amount: 5, origin: 'Thai'} ; let obj03 = {... obj01, ... obj02} ; console.log(arr03) ; console.log(obj03) ; console.log('') ;
【相关说明】
var greetings = ['Hi', 'Howdy', 'Hey, man', 'G\'day mate'] ;
此语句声明了初始数据为数组实例的变量greetings。
var extended_greetings = ['Long time no see', 'Nice to see you', 'Hiya' , ... greetings] ;
此语句声明了初始数据为另一数组实例的变量extended_greetings。扩展运算符【...】使得变量extended_greetings的数组实例,带有变量greetings的数组实例中的所有元素。
var number_texts = ['one', 'two', 'three']
此语句亦声明了初始数据为另一数组实例的变量number_texts。
var number_digits = '123' ;
此语句声明了初始数据为字符串'123'的变量number_digits。
var numbers = [... number_texts, ... number_digits]
扩展运算符【...】使得变量numbers的初始数据,成为合并【变量number_texts与number_digits的个别数组实例】的新数组实例,进而使得变量numbers的数据,成为数组实例["one", "two", "three","1", "2", "3"]。
let birthday = new Date(1999, 11, 25, 20, 30) ;
此语句声明了【初始数据为内含日期与时间1999/12/25 20:30:00的Date对象实例】的变量birthday。
需留意的是,构造函数Date()小括号中的第2个参数值为11,其实是代表12月份的含义。换句话说,此参数值若为0,则代表1月份。
let now = new Date() ;
此语句声明了【初始数据为当前日期与时间的Date对象实例】的变量now。
console.log(birthday.toLocaleString()) ;
此语句会显示出【1999/12/25下午8:30:00】的信息。
console.log(now.toLocaleString()) ;
此语句显示出当前日期与时间的信息。
let arr01 = [1, 2, 3] ; let arr02 = [10, 20, 30] ;
这两个语句声明了【初始数据为不同数组实例】的变量arr01与arr02。
let arr03 = [... arr01, ... arr02, 100, 200, 300] ;
此语句声明了变量arr03,并借助扩展运算符【...】,使得其【初始数据为合并变量arr01与arr02的数组实例,再衔接子数组实例[100, 200, 300]】的新数组实例[1, 2, 3, 10, 20, 30, 100, 200,300]。
let obj01 = {name: 'orange', amount: 10} ; let obj02 = {name: 'durian', amount: 5, origin: 'Thai'} ;
这两个语句声明了【初始数据为对象实例】的变量obj01与obj02。
let obj03 = {... obj01, ... obj02} ;
此语句声明了变量obj03,并借助扩展运算符【...】,使得其【初始数据为合并变量obj01与obj02对象实例】的新对象实例{name: "durian", amount: 5, origin: "Thai"}。
值得注意的是,变量obj01与obj02均存在属性name与amount。经过扩展运算之后,变量obj03的属性name与amount却只有一个,而且其属性的数据,均个别与变量obj02的属性name和amount的数据,是相同的。
2.2.10 逗号运算符
逗号运算符(comma operator)主要用来并联多个赋值表达式或操作数。关于逗号运算符的运用,可参考如下示例。
【2-2-10-comma-operator.js】
let a = 11, b = 21, c = 31 ; let d, e, f ; d = e = a + b, f = b + c ; console.log(d, e, f) ;
【相关说明】
let a = 11, b = 21, c = 31 ;
此语句声明了初始数据均为整数的变量a、b与c。
let d, e, f ;
此语句声明了未设置初始数据的变量d、e与f。
d = e = a + b, f = b + c ;
此语句主要由两个表达式【d = e = a + b】与【f = b + c】构成。
• 第1个表达式先被计算出【a + b】的结果值,再赋给变量d和e。
• 第2个表达式先被计算出【b + c】的结果值,再赋给变量f。
修改此语句中的逗号运算符【,】,成为代表语句结束的分号【;】,并不影响其结果值。只是,原本的单一语句,就变成在同一行的两个语句了。
console.log(d, e, f) ;
通过传入变量d、e和f的数值,作为函数console.log()的参数值,可让这些变量的数值,显示在同一行的信息里,例如【32 32 52】。
2.2.11 删除运算符
JavaScript语言中的删除运算符(delete operator),仅用来删除特定对象实例的特定属性。关于删除运算符的运用,可参考如下示例。
【2-2-11-delete-operator.js】
let person = {name: 'Ivory', gender: 'female', age: '30'} ; let colors = ['RoyalBlue', 'GreenYellow', 'Gold', 'Cyan'] ; var num01 = 123 ; let num02 = 456 ; num03 = 789 ; console.log(num01, num02, num03) ; console.log('') ; delete person.name ; delete colors[1] ; delete num01 ; delete num02 ; delete num03 ; console.log(person.name) ; console.log(person) ; console.log('') ; console.log(colors[1]) ; console.log(colors) ; console.log('') ; console.log(num01) ; console.log(num02) ; console.log(num03) ;
【相关说明】
let person = {name: 'Ivory', gender: 'female', age: '30'} ;
此语句声明了初始数据为对象实例的变量person。
let colors = ['RoyalBlue', 'GreenYellow', 'Gold', 'Cyan'] ;
此语句声明了初始数据为数组实例的变量colors。
var num01 = 123 ; let num02 = 456 ; num03 = 789 ;
无论有无通过关键字var或let进行声明,这3个语句分别声明了初始数据为不同整数的变量num01、num02与num03。
delete person.name ;
此语句使用关键字delete,并配合点运算符【.】,可删除变量person的对象实例的属性name。
delete colors[1] ;
此语句使用关键字delete,并配合中括号运算符[],可在变量colors数组实例中,仅删除索引值为1的元素数据'GreenYellow',但是此元素占用的缓存空间,仍然保留在其数组实例中。
delete num01 ; delete num02 ; delete num03 ;
通过这3个语句,试图删除变量num01、num02与num03;然而实际上,仅有未通过var或let关键字,加以声明的变量num03,可以被成功删除。
console.log(person.name) ;
【person.name】会返回undefined,即代表变量person的属性name,已被删除了。
console.log(person) ;
此语句只会产生{gender: "female", age: "30"}的信息。由此可见,属性name和其数据'Ivory'已被删除了。
console.log(colors[1]) ;
colors[1]在此返回undefined,即代表变量colors中的索引值为1的数据,已经被清除了。
console.log(colors) ;
此语句会产生["RoyalBlue", empty, "Gold", "Cyan"]的信息,可看出colors[1]对应的元素数据已经被清除了,才会被标记为empty。
console.log(num01) ; console.log(num02) ;
查看这两个语句所产生的信息,可得知变量num01与num02并未被删除!那是因为这两个变量,是借助var或let关键字,来加以声明的。
console.log(num03) ;
执行此语句时,会产生【num03 is not defined参考错误(reference error)】的信息。由此可知,变量num03确实被删除了。
2.2.12 运算符的优先级(ES6)
在特定表达式中,运算符的优先级(operators precedence)可用来决定各子表达式的执行顺序。在相邻的子表达式中,其运算符优先级较高的子表达式,会先被执行。关于各运算符的优先级,如表2-5所示。
表2-5 运算符的优先级


2.3 练习题
1.在JavaScript语言里,下列哪些项目是常量?
770、Math.PI、'Nice Day'、"good"、/\w\s\d/g、/.\w.\d/、TRUE、FALSE、Undefined、Null。
2.在JavaScript语言里,应该通过什么语法声明名称为love_you_forever而代表着整数值201314的常量?
3.在JavaScript语言里,应该通过什么语法声明名称为love_me_longer而代表着浮点数值2591.8的常量?
4.在如下JavaScript源代码片段里,哪些是全局变量?哪些是局部变量?
let value01 = 10 ; var value02 = 30 ; function func01(data, identity) { let result ; result = 21 + data + 2 * identity ; return result ; } var str01 = 'Finished', str02 = 'Error' ; function func02(amount, price) { let output ; output = (value01 + value02 + price) * amount ; return output ; }
5.在如下JavaScript源代码片段里,请至少列举出其中的5个表达式。
sphere_volume = r => 4 / 3 * Math.PI * Math.pow(r, 3) ;
6.编写带有上底宽度、下底宽度和高度3个参数,可计算并返回梯形面积的函数定义。
7.编写带有长轴长度、短轴长度2个参数,可计算并返回椭圆面积的函数定义。
8.如下源代码被执行之后,变量result的结果数据是什么?
let result, n01 = 10, n02 = 20 ; result = n01++ + ++n02 % 6 ;
9.如下源代码被执行之后,变量result的结果数据是什么?
let result, n03 = 30, n04 = 40 ; result = n03-- - --n04 % 6 ;
10.如下源代码被执行之后,变量result的结果数据是什么?
let result, n05 = 50, n06 = 60 ; result = (n05 / 5) ** 3 + (n06 / 10) ** 2 ;
11.如下源代码被执行之后,变量result的结果数据是什么?
let result, n07 = 70, n08 = 80 ; result = (n07 / 10) ** 2 + (n08 / 20) ** 0.5 ;
12.通过编写条件运算符【? … :】相关的源代码来实现下述功能。
已知两个变量score和rating。当score的数值低于60时,变量rating的数据为字符串'failed';当score的数值大于或等于60而小于80时,变量rating的数据为字符串'passed';当score的数值大于或等于80而小于或等于100时,变量rating的数据为字符串'nice';当score的数值超过100时,变量rating的数据为字符串'error'。
13.至少列举类型运算符typeof表达式可返回的5种数据类型。