运算符和表达式
一、运算符
1.运算符概述
JavaScript的大多数运算符都是由符号表示,如:+、- 等,另一些则由关键字表示,如:delete、instanceof等。
JavaScript有以下类型的运算符。
赋值运算符 Assignment operators
比较运算符 Comparison operators
算数运算符 Arithmetic operators
位运算符 Bitwise operators
逻辑运算符 Logical operators
字符串运算符 String operators
条件运算符 Conditional operators
逗号运算符Comma operators
一元运算符 Unary operators
关系运算符 Relational operators
运算符 | 分类 | 操作 | 结合性 | 操作数 | 类型 | 优先级 |
---|---|---|---|---|---|---|
() | 圆括号 | - | - | - | 20 | |
. | 成员访问 | - | - | - | 19 | |
[] | 需计算的成员访问 | - | - | - | 19 | |
new(有参数列表) | - | - | - | - | 19 | |
函数调用 | 函数调用 | - | - | - | 19 | |
new(无参数列表) | - | - | - | - | 18 | |
++(后置) | 算数运算符 | 自动增量 | - | 1 | lval→num | 17 |
--(后置) | 算数运算符 | 自动减量 | - | 1 | lval→num | 17 |
++(前置) | 算数运算符 | 前后自动增量 | ← | 1 | lval→num | 16 |
--(前置) | 算数运算符 | 前后自动减量 | ← | 1 | lval→num | 16 |
- | 算数运算符 | 数值求反 | ← | 1 | num→num | 16 |
+ | 算数运算符 | 转换为数字 | ← | 1 | num→num | 16 |
~ | 位运算符 | 按位求反 | ← | 1 | int→int | 16 |
! | 逻辑运算符 | 逻辑非 | ← | 1 | bool→bool | 16 |
delete | 一元运算符 | 删除属性 | ← | 1 | lval→bool | 16 |
typeof | 一元运算符 | 检测操作数类型 | ← | 1 | any→str | 16 |
void | 一元运算符 | 返回undefined值 | ← | 1 | any→undefined | 16 |
await | - | - | - | - | - | 16 |
** | 算数运算符 | 指定数的几次方 | ← | 2 | num,num→num | 15 |
*、/、% | 算数运算符 | 乘、除、求余 | → | 2 | num,num→num | 14 |
+、- | 算数运算符 | 加、减 | → | 2 | num,num→num | 13 |
+ | 字符串运算符 | 字符串连接 | → | 2 | str,str→str | 13 |
<< | 位运算符 | 左移 | → | 2 | int,int→int | 12 |
>> | 位运算符 | 有符号右移 | → | 2 | int,int→int | 12 |
>>> | 位运算符 | 无符号右移 | → | 2 | int,int→int | 12 |
<、<=、>、>= | 比较运算符 | 比较数字顺序 | → | 2 | num,num→bool | 11 |
<、<=、>、>= | 比较运算符 | 比较字母表中顺序 | → | 2 | str,str→bool | 11 |
instanceof | 关系运算符 | 检测对象类型 | → | 2 | obj, func→bool | 11 |
in | 关系运算符 | 检测属性是否存在 | → | 2 | str,obj→bool | 11 |
== | 比较运算符 | 判断相等 | → | 2 | any,any→bool | 10 |
!= | 比较运算符 | 判断不等 | → | 2 | any,any→bool | 10 |
=== | 比较运算符 | 判断恒等 | → | 2 | any,any→bool | 10 |
!== | 比较运算符 | 判断非恒等 | → | 2 | any,any→bool | 10 |
& | 位运算符 | \按位与 | → | 2 | int,int→int | 9 |
^ | 位运算符 | 按位异或 | → | 2 | int,int→int | 8 |
| | 位运算符 | 按位或 | → | 2 | int,int→int | 7 |
&& | 逻辑运算符 | 逻辑与 | → | 2 | any,any→any | 6 |
|| | 逻辑运算符 | 逻辑或 | → | 2 | any,any→any | 5 |
? : | 条件运算符 | 条件运算符 | ← | 3 | bool,any,any→any | 4 |
= | 赋值运算符 | 变量赋值或对象属性赋值 | ← | 2 | lval, any→any | 3 |
*=、/=、%=、+=、-=、&=、^= | 赋值运算符 | 运算且赋值 | ← | 2 | lval, any→any | 3 |
|=、<<=、>>=、>>>= | 赋值运算符 | 运算且赋值 | ← | 2 | lval, any→any | 3 |
yield、yield* | - | - | - | - | - | 2 |
… | 展开运算符 | - | - | - | - | 1 |
, | 逗号运算符 | 忽略第一个操作数,返回第二个操作数 | → | 2 | any,any→any | 0 |
2.运算符的操作数个数
可根据操作数的个数进行分类,大部分的运算符的操作数个数是2个,即二元运算符,还有一些一元运算符,还有一个三元运算符:条件运算符。
3.运算符的操作数类型和结果类型
一些运算符可作用于任何数据类型,但仍希望是它们的操作数是指定类型的数据,并且大多数运算符返回一个特定类型的值,如不满足希望的类型,通常会根据需要对操作数进行类型转换。
4.左值
左值是指:表达式只能出现在赋值运算符的左侧,在JavaScript中,变量、对象属性、数组元素均是左值。
5.运算符的优先级
上表所示的运算符按照优先级从高到低排序,运算符优先级控制着运算符的执行顺序,优先级高的运算符的执行总是先于优先级低的运算符。如:let val = x + y * z;
运算符的执行顺序可通过显式的使用圆括号来改变,如:let val = (x + y) * z;
6.运算符的结合性
上表说明了运算符的结合性,←:从右至左结合、→:从左至右结合。
结合性指定了在多个具有同样优先级的运算符表达式中的运算顺序。
w = x - y -z;
w = ((x - y) - z)
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));
一元运算符、赋值运算符和三元条件运算符具有从右至左的结合性。
7.运算顺序
运算符的优先级和结合性规定了它们在复杂表达式中的运算顺序,JavaScript总是严格按照从左到右的顺序 计算表达式。
二、表达式
在英语中,句子(sentence)是完整表达某个意思的一组词,由一个或多个短语(phrase)组成,它们之间由标点符号或连接词(and、or等)连接起来,短语可以由更小的短语组成。有的短语是不完整的,不能独立表达意思,有些短语相对完整,且能独立表达某个意思。
在JavaScript中,语句相当于句子,表达式相当于短语,运算符则相当于标点符号和连接词。
JavaScript中表达式可返回一个结果值。
1.原始表达式
原始表达式(primary expression)是表达式的最小单位。
包括:常量或直接量、关键字和变量。
1.23
'hello'
/pattern/
true
false
null
this //在程序的不同位置,返回的值也不相同。
i
sum
undefined //undefined是全局变量,和null不同,不是关键字。
2.对象和数组的初始化表达式
数组初始化表达式是通过一个方括号和其内由逗号隔开的列表构成。结果是新创建一个数组。
[]
[1 + 2, 3 + 4]
[[1,2,3], [4,5,6], [7,8,9]] //数组元素也可是数组初始化表达式
var sum = 10;
console.log([1, sum, 3]); //Array(3) [1, 10, 3]
//数组元素的也可以是其他表达式。
数组的元素可以省略,省略的元素值是undefined。数组元素列表结尾处可有单个逗号,这并不会创建一个新的值为undefined的元素。
var arr = [1, , , , 2,];
console.log(arr);//Array(5) [1, …, 2]
for(let ch of arr) {
console.log(ch);
}
//1
//undefined
//undefined
//undefined
//2
对象初始化表达式是用花括号括起来,每个子表达式是都包含一个属性名和一个冒号作为前缀。
对象直接量可以嵌套。
对像表达式可以是任意JavaScript表达式,属性名可以是字符串而不是标识符。
var point = {x: 2, y: 3};
var q = {};
q.x = 2.5;
q.y = 4.2;
var rectangle = {upperLeft: {x: 2, y: 3}, lowerRight: {x: 5, y: 10}};
var square = {"upperLeft": {x: 2, y: 3}, 'lowerRight': {x: 5, y: 10}};
3.函数表达式
函数表达式定义一个JavaScript函数,可称为函数直接量,
函数定义表达式包含关键字function,其后跟随一对圆括号,括号内是以逗号分割的0个或多个标识符的参数列表,然后跟随一个由花括号括起来的JavaScript代码段即函数体。
var square = function(x) { return x * x; };
console.log(square(5)); //25
4.属性访问表达式
属性访问表达式可得到一个对象属性或一个数组元素的值,有两种方式:
- expression.identifier:只适合于要访问的属性名是合法的标识符,且知道要访问的属性的名字。
- expression[expression]:适用于属性名是保留字或含有空格或标点符号,或数字,当属性名不是固定的值时,就必须使用该方式。
var o = {x:1, y:{z:3}};
var a = [o, 4, [5, 6]];
console.log(o.x); //1
console.log(o.y.z);//3
console.log(o["x"]);//1
//console.log(o[x]); //ReferenceError: x is not defined
console.log(a[1]);//4
console.log(a[2]["1"]);//6
console.log(a[0].x);//1
5.调用表达式
调用表达式是一种调用或执行函数或方法的语法表示,以一个函数表达式开始,后跟一对圆括号,括号内是一个以逗号隔开的参数列表,参数可以有0个或多个。
如左圆括号之前的表达式是一个属性访问表达式,则称为方法调用(method invocation)。
6.对象创建表达式
对象创建表达式创建一个对象并调用一个构造函数初始化对象的属性。
和函数调用表达式类似,多了关键字:new。
如不需要传入任何参数给构造函数,则可省略圆括号。
new Object()
new Point(2, 3)
new Object
new Date
7.算术表达式
基本的算术运算符是*(乘法)、/(除法)、%(求余)、+(加法)、-(减法)、**(指数)。
二元加法运算符 + 可对两个数字做加法,也可做字符串连接。
(1)加法运算符的行为表现为:
- 如其中一个操作数是对象,则对象遵循对象到原始值的转换规则转换为原始值:日期对象通过toString()方法执行转换,其他对象通过valueOf()方法执行转换(如果valueOf()方法返回一个原始值的话)。
- 到原始值的转换后,如其中一个操作数是字符串的话,另一个也转换为字符串,然后进行字符串连接。
- 否则,两个操作数都转换为数字或NaN,然后进行加法操作。
当加法和字符串和数字一起使用时,需考虑加法的结合性对运算顺序的影响。
console.log(1 + 2);//3
console.log('1' + '2');//12
console.log('1' + 2);//12
console.log(1 + {});//1[object Object]
console.log(true + false);//1
console.log(2 + null);//2
console.log(2 + undefined);//NaN
console.log(1 + 2 + 'blind mice');//3blind mice
console.log(1 + (2 + 'blind mice'));//12blind mice
(2)一元加法(+)运算符将操作数转换为数字或NaN,并返回转换后的数字。
一元减法(-)运算符根据需要将操作数转换为数字,然后改变运算结果的符号。
自增(++)运算符的返回值依赖于它相对于操作数的位置。在操作数之前(前增量)时,对操作数进行增量操作,比返回计算后的值;在操作数之后(后增量)时,对操作数进行增量计算,但返回的是未增量的值。
表达式++x并不总和x=x+1完全一样。
由于JavaScript会自动进行分号补全,因此不能在后增量运算符和操作数之间插入换行符。
var i = 1;
var j = 1;
console.log(i++);//1
console.log(i);//2
console.log(++j);//2
console.log(j);//2
var k = '1';
console.log(++k);//2
k = '1';
console.log(k + 1);//11
自减(–)运算符和自增运算符除减1外有相同的操作。
位运算符要求 操作数是32位整数,必要时首先将操作数转化为数字,并将数字强制表示为32位整数,会忽略原格式中的小数部分和任何超过32位的二进制位。
会将NaN、Infinity和-Infinity转换为0。
按位与(&)对它的整型操作数逐位进行与操作,即两个操作数中相对应的位都是1,结果中的这一位才是1。
按位与(|)对它的整型操作数逐位进行或操作,即只要其中一个操作数相对应的位是1,那么结果中的这一位就是1。
按位异与(^)对它的整型操作数逐位进行异或操作,即其中一个操作数相对应的位是1(不能同时为1),那么结果中的这一位就是1。
按位非(~)将它的操作数的所有位取反。相当于改变它的符号并减1。
左移(<<)将一个操作数的所有二进制位进行左移操作,移动位数(0~31之间的整数)由第二个操作数指定,左移1位相当于它乘以2,左移2位相当于它乘以4。
带符号右移(>>)将一个操作数的所有二进制位进行右移操作,移动位数(0~31之间的整数)由第二个操作数指定,右移1位相当于它除以2,右移2位相当于它除以4。
无符号右移(>>>)和带符号右移一样,只是左边的高位总是填补0,与原来的操作数符号无关。
8.关系表达式
=:赋值
==:相等
===:严格相等
严格相等运算符首先计算其操作数的值,然后比较这两个值,比较过程无任何类型转换。
相等运算符和严格相等运算符相似,但比较并不严格,如两个操作数不是同一类型,会进行一些类型转换,然后再比较。
var a = null;
var b = null;
console.log(a === b);//true
a = undefined;
b = undefined;
console.log(a === b);//true
console.log( '1' == true);//true
9.逻辑表达式
逻辑与(&&),逻辑或(||),逻辑非(!)
&&与||的短路行为(short circuiting)。
10.赋值表达式
11.表达式计算
JavaScript可通过全局函数eval()解释运行由JavaScript源码组成的字符串,并产生一个值。
虽是一个强大的语言特性,但几乎没有必要在实际中应用。
console.log(eval('3 + 2'));//5
eval()函数只有一个参数,如传入的不是字符串,直接返回该参数,如是字符串,则会把字符串当成JavaScript代码进行编译,如编译失败则抛出一个语法错误的异常,如成功则执行该代码,并返回最后一个表达式或语句的值。如没有值,则返回undefined。
eval使用了调用它的变量作用域,如在最顶层代码中调用eval(),它会作用于全局变量和全局函数。
var x = 'abc';
console.log(eval('x'));//abc
console.log(eval('x = 1;'));//1
console.log(x);//1
console.log(eval('var y = 1;'));//undefined
console.log(y);//1
直接使用使用非限定的eval名称调用eval()函数时,通常称为直接eval,直接调用eval()时,它总是在调用它的上下文作用域内执行,其他的间接调用(别名)则使用全局对象作为其上下文作用域,并且无法读、写、定义局部变量和函数。
ECMAScript 5严格模式对eval()函数的行为施加了更多限制。
在严格模式下,evaluate执行的代码可查询或更改局部变量,但不能在局部作用域中定义新的变量或函数。将eval列为保留字,不能用别名覆盖eval()函数。