第 4 章(表达式和运算符)(4.1~ 4.7)

表达式是JS中的一个短语,JS 解释器会将其计算出一个结果。程序中的常量是最简单的一类表达式。变量名也是一种简单的表达式,它的值就是赋值给变量的值。复杂表达式是由最简单表达式组成的。

将简单表达式组合成复杂表达式最常用的方法就是使用运算符。

4.1 原始表达式

最简单的表达式是“原始表达式”。原始表达式是表达式最小单位 ---- 它们不再包含其他表达式。JS 中的原始表达式包含常量或直接量、关键字和变量。

直接量是直接在程序中国出现的常数值:

1.23       // 数字直接量
“hello”    // 字符串直接量
/pattern/ // 正则表达式直接量

JS 中的一些保留字构成了原始表达式:

true     // 返回一个布尔值:真
false    // 返回一个布尔值:假
null     // 返回一个值:空
this     // 返回“当前”对象

和其他关键字不同,this 并不是一个常量,它在程序的不同地方返回的值也不相同。this 关键字经常在面向对象编程中出现。在一个方法体内,this 返回调用这个方法的对象

最后,第三种原始表达式是常量:

i                // 返回变量 i 的值
sum           // 返回 sum 的值
undefined   // undefined 是全局变量,和null 不同,它不是一个关键字

当 JS 代码中出现了标识符,JS 会将其当做变量而去查找它的值。如果变量名不存在,表达式运算结果为 undefined。然后,在 ECMAScript 5 的严格模式中,对不存在的变量进行求值会抛出一个引用错误异常。

4.2 对象和数组的初始化表达式

对象和数组初始化表达式实际上是一个新创建的对象和数组。这些初始化表达式有时称作“对象直接量”和“数组直接量”。和布尔值不同,它们不是原始表达式,因为它们所包含的成员或者元素都是子表达式。

数组初始化表达式是通过方括号和逗号隔开的列表构成,初始化的结果是一个新创建的数组。

[ ]             // 一个空数组,[] 内留空即表示该数组没有任何元素
[1+2,3+4]  // 拥有两个元素的数组,第一个是3,第二个是7

数组初始化表达式中的元素初始化表达式也可以是数组初始化表达式。也就是说,这些表达式是可以嵌套的:

var matrix = [[1,2,3],[4,5,6],[7,8,9]];

JS 对数组初始化表达式进行求值的时候,数组初始化表达式中的元素表达式也都会各自计算一次,也就是说,数组初始化表达式每次计算的值又可能是不同的。

数组直接量中的列表逗号之间的元素可省略,这时省略的空位会填补 undefined。

var sparseArray = [1,,,,5];    // 第三个元素是 undefined

数组直接量的元素列表结尾处可以留下单个逗号,这时并不会创建一个新的值为 Undefined 的元素。

对象初始化表达式和数组初始化表达式非常类似,只是方括号被花括号代替。并且每个子表达式都包含一个属性名和一个冒号作为前缀:

var  p = {x:2.3 , y: -1.2};       // 一个拥有两个属性成员的对象
var  q = {};                           // 一个空对象
q.x = 2.3; q.y = -1.2;             // q 的属性成员和 p 的一样

对象直接量也可以嵌套:

var  rectamgle = { upperleft:{x:2,y:2},
                            loweRight:{x:4,y:5} };

JS 求对象初始化表达式的值的时候,对象表达式也都会各自计算一次,并且它们不必包含常数值:它们可以是任意JS表达式。同样,对象直接量中的属性名称可以是字符串而不是标识符:

var side = 1;
var square = { "upperleft" : {x:p.x , y:p.y},
                               'lowerRight' : { x:p.x + side , y:p.y + side }};

4.3 函数定义表达式

函数定义表达式定义一个 JS 函数。表达式的值是这个新定义的函数。函数定义表达式可称为“函数直接量”,毕竟对象初始化表达式也称为“对象直接量”。一个典型的函数定义表达式包含关键字 function ,跟随其后一对圆括号,括号内是以逗号分隔的列表,列表含有0个或多个标识符(参数名),然后再跟随一个由花括号包裹的 JS 代码段(函数体)。

// 这个函数返回传入参数值的平方

var square = function(x){return x*x;}

函数定义表达式同样可以包含函数的名字,函数也可以通过函数语句来定义,而不是函数表达式。

4.4 属性访问表达式  

属性访问表达式运算得到一个对象属性或一个数组元素的值,JS为属性访问定义了两种语法:

expression . identifier

expression [expression]

第一种写法是一个表达式后跟随一个句点和标识符。表达式指定对象,标识符则指定需要访问的属性的名称。第二种写法是使用方括号,方括号内是另外一个表达式(这种方法适用于对象和数组)。第二个表达式指定要访问的属性的名称或者代表要访问数组元素的索引。

var o = {x:1,y:{z:3}};       // 一个示例对象
var a = [0,4,[5,6]];         // 一个包含这个对象的示例数组
o.x                          // 1 表达式o 的 x 属性
o.y.z                        // 3 表达式 o.y 的 z 属性
o["x"]                       // 1 对象o 的x 属性
a[1]                         // 4 表达式a 中索引为1 的元素
a[2]["1"]                    // 6 表达式 a[2]中索引为 1的元素
a[0].x                       // 1 表达式 a[0] 的 x 属性

