天天看点

学习JavaScript之——第4章 表达式和运算符(上)学习JavaScript之——第4章 表达式和运算符(上)

学习JavaScript之——第4章 表达式和运算符(上)

学习内容:

4.1原始表达式

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

4.3函数定义表达式

4.4属性访问表达式

4.5调用表达式

4.6对象创建表达式

4.7运算符概述

表达式(expression)是JavaScript中的一个短语,JavaScript解释器会将其计算出一个结果。

4.1原始表达式

  1. 原始表达式:原始表达式是表达式的最小单位——它不再包含其它表达式,JavaScript中的原始表达式包含常量或直接量、关键字、变量。
1.23;//1.23:数字直接量
"hello";//"hello":字符串直接量
/pattern/;//正则表达式直接量
           
  1. JavaScript中的一些保留字构成原始表达式:
true;//返回布尔值:真
false;//返回布尔值:假
null;//返回一个值:空
this;//返回"当前"对象
           
  1. 第三种原始表达式是变量:
i;//返回变量i的值
sum;//返回sum的值
undefined;//undefined是全局变量,和null不同,它不是一个关键字
           

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

  1. 数组的初始化表达式实际上一个新创建的对象和数组,这些初始化表达式有时称作"对象直接量"和"数组直接量"。
  2. 数组初始化表达式是通过一对方括号和其内由逗号隔开的列表构成的。
[];//一个空数组,没有任何元素
[1+2 , 3+4];//[ 3, 7 ]:拥有两个元素的数组
           
  1. 数组初始化表达式中的元素初始化表达式也可以是数组初始化表达式。也就是说,这些表达式是可以嵌套的。
var matrix = [[1,2,3],[4,5,6],[7,8,9]];
matrix;
/*
0: Array(3) [ 1, 2, 3 ]
1: Array(3) [ 4, 5, 6 ]
2: Array(3) [ 7, 8, 9 ]
length: 3
*/
           
  1. JavaScript对数组初始化表达式进行求值的时候,数组初始化表达式中的元素表达式也都会各自计算一次。也就是说,数组初始化表达式每次计算的值有可能是不同的。
  2. 数组直接量的列表逗号之间的元素可以省略,这时省略的空位是undefined。
var spareArray = [1,,,,5];
spareArray;//[ 1, <3 empty slots>, 5 ]
spareArray[2];//undefined
           
  1. 数组直接量的元素列表结尾处可以留下单个逗号,这时并不会创建一个新的值为undefined的元素
  2. 对象初始化表达式和数组初始化表达式非常类似,只是方括号被花括号代替,并且每个子表达式都包含一个属性名和一个冒号作为前缀:
var p = {x : 2.3 , y: -1.2};//一个拥有两个属性成员的对象
var q = {};//一个空对象
q.x = 2.3;//q的属性成员和p的一样
q.y = -1.2;//q的属性成员和p的一样
           
  1. 对象直接量也可以嵌套,比如:
var rectangle = {
  upperLeft : {x:2,y:2},
  lowerRight : {x:4,y:5}
};
rectangle.upperLeft.x;//2
           
  1. JavaScript求对象初始化表达式的值的时候,对象表达式也都会各自计算一次,并且它们不必包含常数值:它们可以是任意JavaScript表达式。同样,对象直接量中的属性名称可以是字符串而不是标识符
var slide = 1;
var square = {
  "upperLeft" : {x:p.x,y:p.y},
  'lowerRight' : {x:p.x +slide , y:p.y + slide}
};
square.upperLeft.y;//-1.2
square.lowerRight.x;//3.3
           

4.3函数定义表达式

一个典型的函数定义表达式包含关键字function,跟随其后的是一对圆括号,括号内是一个以逗号分隔的列表,列表含有0个或多个标识符(参数名),然后再跟随一个花括号包裹的JavaScript代码段(函数体)

var square = function(x){
  return x * x;	//这个函数返回传入参数值的平方
};
           

4.4属性访问表达式

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

expression.identifier//表达式指定对象,标识符指定需要访问的属性的名称
expression[identifier]//(适用于对象和数组)指定要访问的属性的名称或者代表要访问数组元素的索引
           
