JavaScript权威指南--表达式与运算符

本章要点

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

程序中的常量、变量名就是一种简单的表达式。复杂的表达式是由简单的表达式组成的,比如数组访问表达式、函数调用表达式等等。

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

1.原始表达式

最简单的表达式、是表达式的最小的单位。包含常量或直接量、关键字和变量。

当JavaScript代码中出现标识符,JavaScript会将其当做变量而去查找它的值。如果变量名不存在,表达式运算结果为undefined。然而,在ES5严格模式下,这种情况会抛出错误。

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

对象和数组初始化实际上是新创建的对象和数组,这些初始化的表达式有时候叫做“对象直接量”和“数组直接量”。

数组的初始化表达式是通过一对方括号和其内由逗号隔开的列表构成的,可以嵌套。

数组直接量中列表之间的元素可以省略,空位就会填充undefined.例如下面:

var a=[1,,,,5]

数组直接量的结尾处留下逗号,这时不会创建一个新的值为undefined的元素。

对象初始化表达式 和数组初始化表达式非常相似,只是方括号被花括号代替。并每个字表达式包含一个属性名和冒号作为前缀。当然也可以嵌套。

3.函数定义表达式

函数定义表达式定义一个javascript函数。表达式的值是这个新定义的函数。从某种意义上将,函数定义表达式可以成为函数直接量。

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

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

4.属性访问表达式

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

expression.indentifier
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,表达式会抛出类型错误异常,因为这两个值都不能包含任意属性。如果运算结果不是对象或数组,javascript会将其转换为对象。

虽然.identifier的写法更加简单,但需要注意的是,这种方式只适用于要访问的属性名称是合法的标识符。并且需要知道要访问的属性名字。 如果属性名称是一个保留字或者包含空格和标点符号,或是一个数字(对于数组来说),则必须使用方括号的写法。当属性名是通过运算符得出的值而不是固定值的时候,这时候必须使用方括号的写法。

5.调用表达式

javascript中的调用表达式(invocation expression)是一种调用(或者执行)函数或方法的语法表示。

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

当调用表达式进行求值的时候,首先计算函数表达式,然后计算参数表达式,得到一组参数值。

6.对象创建表达式

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

new Object()
new Point(2,3)

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

7.运算符概述

javascript中的 运算符用于算表表达式, 比较表达式, 逻辑表达式 ,赋值表达式等

需要注意的是大多运算符都是标点符号来表示的,比如“+”、“=”;还有的是由关键字表示,比如delete和instanceof。无论是关键字运算符还是符号运算符,所表示的运算符一样都是正规的运算符。

下表是按照运算符的优先级来排序的,前边的运算符优先级高于后边的运算符优先级。被水平分割线隔开的运算符具有不同的优先级。每个水平分隔线内一组运算符有相同的优先级。A表示运算符的结合性:L(从左至右)或者R(从右至左);标题N的列表表示操作数的个数;类型表示期望的操作数的类型,以及运算符的结果类型(在"→"符号之后)。

Ival:left-value的简写,表示“左值”。

7.1.操作数的个数

运算符可以通过操作数的个数进行分类:一元操作符、二元操作符(两个表达式合并)、三元操作符。

7.2.操作数类型和结果类型

上表为“类型”的列中列出了运算符操作数的类型(箭头前)和运算结果的类型(箭头后)。

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

有一些运算符对操作符类型有着不同程度的依赖,比如加法运算符,可以对数字相加,也可以对字符串链接。

7.3.左值

lval类型。

左值是一个古老的术语。它是指“表达式只能出现在赋值运算符的左侧”。javascript中,变量、对象属性和数组元素均是左值。ECMAScript规范允许范围内置函数返回一个左值,但定义的函数则不能返回左值。

var o = { x: 1 }; 
delete o.x;     // true    delete右边只能是左值,因为o.x可以放在赋值符号的左边。

7.4.运算符的副作用

副作用:前后的表达式运算会相互影响:赋值运算符最明显。如果给一个变量或者属性赋值,那么那些使用这个变量或属性的表达式的值都会发生变化。

有副作用的表达式:

