重拾JavaScript(一)-基本概念

语法

语句

JavaScript程序的执行单位为行(line),也就是一行一行地执行。一般情况下,每一行就是一个语句。

语句(statement)是为了完成某种任务而进行的操作,比如下面就是一行赋值语句:

1
var a = 1 + 3;

这条语句先用var命令,声明了变量a,然后将1 + 3的运算结果赋值给变量a

1 + 3叫做表达式(expression),指一个为了得到返回值的计算式。语句和表达式的区别在于,前者主要为了进行某种操作,一般情况下不需要返回值;后者则是为了得到返回值,一定会返回一个值。

凡是JavaScript语言中预期为值的地方,都可以使用表达式。比如,赋值语句的等号右边,预期是一个值,因此可以放置各种表达式。一条语句可以包含多个表达式。

语句以分号结尾,一个分号就表示一个语句结束。多个语句可以写在一行内。

1
var a = 1 + 3 ; var b = 'abc';

分号前面可以没有任何内容,JavaScript引擎将其视为空语句。

1
;;;

上面的代码就表示3个空语句。

表达式不需要分号结尾。一旦在表达式后面添加分号,则JavaScript引擎就将表达式视为语句,这样会产生一些没有任何意义的语句。

1
2
1 + 3;
'abc';

上面两行语句有返回值,但是没有任何意义,因为只是返回一个单纯的值,没有任何其他操作。


if语句

if语句是编程语言最常用的语句,语法

1
2
3
4
5
if(condition){
//true statement
}else {
//false statement
}

其中condition可以是任意表达式,结果不一定是布尔值,JavaScript解释器会自动调用Boolean()将表达式结果转为布尔值,如果表达式为真执行第一个代码块内语句,如果为假执行第二个代码块内语句

只有一条语句的时候代码块不是必需的,出于维护性考虑我们建议添加

if语句可以单独使用,也可以和多个else连续使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if(a > 2){
// statement
}


if( a == 1){

}else if(a == 2){

}else if(a == 3 ){

}else{

}

例:

1
2
3
4
5
6
var age=18;
if(age>=18){
console.log("你已经成年,请输入身份号。");
}else{
console.log("对不起,你未成年,不可登录。");
}

switch语句

多个if...else连在一起使用的时候,可以转为使用更方便的switch结构。

语法:

1
2
3
4
5
6
7
8
9
10
11
switch(expresstion){
case value1: //该条件:(满足)这个值
statement; //则执行该语句
break; //强制输出

case value2:
statement;
break;
default:
statement;
}

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
switch-case 的用法(多个条件情况)
var week=new Date().getDay();
var weekstr="";
switch (week){
case 0:
weekstr="日";
break;
case 1:
weekstr="一";
break;
case 2:
weekstr="二";
break;
case 3:
weekstr="三";
break;
case 4:
weekstr="四";
break;
case 5:
weekstr="五";
break;
case 6:
weekstr="六";
break;
}
console.log("今天是星期"+weekstr);

while和do-while
  • while

while语句属于前测试循环语句,也就是在循环体内的代码被执行之前,就会对条件求值,不符合的话就不会执行

1
2
3
while(expression){
statement;
}

看个例子

1
2
3
4
5
var i = 10;
while(i > 0){
console.log(i);
i--;
}
  • do-while

do-while是后测试循环语句,在出口条件判断之前就会执行一次代码

1
2
3
do{
statement;
}while(expression);

看个例子

1
2
3
4
5
var i = 4;
do{
console.log(i);
i--;
}while(i > 5);

for 语句

for语句是循环命令的另一种形式,可以指定循环的起点、终点和终止条件。它的格式如下。

1
2
3
for (初始化表达式; 条件; 递增表达式) {
语句
}

for语句后面的括号里面,有三个表达式。

  • 初始化表达式(initialize):确定循环变量的初始值,只在循环开始时执行一次。
  • 条件表达式(test):每轮循环开始时,都要执行这个条件表达式,只有值为真,才继续进行循环。
  • 递增表达式(increment):每轮循环的最后一个操作,通常用来递增循环变量。

下面是一个例子。

1
2
3
4
5
6
7
var x = 3;
for (var i = 0; i < x; i++) {
console.log(i);
}
// 0
// 1
// 2

