一、基本语法
1. 语句
- 1.1 每一行是一个语句,语句不需返回值,表达式有返回值。
2. 变量
- 2.1 严格地说,var a = 1 与 a = 1是不完全一样,delete命令无法删除前者。不用var声明的语句不利于表达意图,且容易创建全局变量。
- 2.2 用var声明同一个变量并赋值有覆盖性。
- 2.3 用var命令声明的变量存在变量提升,声明语句都会被提升到代码的头部。
3. 标识符
- 3.1 变量名首个不能是数字,不能包含 * 和运算符,不能用保留字,大小写是不同的,中文可做变量名。
4. 注释
- 4.1 也可用html的单行注释(在行首,否则为运算符)。
5、区块
- 5.1 JavaScript的区块不构成单独的作用域。
6、语句点
- 6.1 else代码块总是跟随离自己最近的那个if语句。
- 6.2 switch、case语句都可以使用表达式,在比较运行结果时,采用的是===,而不是==,比较时不会发生类型转换。
二、数据类型
1. 对象
- 狭义的对象(object)
- 数组(array)
- 函数(function)
2. 类型
- number、string、boolean、function、undefined、object(空数组和null也是)
3. 三种确定类型方法
- typeof运算符
- instanceof运算符
- Object.prototype.toString方法
4. 布尔值
- false: undefined、null、false、0、NaN、 " "或' '(空字符串)
- ture :特别注意空数组([])和空对象({})
三、数值
1. 数值范围
1.1 所有数字都是以64位浮点数形式储存
1.2 JavaScript 浮点数的64个二进制位,从最左边开始:
第1位:符号位,0表示正数,1表示负数
第2位到第12位:指数部分
第13位到第64位:小数部分(即有效数字)
1.3 能表示的数值范围为2^1024 到2^-1023(开区间)(否则返回Infinity)
2. 数值精度
2.1 从-(2^53-1) 到(2^53-1),都可以精确表示
2.2 若只有整数才能完成的运算,会自动把64位浮点数转成32位整数
2.3 浮点数不是精确的值(小数的比较,如0.1+0.2===0.3 //false
)
2.4 小数点前的数字多于21位和其后的零多于5个会自动转为科学计数法
3. 数值进制
3.1 十进制:没有前导0的数值。
3.2 八进制:有前缀0o或0O的数值,或者有前导0、且只用到0-7的八个阿拉伯数字的数值。
3.3 十六进制:有前缀0x或0X的数值。
3.4 二进制:有前缀0b或0B的数值。
4.特殊数值
-
+0===-0
(1 / +0) === (1 / -0) // false +Infinity!=-Infinity
NaN === NaN // false
Infinity > (<) NaN // false
- isNaN为true的值,有可能不是NaN,而是一个字符串、对象和数组。
5. 全局方法
5.1 parselnt
- parseInt:将字符串转为整数,只返回字符串头部可以转为数字,第一个字符不能转化为数字(后面跟着数字的正负号除外),返回NaN。
parseInt('+') // NaN
parseInt('+1') // 1
- parseInt会将科学计数法的表示方法视为字符串
- 进制转换(2到36之间)
parseInt('1000', 2) // 2进制-8
parseInt('1000', 8) // 8进制512
parseInt('10', 37) // NaN
parseInt('10', 1) // NaN
parseInt('10', 0) // 10
parseInt('10', null) // 10
parseInt('10', undefined) // 10
5.2 parseFloat:将一个字符串转为浮点数(会将空字符串转为NaN)
(用法与parseInt类似)
四、字符串
1. 转义符
转义符 | 表达 | 码值 |
---|---|---|
\0 | null | (\u0000) |
\b | 后退键 | (\u0008) |
\f | 换页符 | (\u000C) |
\n | 换行符 | (\u000A) |
\r | 回车键 | (\u000D) |
\t | 制表符 | (\u0009) |
\v | 垂直制表符 | (\u000B) |
' | 单引号 | (\u0027) |
" | 双引号 | (\u0022) |
\ | 反斜杠 | (\u005C) |
- 在非特殊字符前面使用反斜杠,则反斜杠会被省略
- charCodeAt:返回每个字节对应的十进制值
- 在JavaScript引擎内部,所有字符都用Unicode表示
五、对象
1. 新建对象
var o1 = {};
var o2 = new Object();
var o3 = Object.create(Object.prototype); //用在需要对象继承的场合
2. 注意事项
2.1 如果键名不符合标识名的条件(比如第一个字符为数字,或者含有空格或运算符),也不是数字,则必须加上引号,保留字可以不加引号当作键名
2.2 行首是大括号为语句(即代码块)。在大括号前加上圆括号为表达式(即对象)。
3. 属性
- 3.1 读取对象的属性:点运算符(数值键名不能使用(被当成小数点)
方括号运算符(键名必须放在引号里面,否则会被当作变量处理。但数字键不加引号,因为会被当作字符串处理)
o.p // "Hello World"
o['p'] // "Hello World"
- 3.2 用Object.keys方法查看一个对象本身的所有属性。
- 3.3 delete命令用于删除对象的属性,删除成功后返回true,属性就会返回undefined。
- 删除一个不存在的属性,delete不报错,而且返回true。
- 属性存在,且不得删除,delete命令会返回false。
- delete命令不能删除var命令声明的变量,只能用来删除属性。
4. in
- 用于检查对象是否包含某个属性(检查的是键名,不是键值),包含(true),否则(false)。(但不能识别对象继承的属性)
5.for...in循环
- 5.1 用来遍历一个对象的全部属性。
- 5.2 遍历的是对象所有可遍历的属性,跳过不可遍历的属性,遍历继承的属性。
- 5.3 用hasOwnProperty只遍历对象本身的属性
6. with
- 6.1 操作同一个对象的多个属性时,提供一些书写的方便
with (o) {
p1 = 1;
p2 = 2;
}
// 等同于
o.p1 = 1;
o.p2 = 2;
- 6.2 with区块内部的变量,必须是当前对象已经存在的属性,否则会创造一个当前作用域的全局变量,因为with区块没有改变作用域,它的内部依然是当前作用域。
六、数组
1.本质
数组属于一种特殊的对象。typeof运算符会返回数组的类型是object。
2. 类似数组的对象
- 2.1 类似数组的对象是函数的arguments对象,以及大多数DOM元素集,还有字符串。
- 2.2 数组的slice方法将类似数组的对象,变成真正的数组。
var arr = Array.prototype.slice.call(arrayLike);
- 2.3 length属性不过滤空位空位,空位与某个位置是undefined,是不一样的。使用数组的forEach方法、for...in结构、以及Object.keys方法进行遍历,空位都会被跳过。
- 2.4 遍历类似数组的对象,可以采用for循环,也可以采用数组的forEach方法。
七、函数
1. 函数声明
- 1.1 function命令声明的代码区块,就是一个函数
-用function命令声明函数
function print(s) {
console.log(s);
}
- 1.2 采用变量赋值的写法
var print = function(s) {
console.log(s);
};
- 1.3 Function构造函数
var foo = new Function(
'return "hello world"'
);
// 等同于
function foo() {
return 'hello world';
}
- Function构造函数可以不使用new命令,返回结果完全一样,但不直观,一般不用这种方法。
2. 函数重复声明
- 后面的声明就会覆盖前面的声明
3.不能在条件语句中声明函数
- 函数的toString方法返回函数的源码,内部的注释也可以返回
- 变量在函数内部定义,是一个局部变量,函数之外无法读取。
- 函数内部定义的变量,会在该作用域内覆盖同名全局变量。
4.arguments 对象
- arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数。这个对象只有在函数体内部,才可以使用。
- 还可以为参数赋值(严格模式不允许这种用法)
- 判断函数调用时到底带几个参数
function f() {
return arguments.length;
}
5. 闭包(定义在一个函数内部的函数)
- 5.1 JavaScript有两种作用域:全局作用域和函数作用域。函数内部可以直接读取全局变量。
- 在函数外部无法读取函数内部声明的变量,闭包就是将函数内部和函数外部连接起来的一座桥梁。
- 可以看作是函数内部作用域的一个接口。
- 是封装对象的私有属性和私有方法。
- 链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
- 在函数的内部,再定义一个函数得到函数内的局部变量
function f1() {
var n = 999;
function f2() {
console.log(n);
}
return f2;
}
var result = f1();
result(); // 999
//函数f1的返回值就是函数f2,由于f2可以读取f1的内部变量,所以就可以在外部获得f1的内部变量了。
- 上述代码闭包就是函数f2,即能够读取其他函数内部变量的函数。
- 注意:外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。
6. 调用
// 语句
function f() {}
// 表达式
var f = function f() {}
- 行首是function关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。
- 不要让function出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面。
(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();
//最后的分号都是必须的
7. eval
- 将有独自存在的意义字符串当作语句执行
- 没有自己的作用域,都在当前作用域内执行,因此可能会修改当前作用域的变量的值,造成安全问题。
- eval有安全风险。为了防止这种风险规定,如果使用严格模式,eval内部声明的变量,不会影响到外部作用域。
八、运算符
1. 加法运算符
// 加法
1 + 1 // 2
1 + true // 2
// 字符串连接
'1.1' + '1.1' // "1.11.1"
'1' + 1 // "11"
'1' + true // "1true"
//只要有一个运算子是字符串,则两个运算子都转为字符串,执行字符串连接运算。
- 运算子为对象,先自动转成原始类型的值(即先执行该对象的valueOf方法,如果结果还不是原始类型的值,再执行toString方法;如果对象是Date实例,则先执行toString方法)
2. 其他算术运算符(比如减法、除法和乘法)
- 都不会发生重载。它们的规则是:所有运算子一律转为数值,再进行相应的数学运算。
1 - '2' // -1
1 * '2' // 2
1 / '2' // 0.5
3. 余数运算符(%)
- 需要注意的是,运算结果的正负号由第一个运算子的正负号决定。
- 为了得到正确的负数的余数值,需要先使用绝对值函数.
4.对象间的比较
[2] > [11] // true
// 等同于 [2].valueOf().toString() > [11].valueOf().toString()
// 即 '2' > '11'
5. 严格相等运算符
- 5.1 如果两个值的类型不同,直接返回false。
-5.2 类型的原始类型的值(数值、字符串、布尔值)比较时,值相同就返回true,值不同就返回false。 - 5.3 同一类的复合类型值
- 两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个对象。
- 对于两个对象的比较,严格相等运算符比较的是地址,而大于或小于运算符比较的是值。
- 5.4 undefined和null与自身严格相等。
6.相等运算符
- 6.1 原始类型的值
- 原始类型的数据会转换成数值类型再进行比较。
- 6.2 对象与原始类型值比较
- 对象(这里指广义的对象,包括数组和函数)与原始类型的值比较时,对象转化成原始类型的值,再进行比较。
7. 取反运算符
- 形式上是一个感叹号,用于将布尔值变为相反值对于非布尔值的数据,取反运算符会自动将其转为布尔值。
!!x
// 等同于
Boolean(x)
//两次取反就是将一个值转为布尔值的简便写法。
8. 位运算符
- 8.1 位运算符只对整数起作用,如果一个运算子不是整数,会自动转为整数后再执行。虽然在JavaScript内部,数值都是以64位浮点数的形式储存,但是做位运算的时候,是以32位带符号的整数进行运算的,并且返回值也是一个32位带符号的整数。
i = i | 0;
//将i(不管是整数或小数)转为32位整数。
//利用这个特性,可以写出一个函数,将任意数值转为32位整数。
- 8.2 否运算
~ 3 // -4
- 一个数与自身的取反值相加,等于-1。
- 对一个整数连续两次“否运算”,得到它自身。
~~2.9 // 2
~~47.11 // 47
~~1.9999 // 1
//使用否运算取整,是所有取整方法中最快的一种。
- 8.3 异或
- “异或运算”有一个特殊运用,连续对两个数a和b进行三次异或运算,可以互换它们的值(详见维基百科)。这意味着,使用“异或运算”可以在不引入临时变量的前提下,互换两个变量的值。
var a = 10;
var b = 99;
a ^= b, b ^= a, a ^= b;
a // 99
b // 10
- 异或运算也可以用来取整。
12.9 ^ 0 // 12
- 8.4 左移运算
- 如果左移0位,就相当于将该数值转为32位整数,等同于取整,对于正数和负数都有效。
13.5 << 0
// 13
- 8.5 开关作用
var mask = FLAG_A | FLAG_B | FLAG_D;
// 0001 | 0010 | 1000 => 1011
//对ABD三个变量进行“或运算”,得到掩码值为二进制的1011。
flags = flags | mask;
//有了掩码,“或运算”可以确保打开指定的开关。
flags = flags & mask;
//“与运算”可以将当前设置中凡是与开关设置不一样的项,全部关闭。
flags = flags ^ mask;
//“异或运算”可以切换(toggle)当前设置
//即第一次执行可以得到当前设置的相反值,再执行一次又得到原来的值。
flags = ~flags;
//“否运算”可以翻转当前设置,即原设置为0,运算后变为1;原设置为1,运算后变为0。
9. 逗号运算符
'a', 'b' // "b"
//返回后一个表达式的值。
10.优先级
- 从高到低依次为:小于等于(<=)、严格相等(===)、或(||)、三元(?:)、等号(=)。
- 圆括号(())可以用来提高运算的优先级,优先级是最高的(圆括号不是运算符,而是一种语法结构。两种用法:一是把表达式放在圆括号之中,提升运算的优先级;二是跟在函数的后面,作用是调用函数。)
九、数据类型转换
1.强制转换
- 1.1 Number()
- 比parseInt函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为NaN。
valueOf->若原始类型的值->Number
valueOf->若对象->toString->若原始类型的值->Number
valueOf->若对象->toString->若对象->错误
- 比parseInt函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为NaN。
- Number方法的参数是对象时,将返回NaN,除非是包含单个数值的数组
- 1.2 String()
- 数值:转为相应的字符串。
- 字符串:转换后还是原来的值。
- 布尔值:true转为"true",false转为"false"。
- undefined:转为"undefined"。
- null:转为"null"
- 对象
toString->若原始类型的值->String
toString->若对象->valueOf->若原始类型的值->String
toString->若对象->valueOf->若对象->报错
- 1.3 Boolean()
- undefined、null、-0、 0或+0、 NaN、' '(空字符串)
转换结果为false,其他的值全部为true。 - 注意,所有对象(包括空对象)的转换结果都是true,甚至连false对应的布尔对象new Boolean(false)也是true。
- undefined、null、-0、 0或+0、 NaN、' '(空字符串)
2. 自动转换
- 预期什么类型的值,就调用该类型的转换函数。比如,某个位置预期为字符串,就调用String函数进行转换。如果该位置即可以是字符串,也可能是数值,那么默认转为数值。
- 由于自动转换具有不确定性,而且不易除错,建议在预期为布尔值、数值、字符串的地方,全部使用Boolean、Number和String函数进行显式转换。
// 写法一
expression ? true : false
// 写法二
!! expression
有时也用于将一个表达式转为布尔值。它们内部调用的也是Boolean函数。
- 字符串的自动转换,主要发生在加法运算时。当一个值为字符串,另一个值为非字符串,则后者转为字符串。
十、错误处理机制
1. Error对象
- 代码解析或运行时发生错误,JavaScript引擎就会自动产生、并抛出一个Error对象的实例,然后整个程序就中断在发生错误的地方,不再往下执行。
- message:错误提示信息
- name:错误名称(非标准属性)
- stack:错误的堆栈(非标准属性)
if (error.name){
console.log(error.name + ": " + error.message);
}
2. JavaScript的原生错误类型
- 2.1 SyntaxError:是解析代码时发生的语法错误。
- 2.2ReferenceError:是引用一个不存在的变量时发生的错误。
另一种是,将一个值分配给无法分配的对象,比如对函数的运行结果或者this赋值。
console.log() = 1
// ReferenceError: Invalid left-hand side in assignment
this = 1
// ReferenceError: Invalid left-hand side in assignment
- 2.3 RangeError是当一个值超出有效范围时发生的错误。
- 数组长度为负数
- Number对象的方法参数超出范围,以及函数堆栈超过最大值。
- 2.4 TypeError是变量或参数不是预期类型时发生的错误。如对字符串、布尔值、数值等原始类型的值使用new命令,就会抛出这种错误,因为new命令的参数应该是一个构造函数。
- 2.5 URIError是URI相关函数的参数不正确时抛出的错误,主要涉及
- encodeURI()
- decodeURI()
- encodeURIComponent()
- decodeURIComponent()
- escape()
- unescape()
- 2.6 eval函数没有被正确执行时,会抛出EvalError错误。该错误类型已经不再在ES5中出现了,只是为了保证与以前代码兼容,才继续保留。
3. 自定义错误
4. throw语句
- throw语句的作用是中断程序执行,抛出一个意外或错误。它接受一个表达式作为参数,可以抛出各种值。
- throw可以接受各种值作为参数。JavaScript引擎一旦遇到throw语句,就会停止执行后面的语句,并将throw语句的参数值,返回给用户。
5. try…catch结构
- 为了对错误进行处理,需要使用try...catch结构。
- catch代码块捕获错误之后,程序不会中断,会按照正常流程继续执行下去。还可以再抛出错误,甚至使用嵌套的try...catch结构
- catch捕获错误之后,会判断错误类型(EvalError还是RangeError),进行不同的处理。
- try...catch结构允许在最后添加一个finally代码块,表示不管是否出现错误,都必需在最后运行的语句。
6. finally
- 执行finally代码块以后,程序就中断在错误抛出的地方。
- 即使有return语句在前,finally代码块依然会得到执行,且在其执行完毕后,才会显示return语句的值。return语句的执行是排在finally代码之前,只是等finally代码执行完毕后才返回。