第一部分 JavaScript语言核心 第4章 表达式和运算符(未完待续...)
表达式是指JS中的短语,JS解释器会将其计算成结果。
变量名是一种简单的表达式,它的值就是赋值给变量的值。
数组也是表达式,由表示数组的表达式、左方括号、整数表达式、右方括号构成。
函数调用表达式由一个表示函数对象的表达式和0和多个参数表达式构成。
4.1 原始表达式
“原始表达式”是最简单的表达式,是表达式的最小单位。
如:常量、直接量、关键字、变量。
直接量:
1.23 //数字直接量 "hello" //字符串直接量 /pattern/ //正则表达式直接量
JS中的一些保留字构成了原始表达式:
true //返回一个布尔值:真 false //返回一个布尔值:假 null //返回一个值:空 this //返回“当前”对象
特殊关键字this:
在方法体内:this指调用这个方法的对象
变量也是原始表达式:
i //返回变量i的值 sum //返回sum的值 undefined //undefined是全局变量,和null不同,它不是一个关键字
4.2 对象和数组的初始化表达式
对象和数组初始化表达式实际上是一个创建新的对象和数组。
数组初始化表达式: 数组的元素是逗号分隔的表达式的值
[] //一个空数组:[]内留空即表示该数组没有任何元素 [1+2,3+4] //拥有两个元素的数组,第一个是3,第二个是7
数组初始化表达式中的元素初始化表达式也可以是数组初始化表达式,这些表达式可以嵌套:
var matrix =[[1,2,3],[4,5,6],[7,8,9]];
数组直接量中的列表逗号之间的元素可以省略,会自动填充undefined。如:
var sparseArray =[1,,,,5]; //包含5个元素,3个是undefined。 var arr = [1,2,3,]; //数组直接量元素列表可以以逗号结尾,但不会创建一个新的值为undefined的元素。
对象初始化表达式:
var p = {x:2.3,y:-1.2}; //一个拥有两个属性成员的对象 var p = {}; //一个空对象 q.x = 2.3; q.y = -1.2; //q的属性成员和p的一样
对象直接量也可以嵌套:
var rectangle = { upperLeft:{x:2,y:2}, lowerRight:{x:4,y:5}};
JS求对象初始化表达式值的时候,对象表达式也都会各自计算一次,并且它们不必包含常数值:它们可以是任意JS表达式。同样,对象直接量中的属性名称可以是字符串而不是标识符(这在那些只能使用保留字或一些非法标识符作为属性名的地方非常有用):
var side = 1; var squere = {"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属性访问定义的2种语法:
expression.identfier //表达式指对象,标识符指属性名称 expression[expression] //要访问属性的名称或数组元素的索引
具体例子:
var o = {x:1,y:{z:3}}; //一个示例对象 var a = [o,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属性
不管用哪种形式的属性访问表达式,在"."和“[”之前的表达式总是会首先计算。
如果计算结果是null或者undefined,表达式会抛出一个类型错误异常,因为这两个值都不能包含任意属性。
如果运算结果不是对象(或者数组),JS会将其转换为对象。
如果对象表达式后跟随句点和标识符,则会查找有这个标识符所指定的属性的值,并将其作为整个表达式的值返回。
如果对象表达式后跟随一对方括号,则会计算方括号内的表达式的值并将它转换为字符串。
不论哪种情况,如果命名的属性不存在,那么整个属性访问表达式的值就是undefined。
显然.indentifier的写法更简单,但需要注意的是,这种方式只适用于访问的属性名称是合法的标识符,并且需要知道要访问的属性的名字。如果属性名称是一个保留字或者包含空格和标志点符号,或是一个数字(对于数组来说),则必须使用方括号的写法。
当属性名是通过运算得出的而不是固定值的时候,这时必须使用方括号写法。
4.5 调用表达式
JS中的调用表达式是一种调用(或者执行)函数或方法的语句表示。它以一个函数表达式开始,这个函数表达式指代了要调用的函数。函数表达式后跟随一对圆括号,括号内是一个以逗号隔开的参数列表,如:
f(0) //f是一个函数表达式;0是一个参数表达式 Math.max(x,y,z) //Math.max是一个函数;x,y和z是参数 a.sort() //a.sort是一个函数,它没有参数
当对调用表达式进行求值的时候,先计算函数表达式,然后计算参数表达式,得到一组参数值。
如果函数表达式的值不是一个可调用的对象,则抛出一个类型错误异常。
然后实参的值被依次赋值给形参,这些形参是定义函数时指定的,接下来开始执行函数体。如果函数使用return语句给出一个返回值,那么这个返回值就是整个调用表达式的值。
否则,调用表达式的值就是undefined。
任何一个调用表达式都包含一对圆括号和左圆括号之前的表达式。
如果这个表达式是一个属性访问表达式,那么这个调用称作”方法调用“。在方法调用中,执行函数体的时候,作为属性访问主体的对象和数组便是其调用方法内this的指向。这种特性使得在面向对象编程范例中,函数可以调用其宿主对象。
并不是方法调用的调用表达式通常使用全局对象作为this关键字的值。
4.6 对象创建表达式
对象创建表达式创建一个对象并调用一个函数(构造函数)初始化新对象的属性。
对象创建表达式:
new Object();
new Point(2,3);
如果对象创建表达式不需要传参数给构造函数,圆括号可以省略:
new Object;
new Date;
当计算一个对象创建表达式的值时,和对象初始化表达式通过{}创建对象的做法一样,
JS首先创建一个新的空对象,然后JS通过传入指定的参数并将这个新对象当作this的值来调用一个指定的函数。这个函数可以使用this来初始化这个新创建对象的属性。那些被当成构造函数的函数不会返回一个值,并且这个新创建并被初始化后的对象就是整个对象创建表达式的值。如果一个构造函数确实返回了一个对象值,那么这个对象就作为整个对象创建表达式的值,而新创建的对象就废弃了。
4.7 运算符概述
JS中运算符用于算术表达式、比较表达式、逻辑表达式、赋值表达式等。
4.7.1 操作数的个数
运算符可以根据其操作数的个数进行分类。
JS中大多数运算符如(乘"*")是一个二元运算符,将两个表达式合并成一个稍复杂的表达式。它们的操作数均是两个。
JS同样支持一元运算符,它们将一个表达式转换为另一个稍微复杂的表达式。如表达式-x中的"-"运算符就是一元运算符,求负值。
JS支持三元运算符,条件判断运算符"?:",它将3个表达式合并成一个表达式。
4.7.2 操作数类型和结果类型
JS运算符通常会根据需要对操作数进行类型转换。
乘法运算符"*"希望操作数为数字,但表达式”3“*”5“却是合法的,因为JS会将操作数转换为数字。这个表达式的值是数字15,而不是字符串”15“.
JS中的所有值不是真值就是假值,因此对于那些希望操作数是布尔类型的操作符来说,它们的操作数可以是任意类型。
4.7.3 左值
左值:表达式只能出现在赋值运算符的左侧。
在JS中,变量、对象属性、数组元素均是左值。
4.7.4 运算符的副作用
有些表达式具有副作用,前后的表达式运算会相互影响。
赋值运算符是最明显的一个例子:
如果给一个变量或属性赋值,那么那些使用这个变量或属性的表达式的值都会发生改变。
"++"和"--"递增和递减运算符与此类似,因为它们包含隐式的赋值。
delete运算符也具有副作用:删除一个属性就像个这个属性赋值undefined(不完全一样)。
其他JS运算符没有副作用,但函数调用表达式和对象创建表达式有些特别,在函数体或构造函数内部运用了这些运算符并产生了副作用的时候,我们所函数调用表达式和对象创建表达式是有副作用的。
4.7.5 运算符优先级
属性访问表达式和调用表达式的优先级比表4-1的所有运算符都高。
例子:
typeof my.functions[x](y)
尽管typeof是优先级最高的运算符之一,但typeof也是在两次属性访问和函数调用之后执行的。
重要规则:先乘除,后加减。赋值运算优先级非常低,一般总是最后执行的。(不确定的话用括号强制指定运算次序)
4.7.6 运算符的结合性
结合性指定了在多个具有同样优先级的运算符表达式中的运算顺序。
减法运算符具有从左到右的结合性:
w = x-y-z;
等同于:
w=((x-y)-z);
一元操作符、赋值、三元条件运算符具有从右到左的结合性:
x = ~-y; 等同于 x=~(-y);
w = x = y = z; 等同于 w=(x=(y=z));
q = a?b:c?d:e?f:g; 等同于 a?b:(c?d:(e?f:g));
4.7.7 运算顺序
JS总是严格按照从左到右的顺序来计算表达式。
如,表达式w = x+y*z中,首先计算子表达式w,然后计算x、y和z。然后y的值和z的值相乘,再加上x的值,最后将其赋值给表达式w所指代的变量或属性。
给表达式添加圆括号会改变乘法、加法和赋值运算的关系,但从左到右的顺序不会改版的。
只有在任何一个表达式具有副作用而影响到其他表达式的时候,其求值顺序才会和看上去有所不同。如果表达式x中的一个变量自增1,这个变量在表达式z中使用,那么实际上是先计算出了x的值再计算z的值,这一点非常重要。
4.8 算术表达式
基本算术运算符是+加法、-减法、*乘法、/除法、%求余。
-减法、*乘法、/除法、%求余都会讲操作数转换为数字,无法转换为数字的操作数都转换为NaN值。如果操作数(或者转换结果)是NaN值,算术运算的结果也是NaN。
JS中所有数字都是浮点型的,除法运算的结果也是浮点型,比如5/2的结果是2.5而不是2。除数为0的运算结果为正无穷大或负无穷大,而0/0的结果是NaN,所有这些运算均不会报错。
运算符"%"事故求余,结果的符号和第一个操作数(被除数)的符号保持一致。
如,5%2结果是1,-5%2结果是-1。
求余运算符的操作数通常都是整数,但也适用于浮点数,比如,65.%2.1结果是0.2.
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号