[JS权威指南笔记] 第4、5章

冷不防 2022-01-21 06:17 431阅读 0赞

第4章 表达式和运算符

4.1 原始表达式

原始表达式(primary expression)是表达式的最小单位——它们不再包含其它表达式。

1)直接量:数字直接量、字符串直接量、正则表达式直接量

2)保留字:true、false、null、this

3)变量

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

也称“对象直接量”和“数组直接量”,不是原始表达式

数组直接量中的列表逗号之间的元素可以省略,空位填充undefined

4.3 函数定义表达式

也称函数直接量

eg:

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

4.4 属性访问表达式

方括号或点

“.” 和 “[“ 之前的表达式总会先计算,如果计算结果是null或undefined,表达式会抛出一个类型错误异常

4.5 调用表达式(invocation expression)

4.6 对象创建表达式(object creation expression) -> 创建一个对象并调用一个函数(构造函数)初始化新对象的属性,对象创建表达式有一个关键字new

4.7 运算符概述

一个需要注意的例子 -> “5”*“3”结果是数字15

4.8 算术表达式

JS中,所有的数字都是浮点型的,除法运算的结果也是浮点型,比如 5/2=2.5

0/0 -> NaN, someNum/0 -> 正负无穷大 (注意:不会报错)

-5%2 = -1, 6.5%2.1 = 0.2

“1”+2 => “12”

true + ture => 2

2 + null => 2

2 + undefined => NaN

注意:”++” 运算符从不进行字符串连接操作

表达式++x并不总和x=x+1完全一样,例如x是”1”,前者是2,后者是”11”

4.9 关系表达式

4.9.1 相等和不相等运算符

“===” 严格相等运算符(strict equality)或恒等运算符(identity operator)

“==” 相等运算符(equality operator)

建议:

“=” 得到 或 赋值

“==” 相等

“===” 严格相等

“!=” 不相等

“!==” 不严格相等

控制台检验的一些例子:

1==”1” => true

null==undefined => true

null===undefined => false

-————————————-

下面两个和书里写的不一样

null===null

undefined===undefined

-————————————-

“1”==true => true, 如果一个值是数字,另一个是字符串,先将字符串转换为数字

4.9.2 比较运算符

一些例子:

“1” + 2 => 2转换为”2”,结果是”12”

“11” < “3” => 字符串比较,结果为true,注意所有大写的ASCII字母都“小于”小写的ASCII字母

“11” < 3 => 数字比较,”11”转换为11,结果为false

“one” < 3 => 数字比较,如果其中一个操作数是(或转换后是)NaN,那么比较操作符总是返回false(>=,=<同理)

4.9.3 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的元素

4.9.4 instanceof运算符

如果左侧的对象是右侧类的实例,则表达式返回true

第9章将会讲到,JavaScript中对象的类是通过初始化它们的构造函数来定义的。这样的话,instanceof的右操作数应当是一个函数。

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的实例。当通过instanceof判断一个对象是否是一个类的实例的时候,这个判断也会包含对“父类”(superclass)的检测。如果instanceof的左操作数不是对象的话,instanceof返回false。如果右操作数不是函数,则抛出一个类型错误异常。

理解原型链(prototype chain)6.2.2节

4.10 逻辑表达式

x==0 && y==0 //关系运算符的优先级比“&&”和“||”要高,可以不写括号

!(p && q) // “!”优先级很高,和操作数紧密绑在一起

1.逻辑与

&&并不总是返回true或false

  1. 如果计算结果是假值,则返回左操作数的值
  2. 当左操作数是真值时,将计算右操作数的值并将其返回作为整个表达式的计算结果

&&的行为有时称为短路(short circuiting)

eg:以下两行等价

if(a==b) stop();

(a==b) && stop();

具有副作用-> 赋值、递增、递减和函数表达式

2.逻辑或

最常用的方式是从一组备选表达式中选出一个真值表达式,通常用在函数体内,用来给参数提供默认值

var max = max_width || preferences.max_width || 50;

3.逻辑非

总是返回true或false(注意这点和前两者不一样)

可以通过 !!x 得到一个等价的布尔值

4.12 表达式计算(没看明白,先跳过)

4.13.2 typeof运算符

注意:

typeof(null)

“object”

所有对象和数组的typeof返回object

可执行对象(callable)的typeof返回function

了解真正的函数和可执行对象参照8.7.7

4.13.3 delete运算符(看完第6章再看看)

用来删除对象属性或数组元素

delete也具有副作用,它是用来删除操作数的,不是用来返回一个值的

1)delete希望它的操作数是一个左值(变量、对象属性和数组元素),如果它不是左值,那么delete将不进行任何操作同时返回true

2)否则将试图删除 2.1)删除成功返回true,只有操作数是一个属性访问表达式(4.4节)的时候它才会正常工作

2.2)一些内置核心客户端属性不能删除,用户通过var语句声明的变量不能删除,通过functon语句定义的函数和函数参数也不能删除。如果操作非法,delete操作将抛出一个语法错误(SyntaxError)异常

3)在严格模式下,delete操作删除不可配置的属性(6.7节)时会抛出一个类型错误异常,在非严格模式下不会报错仅返回false