赋值运算符:如果给一个变量或者属性赋值,那么那些使用这个变量或属性的表达式的值都会发生变化;
递增、递减也类似,因为它们包含隐式的赋值;
delete运算符删除一个属性就像给这个属性赋值undefined。等等

7.5.运算符的优先级

如上表。

运算符的优先级可以使用园括号来重写。

需要注意的是,属性访问表达式和调用表达式的优先级要比表中的所有运算符都要高。

typeof my.Function[x](y)

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

事实上,如果你真的不确定你所使用的运算符优先级,最简单的方法就是使用园括号来强行指定运算次序。

7.6.运算符的结合性

上表中标题为A的列说明了运算符的结合性。L指从左至右结合,R指从右至左结合。结合性指定了在多个具有同样优先级的运算符表达式中的运算顺序。

由左到右:

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.7.运算顺序

运算符的优先级和结合性规定了它们在复杂的表达式中的运算顺序,但并没有规定子表达式的计算过程中的运算顺序。javascript总是严格按照从左至右的顺序计算表达式,例如:在表达式 w=x+y*z 中,将首先计算表达式w,然后计算x、y和z,然后,y的值和z相乘,在加上x的值。最后将其表达式w所指代的变量或属性。给表达式添加园括号将会改变乘法,加法和赋值运算的关系。但从左至右的顺序是不会改变的。

8.算术表达式

本节涵盖了那些算术计算的运算符、以及对操作数的算术操作。

基本的算术运算符是*、/、%、+、-。

所有那些无法转换为数字的操作都将转换为NaN值。如果操作数(或者转换结果)是NaN值,算术运算结果也是NaN。

运算符“/”用第二个操作数来除以第一个操作数,如果你使用过那些区分整数型和浮点数型的编程语言。那么用一个整数除以一个整数时,则希望得到的结果也是整数。在javascript中所有的数字都是浮点数型的,除法运算的结果也是浮点型。比如5/2结果是2.5,而不是2 。除数为0的运算结果为正无穷大或负无穷大。而0/0的结果是NaN。所有这些运算均不会报错。

运算符“%”结果的符号和第一个操作数(被除数)符号保持一致。例如5%2的结果为1,-5%2为-1。操作数通常都是整数,但也适用于浮点数。6.5%2.1结果是0.2。(0.19999999999999973)。

8.1.“+”运算符

二元加法运算符“+”可以对两个数字做加法,也可以做字符串连接操作。

加号的转换优先考虑字符串连接。如果两个操作数都不是类字符串的,那么将进行算术加法运算。

从技术上来讲,加法操作符的行为表现为:

1、如果一个操作数是对象,则对象会遵循对象到原始值的转换规则为原始类值(参照3章8节3小节)。日期对象toString()方法执行转换,其他对象则通过valueOf()方法执行转换(如果valueOf()方法返回一个原始值的话)。由于多数对象都不具备可用的valueOf()方法,因此他们会通过toString()方法来执行转换。

2、在进行了对象到原始值的转换后,如果其中一个操作数是字符串的话,另一个操作数也会转换为字符串。然后进行字符串连接。

3、否则,两个操作数都将转换为数字(或者NaN),然后进行加法操作。

1 + 2 //=>3 加法
"1" + "2" //=>"12" 字符串连接
"1" + 2 //=>"12"数字转换为字符串后进行字符串连接
1 + {} //=>"1[object object]":对象转换为字符串后进行字符串连接
true + true //=>2 布尔值转换为数字后做加法
2 + null //=>2 null转换为0后做加法
2 + undefined //=>NaN undefined转换为NaN做加法

最后,特别要注意的是。当加号运算符合字符串一起使用时,要考虑加法对运算顺序的影响。也就是说,运算结果是依赖于运算符的运算顺序的,比如

1 + 2 + "bmice" //=> "3 bmice"
1 + (2 + "bmice") => "12bmice"

8.2.一元算术运算符

一元运算符作用于一个单独的操作数,并产生一个新值。

一元运算符(+,-,++和--)

一元加法+:一元加法运算符把操作数数转换为数字(或者NaN),并且返回这个转换后的数字。如果操作数本身就是数字,则直接返回这个数字。
一元减法-:当-号做一元运算符时,它会根据需要把操作数转换为数字,然后改变运算结果的符号。