上面代码中,初始化表达式是var i = 0,即初始化一个变量i;测试表达式是i < x,即只要i小于x,就会执行循环;递增表达式是i++,即每次循环结束后,i增大1。


label语句

JavaScript 语言允许,语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置,标签的格式如下。

1
2
3
4
5
6
7
label:
语句

示例:
start: for (var i=0; i< count; i++){
alert(i);
}

标签可以是任意的标识符,但不能是保留字,语句部分可以是任意语句。

标签通常与break语句和continue语句配合使用,跳出特定的循环。


注释

单行注释以两个斜杠开头,如下所示:

1
// 单行注释

块级注释以一个斜杠和一个星号(/*)开头,以一个星号和一个斜杠(*/)结尾,如下所示:

1
2
3
4
/*
* 这是一个多行
* (块级)注释
*/

变量

变量是对“值”的引用,使用变量等同于引用一个值。每一个变量都有一个变量名。

1
var a = 1;

上面的代码先声明变量a,然后在变量a与数值1之间建立引用关系,也称为将数值1“赋值”给变量a。以后,引用变量a就会得到数值1。最前面的var,是变量声明命令。它表示通知解释引擎,要创建一个变量a

变量的声明和赋值,是分开的两个步骤,上面的代码将它们合在了一起,实际的步骤是下面这样。

1
2
var a;
a = 1;

如果只是声明变量而没有赋值,则该变量的值是undefinedundefined是一个JavaScript关键字,表示“无定义”。

1
2
var a;
a // undefined

如果变量赋值的时候,忘了写var命令,这条语句也是有效的。

1
2
3
var a = 1;
// 基本等同
a = 1;

但是,不写var的做法,不利于表达意图,而且容易不知不觉地创建全局变量,所以建议总是使用var命令声明变量。

严格地说,var a = 1a = 1,这两条语句的效果不完全一样,主要体现在delete命令无法删除前者。不过,绝大多数情况下,这种差异是可以忽略的。

如果一个变量没有声明就直接使用,JavaScript会报错,告诉你变量未定义。

1
2
x
// ReferenceError: x is not defined

上面代码直接使用变量x,系统就报错,告诉你变量x没有声明。

可以在同一条var命令中声明多个变量。

1
var a, b;

JavaScript 是一种动态类型语言,也就是说,变量的类型没有限制,可以赋予各种类型的值。

1
2
var a = 1;
a = 'hello';

上面代码中,变量a起先被赋值为一个数值,后来又被重新赋值为一个字符串。第二次赋值的时候,因为变量a已经存在,所以不需要使用var命令。

如果使用var重新声明一个已经存在的变量,是无效的。

1
2
3
var x = 1;
var x;
x // 1

上面代码中,变量x声明了两次,第二次声明是无效的。

但是,如果第二次声明的同时还赋值了,则会覆盖掉前面的值。

1
2
3
4
5
6
7
8
var x = 1;
var x = 2;

// 等同于

var x = 1;
var x;
x = 2;

标识符

所谓标识符,就是指变量、函数、属性的名字,或者函数的参数。标识符可以是按照下列格式规则组合起来的一或多个字符:

  • 第一个字符必须是一个字母、下划线(_)或一个美元符号($);
  • 其他字符可以是字母、下划线、美元符号或数字。

    按照惯例,ECMAScript标识符采用驼峰大小写格式,也就是第一个字母小写,剩下的每个有意义的单词的首字母大写,例如:

1
2
3
firstSecond 
myCar
doSomethingImportant

关键字和保留字

JavaScript有一些保留字,不能用作标识符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
break         do           instanceof        typeof
case else new var
catch finally return void
continue for switch while
debugger* function this with
default if throw
delete in tr

第5版把在非严格模式下运行时的保留字缩减为下列这些:
class enum extends super
const export import

在严格模式下,第5版还对以下保留字施加了限制:
implements package public
interface private static
let protected yield

区块

JavaScript使用大括号,将多个相关的语句组合在一起,称为“区块”(block)。

与大多数编程语言不一样,JavaScript的区块不构成单独的作用域(scope)。也就是说,区块中的变量与区块外的变量,属于同一个作用域。例如:

1
2
3
4
{
var a = 1;
}
a // 1