第五章 语句

5.2 复合语句和空语句

关于空语句,注意:

[例]

if(( a==0 ) || (b==0)) ; //这一行代码什么都没做,最后的分号是一条空语句

o = null; //这一行代码总会执行

如果有特殊的目的需要使用空语句,最好在代码中添加注释

[例]

for (i =0; i < a.length; a[i++] = 0) /*empty*/;

5.3 声明语句

var和function都是声明语句,它们声明或定义函数或变量。

5.3.1 var

var语句出现在函数体 -> 局部变量,作用域:这个函数

var语句在顶层代码 -> 全局变量,作用域:整个JS

注意:

全局变量是全局对象的属性,但是var声明的变量不能通过delete删除(3.10.2节有提到)。

如果var语句中的变量没有指定初始化表达式,那么这个变量的值为undefined

5.3.2 function

两种定义写法

var f = function(x) {return x+1;} //函数定义表达式

function f(x){return x+1;} //函数声明表达式

1.区别:两种方法都创建了新的函数对象,但1)函数声明语句中的函数名是一个变量,变量指向函数对象。

函数声明语句通常出现在JS代码的最顶层,也可以嵌套在其它函数体内(嵌套时只能出现在所嵌套函数的顶部)。使用函数声明语句,函数名和函数体均提前,可以在声明一个JS函数之前调用它。

2)和通过var声明变量一样,函数定义语句中的函数被显式地“提前”到了脚本或函数的顶部。

  1. 和var语句一样,函数声明语句创建的变量也是无法删除的。

5.4 条件语句(else,else if,switch)

5.4.3 switch

当执行这条switch语句的时候,它首先计算expression的值,然后查找case子句中的表达式是否和expression的值相同(这里的“相同”是按照“===”运算符进行比较的)。

在每一个case语句块的结尾处都使用了关键字break。break语句可以使解释器跳出switch语句或循环语句。在switch语句中,case只是指明了要执行的代码起点,但并没有指明终点。

如果在函数中使用switch语句,可以使用return来代替break,return和break都用于终止switch语句,

eg:

function convert(x){
switch(typeof x){
case ‘number’: // 将数字转换为十六进制数
return x.toString(16);
case ‘string’: // 返回两端带双引号的字符串
return ‘“‘ + x + ‘“‘;
default: // 使用普通的方法转换其他类型
return String(x);
}
}

“default:”标签一般出现在switch的末尾,位于所有case标签之后。实际上,它可以放置在switch语句内的任何地方。

5.5 循环(while,do/while,for,for/in)

5.5.2 do/while

do statement
while(expression); //至少执行一次,最后这个分号不要忘记

5.5.3 for

for(initialize ; test ; increment)
statement

和以上等价的while循环

initialize;

while(test){

statement

increment;

}

for循环中数字是最常用的,但不是必需的。下面这段代码就使用for循环来遍历链表数据结构,并返回链表中的最后一个对象(也就是第一个不包含next属性的对象):

function tail(o) { // 返回链表的最后一个节点对象
for(; o.next; o = o.next) /* empty */ ; // 根据判断o.next是不是真值来执行遍历
return o;
}

for循环中那三个表达式中的任何一个都可以忽略,但是两个分号必不可少。如果省略test表达式,那么这将是一个死循环,同样,和while(true)类似,死循环的另外一种写法是for(;;)。

5.5.3 for/in

for(variable in object)
statement

使用for循环来遍历数组元素是非常简单的:

for(var i = 0; i < a.length; i++) //i代表了数组元素的索引
console.log(a[i]); //输出数组中的每个元素

而for/in循环则是用来更方便地遍历对象属性成员:

for(var p in o) // 将属性名字赋值给变量p
console.log(o[p]); //输出每一个属性的值

在执行for/in语句的过程中,JavaScript解释器首先计算object表达式。

1)如果表达式为null或者undefined,JavaScirpt解释器将会跳过循环并执行后续的代码 。

2)如果表达式等于一个原始值,这个原始值将会转换为与之对应的包装对象(wrapper object)(见3.6节)。

3)否则,expression本身已经是对象了。JavaScript会依次枚举对象的属性来执行循环。然而在每次循环之前,JavaScript都会先计算variable表达式的值,并将属性名(一个字符串)赋值给它。

例如,可以使用下面这段代码将所有对象属性复制至一个数组中:

var o = {x:1, y:2, z:3};
var a = [], i = 0;
for(a[i++] in o)/* empty */;

JavaScript数组不过是一种特殊的对象,因此,for/in循环可以像枚举对象属性一样枚举**数组索引**。

例如,在上面的代码之后加上这段代码就可以枚举数组的索引0、1、2:
for(i in a)console.log(i);

注意:for/in循环并不会遍历对象的所有属性,只有“可枚举”的属性才会遍历到(参照6.7节)。由JavaScript语言核心所定义的内置方法就不是“可枚举的”。比如,所有的对象都有方法toString(),但for/in循环并不枚举toString这个属性。

不可枚举(nonenumerable):内置方法、很多内置对象的属性

