基本语法
- 变量提升:JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升(hoisting)。
- 标识符:第一个字符,可以是任意 Unicode 字母(包括英文字母和其他语言的字母),以及美元符号(
$)和下划线(_)。第二个字符及后面的字符,除了 Unicode 字母、美元符号和下划线,还可以用数字0-9。 - 三元运算符:(条件) ? 表达式1 : 表达式2; 如果“条件”为true,则返回“表达式1”的值,否则返回“表达式2”的值。
数据类型
-
JavaScript 有三种方法,可以确定一个值到底是什么类型:
typeof运算符,可以返回一个值的数据类型。undefined返回undefined,null返回objectinstanceof运算符Object.prototype.toString方法
-
null和undefined
- 在
if语句中,它们都会被自动转为false - 相等运算符(
==)甚至直接报告两者相等 null是一个表示“空”的对象,转为数值时为0;undefined是一个表示"此处无定义"的原始值,转为数值时为NaN。null表示空值,即该处的值现在为空undefined表示“未定义”
- 在
-
布尔值,转换规则是除了下面六个值被转为
false,其他值都视为true。undefinednullfalse0NaN""或''(空字符串)
-
浮点数与整数,JavaScript 内部,所有数字都是以64位浮点数形式储存,即使整数也是如此。所以,
1与1.0是相同的,是同一个数。这就是说,JavaScript 语言的底层根本没有整数,所有数字都是小数(64位浮点数)。容易造成混淆的是,某些运算只有整数才能完成,此时 JavaScript 会自动把64位浮点数,转成32位整数,然后再进行运算。 -
数值精度,精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即-253到253,都可以精确表示。
-
数值范围,JavaScript 提供
Number对象的MAX_VALUE和MIN_VALUE属性,返回可以表示的具体的最大值和最小值。Number.MAX_VALUE // 1.7976931348623157e+308 Number.MIN_VALUE // 5e-324 -
数值的表示法,可以用字面形式直接表示,比如
35(十进制)和0xFF(十六进制),科学计数法允许字母e或E的后面,跟着一个整数,表示这个数值的指数部分。- 十进制:没有前导0的数值。
- 八进制:有前缀
0o或0O的数值,或者有前导0、且只用到0-7的八个阿拉伯数字的数值。 - 十六进制:有前缀
0x或0X的数值。 - 二进制:有前缀
0b或0B的数值。
-
特殊数值
-
正零和负零,JavaScript 内部实际上存在2个
0:一个是+0,一个是-0,区别就是64位浮点数表示法的符号位不同。它们是等价的。 -
NaN,
NaN是 JavaScript 的特殊值,表示“非数字”(Not a Number),主要出现在将字符串解析成数字出错的场合。NaN不等于任何值,包括它本身。
NaN === NaN // false- 数组的
indexOf方法内部使用的是严格相等运算符,所以该方法对NaN不成立。
[NaN].indexOf(NaN) // -1NaN在布尔运算时被当作false。
Boolean(NaN) // falseNaN与任何数(包括它自己)的运算,得到的都是NaN。
NaN + 32 // NaN NaN - 32 // NaN NaN * 32 // NaN NaN / 32 // NaN -
Infinity,
Infinity表示“无穷”,用来表示两种场景。一种是一个正的数值太大,或一个负的数值太小,无法表示;另一种是非0数值除以0,得到Infinity。Infinity有正负之分,Infinity表示正的无穷,-Infinity表示负的无穷。Infinity的四则运算,符合无穷的数学计算规则。
5 * Infinity // Infinity 5 - Infinity // -Infinity Infinity / 5 // Infinity 5 / Infinity // 0- 0乘以
Infinity,返回NaN;0除以Infinity,返回0;Infinity除以0,返回Infinity。
0 * Infinity // NaN 0 / Infinity // 0 Infinity / 0 // InfinityInfinity加上或乘以Infinity,返回的还是Infinity。
Infinity + Infinity // Infinity Infinity * Infinity // InfinityInfinity减去或除以Infinity,得到NaN。
Infinity - Infinity // NaN Infinity / Infinity // NaNInfinity与null计算时,null会转成0,等同于与0的计算。
null * Infinity // NaN null / Infinity // 0 Infinity / null // InfinityInfinity与undefined计算,返回的都是NaN。
undefined + Infinity // NaN undefined - Infinity // NaN undefined * Infinity // NaN undefined / Infinity // NaN Infinity / undefined // NaN
-
字符串
- 单引号字符串的内部,可以使用双引号。双引号字符串的内部,可以使用单引号。
- 由于 HTML 语言的属性值使用双引号,所以很多项目约定 JavaScript 语言的字符串只使用单引号。当然,只使用双引号也完全可以。重要的是坚持使用一种风格,不要一会使用单引号表示字符串,一会又使用双引号表示。
- 反斜杠(\)在字符串内有特殊含义,用来表示一些特殊字符,所以又称为转义符。
\0:null(\u0000)\b:后退键(\u0008)\f:换页符(\u000C)\n:换行符(\u000A)\r:回车键(\u000D)\t:制表符(\u0009)\v:垂直制表符(\u000B)':单引号(\u0027)":双引号(\u0022)\:反斜杠(\u005C)
- 字符串可以被视为字符数组,因此可以使用数组的方括号运算符,用来返回某个位置的字符(位置编号从0开始)。但是,字符串与数组的相似性仅此而已。实际上,无法改变字符串之中的单个字符。
对象
-
对象的引用,如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。
-
表达式还是语句?
- 为了避免这种歧义,JavaScript 引擎的做法是,如果遇到这种情况,无法确定是对象还是代码块,一律解释为代码块。
{ console.log(123) } // 123- 上面的语句是一个代码块,而且只有解释为代码块,才能执行。
- 如果要解释为对象,最好在大括号前加上圆括号。因为圆括号的里面,只能是表达式,所以确保大括号只能解释为对象。
({ foo: 123 }) // 正确 ({ console.log(123) }) // 报错 -
属性的读取。
- 读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符。
- 请注意,如果使用方括号运算符,键名必须放在引号里面,否则会被当作变量处理。
- 方括号运算符内部还可以使用表达式。
- 点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值。
-
属性的删除:delete 命令
delete命令用于删除对象的属性,删除成功后返回true。- 只有一种情况,
delete命令会返回false,那就是该属性存在,且不得删除。 delete命令只能删除对象本身的属性,无法删除继承的属性
-
属性是否存在:in 运算符
in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值),如果包含就返回true,否则返回false
var obj = { p: 1 }; 'p' in obj // true 'toString' in obj // truein运算符的一个问题是,它不能识别哪些属性是对象自身的,哪些属性是继承的。这时,可以使用对象的hasOwnProperty方法判断一下,是否为对象自身的属性。
函数
- 函数名的提升,JavaScript 引擎将函数名视同变量名,所以采用
function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。 - 闭包,可以把闭包简单理解成“定义在一个函数内部的函数”。
- 闭包的最大用处有两个,一个是可以读取外层函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。
- 注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。
比较运算符
- JavaScript 引擎内部首先比较首字符的 Unicode 码点。如果相等,再比较第二个字符的 Unicode 码点,以此类推。
- 与
NaN的比较。任何值(包括NaN本身)与NaN使用非相等运算符进行比较,返回的都是false。 - 如果运算子是对象,会转为原始类型的值,再进行比较。对象转换成原始类型的值,算法是先调用
valueOf方法;如果返回的还是对象,再接着调用toString方法。 - 两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个地址。
- 注意,对于两个对象的比较,严格相等运算符比较的是地址,而大于或小于运算符比较的是值。
undefined和null与自身严格相等。undefined和null只有与自身比较,或者互相比较时,才会返回true;与其他类型的值比较时,结果都为false。
布尔运算符
- 可以这样记忆,以下六个值取反后为
true,其他值都为false。undefinednullfalse0NaN- 空字符串(
'')
- 且运算符(&&):它的运算规则是:如果第一个运算子的布尔值为
true,则返回第二个运算子的值(注意是值,不是布尔值);如果第一个运算子的布尔值为false,则直接返回第一个运算子的值,且不再对第二个运算子求值。如果所有表达式的布尔值都为true,则返回最后一个表达式的值。 - 或运算符(||):如果第一个运算子的布尔值为
true,则返回第一个运算子的值,且不再对第二个运算子求值;如果第一个运算子的布尔值为false,则返回第二个运算子的值。如果所有表达式都为false,则返回最后一个表达式的值。 - 或运算符常用于为一个变量设置默认值。
上面代码表示,如果函数调用时,没有提供参数,则该参数默认设置为空字符串。function saveText(text) { text = text || ''; // ... } // 或者写成 saveText(this.text || '') - 三元条件运算符:三元条件运算符由问号(?)和冒号(:)组成,分隔三个表达式。它是 JavaScript 语言唯一一个需要三个运算子的运算符。如果第一个表达式的布尔值为
true,则返回第二个表达式的值,否则返回第三个表达式的值。
二进制位运算符
-
二进制位运算符用于直接对二进制位进行计算,一共有7个。
- 二进制或运算符(or):符号为
|,表示若两个二进制位都为0,则结果为0,否则为1。 - 二进制与运算符(and):符号为
&,表示若两个二进制位都为1,则结果为1,否则为0。 - 二进制否运算符(not):符号为
~,表示对一个二进制位取反。 - 异或运算符(xor):符号为
^,表示若两个二进制位不相同,则结果为1,否则为0。 - 左移运算符(left shift):符号为
<< - 右移运算符(right shift):符号为
>> - 头部补零的右移运算符(zero filled right shift):符号为
>>>
- 二进制或运算符(or):符号为
-
使用二进制否运算取整,是所有取整方法中最快的一种。
~~2.9 // 2 ~~47.11 // 47 ~~1.9999 // 1 ~~3 // 3 -
对字符串进行二进制否运算,JavaScript 引擎会先调用
Number函数,将字符串转为数值。// 相当于~Number('011') ~'011' // -12 // 相当于~Number('42 cats') ~'42 cats' // -1 // 相当于~Number('0xcafebabe') ~'0xcafebabe' // 889275713 // 相当于~Number('deadbeef') ~'deadbeef' // -1 -
“异或运算”有一个特殊运用,连续对两个数
a和b进行三次异或运算,a^=b; b^=a; a^=b;,可以互换它们的值。这意味着,使用“异或运算”可以在不引入临时变量的前提下,互换两个变量的值。var a = 10; var b = 99; a ^= b, b ^= a, a ^= b; a // 99 b // 10这是互换两个变量的值的最快方法。
-
异或运算也可以用来取整。
12.9 ^ 0 // 12 -
左移运算符: 左移运算符(
<<)表示将一个数的二进制值向左移动指定的位数,尾部补0,即乘以2的指定次方。向左移动的时候,最高位的符号位是一起移动的。如果左移0位,就相当于将该数值转为32位整数,等同于取整,对于正数和负数都有效。 -
右移运算符: 右移运算符(
>>)表示将一个数的二进制值向右移动指定的位数。如果是正数,头部全部补0;如果是负数,头部全部补1。右移运算符基本上相当于除以2的指定次方(最高位即符号位参与移动)。 -
头部补零的右移运算符: 头部补零的右移运算符(
>>>)与右移运算符(>>)只有一个差别,就是一个数的二进制形式向右移动时,头部一律补零,而不考虑符号位。所以,该运算总是得到正值。对于正数,该运算的结果与右移运算符(>>)完全一致,区别主要在于负数。 -
运算顺序: 根据语言规格,这五个运算符的优先级从高到低依次为:小于等于(
<=)、严格相等(===)、或(||)、三元(?:)、等号(=)。 -
左结合与右结合: JavaScript 语言的大多数运算符是“左结合”,请看下面加法运算符的例子。
x + y + z // 引擎解释如下 (x + y) + z -
左结合与右结合: 少数运算符是“右结合”,其中最主要的是赋值运算符(
=)和三元条件运算符(?:)。w = x = y = z; q = a ? b : c ? d : e ? f : g;上面代码的解释方式如下。
w = (x = (y = z)); q = a ? b : (c ? d : (e ? f : g));上面的两行代码,都是右侧的运算数结合在一起。
另外,指数运算符(
**)也是右结合。2 ** 3 ** 2 // 相当于 2 ** (3 ** 2) // 512
数据类型转换
- Number()、String()、Boolean()
错误处理机制
SyntaxError对象是解析代码时发生的语法错误。ReferenceError对象是引用一个不存在的变量时发生的错误。RangeError对象是一个值超出有效范围时发生的错误。主要有几种情况,一是数组长度为负数,二是Number对象的方法参数超出范围,以及函数堆栈超过最大值。TypeError对象是变量或参数不是预期类型时发生的错误。URIError对象是 URI 相关函数的参数不正确时抛出的错误,主要涉及encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()这六个函数。- 对于 JavaScript 引擎来说,遇到
throw语句,程序就中止了。引擎会接收到throw抛出的信息,可能是一个错误实例,也可能是其他类型的值。 try代码块抛出错误,JavaScript 引擎就立即把代码的执行,转到catch代码块,或者说错误被catch代码块捕获了。catch接受一个参数,表示try代码块抛出的值。catch代码块捕获错误之后,程序不会中断,会按照正常流程继续执行下去。catch代码块之中,还可以再抛出错误,甚至使用嵌套的try...catch结构。try...catch结构允许在最后添加一个finally代码块,表示不管是否出现错误,都必需在最后运行的语句。