var i = 1,j = ++i //i和j的值都是2
var i = 1,j = i++; //i是2,j是1

需要注意的是,表达式++x并不和x=x+1完全一样,“++”运算符从不进行字符串连接操作,它总会将操作数转换为数字并增1.如果x是字符串“1”,++x的结果就是数字2,而x+1是字符串"11"。

由于JavaScript会自动进行分号补全,因此不能在后增量运算符合操作数之间插入换行符。

8.3.位运算符

位运算符可以对数字表示的二进制数据进行更低层级的按位运算。

。。。。。。

9.关系表达式

关系运算符用于测试两个值之间中的关系。

关系表达式总是返回一个布尔值。

9.1.相等和不等运算符

“===”也称为严格相等运算符,“==”运算符称作相等运算符,可以允许进行类型转换。

javascript对象的比较是引用的比较,而不是值的比较。对象和本身是相等的,但和其他任何对象都不相等。如果两个对象具有相同数量的属性,相同的属性名和值,它们依然是不相等的。相应位置的数组元素是相等的两个数组也是不相等的。

严格相等运算符"==="首先计算操作数的值,然后比较这两个值,比较过程没有任何类型转换:

· 如果两个值类型不想同,则它们不相等
· 如果两个值都是null或者undefined,则它们不相等
· 如果两个值都是布尔值true或者false, 则它们相等
· 如果其中一个值是NaN,或者两个值都是NaN ,则它们不相等,NaN和其它值都是不相等的,包括它本身。
· 如果两个值为数字且相等,则它们相等。如果一个值为0,令一个值为-0,则它们同样相等。
· 如果两个值为字符串,并且所含对应位上的16位数(参照3章2节)完全相等,则它们相等。如果他们的长度或内容不同,则不相等。两个字符串可能函数完全一样并且所显示出的字符也一样,但具有不用编码的16位值,javascript并不对Unicode进行标准转换,因此这样的字符串通过"==="和"=="运算符的比较结果也不相等。第三部分的String.localeCompare()提供了另外一种比较字符串的方法。
· 如果两个引用值指向同一个对象,数组或函数,则它们是相等的。如果指向不同的对象,则它们是不等的,尽管两个对象有完全相同的属性

相等运算符"==":

· 如果两个操作类型相同,则和上文相等运算符的比较规则一样。如果严格相等,那么比较结果相等。如果他们不严格相等,则比较结果不相等。
· 如果两个操作类型不同,“==”相等操作符也会认为它们相等。检测相等会遵循如下的规则和类型转换:

  1.如果一个类型是null,令一个是undefined,则它们相等
  2.如果一个值是数字,另一个是字符串,先将字符串转换为数字,然后使用转换后的值进行比较。
  3.如果一个值是true,则将其转换为1再进行比较,如果一个值是false,则转换为0比较。
  4.如果一个值是对象,另一个值是数字或字符串,则使用3章8节3小节的方法的转换规则将对象转换为原始值,然后进行比较。对象通过toString()方法或者valueOf()方法转换为原始值。javascript语言核心的内置类首先尝试使用valueOf()再尝试使用toString(),除了日期类,日期类只能通过toString()转换。那些不是javascript 语言核心中的对象则通过实现中定义的方法转换为原始值。
  5.其它不同类型之间的比较均不相等

9.2.比较运算符

比较操作符的操作数可能是任意类型。然而只有数字和字符串才能真正执行比较操作符 ,因此,那些不是数字和字符串的操作数都将进行类型转换。类型转换规则如下:

  · 如果操作数为对象,则按照3章8节3小节处锁描述的转换规则转换为原始值:如果valueOf()返回一个原始值,那么直接使用这个原始值。否则使用toString() 的转换结果进行比较。
  · 在对转换为原始值之后,如果两个操作数都是字符串,那么将依字母表的顺序对两个字符串进行比较,这里提到的“字母表顺序”是组成这两个字符串的16位Unicode字符的索引顺序。
  · 在对象转换为原始值之后,如果至少一个操作数不是字符串,那么两个操作数都将转换为数字进行数值的比较。0和-0是相等的。Infinty壁其它任何数字都大(除了infinty本身),-infinty比任何数字都小(除了它自己本身。)如果一个操作数(或转换后)为NaN,那么比较符总是返回false。