var o = {x:1,y:{z:3}};//一个示例对象
var a = [o,4,[5,6]];//一个包含这个对象的数组
o.x;//1:表达式o的x属性
o.y;//{ z: 3 }:表达式o的y属性
o.y.z;//3:表达式o.y的z属性
o["x"];//1:对象o的属性
a[1];//4:表达式a中索引为1的元素
a[2]["1"];//6:表达式a[2]中索引为1的元素
a[2][1];//6
a[0].x;//1:表达式a[0]的x属性
           
  1. 不管使用哪种形式的属性访问表达式,在".“和”["之前的表达式总是会首先计算。
  2. 如果计算结果是null或undefined,表达式会抛出一个类型错误异常,因为这两个值都不能包含任意属性。
  3. 如果运算结果不是对象(或数组),JavaScript会将其转换为对象。
  4. 如果对象表达式后跟随句点和标识符,则会查找由这个标识符所指定的属性的值,并将其作为整个表达式的值返回。
  5. 如果对象表达式后跟随一对方括号[],则会计算方括号内的表达式的值并将它转换为字符串。
  6. 不论哪种情况,如果命名的属性不存在,那么整个属性访问表达式的值就是undefined
  7. 如果属性名称是一个保留字或者包含空格和标点符号,或数字,则必须使用方括号的写法;当属性名是通过运算得出的值而不是固定的值的时候,这是必须使用方括号写法

4.5调用表达式

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

f(0);
Math.max(x,y,z);
a.sort();
           
  1. 当对调用表达式进行求值的时候,首先计算 函数表达式,然后计算参数表达式,得到一组参数值。
  2. 如果这个表达式是一个属性访问表达式,那么这个调用称作"方法调用"在方法调用中,执行函数体的时候,作为属性访问主题的对象和数组便是其调用方法内this的指向。这种特性使得在面向对象编程范例中,函数可以调用其宿主对象。

4.6对象创建表达式

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

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

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

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

4.7运算符概述

关键字运算符和标点符号所表示的运算符一样都是正规的运算符。

4.7.1. 操作数的个数

运算符可以根据操作数的个数进行分类:一元运算符(!)、二元运算符(*)、三元运算符(?: )

4.7.2操作数类型和结果类型

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

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

JavaScript中的所有值不是真值就是假值,因此对于那些希望操作数是布尔类型的操作符来说,它们的操作数可以是任意类型。

4.7.3左值

左值(lval):表达式只能出现在赋值运算符的左侧。在JavaScript中变量、对象属性、数组元素均是左值。ECMAScript规范允许内置函数返回一个左值,但自定义的函数则不能返回左值。

4.7.4运算符的副作用

  • 计算一个简单的表达式不会对程序的运行状态造成任何影响,程序后续执行的计算也不会受到该计算的影响。而有一表达式则具有很多副作用,前后的表达式运算会相互影响。
  • 赋值运算符是一个最明显的例子:如果给一个变量或属性赋值,那么那些使用这个变量或属性的表达式的值都会发生改变。
  • "++“和”–"递增和递减运算符与此类似,因为它们包含隐式的赋值。
  • delete运算符同样具有副作用:删除一个属性就像(但不完全一样)给这个属性赋值undefined。
  • 函数调用表达式和对象创建表达式是有副作用的。

    4.7.5运算符的优先级

    运算符优先级控制着运算符的执行顺序,优先级高的运算符的执行总是先于优先级低的运算符

  • w = x + y * z;

    "*“比”+“具有更高的优先级,所以乘法先执行,加法后执行,由于”="具有最低的优先级,因此赋值操作是在右侧的表达式计算出结果之后进行的。
  • 运算符的优先级可以通过显式使用圆括号来重写,为了让加法先执行,乘法后执行可以这样写:

    w = (x + y) * z;

    需要注意的是:属性访问表达式和调用表达式的优先级要比表中列出的所有运算符都高。
  • typeof my.functions[x](y);

    尽管typeof是优先级最高的运算符之一,但typeof也是在两次属性访问和函数调用之后执行的。

    4.7.6运算符的结合性

    结合性指定了在多个具有同样优先级的运算符表达式中的运算顺序

    这个表达式:

x = ~-y;
w = x = y = z;
q = a ? b : c ? d : e ? f : g;
           

和这段代码一模一样:

x = ~(-y);
w = (x = (y = z));
q = a ? b : (c ? d : (e ? f :g));
           

因为一元操作符、赋值和三元条件运算符都具有从右至左的结合性

4.7.7运算顺序

  • 运算符的优先级和结合性规定了它们在复杂的表达式中的运算顺序,但并没有规定子表达式的计算过程中的顺序。
  • 只有在任何一个表达式具有副作用而影响到其他表达式的时候其求值顺序才会和看上去有所不同。如果表达式x中的一个变量自增1,这个变量在表达式z中使用,那么实际上是先计算出了x的值再计算z的值,这一点非常重要!

继续阅读