如果计算结果是 unll 或 undefined ,表达式会抛出一个类型错误的异常,因为这两个值都不能包含任意属性。如果运算结果不是对象(或数组),JS 会将其转换为对象。如果对象表达式后跟随句点和标识符,则会查找由这个标识符所指定的属性的值,并将其作为整个表达式的值返回。如果对象表达式后跟随一对方括号,则会计算方括号内的表达式的值并将它转换为字符串。

无论哪种情况,如果命名的属性不存在,那么整个属性访问表达式的值就是 Undefined.

4.5 调用表达式

JS 中的调用表达式是一种调用(或者执行)函数或方法的语法表示,它以一个函数表达式开始,这个函数表达式指代了要调用的函数。函数表达式后跟随一对圆括号,括号内是 一个以逗号隔开的参数列表,参数可以有0个也可以有多个,例如:

f(0)               // f是一个函数表达式,0 是一个参数表达式
Math.max(x,y,z)    // Math.max 是一个函数;x,y,z 是参数
a.sort();          //  是一个函数,它没有参数

当对调用表达式进行求值的时候,首先计算函数表达式,然后计算参数表达式,得到一组参数值。如果函数表达式的值不是一个可调用的对象,则抛出一个类型错误异常(所有的函数都是可调用的,即使宿主对象不是函数它也有可能被调用)。然后,实参的值被一次赋值给形参,这些形参是定义函数时指定的,接下来开始执行函数体。如果函数使用return 语句给出一个返回值,那么这个返回值就是整个调用表达式的值,否则,调用表达式的值就是 Undefined。函数调用 ---- 包括当形参表达式的个数和函数定义中实参的个数不匹配的时候的运行情况。

如果这个表达式是一个属性访问表达式,那么这个调用称作“方法调用”,在方法调用中,执行函数体的时候,作为属性访问主题的对象和数组便是其调用方法内 this 的指向。这种特性使得在面向对象编程规范中,函数(其oo名称为“方法”)可以调用其宿主对象。

并不是方法调用的调用表达式通常使用全局对象作为this 关键字的值,然而在ECMAScript 5中,那些通过严格模式定义的函数在调用时将使用undefined 作为 this 的值,this 不会指向全局对象。

4.6 对象创建表达式

对象创建表达式创建一个对象并调用一个函数(这个函数称作构造函数)初始化新对象的属性。对象创建表达式和函数调用表达式非常类似,只是对象创建表达式之前多了一个关键字 new :

new Object();
new Point(2,3);

如果一个对象创建表达式不需要传入任何参数给构造函数的话,那么这对空圆括号可以省略

new Object
new Date

当计算一个对象创建表达式的值时,和对象初始化表达式通过{ } 创建对象的做法一样,JS 首先创建一个新的空对象,然后,JS 通过传入指定的参数并将这个新对象当做 this 的值来调用 一个指定的函数。这个函数可以使用 this 来初始化这个新创建对象的属性。那些被当做构造函数的函数不会返回一个值,并且这个新创建并被初始化后的对象就是整个对象创建表达式的值。如果一个构造函数确实返回了一个对象值,那么这个对象就作为对象创建表达式的值,而新创建的对象就废弃了。

4.7 运算符概括 

JS 中的运算符用于算术表达式、比较表达式、逻辑表达式、赋值表达式等

4.7.1 操作的个数

运算符可以根据其操作数的个数进行分类。JS 中大多数运算符是一个二元运算符,将两个表达式合并成一个复杂的表达式(+-*/)。它们的操作数均是两个,JS 同样支持一些一元运算符,它们将一个表达式转换为另一个复杂的表达式(- 符号)。JS 支持一个三元运算符,条件判断运算符“?:”,它将三个表达式合并成一个表达式。

4.7.2 操作数类型和结果类型

 一些运算符可以作用于任何数据类型,但仍然希望它们的操作数是指定类型的数据,并且大多数运算符返回(或计算出)一个特定类型的值。

JavaScript 运算符通常会根据需要对操作数进行类型转换。

4.7.3 左值

赋值运算符和其他少数运算符期望它们的操作数是lval 类型。左值是一个古老的术语,它是指“表达式只能出现在赋值运算符的左侧”。在JavaScript,变量、对象属性和数组元素均是左值。

ECMAScript 规范允许内置函数返回一个左值,但自定义的函数则不能返回左值。

4.7.4 运算符的副作用

计算一个简单的表达式(比如2*3)不会对程序运行状态造成任何影响,程序后续执行的计算也不好受到该计算的影响。而有一些表达式则具有很多副作用,前后的表达式运算会相互影响。

赋值运算符是最明显的:如果给一个变量或属性赋值,那么那些使用这个变量或属性的表达式的值都会发生改变。“++”和“--” 递增和递减运算符与此类似,因为它们包含隐式的赋值。delete 运算符同样有副作用:删除一个属性就像(但不完全一样)给这个属性赋值 undefined。

其他的JS 运算符都没有副作用,但函数调用表达式和对象创建表达式有些特别,在函数体或者构造函数内部运用了这些运算符并产生了副作用的时候,我们说函数调用表达式和对象创建表达式是有副作用的。

4.7.5 运算符优先级

4.7.6 运算符的结合性

结合性指定了在多个具有同样优先级的运算符表达式中的运算顺序。从左至右是指运算的执行是按照由左到右的顺序进行。例如:减法运算符具有从左至右的结合性,因此:

4.7.7 运算顺序

运算符的优先级和结合性规定了它们在复杂的表达式中的运算顺序,但并没有规定子表达式的计算过程中的运算顺序。JS 总是严格按照从左至右的顺序来计算表达式。

原文地址:https://www.cnblogs.com/cimuly/p/7372691.html