上面代码在区块内部,声明并赋值了变量a,然后在区块外部,变量a依然有效,这说明区块不构成单独的作用域,与不使用区块的情况没有任何区别。所以,单独使用的区块在JavaScript中意义不大,很少出现。区块往往用来构成其他更复杂的语法结构,比如forifwhilefunction等。


数据类型

JavaScript 语言的每一个值,都属于某一种数据类型。JavaScript 的数据类型,共有六种。(ES6 又新增了第七种 Symbol 类型的值)

  • 数值(number):整数和小数(比如1和3.14)
  • 字符串(string):字符组成的文本(比如”Hello World”)
  • 布尔值(Boolean):true(真)和false(假)两个特定值
  • undefined:表示“未定义”或不存在,即由于目前没有定义,所以此处暂时没有任何值(一个变量没有赋值,只能是undefined,不会是null
  • )
  • null:表示无值,即此处的值就是“无”的状态。
  • 对象(object):各种值组成的集合

typeof运算符

JavaScript有三种方法,可以确定一个值到底是什么类型。

  • typeof运算符
  • instanceof运算符
  • Object.prototype.toString方法

instanceof运算符和Object.prototype.toString方法,将在后文相关章节介绍。这里着重介绍typeof运算符。

typeof运算符可以返回一个值的数据类型,可能有以下结果。

(1)原始类型

数值、字符串、布尔值分别返回numberstringboolean

1
2
3
typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean"
(2)函数

函数返回function

1
2
3
function f() {}
typeof f
// "function"
(3)undefined

undefined返回undefined

1
2
typeof undefined
// "undefined"

利用这一点,typeof可以用来检查一个没有声明的变量,而不报错。

1
2
3
4
5
v
// ReferenceError: v is not defined

typeof v
// "undefined"

上面代码中,变量v没有用var命令声明,直接使用就会报错。但是,放在typeof后面,就不报错了,而是返回undefined

实际编程中,这个特点通常用在判断语句。

1
2
3
4
5
6
7
8
9
10
// 错误的写法
if (v) {
// ...
}
// ReferenceError: v is not defined

// 正确的写法
if (typeof v === "undefined") {
// ...
}
(4)其他

除此以外,其他情况都返回object

1
2
3
4
typeof window // "object"
typeof {} // "object"
typeof [] // "object"
typeof null // "object"

从上面代码可以看到,空数组([])的类型也是object,这表示在JavaScript内部,数组本质上只是一种特殊的对象。


布尔值

(1)作用

布尔值代表“真”和“假”两个状态。“真”用关键字true表示,“假”用关键字false表示。布尔值只有这两个值。

(2)下列运算符会返回布尔值

A、两元逻辑运算符: && (and),|| (or)

B、前置逻辑运算符: ! (not)

C、相等运算符:===!====!=

D、比较运算符:>>=<<= 如:

1
如:4>3-->true

(3)如果JavaScript预期某个位置应该是布尔值,会将该位置上现有的值自动转为布尔值。转换规则是除了下面六种值被转为false,其他值都视为true。

  • undefined –> false
  • null –>false
  • false –>false
  • +0,-0 ,NaN –>false 【其他number(数字)为true】
  • ""''(空字符串)–>false【其他string(字符串,包含” “空白字符串)为true】

A、关于“”或’ ‘(空字符串)布尔值往往用于程序流程的控制,如:

1
2
3
4
5
6
7
如:
if ('') {
console.log(true);
}
// 没有任何输出
上面代码的if命令后面的判断条件,预期应该是一个布尔值,所以JavaScript自动将空字符串,
转为布尔值false,导致程序不会进入代码块,所以没有任何输出。

B、空数组([])和空对象({})对应的布尔值,都是true

1
2
3
4
5
6
7
8
9
10
如:
if ([]) {
console.log(true);
}
// true

if ({}) {
console.log(true);
}
// true

运算符

JavaScript中运算符主要用于连接简单表达式,组成一个复杂的表达式。

有些操作符对不同的数据类型有不同的含义,比如 +

  • 在两个操作数都是数字的时候,会做加法运算
  • 两个参数都是字符串或在有一个参数是字符串的情况下会把另外一个参数转换为字符串做字符串拼接
  • 在参数有对象的情况下会调用其valueOf或toString
  • 在只有一个字符串参数的时候会尝试将其转换为数字
  • 在只有一个数字参数的时候返回其正数值