注意,字符串的比较是区分大小写的,所有的大写的ASCII字母都“小于”小写的ASCII字母。

对于数字和字符串操作符来说,加号运算符和比较运算符的行为有所不同 ,前者更偏爱字符串。而比较运算符则更偏爱数字,只有在两个操作数都是字符串串的时候,才会进行字符串的比较。

1 + 2 //=>3 加法,结果为3
"1" + "2" //字符串连接,结果为"12"
"1" + 2 //字符串连接,2转换为"2",结果"12"
11 < 3 //数字比较,结果true
"11" < "3" //字符串比较,结果为true
"11" < 3 //数字的比较,“11”转换为11,结果为true
"one" < 3 //数字比较,"one"转换为NaN,结果为falase

最后需要注意的是,“<=”和“>=”运算符在判断相等的时候,并不依赖相等运算符和和严格相等运算比较规则。相反,小于等于运算符芝是简单的“不大于”,大于等于运算只是“不小于”。只有一个例外,那就是当其一个操作数(后转换后)是NaN的时候,所有4个比较运算符均会返回fasle.

9.3.in运算符

in运算符希望它的左操作数是一个字符串或者可以转换为字符串,希望它的右侧是一个对象。如果右侧的对象拥有一个名为左操作数值的属性名,那么表达式返回true.例如:

var point = {x: 1,y: 1} //定义一个对象
"x" in point //=>true 对象有一个名为x的属性
"z" in point //=>false 对象无名为z的属性
"toString" in point // =>true 对象继承了toString方法

var data = [7, 8, 8]
"0" in data //=>true 数组包含0
1 in data //=>true 数字转换为字符串
3 in data //=>fase 没有索引为3的元素

9.4.instanceof运算符

instanceof运算符希望左操作符为一个对象,右操作数标示对象的类。如果左侧的对象是右侧类的实例,则表达式返回true;负责返回false

var d = new Date(); //构造一个新对象
d instanceof Date; //计算结果为true, d是Date() 创建的
d instanceof Object //计算结果为true ,所有的对象都是Object的实例
d instanceof Number //计算结果为 false,d不是一个Number对象
var a = [1,2,3] //数组直接量创建数组
a instanceof Array //计算结果true a为数组
a instanceof Object //true 所有的数组都是对象
a instanceof RegExp //fasle 数组不是正则表达式

这个判断也会包含对“父类”(superclass)的检测 。

为了 理解instanceof运算符是如何工作的,必须首先理解“原型类”(prototype chain)。

为了计算表达式o instanceof f ,javascript首先计算f.prototyoe,然后在原型链中查询o,如果找到,那么o是f(或者f的父类)的一个实例,那么返回true 。反之false

10.逻辑表达式

逻辑运算符"&&"、“||”、“!”是对操作进行布尔算术运算。

10.1.逻辑与

最简单一层理解是, 当操作数都是布尔值的时候。

关系运算符的优先级要比"&&"(和“||”)要高.

"&&"操作数并不一定是布尔值,回想一下,有些值是可以当做“真值”和“假值”的。(假值是:false null undefined 0 -0 NaN和"",所有其它的值包括所有的对象都是真值)。

对“&&”第二层理解是 ,“&&”是可以对真值和假值进行布尔与(AND)操作。如果两个操作数都是真值的,则那么返回一个真值;否则,至少一个操作数是假值的。

对“&&”第三层的理解 。运算符首先计算左操作数的值,即首先计算“&&”左侧的表达式,如果计算结果是假值,那么整个表达式的结果一定是假值,因此“&&”这时简单的返回左操作的值,而并不会对右边的操作数进行计算。

"&&"的行为有时候被称为“短路”(short circuiting),我们经常能看到很多代码利用了这一特性来有条件的执行代码。例如下面的两条代码是等价的:

if (a == b) stop(); //只有a==b时才能调运stop()
(a == b) && stop(); //同上

10.2.逻辑或

它首先计算第一个操作数的值,也就是说回首先计算左侧的表达式,如果计算结果为真,则返回真值,否则,再计算第二个值。

这个运算符最常用的方式是用来从一组备选的表达中选取第一个真值的表达式:

//如果max_width已经定义了,则直接使用它。赋值在preferences对象中查找max_width
//如果没有定义它,则使用一个写死的常量。
var max =max_width || preferences.max_windth || 500;

这种贯用法通常在函数体内,用来给参数提供默认值:

//将o成功的属性复制到p中,并返回p
function copy(o, p) {
    p = p || {}; //如果向参数p没有传入任何对象,则使用一个新创建对象。
    //函数体内的主逻辑
}

10.3.逻辑非

"!"运算符是一元运算符。

和"&&"、"||"运算符不同, “!”运算符首先将其操作数转换为布尔值,然后再对布尔值求反。也就是"!"总是返回true和 false 。并且,可以通过使用两次逻辑非运算来得到一个值的布尔值。

“!”具有很高的优先级。

!(p && q) === !p || !q
!(p || q) === !p && !q

11.赋值表达式

javascript使用"="运算符给变量或者属性来赋值。

“=”运算符希望它的左操作数为一个左值:一个变量或者对象属性(或数组元素),它的右操作数可以是任意的类型的任意值。赋值表达式的值就是右操作数的值。赋值表达式的副作用是,右操作数的值赋值给左侧的变量或对象属性。这样的话,后续对这个变量和对象的属性的引用都将得到这个值。

赋值操作符的结合性是从右至左,也就是说,一个表达式中出现了多个赋值运算符,运算顺序也从右至左,因此,可以通过以下方式对多个变量赋值。

i=j=k=0; //把三个变量初始化为0

带操作的赋值运算:

示例中的表达式a计算了一次,“等价于”的表达式a计算了两次。只有 a包含具有副作用的表达式(比如函数调用和赋值操作)的时候,两者才不等价。如下两个表达式不等价:

data[i++] *= 2;
data[i++] = data[i++] * 2

12.表达式计算

和很多解释性语言一样,javascript同样可以解释运行由javascript源代码组成的字符串,并产生一个值。javascript通过全局函数eval()来完成这个工作。

eval("3+2") //=>5

12.1.eval

eval()只有一个参数,如果传入的参数不是字符串,它直接返回这个参数。如果参数是字符串,它会把字符串当成javascript进行编译(parse),如果编译失败则抛出一个语法错误(SyntaxError)。如果编译成功,则开始执行这段代码,并返回字符串中最后一个表达式或语句的值,如果最后一个表达式没有语句或者值,则最终返回undefined。如果字符串抛出一个异常,这个异常把该调用的传给eval()。

12.2.全局eval()

 当通过别名调用时,eval()会将其字符串当成顶层的全局代码来执行。执行代码可能会定义新的全局变量和全局函数。执行的代码可能会定义新的全局变量和全局函数,或者给全局变量赋值。但却不能修改或修改主调函数中的局部变量,因此这不会影响到函数内的代码优化。

ECMAScript5是反对使用EvalError的,并且规范了eval()的行为。“直接的eval”,当直接使用非限定的“eval”名称,来调用eval()函数时,它总共是在它的上下文作用域内支线。其它间接调用则使用全局函数为其上下文作用域。并且无法读、写、定义局部变量和函数。下面有一段代码实例:

var geval = eval; //使用别名调用eval将是全局eval
var x = "global",
y = "global"; //两个全局变量
function f() { //函数内执行的局部eval
    var x = "local" //定于局部变量
    eval("x += 'changed';"); //直接eval更改了局部变量的
    return x; //返回更改后的局部变量
}
function g() { //这个函数执行了全局eval
    var y = "local" //定义了局部变量
    geval("y += 'changed';"); //间接改变了局部变量的值
    return y; //返回未更改的局部变量
}
console.log(f(), x); //更改了局部变量,输出local changed global
console.log(g(), y); //更改了全局变量,输出local globalchanged

12.3.严格eval()

ES5严格模式下对eval做了很多限制,都被视为是私有上下文环境中的局部eval,也就是说,在严格模式下,eval执行的代码段可以查询或更改局部变量,但不能在局部作用域中定义新的变量或函数。

不能用一个别名覆盖eval函数。

13.其他运算符

13.1.条件运算符

javascript中的唯一一个三元运算符。

