02. 初识ECMAScript
一、什么是ECMAScript
ECMAScript 是由网景的布兰登·艾奇开发的一种脚本语言的标准化规范;最初命名为 Mocha,后来改名为 LiveScript,最后重命名为 JavaScript。1995 年 12 月,升阳与网景联合发表了 JavaScript。1996 年 11 月,网景公司将 JavaScript 提交给欧洲计算机制造商协会进行标准化。ECMA-262 的第一个版本于 1997 年 6 月被 Ecma 组织采纳。ECMA Script 是 ECMA-262 标准化的脚本语言的名称。尽管 JavaScript 和 JScript 与 ECMAScript 兼容,但包含超出 ECMA Script 的功能。
ECMAScript 是一种可以在宿主环境中执行计算并能操作可计算对象的基于对象的程序设计语言。ECMAScript 最先被设计成一种 Web 脚本语言,用来支持 Web 页面的动态表现以及为基于 Web 的客户机—服务器架构提供服务器端的计算能力。但作为一种脚本语言, ECMAScript 具备同其他脚本语言一样的性质,即 "用来操纵、定制一个已存在系统所提供的功能,以及对其进行自动化"。
二、基础语法
2.1、注释
在开发程序过程中,如果一段代码的逻辑比较复杂,不是特别容易理解,可以适当添加注释,以辅助自己或其他开发人员解读代码。注释是给程序员看的,为了让程序员方便阅读代码,解释器会忽略注释。在 Pyton 中,注释分为单行注释和多行注释。
在 ECMAScript 中,单行注释 以 //
开头,直到换行为止。
// 单行注释
在 ECMAScript 中,多行注释 以 /*
开头,以 */
为止,被 /*
和 */
两个符号括起来的部分是程序的注释。
/*
多行注释
*/
2.2、标识符
所谓 标识符,就是指 变量、函数、属性 的名字,或者 函数的参数。标识符 可以是按照下列格式规则组合起来的一或多个字符。
-
标识符中可以包含 字母、数字、下划线
_
、美元符号$
,但是不能使用数字开头。 -
标识符不能是 ECMAScript 中的关键字和保留字
-
标识符 区分大小写。
对于变量(包括函数名),以小写字母开始,单词之间采用驼峰命名法。对于类名,以大写字母开始,单词之间采用驼峰命名法。常量通常使用全大写加下划线。
2.3、语句
ECMAScript 则允许开发者自行决定是否以分号结束一行代码。如果没有分号,ECMAScript 就把这行代码的结尾看作该语句的结束。
var width = 800
var height = 600;
2.4、代码块
代码块表示一系列应该顺序执行的语句,这些语句被封装在 左花括号 {
和 右花括号 }
之间。
if (focus == true)
{
border.width = 2
border.color = "blue"
}
三、变量
3.1、什么是变量
变量(variable),就是可以变量的量,它可以用来保存字面量,并且变量中保存的字面量是不定的。变量本身没有任何意思,它会根据不同的字面值表示不同的意思。
ECMAScript 中的变量没有特定的类型,定义变量时只用 var
运算符,可以将它初始化为任意的值,你可以随时改变变量所存储的数据类型。
var a = 10
一个 var
语句定义的多个变量可以有不同的类型,变量之间使用 逗号 ,
分隔。
var a = 10, name = "Sakura"
因为 ECMAScript 是 弱类型的,你也可以使用一个变量存储不同类型的值。
var a = "Sakura"
var a = 10
3.2、原始类型
ECMAScript 有 5 种原始类型,即 Undefined
、Null
、Boolean
、Number
和 String
。每种原始类型定义了它包含的值的范围及其字面量表示形式。
ECMAScript 提供了 typeof
运算符来判断一个值的类型,如果这个值是原始类型,typeof
还会返回它具体的类型名字;而如果这个值是引用值,那么 typeof
统一返回 "object"
作为类型名字。
3.2.1、Undefined类型
Undefined
类型只有一个值,即 undefined
。当 使用 var 声明的变量未初始化 时,该变量的默认值就是 undefined
。
var a
console.log(a) // undefined
console.log(typeof a) // undefined
当函数没有明确的返回值时,返回的值也是 undefined
。
3.2.2、Null类型
Null
类型也只有一个值,即 null
。你可以显式地将一个变量初始化为 null
。
var a = null
console.log(a) // null
console.log(typeof a) // object
调用
typeof null
会返回"object"
,因为特殊值null
被认为是一个空的对象引用。
3.2.3、Boolean类型
Boolean
类型只有 true
和 false
两个字面量值。
var flag = true
console.log(flag) // true
console.log(typeof flag) // boolean
var flag = false
console.log(flag) // false
console.log(typeof flag) // boolean
3.2.4、Number类型
Number
类型既可以表示 整数,也可以表示 浮点数。
var integer = 10
console.log(integer) // 10
console.log(typeof integer) // number
var decimal = 10.5
console.log(decimal) // 10.5
console.log(typeof decimal) // number
整数 也可以用 二进制(以 2 为基数、八进制(以 8 为基数)或 十六进制(以 16 为基数)字面量表示。对于 二进制字面量,需要在数值前面个加上 0b
前缀(大小写都可以),后面二进制数字。对于 八进制字面量,需要在数值前面个加上 0o
前缀(大小写都可以),后面八进制数字。对于 十六进制字面量,需要在数值前面加上前缀 0x
(大小写都可以),然后是十六进制数字(0 ~ 9
以及 A ~ F
)。十六进制数字中的字母大小写均可。
// 10
var binaryInterger = 0b1010
console.log(binaryInterger)
var binaryInterger = 0B1010
console.log(binaryInterger)
var octalInterger = 0o12
console.log(octalInterger)
var octalInterger = 0O12
console.log(octalInterger)
var hexInterger = 0x0A
console.log(hexInterger)
var hexInterger = 0X0A
console.log(hexInterger)
对于非常大或非常小的数值,浮点值 可以用 科学记数法 来表示。科学记数法 用于表示一个应该 乘以 10 的给定次幂的数值。ECMAScript 中科学记数法的格式要求是一个数值(整数或浮点数)后跟一个大写或小写的字母 e,再加上一个要乘的 10 的多少次幂。
var integer = 3.15e8
console.log(integer)
var integer = 315E6
console.log(integer)
var decimal = 240E-6
console.log(decimal)
var decimal = 32.768e-6
console.log(decimal)
由于内存的限制,ECMAScript 并不支持表示这个世界上的所有数值。ECMAScript 可以表示的最小数值保存在 Number.MIN_VALUE
中,可以表示的最大数值保存在 Number.MAX_VALUE
中。如果某个计算得到的数值结果超出了 ECMAScript 可以表示的范围,那么这个数值会被自动转换为一个特殊的 Infinity
(无穷)值。任何无法表示的负数以 -Infinity
(负无穷大)表示,任何无法表示的正数以 Infinity
(正无穷大)表示。
如果计算返回 Infinity
或 -Infinity
,则该值将不能再进一步用于任何计算。这是因为 Infinity
没有可用于计算的数值表示形式。要确定一个值是不是有限大(即介于 ECMAScript 能表示的最小值和最大值之间),可以使用 isFinite()
函数。
var minValue = Number.MIN_VALUE // 5e-324
console.log(minValue)
var maxValue = Number.MAX_VALUE // 1.7976931348623157e+308
console.log(maxValue)
console.log(Number.MAX_VALUE + 1) // 1.7976931348623157e+308
console.log(isFinite(Number.MAX_VALUE + 1)) // true
有一个特殊的数值叫 NaN
,意思是 "不是数值"(Not a Number),用于表示本来要返回数值的操作失败了(而不是抛出错误)。比如,用 0 除任意数值在其它语言中通常都会导致错误,从而中止代码执行。但在 ECMAScript 中,0、+0 或 -0 相除会返回 NaN
。要确定一个值是不是 NaN
,可以使用 isNaN()
函数。
var value = 0 / 0
console.log(value) // NaN
console.log(typeof value) // number
console.log(isNaN(value)) // true
3.2.5、String类型
String
(字符串)数据类型表示零或多个 16 位 Unicode 字符序列。字符串可以使用 双引号 "
、单引号 '
或 反引号 ` 括起来。
var str = "Hello World!"
console.log(str)
console.log(typeof str)
var str = 'Hello World!'
console.log(str)
console.log(typeof str)
var str = `Hello World!`
console.log(str)
console.log(typeof str)
以某种引号作为字符串开头,必须仍然以该种引号作为字符串结尾。
字符串数据类型包含一些字符字面量,用于表示非打印字符或有其它用途的字符。
字面量 | 含义 |
---|---|
\n |
换行 |
\t |
制表 |
\b |
退格 |
\r |
回车 |
\f |
换页 |
\\ |
反斜杠 / |
\' |
单引号 ' |
\" |
双引号 " |
\` | 反引号 ` |
\xnn |
以十六进制编码 nn 表示的字符,其中 n 是十六进制数字 |
\unnn |
以十六进制编码 nnn 表示的 Unicode 字符,其中 n 是十六进制数字 |
ECMAScript 中的字符串是不可变的,意思是一旦创建,它们的值就不能变了。要修改某个变量中的字符串值,必须先销毁原始的字符串,然后将包含新值的另一个字符串保存到该变量。我们也可以将 String 看作一个字符数组,通过从 0 开始的索引下标来访问其中的字符。
var temp = "Sakura"
console.log(temp[0]) // S
console.log(typeof temp[0]) // string
3.3、类型转换
3.3.1、转换成字符串
Boolean
、Number
、String
三种原始类型,都有 toString()
方法,可以把它们的值转换为字符串。
var str = "Hello World!".toString()
console.log(str)
console.log(typeof str)
var str = true.toString()
console.log(str)
console.log(typeof str)
var str = 3.14.toString()
console.log(str)
console.log(typeof str)
var str = NaN.toString()
console.log(str)
console.log(typeof str)
Number
类型的 toString()
方法还可以 按基转换。如果你不指定数基,那不管原来是用什么形式声明的 Number
类型,toString()都按 十进制 输出。
var integer = 10
console.log(integer.toString(2))
console.log(integer.toString(8))
console.log(integer.toString(16))
3.3.2、转换成数字
parseInt()
和 parseFloat()
可以把字符串的原始值转换成数字,前者把值转换为 整数,后者把值转换成 浮点数。这两个方法只能用于 String
类型,如果你对其它类型调用它们,返回值是的 NaN
。parseInt()
和 parseFloat()
会扫描字符串,直到遇到第一个非数字字符时停止,将转换的结果返回。对于 parseFloat()
,会将第一个小数点作为有效字符,而 parseInt()
则不会。
console.log(parseInt("1000")) // 1000
console.log(parseInt("0x0A")) // 10
console.log(parseInt("3.14")) // 3
console.log(parseInt("2000年01月01日")) // 2000
console.log(parseInt("blue")) // NaN
console.log(parseFloat("1000")) // 1000
console.log(parseFloat("3.14159")) // 3.14159
console.log(parseFloat("1.2.3")) // 1.2
console.log(parseFloat("0x0A")) // 0
console.log(parseFloat("2000年01月01日")) // 2000
console.log(parseFloat("blue")) // NaN
parseInt()
还支持基模式。
console.log(parseInt("AK47", 16)) // 10
console.log(parseInt("AK47", 10)) // NaN
console.log(parseInt("010", 8)) // 8
console.log(parseInt("010", 10)) // 10
3.3.3、强制转换类型
ECMAScript 也支持强制类型转换,有三种转换:
Boolean(value)
,把value
转换成Boolean
布尔类型。Number(value)
,把value
转换为Number
数字(整数或浮点数)。String(value)
,把value
转换成String
字符串。
【1】、Boolean()
当要转换的值是一个非空字符串、非 0 数字或对象时,Boolean()
函数将返回 true
。如果该值为 空字符串、数字 0
、NaN
、undefined
或 null
,它将返回 false
。
console.log(Boolean("")) // false
console.log(Boolean("Qt Quick")) // true
console.log(Boolean(0)) // false
console.log(Boolean(1)) // true
console.log(Boolean(NaN)) // false
console.log(Boolean(null)) // false
console.log(Boolean(undefined)) // false
【2】、Number()
Number()
的强制类型转换与 parseInt()
和 parseFloat()
方法的不同之处在于:Number()
转换的是整个值,而 parseInt()
和 parseFloat()
只转换第一个无效字符之前的字符串。
console.log(Number("3.14")) // 3.14
console.log(Number("1.2.3")) // NaN
【3】、String()
String()
可以把任何值转换为字符串,它与调用 toString()
方法的唯一不同在于:对 null
或 undefined
值强制类型转换可以生成字符串而不引发错误。
console.log(String(true)) // "true"
console.log(String(3.14)) // "3.14"
console.log(String(NaN)) // "NaN"
console.log(String(null)) // "null"
console.log(String(undefined)) // "undefined"
四、表达式与运算符
运算符 就是对字面量或者变量进行操作的符号;表达式 是指用运算符把字面量或者变量连接起来,符合 ESMAScript 语言语法的式子。不同运算符连接的表达式体现的是不同类型的表达式;每个表达式都有一个值。要想获取这个值,必须根据运算符优先级的顺序来执行操作。
4.1、算术运算符
算术运算符 用于执行变量与(或)值之间的算术运算。
算术运算符 | 描述 | 例子 | 结果 |
---|---|---|---|
+ | 加 | 5 + 2 | 7 |
- | 减 | 5 - 2 | 3 |
* | 乘 | 5 * 2 | 10 |
/ | 除 | 5 / 2 | 2.5 |
% | 取余 | 5 % 2 | 1 |
++ | 递增 | a = 2; b = ++a; a = 2; b = a++; |
a = 3; b = 3; a = 3; b = 2; |
-- | 递减 | a = 2; b = --a; a = 2; b = a--; |
a = 1; b = 1; a = 1; b = 2; |
console.log("5 + 2 = ", 5 + 2) // 5 + 2 = 7
console.log("5 - 2 = ", 5 - 2) // 5 - 2 = 3
console.log("5 * 2 = ", 5 * 2) // 5 * 2 = 10
console.log("5 / 2 = ", 5 / 2) // 5 / 2 = 2.5
console.log("5 % 2 = ", 5 % 2) // 5 % 2 = 1
console.log("\n前++")
var a = 2
var b = ++a;
console.log("a = ", a) // a = 3
console.log("b = ", b) // b = 3
console.log("\n后++")
var a = 2
var b = a++
console.log("a = ", a) // a = 3
console.log("b = ", b) // b = 2
console.log("\n前--")
var a = 2
var b = --a
console.log("a = ", a) // a = 1
console.log("b = ", b) // b = 1
console.log("\n后--")
var a = 2
var b = a--
console.log("a = ", a) // a = 1
console.log("b = ", b) // b = 2
4.2、赋值运算符
赋值运算符 用于给变量赋值。
赋值运算符 | 描述 | 例子 |
---|---|---|
= | 赋值 | a = 10 |
+= | 加后赋值 | a += b 等价于 a = a + b |
-= | 减后赋值 | a -= b 等价于 a = a - b |
*= | 乘后赋值 | a *= b 等价于 a = a * b |
/= | 除后赋值 | a /= b 等价于 a = a / b |
%= | 取余后赋值 | a %= b 等价于 a = a % b |
<<= | 左移后赋值 | a <<= b 等价于 a = a << b |
>>= | 右移后赋值 | a >>= b 等价于 a = a >> b |
&= | 按位与后赋值 | a &= b 等价于 a = a & b |
| = | 按位或后赋值 | a |= b 等价于 a = a | b |
^= | 按位异或后赋值 | a ^= b 等价于 a = a ^ b |
var a = 5;
var b = 2;
a += b
console.log("a = ", a) // 7
console.log("b = ", b) // 2
var a = 5
var b = 2
a -= b
console.log("a = ", a) // 3
console.log("b = ", b) // 2
var a = 5;
var b = 2;
a *= b
console.log("a = ", a) // 10
console.log("b = ", b) // 2
var a = 5
var b = 2
a /= b
console.log("a = ", a) // 2.5
console.log("b = ", b) // 2
var a = 5
var b = 2
a %= b
console.log("a = ", a) // 1
console.log("b = ", b) // 2
4.3、关系运算符
关系运算符 也被称为 比较运算符。关系运算符 用于表达式的比较,并返回一个真值或假值。
关系运算符 | 描述 | 例子 | 结果 |
---|---|---|---|
== | 相等 | 4 == 3 | false |
!= | 不等于 | 4 != 3 | true |
< | 小于 | 4 < 3 | false |
> | 大于 | 4 > 3 | true |
<= | 小于等于 | 4 <= 3 | false |
>= | 大于等于 | 4 >= 3 | true |
console.log("4 == 3: ", 4 == 3) // false
console.log("4 != 3: ", 4 != 3) // true
console.log("4 < 3: ", 4 < 3) // false
console.log("4 > 3: ", 4 > 3) // true
console.log("4 <= 3: ", 4 <= 3) // false
console.log("4 >= 3: ", 4 >= 3) // true
4.4、逻辑运算符
逻辑运算符 用于根据表达式的值返回真值或假值。
逻辑运算符 | 描述 |
---|---|
&& | 逻辑与,全真为真,有假为假 |
|| | 逻辑或,有真为真,全假为假 |
! | 逻辑非,非假为真,非真为假 |
运算结果:
a | b | a && b | a|| b | !a |
---|---|---|---|---|
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 0 | 1 |
0 | 0 | 0 | 0 | 1 |
console.log("false && false : ", false && false) // false
console.log("true && false : ", true && false) // false
console.log("true && true : ", true && true) // true
console.log("false || false : ", false || false) // false
console.log("true || false : ", true || false) // true
console.log("true || true : ", true || true) // true
console.log("!true: ", !true) // false
console.log("!false: ", !false) // true
在 Python 中 与运算 和 或运算 都具有短路效果,即当左边的表达式能确定最终结果时,那么右边就不会参与运算了;
- 与运算符:与运算是找 false 的,如果第一个值为 false,则不再看第二个值。
- 或运算符:或运算是找 true 的,如果第一个值为 true,则不再看第二个值。
console.log("与运算的短路运算")
true && console.log("我执行了")
false && console.log("我不会执行")
console.log()
console.log("或运算的短路运算")
true || console.log("我不会执行")
false || console.log("我执行了")
当我们对非布尔值进行与或运算时,ECMAScript 会将其当作布尔值运算,最终会返回原值。与运算 是找 false
的,如果第一个值是 fasle
,则直接返回第一个值,否则返回第二个值。或运算 是找 true
的,如果第一个值是 true
,则直接返回第一个值,否则返回第二个值。
// true && true
console.log("1 && 2 : ", 1 && 2)
// true && false
console.log("1 && undefined : ", 1 && undefined)
// false && true
console.log("null && 1 : ", null && 1)
// false && false
console.log("NaN && 0 : ", NaN && 0)
// true || true
console.log("1 || 2 : ", 1 || 2)
// true || false
console.log("1 || undefined : ", 1 || undefined)
// false || true
console.log("null || 1 : ", null || 1)
// false || false
console.log("NaN || 0 : ", NaN || 0)
4.5、位运算符
位运算符是直接对整数的二进制进行的运算; 所有数字在计算机底层都以二进制形式执行;所有的整数值底层都以补码的方式存储;
- 正数:三码合一,符号位为 0;
- 负数:符号位为 1
- 原码:直接将数值转成二进制数,最高位是符号位;
- 反码:对原码按位取反,符号位不变;
- 补码:其反码加 1;
位运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
<< | 左移,空位补0,被移除的高位舍弃 | 3 << 2 | 12 |
>> | 右移,空位使用符号位填充,被移除的低位舍弃 | 3 >> 1 | 1 |
>>> | 带 0 扩展右移,空位使用 0 填充,对于负数,移动后会变成正数 | -3 >>> 1 | 2147483645 |
& | 与运算,二进制位进行与运算,全1为1,有0为0 | 6 & 3 | 2 |
| | 或运算,二进制位进行或运算,有1为1,全0为0 | 6| 3 | 7 |
^ | 异或运算,二进制位进行异或运算,相同为0,不同为1 | 6 ^ 3 | 5 |
~ | 按位取反,二进制位进行取反运算,非0为1,非1为0 | ~6 | -7 |
console.log("3 << 2: ", 3 << 2) // 12
console.log("3 >> 1: ", 3 >> 1) // 1
console.log("-3 >>> 1: ", -3 >>> 1) // 2147483645
console.log("6 & 3: ", 6 & 3) // 2
console.log("6 | 3: ", 6 | 3) // 7
console.log("6 ^ 3: ", 6 ^ 3) // 5
console.log("~6: ", ~6) // -7
<<
:在一定范围内,每向左移一位,相当于乘以 2。
>>
:在一定范围内,每向右移一位,相当于除以 2。
4.6、三元运算符
三元运算符 的格式如下:
(条件表达式) ? 表达式1 : 表达式2
它的执行流程如下:
- 三元运算符 在执行时,会先对条件表达式进行求值判断
- 如果条件表达式结果为 真,运算后的结果是 表达式 1。
- 如果条件表达式结果为 假,运算后的结果是 表达式 2。
console.log(10 > 20 ? 10 : 20)
三元运算符一定可以改成 if-else 语句,反之不成立。
如果程序既可以使用三元运算符又可以使用 if-else 语句,优先使用三元运算符。
4.7、逗号运算符
逗号表达式就是用逗号隔开的一串表达式,逗号表达式的特点是是从左向有依次计算的,整个表达式的结果是最后一个表达式的结果。
var a = 0
var b = 3
var c = 5
var d = (a = b + 2, c = a - 4, b = c + 2) // a = 5, c = 1, b = 3
console.log(d) // d = 3
4.8、运算符的优先级
我们可以使用小括号来提升运算符的优先级。
优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 | 说明 |
---|---|---|---|---|---|
1 | [] | 数组下标 | 数组名[常量表达式] | 左到右 | |
() | 圆括号 | (表达式)/函数名(形参表) | |||
. | 成员选择(对象) | 对象.成员名 | |||
2 | + | 正号运算符 | +表达式 | 右到左 | 单目运算符 |
- | 负号运算符 | -表达式 | 单目运算符 | ||
++ | 前置自增运算符 | ++变量名 | 单目运算符 | ||
++ | 后置自增运算符 | 变量名++ | 单目运算符 | ||
-- | 前置自减运算符 | --变量名 | 单目运算符 | ||
-- | 后置自减运算符 | 变量名-- | 单目运算符 | ||
* | 取值运算符 | *指针变量 | 单目运算符 | ||
& | 取地址运算符 | &变量名 | 单目运算符 | ||
! | 逻辑非运算符 | !表达式 | 单目运算符 | ||
~ | 按位取反运算符 | ~表达式 | 单目运算符 | ||
3 | / | 除 | 表达式/表达式 | 左到右 | 双目运算符 |
* | 乘 | 表达式*表达式 | 双目运算符 | ||
% | 余数(取模) | 整型表达式/整型表达式 | 双目运算符 | ||
4 | + | 加 | 表达式+表达式 | 左到右 | 双目运算符 |
- | 减 | 表达式-表达式 | 双目运算符 | ||
5 | << | 左移 | 变量<<表达式 | 左到右 | 双目运算符 |
>> | 右移 | 变量>>表达式 | 双目运算符 | ||
6 | > | 大于 | 表达式>表达式 | 左到右 | 双目运算符 |
>= | 大于等于 | 表达式>=表达式 | 双目运算符 | ||
< | 小于 | 表达式<表达式 | 双目运算符 | ||
<= | 小于等于 | 表达式<=表达式 | 双目运算符 | ||
7 | == | 等于 | 表达式==表达式 | 左到右 | 双目运算符 |
!= | 不等于 | 表达式!=表达式 | 双目运算符 | ||
8 | & | 按位与 | 表达式&表达式 | 左到右 | 双目运算符 |
9 | ^ | 按位异或 | 表达式^表达式 | 左到右 | 双目运算符 |
10 | | | 按位或 | 表达式|表达式 | 左到右 | 双目运算符 |
11 | && | 逻辑与 | 表达式&&表达式 | 左到右 | 双目运算符 |
12 | || | 逻辑或 | 表达式||表达式 | 左到右 | 双目运算符 |
13 | ?: | 条件运算符 | 表达式1?表达式2:表达式3 | 右到左 | 三目运算符 |
14 | = | 赋值运算符 | 变量=表达式 | 右到左 | |
/= | 除后赋值 | 变量/=表达式 | |||
*= | 乘后赋值 | 变量*=表达式 | |||
%= | 取模后赋值 | 变量%=表达式 | |||
+= | 加后赋值 | 变量+=表达式 | |||
-= | 减后赋值 | 变量-=表达式 | |||
<<= | 左移后赋值 | 变量<<=表达式 | |||
>>= | 右移后赋值 | 变量>>=表达式 | |||
&= | 按位与后赋值 | 变量&=表达式 | |||
^= | 按位异或后赋值 | 变量^=表达式 | |||
|= | 按位或后赋值 | 变量|=表达式 | |||
15 | , | 逗号运算符 | 表达式,表达式,… | 左到右 | 从左向右顺序运算 |
五、程序控制
5.1、分支结构
选择语句 也被称为 分支语句,它通过给定的条件进行判断,从而决定执行两个或者多个分支中哪一只。因此,在编写选择语句之前,应该首先明确判断条件是什么,并确定判断结果为“真”或“假”时应分别执行什么样的操作/算法。在 C 语言中选择语句主要提供了两种,一个是 if 语句,另一个则是 switch 语句。
5.1.1、if-else语句
if(关系表达式1)
{
语句体1
}
else if (关系表达式2)
{
语句体2
}
……
else
{
语句体n+1
}
执行流程:
- 首先计算 关系表达式 1 的值。
- 如果为 真(非 0) 就执行 语句体 1;如果为 假(0) 就计算 关系表达式 2 的值。
- 如果为 真(非 0) 就执行 语句体 2;如果为 假(0) 就计算 关系表达式 3 的值。
- ……
- 如果 所有的关系表达式 结果都为 假(0),就执行 else 子句中的 语句体 n+1。
- 继续执行其它语句。
var num = 10
if (num > 10) {
console.log("你输入的数字比10大")
} else if(num < 10) {
console.log("你输入的数字比10小")
} else {
console.log("你输入的数为10")
}
console.log("你输入的数字是:" + num)
else 结构是可选的。
if-else if-else 结构中最多只会有一个语句体执行。
如果多个条件表达式之间是 “互斥” 关系,哪个判断和执行语句声明在上面还是下面,无所谓。
如果多个条件表达式之间有 “交集” 关系,需根据实际情况,考虑清除应该将哪个结构写在上面。
如果多个条件表达式之间有 “包含” 关系,通常情况下,需要 将范围小的声明在范围大的上面。否则,范围小的没机会执行。
5.1.2、switch语句
switch(整型表达式)
{
case 整型常量表达式1:
语句体1
break
case 整型常量表达式2:
语句体2
break
……
case 整型常量表达式n:
语句体n
break
default:
语句体n+1
break
}
执行流程:
- 首先计算 表达式 的值。
- 依次和 case 后面的值 进行比较,如果 有对应的值,就会执行相应的语句, 在执行过程中,遇到 break 就会结束。
- 如果 所有的 case 后面的值 和 表达式 的值都不匹配,就会执行 default 里面的语句体,然后结束整个 switch 语句。
格式说明:
- 表达式:表达式的结果是 整型变量。
- case:后面跟的是 要和 switch 中的表达式进行比较的常量表达式,它只能是 枚举常量、数值常量、字符常量、常变量 或 宏定义常量 的一种,且 case 给出的值不能重复。
- break:表示中断,结束的意思,用来 结束 switch 语句,break 关键字是可选的。
- default:表示 所有情况都不匹配的时候,就执行该处的内容,相当于 if……else 中的 else,default 结构是可选的,而且位置是灵活的。
如果在语句体中 没有写 break 关键字,可能会导致 case穿透现象;case穿透现象 的执行流程如下:首先还是会拿着小括号中表达式的值跟下面的每一个 case 进行匹配,如果匹配上了,就会执行相应的代码,如果此时发现了 break,那么结束整个 switch 语句。如果没有发现 break,那么程序会继续执行下一个 case 语句体,一直遇到 break 或者 右大括号为止;
var day = 3;
switch(day)
{
case 1:
console.log("你输入的是星期一")
break
case 2:
console.log("你输入的是星期二")
break
case 3:
console.log("你输入的是星期三")
break
case 4:
console.log("你输入的是星期四")
break
case 5:
console.log("你输入的是星期五")
break
case 6:
console.log("你输入的是星期六")
break
case 7:
console.log("你输入的是星期天")
break
default:
console.log("你输入的星期数不对")
break
}
凡是可以使用 switch……case 的结构,都可以转换为 if……else 。反之,不成立。
分支结构,既可以使用 switch……case(同时,switch 中表达式的取值不多时)又可以使用 if……else 时,优先使用 switch……case。
5.2、循环结构
循环就是重复的做某件事情;它具有明确的开始和停止标志;一组被重复执行的语句称之为 循环体,能否继续执行,取决于 循环的终止条件。在 ECMAScript 语言中循环分为 for循环、while循环、do..while循环。循环结构具有以下四个组成部分:
- 初始化部分(init_statement)
- 循环条件部分(test_exp)
- 循环体部分(body_statement)
- 迭代部分(alter_statement)
通常情况下,循环结构都是因为 ② 中循环条件返回 false。
5.2.1、for语句
for(①初始化条件;②循环条件;④迭代条件)
{
③循环体
}
执行流程:
- 执行 初始化语句。
- 执行 条件判断语句,看其结果是 真(非 0) 还是 假(0)。
- 如果是 假(0),循环结束,继续执行后面的其它语句。
- 如果是 真(非 0),执行循环体语句。
- 执行 条件控制语句。
- 回到 ② 继续执行 条件判断语句。
for (var i = 0; i < 10; i++)
{
console.log(i + 1, ". Hello world!")
}
初始化语句作为循环的开始,只会执行一次。
如果判断条件为 真(非 0),则循环继续;如果判断语句为 假(0),则循环结束。
for 循环 中可以添加多个初始化条件、条件判断内容、条件控制语句,多条内容之间中间用逗号分隔。
5.2.2、while语句
①初始化条件
while(②循环条件)
{
③循环体
④迭代条件
}
执行流程:
- 执行 初始化语句。
- 执行 条件判断语句,看其结果是 真(非 0) 还是 假(0)。
- 如果是 假(0),循环结束,继续执行后面的其它语句。
- 如果是 真(非 0),执行循环体语句。
- 执行 条件控制语句。
- 回到 ② 继续执行 条件判断语句。
var i = 0
while(i < 10)
{
console.log(i + 1,". Hello world!")
i++
}
如果判断条件为 true,则循环继续;如果判断语句为 false,则循环结束;
for 循环和 while 循环可以相互转换的;
5.2.3、do-while语句
①初始化条件
do{
③循环体
④迭代条件
}while(②循环条件)
执行流程:
- 执行 初始化语句。
- 执行 循环体语句。
- 执行 条件控制语句。
- 执行 条件判断语句,看其结果是 真(非 0) 还是 假(0)。
- 如果是 假(0),循环结束,继续执行后面的其它语句。
- 如果是 真(非0),执行循环体语句。
- 执行 条件控制语句。
- 回到 ② 继续执行 条件判断语句。
var i = 0
do{
console.log(i + 1, ". Hello world!")
i++
}while(i < 10)
do...while 循环先执行后判断,即循环体至少执行一次。
5.3、跳转语句
break 的作用是 结束当前循环,后面不能直接写执行语句,默认跳出包裹此关键字最近的一层循环。执行完 break 语句后会直接执行循环后面的第 1 条语句,连更新部分也跳过。嵌套循环内层的 break 只会让程序跳出包含它的当前循环,要跳出外层循环还需要一个 break。
for (var i = 0; i <= 5; i++)
{
if (i == 3)
break
console.log(i)
}
continue 的作用是 结束当次循环,后面不能直接写执行语句,默认结束包裹此关键字最近的循环的一次。对于 while 循环 和 do…while 循环,执行 continue 语句后的下一个行为是对循环的测试表达式求值;对于 for 循环,执行 continue 后的下一个行为是对更新表达式求值,然后是对循环测试表达式求值。
for (var i = 0; i <= 5; i++)
{
if (i == 3)
continue
console.log(i)
}
continue 语句只能配合循环语句使用,不能单独和 switch/if 使用。
break 除了可以用在循环中,还可以和 swtich 语句搭配使用。
六、函数
函数是程序的基本模块,是用于完成特定任务的程序代码单元。使用函数后,我们可以省去编写重复代码的苦差。并且,函数让程序更加模块化,从而提供了编写代码的可读性,更方便后期修改、完善。
function 函数名(参数1=默认值1, 参数2=默认值2, ...)
{
// 函数体
return 返回值
}
function getSum(num1, num2)
{
var result = num1 + num2
return result
}
function
是定义函数时必须使用的关键字。
【1】、函数名
函数名属于标识符,遵循标识符的规格和规范,见名知意。函数名就是指向函数的指针,所以它们跟其它包含对象指针的变量具有相同的行为。
【2】、形参列表
ECMAScript 函数既不关心传入的参数个数,也不关心这些参数的数据类型。如果定义函数时要接收两个参数,并不意味着调用时就传两个参数。你可以传一个、三个,甚至一个也不传,解释器都不会报错。
之所以会这样,主要是因为 ECMAScript 函数的参数在内部表现为一个 数组。函数被调用时总会接收一个数组,但函数并不关心这个数组中包含什么。如果数组中什么也没有,那没问题;如果数组的元素超出了要求,那也没问题。事实上,在使用 function
关键字定义(非箭头)函数时,可以在函数内部访问 arguments
对象,从中取得传进来的每个参数值。
arguments
对象是一个类数组对象(但不是 Array
的实例),因此可以使用中括号语法访问其中的元素(第一个参数是arguments[0]
,第二个参数是 arguments[1]
)。而要确定传进来多少个参数,可以访问 arguments.length
属性。
ECMAScript
函数的参数只是为了方便才写出来的,并不是必须写出来的。与其他语言不同,在ECMAScript 中的命名参数不会创建让之后的调用必须匹配的函数签名。这是因为根本不存在验证命名参数的机制。
arguments
对象的另一个有意思的地方就是,它的值始终会与对应的命名参数同步。但这并不意味着它们都访问同一个内存地址,它们在内存中还是分开的,只不过会保持同步而已。如果只传了一个参数,然后把 arguments[1]
设置为某个值,那么这个值并不会反映到第二个命名参数。这是因为 arguments
对象的长度是根据传入的参数个数,而非定义函数时给出的命名参数个数确定的。
对于命名参数而言,如果调用函数时没有传这个参数,那么它的值就是 undefined
。这就类似于定义了变量而没有初始化。
在使用默认参数时,arguments
对象的值不反映参数的默认值,只反映传给函数的参数。默认参数值并不限于原始值或对象类型,也可以使用调用函数返回的值。函数的默认参数只有在函数被调用时才会求值,不会在函数定义时求值。而且,计算默认值的函数只有在调用函数但未传相应参数时才会被调用。
ECMAScript 中的所有参数都按值传递的。不可能按引用传递参数。如果把对象作为参数传递,那么传递的值就是这个对象的引用。
【3】、函数体
函数体是由一对大括号括起来的,大括号决定了函数体的范围。函数要实现的特定功能,都是在函数体部分通过代码完成的,最后通过 return 语句返回是实现的结果。
【4】、返回值类型
ECMAScript中的函数,默认都是有返回值的,即便你没有显式使用 return
语句,它也会返回 undefined
。
return 关键字可以用来结束函数。
函数不调用就不执行;
函数的编写顺序和执行顺序无关;
七、对象
7.1、Object
Object
类是所有 ECMAScript 类的基类,Object
类的所有属性和方法都会出现在其它类中。
Object
类具有下列属性:
constructor // 指向创建对象的函数,对于Object类,它指向object()函数
prototype // 对该对象的对象原型的引用
Object
类还有几个方法:
hasOwnProperty(property) // 判断对象是否有某个属性,property为字符串
isPrototypeOf(object) // 判断该对象是否为另一个对象的原型
propertyIsEnumerable(property) // 判断给定的属性是否可以用for…in语句进行枚举
toString() // 返回对象的字符串表示
valueOf() // 返回最适合该对象的原始值
7.2、String
String
类是 String
原始类型的对象表示法。我们可以使用 new
关键字构建一个 String
对象。
var str = new String("Hello Sakura!")
String
对象的 valueOf()
方法和 toString()
方法都会 返回 String
类型的原始值。
var str = new String("Hello Sakura!")
console.log(str.valueOf())
console.log(str.toString())
length
属性 返回字符串中的字符个数。
var str = new String("Hello Sakura!")
console.log(str.length)
charAt()
方法可以 访问指定索引位置的字符。而 charCodeAt()
方法 返回指定位置字符对应的 Unicode 编码。
var str = new String("Hello Sakura!")
console.log(str.charAt(0))
console.log(str.charCodeAt(0))
indexOf()
方法 从字符串的开头检索子串,lastIndexOf()
方法 从字符串的结尾开始检索子串,它们 返回给定的子串在本对象代表的字符串中的位置,如果找不到则返回 -1。这两个方法还有一个可选的参数,指定开始查找的位置。
search()
方法用于 检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。它是区分大小写的。它总是从字符串的开始进行查找。
match()
方法可 在字符串内检索指定的值,或者寻找匹配指定正则表达式的一个或多个子串。如果检索到了符合规则的子串,它会返回一个存放所有子串的数组。
var str = new String("Hello Sakura!")
console.log(str.indexOf("Sakura"))
console.log(str.indexOf("Sakura", 7))
console.log(str.lastIndexOf("Sakura"))
console.log(str.lastIndexOf("Sakura", 5))
console.log(str.search("Sakura"))
console.log(str.match("Sakura"))
我们可以用大于 >
、小于 <
、等于 ==
三个运算符可用于字符串比较,但它们使用字符的Unicode编码进行比较,忽略了与本地语言环境相关的语义和排序规则。localeCompare()
方法在比较字符串时,默认采用底层操作系统提供的排序规则。当本对象等于参数字符串时此方法返回 0,小于时返回小于 0 的值(QM L中为 -1),大于时返回大于 0 的值(QML 中返回 1)。
var str1 = "Qt Quick"
var str2 = "qt quick"
console.log(str1 < str2)
console.log(str1 == str2)
console.log(str1 > str2)
console.log(str1.localeCompare(str2))
console.log(str2.localeCompare(str1))
我们可以使用 concat()
方法用于 连接两个或多个字符串,返回一个新的字符串原始值。我们还可以使用 +
运算符更方便地连接字符串。
var str1 = "Hello"
var str2 = "Sakura!
console.log(str1.concat(str2, "Nice ", "to ", "meet ", "you."))
console.log(str1 + " " + str2)
无论是
concat()
还是+
,都不会在字符串之间插入填充字符。如果你想连接多个字符串并且指定它们之间的分隔符(填充字符),则可以使用Array
对象存储字符串,然后调用Array
的join()
方法。
ECMAScript 的 String
提供了 slice()
和 substring()
两个方法来 提取子串,它们接受一个或两个参数,第一个参数指定起始位置,第二个参数指定结束位置(提取出的子串不包含该位置的字符),如果忽略第二个参数,结束位置默认为字符串的长度。
slice()
和 substring()
这两个方法的不同之处在于对负数参数的处理。slice()
遇到负数参数时,会应用 “从串尾倒数” 这种策略,比如 slice(-3)
,会提取母串的后三个字符作为子串;而 substring()
方法会把负数参数作为0处理,还有一点,substring()
总是把较小的参数作为起始位置。
substr()
也可以提取子串,它的第一个参数指定起始位置,第二个参数指定要提取的字符个数,如果不指定,substr()
会返回从起始位置到母串结束的所有字符。
var str = new String("Hello Sakura!");
console.log(str.slice(-1));
console.log(str.slice(0, 6));
console.log(str.slice(6, 12));
console.log(str.substring(0, 6));
console.log(str.substring(-3))
console.log(str.substr(6, 6))
如果你想将一个字符串 转换为小写,可以使用 toLowerCase()
或 toLocaleLowerCase()
方法;转换为大写,可以使用toUpperCase()
或 toLocaleUpperCase()
方法。
var str = new String("Hello Sakura!")
console.log(str.toLocaleLowerCase())
console.log(str.toLowerCase())
console.log(str.toLocaleUpperCase())
console.log(str.toUpperCase())
replace()
方法可以将母串中的部分子串替换为指定的新字符串。它的第一个参数是一个字符串原始值或者正则表达式,第二个参数是新字符串或产生新字符串的函数。
var str = new String("Hello World!")
console.log(str.replace("World", "Sakura"))
console.log(str)
7.3、Array
ECMAScript 定义的 Array
是动态数组,其大小可以动态变化。而且数组中的元素,类型可以不同。我们可以如下方式创建数组。
var a = new Array()
var a = new Array(10) // 指定数组对象的初始长度
var a = new Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Array
类的 length
属性表示 数组中元素的个数。数组中的元素,可以通过下标访问。
var a = new Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
console.log(a.length)
console.log(a[2])
我们可以使用 push()
方法 向数组末尾插入一个或多个元素,它 返回数组新的长度。你可以使用 pop()
方法 删除并返回数组的最后一个元素。我们可以使用 shift()
方法 删除并返回数组的第一个元素。unshift()
方法 向数组的开始添加一个元素并返回新的数组长度。
var a = new Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
console.log(a.push(11))
console.log(a)
console.log(a.pop())
console.log(a)
console.log(a.shift())
console.log(a)
console.log(a.unshift(1))
console.log(a)
我们可以使用 reverse()
方法可以颠倒数组中元素的顺序。注意,它会改变原来的数组。
var a = new Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
console.log(a.reverse())
sort()
方法可以对数组内元素排序并返回对原数组的引用。注意,它会改变原来的数组。它有一个可选的参数 sortby
,让你指定一个比较大小的函数,如果你不指定,QML 的实现是将每个元素转换为字符串,然后根据字符串比较的结果升序排列。
var a = new Array(1, 3, 5, 7, 9, 2, 4, 6, 8, 10)
console.log(a.sort())
join()
方法可以 把数组中的所有元素组合成一个字符串,字符串之间可以用给定的分隔符来填充。toString()
方法可以 把数组转换为字符串,与不带参数的 join()
方法结果一样。toLocaleString()
方法可以 把数组转换为本地字符串。
var a = new Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
console.log(a.join(", "))
console.log(a.toString())
console.log(a.toLocaleString())
concat()
方法可以 连接两个或多个数组,它的参数既可以是一个单一的值,也可以是一个数组对象。它不会改变现有数组,而是返回一个新的数组对象。
var a1 = new Array(1, 3, 5, 7, 9)
var a2 = new Array(2, 4, 6, 8, 10)
console.log(a1.concat(a2))
console.log(a1)
console.log(a2)
slice(start, end)
方法它将母数组中 start
到 end
(不含 end
位置的元素)之间的元素放到一个新数组中返回。
var a = new Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
console.log(a.slice(2, 5))
console.log(a)