加法运算符

加法运算符(+)是最常见的运算符,用来求两个数值的和。

1
1 + 1 // 2

JavaScript 允许非数值的相加。

1
2
true + true // 2
1 + true // 2

上面代码中,第一行是两个布尔值相加,第二行是数值与布尔值相加。这两种情况,布尔值都会自动转成数值,然后再相加。

比较特殊的是,如果是两个字符串相加,这时加法运算符会变成连接运算符,返回一个新的字符串,将两个原字符串连接在一起。

1
'a' + 'bc' // "abc"

如果一个运算值是字符串,另一个运算值是非字符串,这时非字符串会转成字符串,再连接在一起。

1
2
1 + 'a' // "1a"
false + 'a' // "falsea"

加法运算符是在运行时决定,到底是执行相加,还是执行连接。也就是说,运算值的不同,导致了不同的语法行为,这种现象称为“重载”(overload)。由于加法运算符存在重载,可能执行两种运算,使用的时候必须很小心。

1
2
'3' + 4 + 5 // "345"
3 + 4 + '5' // "75"

上面代码中,由于从左到右的运算次序,字符串的位置不同会导致不同的结果。

除了加法运算符,其他算术运算符(比如减法、除法和乘法)都不会发生重载。它们的规则是:所有运算值一律转为数值,再进行相应的数学运算。

1
2
3
1 - '2' // -1
1 * '2' // 2
1 / '2' // 0.5

上面代码中,减法、除法和乘法运算符,都是将字符串自动转为数值,然后再运算。


如果运算值是对象,必须先转成原始类型的值,然后再相加。

1
2
var obj = { p: 1 };
obj + 2 // "[object Object]2"

上面代码中,对象obj转成原始类型的值是[object Object],再加2就得到了上面的结果。

对象转成原始类型的值,规则如下。

首先,自动调用对象的valueOf方法。

1
2
var obj = { p: 1 };
obj.valueOf() // { p: 1 }

一般来说,对象的valueOf方法总是返回对象自身,这时再自动调用对象的toString方法,将其转为字符串。

1
2
var obj = { p: 1 };
obj.valueOf().toString() // "[object Object]"

对象的toString方法默认返回[object Object],所以就得到了最前面那个例子的结果。

知道了这个规则以后,就可以自己定义valueOf方法或toString方法,得到想要的结果。

1
2
3
4
5
6
7
var obj = {
valueOf: function () {
return 1;
}
};

obj + 2 // 3

上面代码中,我们定义obj对象的valueOf方法返回1,于是obj + 2就得到了3。这个例子中,由于valueOf方法直接返回一个原始类型的值,所以不再调用toString方法。

下面是自定义toString方法的例子。

1
2
3
4
5
6
7
var obj = {
toString: function () {
return 'hello';
}
};

obj + 2 // "hello2"

上面代码中,对象objtoString方法返回字符串hello。前面说过,只要有一个运算值是字符串,加法运算符就变成连接运算符,返回连接后的字符串。

这里有一个特例,如果运算值是一个Date对象的实例,那么会优先执行toString方法。

1
2
3
4
5
var obj = new Date();
obj.valueOf = function () { return 1 };
obj.toString = function () { return 'hello' };

obj + 2 // "hello2"

上面代码中,对象obj是一个Date对象的实例,并且自定义了valueOf方法和toString方法,结果toString方法优先执行。


算术运算符

包括加法运算符在内,JavaScript 共提供10个算术运算符,用来完成基本的算术运算。

  • 加法运算符x + y
  • 减法运算符x - y
  • 乘法运算符x * y
  • 除法运算符x / y
  • 指数运算符x ** y
  • 余数运算符x % y
  • 自增运算符++x 或者 x++
  • 自减运算符--x 或者 x--
  • 数值运算符+x
  • 负数值运算符-x

减法、乘法、除法运算法比较单纯,就是执行相应的数学运算。


赋值运算符

赋值运算符(Assignment Operators)用于给变量赋值。

最常见的赋值运算符,当然就是等号(=)。