可枚举(enumerable):代码中定义的所有属性和方法、继承的自定义属性

属性枚举的顺序(暂时没看明白 囧)

5.6 跳转

5.6.1 标签语句

break和continue是JS中唯一可以使用语句标签的语句

mainloop: while(token != null){
// 忽略这里的代码…
continue mainloop; // 跳转到下一次循环
// 忽略这里的代码…
}

5.6.2 break语句

单独使用break语句的作用是立即退出最内层的循环或switch语句。

break labelname;

当break和标签一块使用时,程序将跳转到这个标签所标识的语句块的结束,或者直接终止这个闭合语句块的执行。

当你希望通过break来跳出非就近的循环体或者switch语句时,就会用到带标签的break语句。下面是示例代码:

var matrix = getData(); // 从某处得到一个二维数组
// 将矩阵中所有元素进行求和
var sum = 0, success = false;
//从标签名开始,以便在报错时退出程序
compute_sum: if(matrix){
for(var x = 0; x < matrix.length; x++){
var row = matrix[x];
if(!row)break compute_sum;
for(var y = 0; y < row.length; y++){
var cell = row[y];
if(isNaN(cell))break compute_sum;
sum += cell;
}
}
success = true;
}
//break语句跳转至此
//如果在success == false的条件下到达这里,说明我们给出的矩阵中有错误
//否则将矩阵中所有的元素进行求和

最后,需要注意的是,不管break语句带不带标签,它的控制权都无法越过函数的边界。比如,对于一条带标签的函数定义语句来说,不能从函数内部通过这个标签来跳转到函数外部。

5.6.2 continue语句

不管continue语句带不带标签,它只能在循环体内使用。在其他地方使用将会报语法错误。

注意:continue语句在while和for循环中的区别,while循环直接进入下一轮的循环条件判断,但for循环首先计算其increment表达式,然后判断循环条件。

和break语句类似,带标签的continue语句可以用在嵌套的循环中,用以跳出多层次嵌套的循环体逻辑。

5.6.4 return语句

return语句只能在函数体内出现。return语句不带表达式,函数会向调用程序返回undefined。

5.6.5 throw语句

5.6.6 try/catch/finally语句

1)当try块内某处发生了异常时,调用catch内的代码逻辑。

catch从句后跟随finally块,后者中放置清理代码,不管try块中是否产生异常,finally块内的逻辑总是会执行。

2)尽管catch和finally都是可选的,但try从句需要至少二者之一与之组成完整的语句。

3)try、catch和finally语句块都需要使用花括号括起来(即使只有一条语句也不能省略)。

如果使用try/finally语句,就能使用while循环来正确模拟包含continue的for循环:

//模拟for(initialize;test;increment)body;initialize ;
while(test){
try { body ; }
finally { increment ; }
}

5.7 其它语句类型

5.7.1 with语句

with语句用于临时扩展作用域链(scope chain),它具有如下的语法:

with(object)statement

这条语句将object添加到作用域链的头部,然后执行statement,最后把作用域链恢复到原始状态。

在严格模式中(参照5.7.3节)是禁止使用with语句的,并且在非严格模式里也是不推荐使用with语句的,尽可能避免使用with语句

在对象嵌套层次很深的时候通常会使用with语句来简化代码编写。例如,在客户端JavaScript中,可能会使用类似下面这种表达式来访问一个HTML表单中的元素:

document.forms[0].address.value

如果这种表达式在代码中多次出现,则可以使用with语句将form对象添加至作用域链的顶层:

with(document.forms[0]){
// 直接访问表单元素,例如:
name.value = “”;
address.value = “”;
email.value = “”;
}

不使用with语句的等价代码可以写成这样:

var f = document.forms[0];
f.name.value = “”;
f.address.value = “”;
f.email.value = “”;

只有在查找标识符的时候才会用到作用域链,创建新的变量的时候不使用它,看一下下面这行代码:

with(o)x = 1;

如果对象o有一个属性x,那么这行代码给这个属性赋值为1。但如果o中没有定义属性x,这段代码和不使用with语句的代码x=1是一模一样的。它给一个局部变量或者全局变量x赋值,或者创建全局对象的一个新属性。with语句提供了一种读取o的属性的快捷方式,但它并不能创建o的属性。

5.7.2 debugger

这条语句用来产生一个断点(breakpoint)

5.7.3 “use strict”

“use strict”是ECMAScript 5引入的一条指令。

严格模式和非严格模式之间的最重要的三条区别:

1)在严格模式中禁止使用with语句。

2)在严格模式中,所有的变量都要先声明,如果给一个未声明的变量、函数、函数参数、catch从句参数或全局对象的属性赋值,将会抛出一个引用错误异常。

3)在严格模式中,调用的函数(不是方法)中的一个this值是undefined。(在非严格模式中,调用的函数中的this值总是全局对象)。可以利用这种特性来判断JavaScript实现是否支持严格模式:

var hasStrictMode =(function(){ “use strict”; return this===undefined}());

发表评论

表情:
评论列表 (有 0 条评论,431人围观)

还没有评论,来说两句吧...

相关阅读