表达式和运算符
语句
表达式和运算符
- 属性访问表达式两种写法
(1)expression . identifer
(2)expression [ expression ] (适用于对象和数组)
注意:无论使用哪种方式,在“.”和“[”之前的表达式总是会先计算。如果计算结果是null或者undefined,表达式会抛出一个类型错误异常,因为两个值都不能包含任意属性。第一种写法适用于要访问的属性名是合法的标识符,并且需要知道要访问的属性的名字。如果属性名称是一个保留字或者包含空格和标点符号,或是一个数组(对于数组来说),则必须使用方括号的写法,同时如果属性名是通过运算的出来的而不是固定值的时候也要用第二种写法 - 关键字运算符 delete(删除属性)、typeof(检测操作数类型)、void(返回undefined值)、instanceof(测试对象类)、in(测试属性是否存在)
- 在JS中所有的数字都是浮点数,除法运算的结果也是浮点型,比如5/2的结果是2.5而不是2.除数为0的运算结果为正无穷大或者负无穷大,而0/0的结果是NaN
- 取模运算“%”结果的符号和第一个操作数(被除数)的符号保持一致,如5%2=1,-5%2的结果是-1,5%-2=1,取模运算的操作数也适用于浮点数,如6.5%2.1=0.2
- “+”运算符。加好的转换规则优先考虑字符串的连接,如果其中一个操作数是字符串或者转换成字符串的对象,另外一个操作数将会转换为字符串,加法将进行字符串的连接操作。如果两个操作数都不是类字符串(string-like)的,那么都将进行算数加法运算
一些例子如下:
1 + 2 // =>3: 加法
"1" + "2" // =>"12": 字符串连接
"1" + 2 // =>"12": 数字转换成字符串后进行字符串拼接
1 + {} // =>"1[object Object]": 对象转换成字符串后进行字符串拼接
true + true // =>2: 布尔值转换成数字后做加法
1 + null // =>1: null转换为0后做加法
1 + undefined // =>NaN: undefined转换成NaN后做加法
需要注意的是,要考虑加法的结合律
1 + 2 + "blind mice" // => "3 blind mice"
1 + (2 + "bind mice") // => "12bind mice"
- 关系表达式。关系运算符用于测试两个值之间的关系(比如相等、小于或“是...的属性”),根据关系是否存在而返回true和false
- 相等运算符“==”和恒等运算符“===” 不相等“!=”和不严格相等“!==”
- JS对象的比较是引用的比较而不是值的比较。对象和其本身是相等的,但和其他任何对象都不相等。如果两个不同的对象具有相同数量的属性,相同的属性名和值,他们依然是不相等的。相应位置的数组元素是相等的两个数组也是不相等的
- 严格运算符“===”首先计算其操作数的值,然后比较这两个值,比较过程没有任何类型转换:
(1)若两个值类型不相同,则不相等
(2)若两个值都是null或者都是undefined,则不相等
(3)若两个值都是布尔值true或者布尔值false,则相等
(4)若其中一个值是NaN或者两个值都是NaN,则不相等。NaN和其他任何值都不相等,包括它本身,通过x!==x来判断x是否是NaN,只有在x为NaN的时候,这个表达式才是true
(5)若两个值都是数字且值相等,则它们相等。若一个值是0,一个值是-0,则它们同样相等
(6)若两个值是字符串,且所含的对应位上的16位数完全相等,则它们相等。若两个字符串可能含义完全一样且所显示出来的字符也一样,但具有不同编码的16位值,由于JS并不对Unicode进行标准化的转换,因此它们不相等。
(7)若两个引用值指向同一个对象、数组或函数,则相等。如果指向不同的对象,则它们是不等的,尽管两个对象具有完全一样的属性。 - 相等运算符“==”并不是很严格。如果两个操作数不是同一类型,那么相等运算符会尝试进行一些类型转化,然后进行比较:
若两个操作数类型不同,“==”相等运算符也可能会认为它们相等。检测箱等会遵守如下规则和类型转化:
1> 若一个值是null,另一个值是undefined,则它们相等;
2> 若一个值是数字,另一个是字符串,先将字符串转换成数字,然后使用转换后的值进行比较;
3> 若一个值是true,则将其转换成1后再进行比较。若其中一个是false,则将其转换成0后再进行比较;
4> 若一个值是对象,另一个值是数字或者字符串,则是用转换规则将对象转换为原始值,然后再进行比较。对象通过toString()方法或者valueOf()方法转换为原始值。JS语言核心的内置类首先尝试使用valueOf(),在尝试使用toString(),除了日期类,日期类只使用toString()转换,那些不是JS语言核心中的对象通过各自的实现中定义的方法转换成原始值。 - 这里举一个判断相等的小例子:"1" == true 这个表达式的结果是true,布尔值先转成数字1,字符串"1"也转换成了数字1,因为两个数字相等,所以结果是true.
- 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, 9]; // 拥有三个元素的数组
"0" in data // =>true:数组包含元素"0"
1 in data // =>true:数字转换成字符串
3 in data // =>false:没有索引为3的元素
- instanceof运算符,该运算符希望左操作数是一个对象,右操作数标识对象的类。如果左侧的对象是右侧类的实例,则表达式返回true,否则返回false
var d = new Date(); // 通过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; // false 数组不是正则表达式
需要注意的是,所有的对象都是Object实例
- 逻辑表达式--逻辑与&&,“&&”的行为有时候称作“短路”,利用这个特性下边两个代码语句完全相同:
if (a==b) stop() // 只有在a==b的时候才调用stop()
(a == b) && stop() // 同上 - 逻辑或|| 该云素服最常用的方式是用来从一组备选表达式中选出第一个真值表达式:
// 如果max_width已经定义了,直接使用它;否则在preferences对象中查找max_width
// 如果没有定义它,则是用一个写死的常量
var max = max_width || preferences.max_width || 500;
这种惯用法通常在函数体内,用来给参数提供默认值
// 将o的成员属性复制到p中,并返回p
function copy(o, p) {
p = p || {}; // 如果向参数p没有传入任何对象,则使用一个新创建的对象
// 函数体内的主逻辑
}
- 表达式计算。和其他很多解释性语言一样,JS同样可以解释运行由JS源代码组成的字符串,并产生一个值。JS通过全局函数eval()来完成这个工作:eval("3+2") // =>5
eval()只有一个参数.如果传入的不是字符串,它直接返回这个参数。如果参数是字符串,它会把字符串当成JS代码进行编译(解析这段字符串),如果编译失败则抛出一个语法错误(SyntaxError)异常。如果编译成功,则开始执行这段代码,并返回字符串中的最后一个表达式或语句的值,如果最后一个表达式或语句没有值,则最终返回undefined。如果字符串抛出一个异常,这个异常将把该调用传递给eval() - typeof运算符。typeof是一元运算符,放在单个操作数的前边,操作数可以是任何类型。返回值为表示操作数类型的一个字符串
任意值在typeof运算后的返回值
x | typeof x |
---|---|
undefined | "undefined" |
null | "object" |
true或false | "boolean" |
任意数字或NaN | "number" |
任意字符串 | "string" |
任意函数 | "function" |
任意内置函数(非对象) | "object" |
任意宿主对象 | 由编译器各自实现的字符串,但不是"undefined"、"boolean"、"number"或"string" |
- 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:注意数组长度并没有变,尽管上一行代码删除了这个元素,但删除操作留下了一个"洞"
(在该处设置了一个undefined的值),实际上并没有修改数组的长度,因此a数组的长度仍是3
当删除一个属性时,这个属性将不再存在。读取一个不存在的属性将返回undefined,但是可以通过in运算符
来检测这个属性是否在对象中存在
- delete希望他的操作数是一个左值,如果它不是左值,那么delete将不进行任何操作同时返回true。否则,delete将试图删除这个指定的左值。如果删除成功,delete将返回true。然而并不是所有属性都可以删除,一些内置核心和客户端属性是不能删除的,用户通过var语句声明的变量不能删除。同样通过function语句定义的函数和函数参数也不能删除(对变量、函数或函数参数进行delete操作将抛出一个语法错误异常)。在严格模式下,delete删除不可配置的属性时会抛出一个类型错误异常。
var o = {x: 1, y: 2}; // 定义一个变量,初始化为对象
delete o.x; // 删除一个对象属性,返回true
typeof o.x; // 属性不存在,返回"undefined"
delete o.x; // 删除不存在的属性,返回true
delete o; // 不能删除通过var变量声明的变量,返回false
// 在严格模式下,将抛出一个异常
delete 1; // 参数不是一个左值,返回true
this.x = 1; // 给全局对象定义一个属性,这里没有使用var
delete x; // 试图删除它,在非严格模式下返回false
// 在严格模式下会抛出异常,这是使用"delete this.x"来代替
x; // 运行时错误,没有定义x
语句
- 全局变量是全局对象的属性。然而和其他全局对象属性不同的是,var声明的变量无法通过delete删除。如果var语句中的变量没有指定初始化表达式,那么这个变量的值初始为undefined。
- 变量在声明它们的脚本或者函数中都是有定义的,变量声明语句会被“提前”至脚本或者函数的顶部,但是初始化的操作还是在原来var语句的位置执行,在声明语句之前变量的值是undefined。函数声明语句创建的变量也是无法删除的。
- for-in循环并不会遍历对象的所有属性,只有“可枚举”的属性才会遍历到。由JS语言核心所定义的内置方法就不是“可枚举”的。如所有的对象都有方法toString(),但for-in循环并不枚举toString()这个属性。
- 标签语句--语句是可以添加标签的,标签是由语句前的标识符和冒号组成:
identifier: statement - 函数调用是一种表达式,而所有表达式都有值。函数中的return语句既是指定函数调用后的返回值。return语句只能在函数内出现,如果不是的话会报语法错误。如果没有return语句,则函数调用仅依次执行函数体内的每一条语句直到函数结束,最后返回调用程序。这种情况下,调用表达式的结果是undefined。
- try/catch/finally语句是JS的异常处理机制。其中try从句定义了需要处理的异常所在的代码块,catch语句跟随在try从句之后,当try块内某处发生了异常时,调用catch内的代码逻辑。catch从句后跟随finally块,后者中放置清理代码,不管try块中是否产生异常,finally块内的逻辑总是会执行。尽管catch和finally都是可选的,但try从句需要至少二者之一与之组成完整的语句。try,catch和finally语句块都需要花括号括起来,这里的花括号是必须的,即使从句中只有一条语句也不能省略花括号。
try/catch/finally的语法和使用目的
try {
// 通常来讲,这里的代码会从头执行到尾而不会产生任何问题
// 但有时会抛出一个异常,要么是由throw语句直接抛出异常
// 要么是通过调用一个方法间接抛出异常
} catch(e) {
// 当且仅当try语句抛出了异常,才会执行这里的代码
// 这里可以通过局部变量e来获得对Error对象或者抛出的其他值的引用
// 这里的代码块可以基于某种原因处理这个异常,也可以忽略这个异常
// 还可以通过throw语句重新抛出异常
} finally {
// 不管try语句块是否抛出了异常,这里的逻辑总会执行,终止try语句块的方式有:
// 1)正常终止,执行完语句块的最后一条语句
// 2)通过break、continue或return语句终止
// 3)抛出一个异常,异常被catch从句捕获
// 4) 抛出一个异常,异常未被捕获,继续向上传播
}
- with语句用于临时扩展作用于链,它的语法如下:
with(object)
statement
这条语句将object添加到作用域链的头部,然后执行statement,最后把作用域链恢复到原始状态。 - debugger语句(断点器调试)和“use strict”(对脚本和函数使用应用严格模式)