1
2
3
4
// 将 1 赋值给变量 x
var x = 1;
// 将变量 y 的值赋值给变量 x
var x = y;

赋值运算符还可以与其他运算符结合,形成变体。

下面是与算术运算符的结合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 等同于 x = x + y
x += y

// 等同于 x = x - y
x -= y

// 等同于 x = x * y
x *= y

// 等同于 x = x / y
x /= y

// 等同于 x = x % y
x %= y

// 等同于 x = x ** y
x **= y

下面是与位运算符的结合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 等同于 x = x >> y
x >>= y

// 等同于 x = x << y
x <<= y

// 等同于 x = x >>> y
x >>>= y

// 等同于 x = x & y
x &= y

// 等同于 x = x | y
x |= y

// 等同于 x = x ^ y
x ^= y

这些复合的赋值运算符,都是先进行指定运算,然后将得到值返回给左边的变量。


比较运算符

比较运算符用于比较两个值的大小,然后返回一个布尔值,表示是否满足指定的条件。

1
2 > 1 // true

上面代码比较2是否大于1,返回true

注意,比较运算符可以比较各种类型的值,不仅仅是数值。

JavaScript 一共提供了8个比较运算符。

  • < 小于运算符
  • > 大于运算符
  • <= 小于或等于运算符
  • >= 大于或等于运算符
  • == 相等运算符
  • === 严格相等运算符
  • != 不相等运算符
  • !== 严格不相等运算符

这八个比较运算符分成两类:相等比较和非相等比较。两者的规则是不一样的,对于非相等的比较,算法是先看两个运算子是否都是字符串,如果是的,就按照字典顺序比较(实际上是比较 Unicode 码点);否则,将两个运算子都转成数值,再比较数值的大小。


布尔运算符

布尔运算符用于将表达式转为布尔值,一共包含四个运算符。

取反运算符:!

取反运算符是一个感叹号,用于将布尔值变为相反值,即true变成falsefalse变成true

1
2
3
4
5
6
7
8
9
10
!true // false
!false // true

对于非布尔值,取反运算符会将其转为布尔值。以下六个值取反后为true,其他值都为false:
undefined
null
false
0
NaN
空字符串('')
且运算符:&&

且运算符(&&)往往用于多个表达式的求值。

它的运算规则是:如果第一个运算子的布尔值为true,则返回第二个运算子的值(注意是值,不是布尔值);如果第一个运算子的布尔值为false,则直接返回第一个运算子的值,且不再对第二个运算子求值。

或运算符:||

或运算符(||)也用于多个表达式的求值。它的运算规则是:如果第一个运算子的布尔值为true,则返回第一个运算子的值,且不再对第二个运算子求值;如果第一个运算子的布尔值为false,则返回第二个运算子的值。

三元运算符:?:

三元条件运算符由问号(?)和冒号(:)组成,分隔三个表达式。它是 JavaScript 语言唯一一个需要三个运算子的运算符。如果第一个表达式的布尔值为true,则返回第二个表达式的值,否则返回第三个表达式的值。

1
2
't' ? 'hello' : 'world' // "hello"
0 ? 'hello' : 'world' // "world"

上面代码的t0的布尔值分别为truefalse,所以分别返回第二个和第三个表达式的值。

1
2
3
4
5
6
7
8
使用三元运算符表达:
if (a > 10) {
b = a
}
else {
b = a - 2
}
答:b = a>10? a : a-2

位运算符
  • 或运算(or):符号为|,表示两个二进制位中有一个为1,则结果为1,否则为0。
  • 与运算(and):符号为&,表示两个二进制位都为1,则结果为1,否则为0。
  • 否运算(not):符号为,表示将一个二进制位变成相反值。
  • 异或运算(xor):符号为ˆ,表示两个二进制位中有且仅有一个为1时,结果为1,否则为0。
  • 左移运算(left shift):符号为<<
  • 右移运算(right shift):符号为>>
  • 带符号位的右移运算(zero filled right shift):符号为>>>

逗号运算符

逗号运算符用于对两个表达式求值,并返回后一个表达式的值。

1
2
3
4
5
6
'a', 'b' // "b"

var x = 0;
var y = (x++, 10);
x // 1
y // 10

上面代码中,逗号运算符返回后一个表达式的值。