JS程序的执行单位是行(line)
**语句(statement)**是为了完成特定任务而进行的操作,如下面的赋值语句
var a = 3 + 3;
该语句使用var命令声明变量a
而后将3 + 3的运算结果赋值给变量a
3 + 3是为表达式(expression),是一个用于获得返回值的计算式
语句以分号结尾,一个分号表示一条语句结束
因此语句可以写在同一行内
var a = 1 + 1; var b = 'zane';
分号前面也可以没有内容,JS引擎视为空语句
;;;
变量是对值的具名引用
JS的变量名区分大小写
变量的声明和赋值是两个分开的步骤
var a = 1;
var a;
a = 1;
若只是声明变量而不赋值,则变量的值为 undefined,是特殊的值,表示无定义
变量赋值忘记var命令,语句同样有效
但这种做法不利于表达意图,容易莫名创建全局变量
若变量没有声明便直接使用,则JS将报错
ReferenceError: x is not defined
在一条var命令中可以声明多个变量
var a, b;
JS是动态类型语言,变量的类型没有限制,可以随时更改类型
var a = 666;
a = 'Zane';
使用var重新声明一个已经存在的变量是无效的(不会覆盖为undefined)
但是如果声明的同时进行赋值则会覆盖
JS引擎的工作方式是首先解析代码并获取所有声明的变量,然后进行逐行运行
导致所有变量的声明语句会被提升到代码头部,是为变量提升(hoisting)
console.log(a);
var a = 1;
以上代码首先使用console.log方法在console显示变量a的值,但此时a还没有声明与赋值,但实际上不会报错,由于变量提升导致真正运行的代码逻辑是
var a;
console.log(a);
a = 1;
因此最后显示undefined,表示声明但未赋值定义
identifier 标识符是指用于识别各种值的合法名称
常见的标识符有变量名和函数名(之后还有标签label啥的)
JS的标识符对大小写敏感
标识符命名规则是
合法的标识符:
arg0
_tmp
$elem
π
临时变量 // 中文是合法的
不合法的标识符
1a // 数字
*** // 不能包含*
a+b // 不能包含+
-d // 不能包含-
JS保留字有:
argument, break, case, catch, class, const, continue, debugger, default, delete, do, else, enum, eval, export, extends, false, finally, for, function, if, implements, import, in, instanceof, interface, let, new, null, package, private, protected, public, return, static, super, switch, this, throw, true, try, typeof, var, void, while, with, yield
源码中被JS引擎忽略的部分即为注释
单行 //
多行 /**/
同时JS可以兼容HTML代码的注释
function countdown(n) {
while (n --> 0) console.log(n);
}
countdown(3)
// 上面的代码中n-->0会被解释为n-- >0
// 因此输出2、1、0
JS使用大括号将多个相关语句组合为区块(block)
对于var命令,JS区块并不构成单独作用域(scope),与不使用block没有任何区别
{
var a = 1;
}
a // 1
JS单独使用区块并不常见,block通常用来构成复杂的语法结构如for, if, while, function等
JS提供 if 和 switch结构以及三元运算符用于条件判断
满足预设条件方可执行相应语句
if 结构将判断条件表达式的布尔值
由布尔值的真伪(true or false)决定执行不同的语句
// 圆括号表示对表达式求值
if (布尔值)
语句;
// or
if (布尔值) 语句;
这种写法只能用于条件表达式后面只有一条语句的情况,否则必须在判断之后加上大括号构成代码块
(block使得多条语句合并为一条),并且我们建议always如此,因为这样方便插入语句同时结构清晰
if (m === 3) {
m += 1;
m -= 2;
}
var x = 1;
var y = 2;
if (x = y) {
console.log(x);
}
// "2"
上面代码的原意是,当x等于y的时候,才执行相关语句
但是不小心将严格相等运算符写成赋值表达式,结果变成了将y赋值给变量x,再判断变量x的值(等于2)的布尔值(结果为true)
这种错误可以正常生成一个布尔值,因而不会报错
为了避免这种情况,有些开发者习惯将常量写在运算符的左边,这样的话,一旦不小心将相等运算符写成赋值运算符,就会报错,因为常量不能被赋值
if (m === 6) {
// 满足条件
} else {
// 不满足条件
}
// 还可以对同一个变量进行多次判断,使用连写
if (m === 0) {
// ...
} else if (m === 1) {
// ...
} else if (m === 2) {
// ...
} else {
// ...
}
else代码块总是与离自己最近的那个if语句配对
var m = 1;
var n = 2;
if (m !== 1)
if (n === 2) console.log('hello');
else console.log('world');
以上代码没有输出,else代码块与第二个if匹配,都无法执行,相当于
if (m !== 1) {
if (n === 2) {
console.log('hello');
} else {
console.log('world');
}
}
若想要第二个代码块得到执行,可以改变大括号的位置
if (m !== 1) {
if (n === 2) {
console.log('hello');
}
} else {
console.log('world');
}
// world
多个if…else连在一起使用的时候,可以转为使用更方便的switch结构
switch (fruit) {
case "banana":
// ...
break;
case "peach":
// ...
break;
default:
// ...
}
上面代码根据变量fruit的值,选择执行相应的case
若所有case都不符合,则执行最后的default部分
switch语句部分和case语句部分,都可以使用表达式
switch (1 + 3) {
case 2 + 2:
f();
break;
default:
neverHappens();
}
需要注意的是,switch语句后面的表达式,与case语句后面的表示式比较运行结果时,采用的是严格相等运算符(===),这意味着比较时不会发生类型转换
var x = 1;
switch (x) {
case true:
console.log('x 发生类型转换');
break;
default:
console.log('x 没有发生类型转换');
}
// x 没有发生类型转换
三元运算符需要三个运算子
(条件) ? 表达式1 : 表达式2
var even = (n % 2 == 0) ? true : false;
// 如果n可以被2整除,则even等于true,否则等于false
// 等同于
var even;
if (n % 2 === 0) {
even = true;
} else {
even = false;
}
三元运算符可以看做是简写if…else…
有许多应用场合
var myVar;
console.log(
myVar ?
'myVar has a value' :
'myVar does not have a value'
)
// myVar does not have a value
var msg = '数字' + n + '是' + (n % 2 === 0 ? '偶数' : '奇数');
重复
while语句包括一个循环条件和一段代码块,只要条件为真,就不断循环执行代码块
while (条件)
语句;
// or
while (条件) 语句;
当然和if语句一样最好加上大括号,如下面的无限循环
while (true) {
console.log('Zanebla is a cool boy');
}
for语句是循环命令的另一种形式
for (初始化表达式; 条件; 递增表达式)
语句
// 或者
for (初始化表达式; 条件; 递增表达式) {
语句
}
var x = 3;
for (var i = 0; i < x; i++) {
console.log(i);
}
// 0
// 1
// 2
所有for循环,都可以改写成while循环。上面的例子改为while循环,代码如下
var x = 3;
var i = 0;
while (i < x) {
console.log(i);
i++;
}
当然initialize, test, increment都可以省略,如下面的无限循环
for (;;) {
console.log('Zanebla is a nice man');
}
do…while循环与while循环类似,唯一的区别就是先运行一次循环体,然后判断循环条件
do
语句
while (条件);
// 或者
do {
语句
} while (条件);
break语句和continue语句都具有跳转作用,可以让代码不按既有的顺序执行
break语句用于跳出代码块或循环
var i = 0;
while (i < 100) {
console.log('i 当前为: ' + i);
i++;
if (i === 10) break;
}
上面代码只会执行10次循环,一旦i等于10,就会跳出循环
for循环也可以使用break语句跳出循环
而continue语句用于立即终止本轮循环,返回循环结构的头部,开始下一轮循环
var i = 0;
while (i < 100) {
i++;
if (i % 2 === 0) continue;
console.log('i 当前是: ' + i);
}
上面代码只有在i为奇数时,才会输出i的值
如果i为偶数,则直接进入下一轮循环
如果存在多重循环,不带参数的break语句和continue语句都只针对最内层循环
JS允许语句前有label作为定位符,用于跳转至程序的任意位置
标签可以是任意的标识符,但不能是保留字,语句部分可以是任意语句
标签通常与break语句和continue语句配合使用来跳出特定的循环或者代码块
top1:
for (var i = 0; i < 3; i++){
for (var j = 0; j < 3; j++){
if (i === 1 && j === 1) break top1;
console.log('i=' + i + ', j=' + j);
}
}
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0
foo: {
console.log(1);
break foo;
console.log('本行不会输出');
}
console.log(2);
// 1
// 2
top2:
for (var i = 0; i < 3; i++){
for (var j = 0; j < 3; j++){
if (i === 1 && j === 1) continue top2;
console.log('i=' + i + ', j=' + j);
}
}
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0
// i=2, j=0
// i=2, j=1
// i=2, j=2
上面代码中,continue命令后面有一个标签名,满足条件时,会跳过当前循环,直接进入下一轮外层循环,否则只能进入下一轮的内层循环