任何语言的核心,都必然会描述这门语言最基本的工作原理。而描述的内容通常都要涉及这门语言的语法、操作符、数据类型、内置功能等用于构建复杂解决方案的基本理念。为此,这篇文章主要用来介绍 JavaScript
这门语言的 基本概念。
1. 语法
合法的格式与指令的组合规则被称为一种 计算机语言,有时被称作它的 语法,这和英语教你如何拼写单词,和如何使用单词与标点创建合法的句子差不多是相同的。
1.1 区分大小写
ECMAScript 中的一切(变量、函数名和操作符)都区分大小写。
也就是说,
test
和Test
,分别表示两个不同的变量。但函数名不能用typeof
,因为它是一个 关键字,但typeOf
则完全可以是一个有效的函数名。
1.2 标识符
所谓 标识符,就是指变量、函数、属性的名字,或者函数的参数。标识符有如下规则:
一个标识符必须以
a-z
,A-Z
,$
,或_
开头。它可以包含任意这些字符外加数字0-9
。
标识符中的字母也可以包含扩展的 ASCII
或 Unicode
字母字符,但是我们不推荐这样做。
按照惯例,ECMAScript
标识符采用 驼峰大小写 格式,也就是第一个字母小写,剩下的每个单词的首字母大写,如 myFirstClass
。
不能把关键字(如,do
、else
、this
、typeof
等)、保留字(如,boolean
、export
、float
、public
等)、true
、false
、和 null
用作标识符。
1.3 注释
ECMAScript
使用C
风格的注释,包括 单行注释 和 块级注释。
// 单行注释
/*
* 这是一个多行
* (块级)注释
*/
1.4 严格模式
ES5在语言中加入了一个“
strict模式
”,它收紧了一些特定行为的规则,使代码符合一组更安全和更合理的指导方针。
坚持 strict模式
一般会使你的代码对引擎有更强的可优化性。strict模式
对代码有很大的好处,你应当在你所有的程序中使用它。
根据你摆放 strict模式
注解的位置,你可以为一个单独的函数,或者是整个一个文件切换到 strict模式
:
function foo() {
"use strict";
//这部分代码是 strict模式 的
function bar() {
//这部分代码是 strict模式 的
}
}
// 这部分代码不是 strict模式 的
将它与这个相比:
"use strict";
function foo() {
//这部分代码是 strict 模式的
function bar() {
//这部分代码是 strict模式 的
}
}
// 这部分代码是 strict模式 的
使用 strict模式
的一个关键不同是,它不允许因为省略了 var
而进行隐含的自动全局变量声明:
function foo() {
"use stirct"; //打开 strict模式
a = 1; //缺少 `var` ,ReferenceError
}
foo();
1.5 语句
在一门计算机语言中,一组单词,数字,和执行一种具体任何的操作符构成了一个 语句。语句以一个 分号 结尾。
在 JavaScript
中,语句可能看起来像下面这样:
var sum = a + b //即使没有分号也是有效的语句 --- 不推荐
var diff = a -b; //有效的语句 --- 推荐
虽然语句结尾的分号不是必需的,但我们建议任何时候都不要省略它。因为加上分号可以避免错误(如不完整的输入),开发人员可以放心地通过删除多余的空格来压缩代码(如果代码没有分号结尾,容易导致压缩错误)。另外,加上分号可以增进代码的性能,因为这样解析器就不必再花时间推测应该在哪里插入分号了。
2. 变量
大多数有用的程序都需要在程序运行整个过程中,追踪由于你的程序所意图的任务被调用的底层不同的操作而发生的值的变化。
要这样做的最简单的方法是将一个值赋予一个符号容器(占位符),称为 —— 变量,这个容器中的值可以根据需要不时 变化 而得名。
定义变量时要使用 var
操作符(注意:var
是一个 关键字),后跟 变量名(即一个 标识符),如下所示:
var message; //该变量可以用来保存任何值,初始状态的值为:undefined
有一点必须注意,即用 var
操作符定义的变量将成为定义该变量的作用域中的 局部变量。也就是说,如果在函数中用 var
定义了一个变量,那么这个变量在函数被调用后就会被 销毁,例如:
function test() {
var message = 'hello'; //局部变量
}
test();
alert(message); //报错
如果省略 var
操作符,从而创建一个全局变量:
function test() {
message = 'hello'; //全局变量
}
test();
alert(message); //'hello'
这个例子省略了 var
操作符,因而 message
就成了 全局变量。这样,只要调用过一次 test()
函数,这个变量就有了定义,就可以在函数外部的任何地方被访问到。
3. 数据类型
ECMAScript
中有5种 简单数据类型(也称为 基本数据类型)即:——Undefined
、Null
、Boolean
、Number
和String
。还有一种 复杂数据类型 ——Object
。
3.1 typeof 操作符
由于 ECMAScript
的变量是 松散类型(即可以用来保存任何类型的数据),因此,JavaScript
提供了一个 typeof
操作符,它可以检查一个值,并告诉你它的类型是什么:
var a;
typeof a; // "undefined"
a = "hello world";
typeof a; // "string"
a = 42;
typeof a; // "number"
a = true;
typeof a; // "boolean"
a = null;
typeof a; // "object" -- 特殊值 null 被认为是一个空的对象引用(空对象指针),因此为 "object"
a = undefined;
typeof a; // "undefined"
a = { b: "c" };
typeof a; // "object"
3.2 Undefined类型 和 Null类型
实际上,undefined
值是派生自 null
值的,因此 ECMA-262
规定对它们的相等性测试要返回 true
;
alert( null == undefined ); //true
尽管 null
和 undefined
有这样的关系,但它们的用途完全不同。无论任何情况下,都没必要把一个变量的值显式地设为 undefined
,但 null
则不然。只要意在保存对象的变量还没有真正保存对象,就应该明确地让该变量保存 null
值。这样做不仅体现 null
作为空对象指针的惯例,而且有助于进一步区分 null
和 undefined
。
3.3 Boolean 类型
Boolean
类型是ECMAScript
中使用最多的一种类型,该类型只有两个字面值:true
和false
。
Truthy 与 Falsy:当一个非 boolean
值被强制转换为一个 boolean
时,它是变成 true
还是 false
。
在 JavaScript
中 falsy
的明确列表如下:
-
" "
(空字符串) -
0
,-0
,NaN
(非法的number
) -
null
,undefined
false
任何不在这个 falsy
列表中的值都是 truthy
。例如:
"hello"
42
true
-
[ ]
,[ 1 , "2" , 3 ]
(数组) -
{ }
,{ a : 42 }
(对象) -
function foo() { ... }
(函数)
3.4 Number 类型
包括 整数 和 浮点数值
NaN
,即非数值(Not a Number
)是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作属未返回数值的情况(这样就不会抛出错误了)。
任何数值除以非数值都会返回 NaN
,不会影响其他代码的执行。
NaN
有两个非同寻常的特点:
- 任何涉及
NaN
的操作 (例如 NaN/10)都会返回NaN
; -
NaN
与任何值都不相等,包括NaN
本身。
alert( NaN == NaN ) //false
针对 NaN
的这两个特点,ECMAScript
定义了 isNaN()
函数。这个函数接受一个参数,该参数可以是任何类型,而函数会帮我们确定这个参数是否 “不是数值”。
isNaN()
在接收到一个值之后,会尝试将这个值转换为数值,某些不是数值的值会直接转换为数值。而任何不能被转换为数值的值都会导致这个函数返回 true
。例如:
alert( isNaN(NaN) ); //true
alert( isNaN(10) ); //false ( 10 是一个数值 )
alert( isNaN("10") ); //false (可以被转换成数值 10)
alert( isNaN("blue") ); //true ( 不能转换成数值 )
alert( isNaN(true) ); //false ( 可以被转换成数值 1 )
3.5 String 类型
字符串可以由双引号
(" ")
或 单引号( ' )
表示。
例如以下两种写法:
var a = 'aa';
var b = "b";
字符串的特点:不可变,也就是说,字符串一旦创建,它们的值就不能改变。
要把某个值转换为字符串,有以下两种方法:
1.用 toString()
方法;
2.用 +
号把它与一个字符串加在一起。
例如:
var num = 10;
alert( num.toString() ); //"10"
var str = "20"
alert( num + str ); //"1020"
3.6 Object 类型
ECMAScript
中的对象其实就是一组数据和功能的集合。对象可以通过执行new
操作符后跟要创建的对象类型的名称来创建。
而创建 Object
类型的实例并为其添加 属性 和(或)方法,就可以创建 自定义对象,如下所示:
var o = new Object()
在 ECMAScript
中,Object
类型是所有它的实例的基础。换句话说,Object
类型所具有的任何属性和方法也同样存在于更具体的对象中。
Object
的每个实例都具有下列属性和方法:
-
constructor
: 保存着用于创建当前对象的函数。对于前面的例子而言,构造函数(constructor
)就是Object()
; -
hasOwnProperty(propertyName)
: 用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中,作为参数的属性名(propertyName
)必须以字符串形式指定(例如:o.hasOwnProperty("name")
);
var o = new Object();
o.name = "lily";
o.hasOwnProperty("name"); //true
o.hasOwnProperty("age"); //false
var obj = {a:'aa',b:'bb'};
obj.hasOwnProperty("a"); //true
obj.hasOwnProperty("c"); //false
-
isPrototypeOf(object)
: 用于检查传入的对象是否是当前对象的原型 ; -
propertyIsEnumerable(propertyName)
: 用于检查给定的属性是否能够使用for-in
语句来枚举,与hasOwnProperty()
方法一样,作为参数的属性名必须以字符串形式指定; -
toLocaleString()
: 返回对象的字符串表示,该字符串与执行环境的地区对应; -
toString()
: 返回对象的字符串表示; -
valueOf()
: 返回对象的字符串、数值或布尔值表示,通常与toString()
方法的返回值相同。
4. 操作符
ECMA-262
描述了一组用于操作数据值的 操作符,包括 算术操作符(如加号和减号)、位操作符、关系操作符 和 相等操作符。
4.1 一元操作符
只能操作一个值的操作符叫做 一元操作符。
一元操作符有:++
、--
、+=
、-=
4.2 位操作符
位操作符 用于在最基本的层次上,即按内存中表示数值的位来操作数值。
4.3 布尔操作符
一共有三个值:
与(&&)
、或(||)
、非(!)
!
: 非真即为假,非假即为真。
&&
和 ||
都有短路操作:即如果第一个操作数能够决定结果,那么就不会再对第二个操作数求值。
-
&&
: 第一个操作数为假,便不会再往后执行; -
||
: 第一个操作数为真,便不会再往后执行。
4.4 算术运算符
算术运算符有:
+
、-
、*
、/
、%
-
+
有以下用法:
1.数值相加:如果加号两边的操作数都是数值,进行正常的求和计算
2.字符拼接:加号两边的操作数只要有一个是字符串,则进行字符拼接var sum = 3 + 2; //5
var sum = "3" + 2; //32
-
-
用法:进行减法运算,如果有一个操作数不是数值,则先在后台调用Number()
函数将其转换为数值,再进行运算。如:var res = 5 - true; //4,true 被转换成了 1 var res = NaN - 1; //NaN var res = 5 - 3; //2 var res = 5 - ""; //5," "被转换成了 0 var res = 5 - "3"; //2,"3" 被转换成了 3 var res = 5 - null; //5,null 被转换成了 0
-
*
、/
、%
用法:均为正常的数学运算,与NaN
进行运算均为NaN
。
4.5 关系操作符
关系操作符有:
>
、<
、>=
、<=
关系操作符有以下几种规则:
- 如果两个操作数都是数值,则进行数值比较;
- 如果两个操作数都是字符串,则比较字符串对应的字符编码值;
- 如果一个操作数是数值,则将另一个操作数转为数值,再进行数值比较;
- 如果一个操作数是对象,则调用这个对象的
valueOf()
方法,用得到的结果按照前面的规则进行比较。如果没有valueOf()
方法,则调用toString()
方法,并用得到的结果与前面的规则进行比较。
例如:var res = 3 > 5; //false var res = "23" < "3"; //true var res = "23" < 3; //false var res = "a" < 3; //false,"a" 被转换成了 NaN
注意: 任何操作数与 NaN
进行关系比较,结果都是 false
。
4.6 相等操作符
==
和!=
—— 先转换再比较;===
和!==
—— 仅比较而不转换。
1.在转换不同的数据类型时,相等(==
) 和 不相等(!=
) 操作符遵循下列基本规则:
- 如果有一个操作数是布尔值,则比较之前先将其进行数值转换——
false
转换为0
,true
转换为1
; - 如果操作数一个是字符串,另一个是数值,先将字符转进行转换,再进行对比;
- 如果一个操作数是对象,另一个不是,则调用对象的
valueOf()
方法,用得到的值按之前的规则进行对比; -
null
==undefined
; - 要比较等价性之前,不能将
null
和undefined
转换成其他任何值; -
NaN
不等于任何值,包括其自身; - 如果两个操作数都是对象,则要比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回
true
,否则返回false
。
有以下例子:null == undefined; //true NaN == NaN; //false NaN != NaN; //true 5 == NaN; //false false == 0; //true true == 1; //true true == 2; //false null == 0; //false "3" == 3; //true
- 全等(
===
) 和 不全等(!==
):不经过类型转换就进行比较。
如下所示:
"33" == 33; //true,类型转换后值相等 "33" === 33; //false,不同的数据类型不同
- 全等(
4.7 条件操作符
var max = (num1 > num2) ? num1 : num2
在这个例子中,max
中将会保存一个最大的值。这个表达式的意思是,如果 num1
大于 num2
(关系表达式返回 true
),则将 num1
的值赋给 max
;否则,将 num2
的值赋值给 max
。
4.8 赋值操作符
简单的赋值操作符由
=
表示,其作用就是把右侧的值赋值给左侧的变量。
例如:
var num = 10;
5. 语句
ECMA-262
规定了一组语句(也称为流控制语句)。从本质上看,语句定义了ECMAScript
中的主要语法,语句通常使用一个或多个关键字来完成给定任务。
5.1 if 语句
语法:
if (condition) statement1 else statement2
例如:
if( i > 5 ){
aler( "it larger than 5" )
}else if( i < 0 ){
aler( "it smaller than 0" )
}else{
alert( "Between 0 and 5" )
}
5.2 do-while 语句
在对条件表达式求值之前,循环体内的代码至少会被执行一次。
语法:
do {
statement
} while ( expression );
例如:
var i = 0;
do {
i += 2;
} while ( i < 10 );
alert (i); //10
5.3 while 语句
在循环体内的代码被执行之前,就会对出口条件求值。因此,循环体内的代码有可能永远不会被执行。
语法:
while ( expression ) statement
例如:
var i = 0;
while ( i < 10 ){
i += 2;
}
aler (i); //10
5.4 for 语句
语法:
for ( initialization; expression; post-loop-expression ) statement
例如:
var count = 10;
for ( var i = 0; i < count; i++ ){
console.log(i); //0,1,2,3,4,5,6,7,8,9
}
相当于:
var count = 10;
var i = 0;
while ( i< count ){
console.log(i);
i++;
}
注意:for
语句中的初始化表达式、控制表达式和循环后表达式都是可选的。将这三个表达式全部省略,就会创建一个无限循环,例如:
for ( ; ; ) { //无限循环
doSomething();
}
而只给出控制表达式实际上就把 for
循环转换成了 while
循环,例如:
var count = 10;
var i = 0;
for ( ; i < count; ){
console.log(i);
i++;
}
5.5 for-in 语句
for-in
语句是一种精准的迭代语句,可以用来枚举对象的属性。
语法:
for ( property in expression ) statement
例如:
var obj = {
a:'aa',
b:'bb',
c:'cc'
}
for(var propName in obj){
console.log(propName); //"a","b","c"
}
5.6 break 和 continue 语句
break
和continue
语句用于在循环中精确地控制代码的执行。其中,break
会立即退出循环,强制执行循环后面的语句。而continue
语句虽然也是立即退出循环,但退出循环后,会再次进入循环,执行循环语句。
例如:
var num = 0;
for( var i = 1; i < 10; i++ ){
if( i % 5 == 0 ){
break;
}
num++;
}
alert(num); //4
在这个例子中,循环体中的 i
为 5
的时候,则执行 break
语句退出循环。num++
总共执行了 4
次,所以 num
的值为 4
。如果这里把 break
替换为 continue
,则可以看到另一种结果:
var num = 0;
for( var i = 1; i < 10; i++ ){
if( i % 5 == 0 ){
continue;
}
num++;
}
alert(num); //8
例子的结果为 8
,也就是 num++
总共执行了 8
次。当 i
等于 5
时,循环会在 num
再次递增之前退出,但接下来,会继续执行下一次循环,即 i
的值等于 6
的循环,此时 num
为 5
,然后继续执行循环,知道 i
等于 10
结束循环。而最终 num
值之所以为 8
,是因为 continue
语句导致它少递增了一次。
5.7 switch 语句
switch
语句与if
语句的关系最为密切,而且也是在其他语言中普遍使用的一种流控制语句。
语法:
switch (expression) {
case value: statement
break;
case value: statement
break;
case value: statement
break;
default: statement
}
switch
语句中的每一种情形(case
)的含义是:“如果表达式等于这个值(value
),则执行后面的语句(statement
)”。而 break
关键字会导致代码执行流跳出 switch
语句。如果省略 break
关键字,就会导致执行完当前 case
后,继续执行下一个 case
。最后的 default
关键字则用于在表达式不匹配前面任何一种情形的时候,执行机动代码(因此,也相当于一个 else
语句)。
例如:
if( i == 25 ){
alert("25");
}else if( i == 35 ){
alert("35");
}else if( i == 45 ){
alert("45");
}else{
alert("Other");
}
而与此等价的 switch
语句如下所示:
switch( i ) {
case 25;
alert ("25");
break;
case 35;
alert ("35");
break;
case 45;
alert ("45");
break;
default:
alert ("Other");d
}
6. 函数
通过函数可以封装任意多条语句,而且可以在任何地方,任何时候调用执行。
ECMAScript
中的函数使用function
关键字来声明,后跟一组参数以及函数体。
语法:
function fn ( a,b,...,n ){
statements
}
例如:
function sayHi( name , message ){
alert( "Hello" + name + "," + message )
}
调用 sayHi()
函数的代码如下所示:
sayHi( " Lily" , "how are you today ?");
ECMAScript
中的函数在定义时不必指定是否返回值。实际上,任何函数在任何时候都可以通过 return
语句后跟要返回的值来实现返回值。例如:
function sum ( num1 , num2 ){
return num1 + num2;
}
调用这个函数的代码如下:
var result = sum ( 3 , 5 );
位于 return
语句之后的任何代码都永远不会执行。
function sum( num1 , num2 ){
return num1 + num2;
alert("hello"); //永远不会执行
}