一个"?:"的典型使用场景,判断一个变量是否有定义,如果有定义则使用它,如果无定义,则使用一个默认值:

grett = "hello" + (username ? username : "three");

13.2.typeof()运算符

typeof是一元运算符,放在单个操作数前面,操作数可以是任何类型,返回值表示操作类型的一个字符串。

x                     __ typeof x
undefined             __ "undefined"
null                  __  "object"
ture或false           __"boolean"
任意数字或NaN          __ "Number"
任意字符串             __ "String"
任意函数               __ "function"
任意内容对象(非函数)   __ "object"
任意宿主对象           __ 由编译器各自实现的字符串,但不是"undefined" "boolean" "number" "string"

typeof最常用的用法写在表达式中们就像这样:

(typeof value == "string") ? "" + value + "":value;

typeof运算符同样在swith语句中(5.4.3)非常有用,需要注意的是,typeof运算可以带上圆括号。这样让typeof看起来像一个函数名,而非关键字

如果想将null和对象区分开,则必须针对特殊值显示检验。

对于宿主对象来说,typeof可能并不返回“object”,而返回字符串。

所有对象和数组的typeof运算结果是“object”而不是“function”,因此对于区分对象和其他原始值很有帮助。如果想区分对象的类,可以使用instanceof运算符。
JavaScript中的函数是对象的一种,但是typeof运算符还是将函数特殊对待,对函数返回特殊值“function”。在JavaScript中,函数和可执行对象有微妙的区别。所有的函数都是可执行的,但是对象也有可能是可执行的,可以像调用函数一样调用它,但它并不是一个真正的函数。在ECMAScript 3中,对于所有内置可执行对象,typeof运算符一律返回“function”。ECMASript 5则扩充至所有可执行对象,包括内置对象和宿主对象。

typeof Array   //"function"
typeof Number     //"function"

13.3.delete运算符

delete是一元操作符,它用来删除对象的属性或者数组的元素。就像赋值、递增、递减运算符一样。delete也是具有副作用的。它是用来做删除操作的。不是用来返回一个值的。

var o = {
    x: 1,
    y: 2
}
delete o.x;
"x" in o; //=>false

var a = [1, 2, 3];
delete a[2]; // 删除数组中最后一个元素
2 in a; //=> false 元素2已经在数组中不存在了
a.length; //=>3,注意,数组长度并没有改变,尽管上一行删除了这个元素,但删除操作留下了一个洞。实际上并没有修改数组的长度,因此a的长度仍然为3

delete希望它的操作数是一个左值,如果它不是一个左值,那么delete将不进行任何操作同时返回true,否则delete将试图删除这个指定的左值。如果删除成功,delete将返回true。但是一些内置核心和客户端属性是不能删除的,用户通过var语句声明的变量不能删除,同样,通过function语句定义的函数和函数参数也不能删除。

在ECMASript 5严格模式中,如果delete的操作数是非法的,比如变量、函数或函数参数,delete操作将抛出一个语法错误(SyntaxError)异常,只有操作数是一个属性表达式的时候才会正常工作。在严格模式下,delete删除不可配置的属性时会抛出一个类型错误异常。在非严格模式下,这些delete操作都不会报错,只是简单的返回false,以表明操作数不能执行delete操作。 

var o = {x:1 , y:2}; 
delete o.x; //true:删除一个对象属性 
typeof o.x; //undefined 属性不存在 
delete o.x; //true:删除不存在的属性 
delete o;   //这个时候应该是不能删除的,返回false,严格模式下,抛出一个异常  但是浏览器有不同的定义,chrome是true可以删除 o.x;

13.4.void运算符

作用:操作数会照常计算,但忽略计算结果并返回undefined,由于void会忽略操作数的值,因此在操作数具有副作用的时候使用void来让程序更具语义。

13.5.逗号运算符

它首先计算左操作数,然后计算右操作数,最后返回右操作数的值: 

i=0, j=1 ,z=2;  //2  等价   i = 0; j = 1; z = 2; 

逗号运算符最常用的场景是在for循环中,这个for循环通常具有多个循环变量。 

原文地址:https://www.cnblogs.com/Chen-XiaoJun/p/6496590.html