本文根据JavaScript标准参考教程-阮一峰 中的语法一节内容所做的笔记, 文中会有代码来自参考文章
数值
数值范围及精度
JavaScript中所有数字都是以64
位浮点数形式存储, 在某些情况下会将64
位浮点数自动转为32
位
JavaScript中采用IEEE754存储
第一个位为符号位, 0为正, 1为负
2-12位位指数部分(11位)
13-64位为小数部分也是有效数字部分(52位)
由于指数部分有11位则JavaScript表示的范围为
(2−1023,21024]
(
2
−
1023
,
2
1024
]
(注意左开区间符, 右闭区间符)
当大于
21024
2
1024
(正向溢出) 返回Infinity
, 小于
2−1023
2
−
1023
(负向溢出)返回0
Number.MAX_VALUE
=1.7976931348623157e+308 表示最大值
Number.MIN_VALUE
=5e-324表示最小值(大多数浏览器)
当数字的整数部分多余21位时, 或小数点后的0大于等于6个时采用科学计数法
由于IEEE的表示法, +0
和-0
的表示也不相同,但是他们等价
区别: 1/+0
返回+Infinity
, 1/-0
返回-Infinity
NaN和Infinity
NaN
的数据类型为Number
, 但是它表示非数值not a number
.
typeof NaN
返回"number"
- 任何涉及
NaN
的运算都返回NaN
NaN
不等于NaN
0/0
位NaN
Infinity
表示无穷, +Infinity
和-Infinity
分别表示正无穷和负无穷
1/0 //+Infinity
1/-0 //-Infinity
//Infinity和NaN比较返回false
0*Infinity // NaN
Infinity
和数字之间的四则运算
5 * Infinity // Infinity
5 - Infinity // -Infinity
Infinity / 5 // Infinity
5 / Infinity // 0
Infinity
和0
0 * Infinity // NaN
0 / Infinity // 0
Infinity / 0 // Infinity
Infinity
之间的运算
Infinity + Infinity // Infinity
Infinity * Infinity // Infinity
Infinity - Infinity // NaN
Infinity / Infinity // NaN
Infinity
和null
的计算可以看成Infinity
和0
计算
null * Infinity // NaN
null / Infinity // 0
Infinity / null // Infinity
Infinity
和undefined
计算全部返回NaN
undefined + Infinity // NaN
undefined - Infinity // NaN
undefined * Infinity // NaN
undefined / Infinity // NaN
Infinity / undefined // NaN
isNaN()
用于判断一个值是否是NaN
, 是则返回true
isNaN(NaN) //true
isNaN(1) //false
当传入非数值时, 看能否先转换成数值, 再判断. 如若传入的时字符串, 则会先转换为NaN
, 再返回true
isNaN('23') //false
isNaN(true) //false
isNaN(0*Infinity) //true, 0*Infinity值为NaN
传入空数组或只有一个数值成员的数组时, 返回false
数值转换
parseInt()
只会返回一个十进制数或者NaN
, 它从头开始解析并尽力转换为数值
parseInt(' 123asf') //123
parseInt('a23afd') //NaN
parseInt('fas') //NaN
parseInt()
可接受第二个参数,表示基数(取值为2-36), 基数为几待转换值(第一个参数)就被视为是几进制
基数若超出返回则自动转为NaN
parseInt('12', 8) //10, 将'12'视为八进制, 转换为十进制的10
parseInt('12', 80) //NaN
基数若是0
, undefined
, null
则忽略基数参数
parseInt('12', 0) //12
parseInt('12', undefined) //12
parseInt('12', null) //12
isFinite()
返回布尔值, 表示某个值是否为正常的数值, 若是则返回true
, 反之返回false
isFinite(2) //true
isFinite(Math.pow(2, 1025)) //false, Math.pow(2, 1025)超过了JavaScript所能表示的范围, 见前文
isFinite()
对+/-Infinity
, NaN
, undefined
返回false
, 其他都返回true
isFinite(Infinity) //false
isFinite(NaN) //false
isFinite(undefined) //false
字符串
字符串是不可变的,字符串中一个换行的两种表示
var s1 = `通过esc下面的键
断行` //注意使用的是键盘esc下面的键(`), 不是单引号
var s2 = "这有\n断行" //使用转义字符\n
转义字符中注意下面几个:
\HHH //HHH为Unicode的码店
\xHH //HH为Unicode的码店
\uHHHH //HHHH为Unicode的码店
'\a' `//会忽略反斜杠
字符串可以视为数组, 可以通过数组属性获取值, 但无法通过数组的属性更改任何值
var s = 'asdf'
console.log(s[0]) //'a'
console.log(s[3]) //'f'
//但是无法更改
s[0] = '3'
console.log(s) //'asdf', 'a'并没有变为'3'
//这对length属性也同样适用
s.length = 5 //先设置length属性值
console.log(s.length) //返回的还是4, 并没有改变
字符集
JavaScript使用Unicode
字符集, JavaScript引擎内部所有字符都使用Unicode
表示
但在解析代码时会将Unicode
转换为字面形式
JavaScript在内部以16位的UTF-16
进行存储字符
Base64转码
Base64
可以将任意值转换为0-9
,a-z
, A-Z
,+
,-
共64个字符(主要为了简化程序)
btoa()
将任意值转为Base64
编码
var base = btoa('abcd')
console.log(base) //"YWJjZA=="
atob()
将Base64
编码转为原来的值
var str = atob(base)
console.log(str) //"abcd"
对象
什么是对象
对象是一组键值对, 是一种无序的复合数据集合.
对象的所有键名是字符串, 通常我们可以省略键名的两边引号, 但是要符合标识符规范
var obj = {
1: 'a',
'2': 'b'
af: 'c' //正确
//23af: 'd' //这样写会报错
}
//obj中的属性1等同于'1', 可以将单引号省略. af也一样
对象使用堆内存存储, 若两个变量指向同一个对象, 则修改一个变量的属性也会映射到另一个对象
var a = {name: 'javascript'}
var b = a //b, a 指向堆内的同一块内存
b.name = 'java'
console.log(a.name) //'java'
JavaScript规定, 如果行首是大括号, 全部解释为代码块(语句).例如: {}
若想解释为对象(表达式), 必须在大括号前加上圆括号.例如: ({})
对象的属性
对象可以通过点运算符和方括号运算符读取属性, 也可以设置属性, 新增属性
方括号运算符在读取属性时括号内的参数必须加引号
var obj = {name: 'javascript'}
obj['name'] //'javascript', 对象中的键名为字符串, 符合标识符规则的可以去掉引号
obj[name] //undefined, 这个name是变量
数值键会自动转换成字符串所以不用加,数值键只能使用方括号
var obj = {1: 'a'}
obj[1] //'a'
obj['1'] //'a'
obj.1 //报错, 数字键不能这样使用
对象通过Object.keys()
方法查看所有属性, 返回一个数组
var myObj = {
name: 'javascript',
age: 20
}
var arr = Object.keys(myObj)
console.log(arr) //['name', 'age']
对象通过delete
命令删除对象的属性(成功返回true
), 删除不存在的属性也返回true
var obj = {name: 'javascript'}
delete obj.name
console.log(obj.name) //undefined
delete
只有在已删除存在的属性且该属性不可删除时返回false
var obj = Object.defineProperty({}, 'p', {
value: 123,
configurable: false
}); //将obj的p属性定义为不可删除
obj.p // 123
delete obj.p // false
delete
只能删除对象本身的属性, 无法删除继承的属性
可以用in
操作符来判断对象是否包含某属性(包括继承属性), 若包含则返回true
var obj = {name: 'javascript', age: 20}
console.log('name' in obj) //true
对象的遍历
用for-in
循环遍历一个对象的可遍历(enumerable)的属性(包括继承的属性)
var obj = {name: 'javascript', age: 20}
for (var key in obj) {
console.log(key+" : " + obj[key])
}
//name : 'javascript'
//age : 20
常和hasOwnProperty()
方法一起使用判断属性是否是对象自身属性
数组
概念
数组是按次序排列的一组
任何类型的数组都可以写入数组
若数组的元素还是数组就构成了多维数组
var a = [1, 2, [3.1, 3.2]]
a[2][0] //3.1
数组是一种特殊的对象(重要), 使用typeof
可以返回"object"
, 其键名从0
开始按次序排列的整数
var a = ["a", 'b']
typeof a //"object"
a[0] //"a"
数组对于键名为数值的属性使用方括号符取值
length属性
数组的length
属性返回数组的成员数量, length
可以通过点操作符和方括号符取值
length
的值位动态值, 等于键名中最大的整数+1(重要), 这意味着length
不一定准确
length
属性可写的(重要). 当length
值小于数组元素个数, 则数组成员也减少
length
=0
, 则清空数组
length
值大于数组元素个数, 则产生空位
var a = ['a', 'b']
a.length //2
a[0] //'a'
a[4] = 4 //产生两个空位, ['a', 'b', , , 4]
a.length //5
a.length = 0
a[0] //undefined
a //返回一个空数组[]
数组添加其他非数值属性时不报错(因为数组也是对象), 但是不改变length
值(应为length
值由键名的整数最大值决定)
var a = ['a', 'b']
a.xxx = 'x'
a //返回['a', 'b', xxx: 'x']
数组的间名超出了数值的表示范围, 则自动转为字符串
数组的检测及遍历
可以用in运算符检测数组是否包含某键名(数组也是对象)
in
对空位返回false
var a = ['a', 'b', , 'c']
0 in a //true, 注意检测的事键名(索引), 不是值
a.map((item, index, a)=>{return index in a}) //[true, true, , true], 因为空位返回false, 所以map函数不会将其添加到数组中
for-in
会遍历数组的所有属性(包括非数字键), 推荐使用for
和while
遍历数组
var a = ['a', 'b']
for (var key in a) {
console.log(a[key]) //'a', 'b'
}
forEach
也可遍历数组
数组的空位
两个逗号之间没有任何值时产生空位, 例如: [1, , 3]
空位不影响length
(length
值由键名的整数最大值决定)
取值时视空位为undefined
delete
删除数组成员时形成空位, 但是也不影响length
值
forEach
, for-in
, Object.keys()
方法忽略空位, 虽然取值时将空位视为undefined
(注意数组元素值为undefined时不会忽略)
空位表示这个数组没有这个元素, undefined
表示数组有这个元素, 但是元素值为undefined
类数组对象
可以通过对象构造类似数组的对象(array-like object)
var obj = {
0:'a',
1;'b',
length: 2
}
obj
是对象不是数组, 无法使用数组其他方法, length
无法自动改变
arguments
对象也是array-like object
, 还有大多数DOM
元素集
函数
函数声明
函数的声明的三种方式
function fun(){}
var f = function(){} //匿名函数, 当此种方法的function后有函数名时只在内部有效
var f = new Function() // 最后一个参数当做函数体
重复声明函数会覆盖前面的声明
函数没有return
时, 不会返回任何值,或者返回undefined
函数声明提升(重要), 采用function
命令声明函数时, 整个函数像变量声明一样被提升到代码头部
add(2, 3)
function add(value1, value2) {
return (value1+value2)
}
不能在非函数的代码块中声明函数(可能无效, 因为函数声明提升, 且JavaScript
中无块级作用域)
函数的属性和方法
函数的name
属性返回函数名
函数的length
属性返回函数定义时参数的个数
toString()
属性返回一个字符串, 内容为函数的源码(包含函数内部的注释)
function add(value1, value2) {
return (value1+value2)
}
add.toString()
/*返回如下
"function add(value1, value2) {
return (value1+value2)
}"
*/
函数的作用域
- 函数内部可以读取全局变量
- 函数内部定义的变量(局部变量)会在该作用域内覆盖同名的全局变量, 且局部变量外部无法读取
- 函数内部存在变量提升
- 函数本身的作用域就是声明时所在的作用域, 与运行时的作用域无关. 函数体内部声明的函数, 作用域绑定函数体内部
函数参数
参数若是原始类型(数值, 字符串等)的值则传递方式是传值传递, 若是复合类型(对象, 数组,函数等)的值则传递方式为传址传递
传址传递时, 若在函数内部替换掉整个参数值(而不是参数的某个属性)则不会影响原始值
同名的参数取最后出现的值
arguments
对象包含了函数运行时的所有参数, 这个对象只可以在函数体内部使用
正常模式下arguments
对象也可以再运行时修改, 严格模式下arguments
是一个只读对象, 修改无效但不报错
arguments.length
属性返回函数运行时的参数个数
function fn(value1, value2) {
console.log(arguments[0])
console.log(arguments.length)
}
fn("a", 3) //"a", 2
闭包
函数内部可以访问外部环境的所有变量, 但是外部环境不可以访问函数内部的变量
函数内部的子函数可以读取内部变量, 因此闭包可以理解”定义在一个函数内部的函数”
function a() {
var x = 1
function b() {
console.log(x) //无法访问函数b环境中的x, 因为没有. 那么向上找到函数a环境中的x
}
}
a() //1
闭包可以看做函数内部作用域的一个接口
闭包可以封装对象的私有属性和私有方法
function
关键字出现在行首, 一律解释成语句
立即调用的函数表达式(IIFE
, Immediately-Invoked Function Expression
)为:
(function(){}());
(function(){})();
eval命令
eval()
将字符串当做语句执行
eval
在当前作用域内执行, 它没有自己的作用域
eval('console.log(2)') //2
严格模式下, eval
内部声明的变量不会影响到外部作用域, 但实际上也是也会影响外部作用域
运算符
加法运算符
两操作数想加时, 若有一个是字符串, 则另一操作数转成字符串再连接在一起
减, 除, 乘法都将字符串自动转为数值再运算
对象相加想调用valueOf()
方法,在调用toString()
方法, 再运算
若操作数中有一个是Date
对象的实例, 那么会优先执行toString()
方法
算术运算符
+
,-
, *
, /
, **
, %
, ++
, --
, 正+
, 负-
余数运算符%
的运算结果正负号由第一个操作数决定
余数运算可以用于浮点数的计算, 但不准确
++
,--
为一元运算符
正+
, 负-
运算符也是一元运算符, 它们可以将任何值转为数值, +
的作用和Number()
函数相同, -
转为+
的相反数
赋值运算符
=
在数学中念作”等于”, 但是在计算机中时赋值符号. 表示将一个数赋值给一个变量
var a = 3 //将3赋值给变量a
if (b = 3) {
console.log(2) //永远会打印出2
}
if
括号里应该时布尔表达式, 但是b=3
是赋值, 而不是布尔表达式, 应该表示为b === 3
比较运算符
<
, >
, >=
, <=
, ==
, ===
字符串之间的比较, JavaScript
将依次比较单个字符的Unicode
码点, 谁的先大则谁就大
对于除字符串之间的原始类型的值, 除==
和===
外, 其余运算符将操作数先转为数值再比较
NaN
与任何值(包括NaN
)比较都返回false
, 因为NaN
和任何值(包括NaN
)都不想等
对于对象, 则会转为原始类型再比较(先调用valueOf
, 若返回对象再调用toString
)
==
将两个操作数转为同一类型再比较, ===
不对操作数转换. ==
中两值相等的条件是可以转换为同值, ===
中两值相等的条件是同类型同值
对于复合类型的值,比较它们是否指向同一内存地址
两个对象的比较, ===
比较的是地址, >
,<
比较的是值
var a = {age: 23}
var b = a
a.age === b.age //true
a.age > b.age //false
a.age < b.age //false
undefined
===null
返回true
!==
先求===
的运算结果, 再取反
==
中原始类型的值会转换为数值再比较
==
中复合类型和原始类型的值比较, 复合类型会转为原始类型再比较
==
中undefined和null不与除这两个值之外的任何值相等
!=
与==
的运算结果相反
布尔运算符
!
, &&
, ||
, ?:
!
对undefined
, null
, false
, 0
, NaN
, ""
(空字符串)返回true
, 其他返回false
!!
等同于Boolean()
函数
&&
, ||
是短路运算符
&&
(逻辑与) 在全为true
的情况下返回true
, 只要有false
就返回false
||
(逻辑或) 在全为false
的情况下返回false
, 只要有true
就返回true
位运算符
|
, &
, ~
, ^
, <<
, >>
, >>>
位运算符用来处理两个比特位(1
,0
)
|
(按位或), 全0
为0
, 有1
为1
&
(按位与), 全1
为1
, 有0
为0
~
(取反, 否), 1
取0
, 0
取1
, 因为是二进制位则可以简记为: 数的相反数减1
^
(异或), 不同为1
, 相同为0
, 连续对两个变量进行三次^运算会交换两个变量的值(a^=b
, b^=a
, a^=b
)
所有位运算符只对整数有效, 遇到小数时将小数部分直接舍去(不四舍五入), 只保留整数部分(则对某小数进行两次~运算得到整数)
对其他非数值进行~运算, JavaScript
会先调用Number()
函数在运算
<<
(左移), 将二进制数向左移动指定位(符号位不移动), 尾部补0
(左移相当于乘以2
的幂次方)
>>
(右移), 将二进制数向右移动指定位(符号位不移动), 头部补0
(右移相当于除以2
的幂次方)
>>>
(带符号的右移), 将一个二进制数向右移动(包括符号位), 头部补0
(正数相当于>>
, 负数则不是)
数据类型转换
强制转换
有三种强制类型转换函数: Number()
, String()
, Boolean()
Number()
Number()
方法的参数是原始类型的值时参数为数值则不变- 若是字符串,如果可以解析位数值则解析, 不能则为
NaN
- 空字符串
""
转为0
true
转为1
,false
转为0
undefined
转为NaN
null
转为0
Number()
方法的参数是对象时, 返回NaN
, 除非是包含单个数值的数组, 具体如下
- 调用
valueOf
, 若返回原始类型值则对该值调用Number()
- 若
valueOf
返回对象, 则改为调用toString()
方法 - 若
toString()
方法返回原始类型值, 则调用Number()
- 若
toString()
方法返回对象, 则报错
- 调用
String()
String()
可以将任意类型的值转为字符串
- 数值,转为相应的字符串
- 字符串, 不变
true
转为"true"
, false转为"false"
undefined
转为"undefined"
null
转为"null"
- 对象, 返回字符串
”[object Object]"
. 若为数组, 则返回数组的字符串形式
- 调用
toString()
方法, 若返回原始类型值则对该值使用String()
- 若返回对象, 则调用
valueOf()
方法 - 若
valueOf()
方法返回原始类型值, 则对该值返回String()
函数 - 若调用
valueOf()
方法返回对象, 则报错
- 调用
Boolean()
Boolean()
可以将false
, undefined
, null
,0
, NaN
, ""
(空字符串)转为false
, 其余转为true
自动转换
123+'abc'
返回‘123abc'
if('abc') 'abc'
隐式转为true
+{a:b}
返回NaN
-[1,2]
返回NaN
自动转换为布尔值:JavaScript遇到预期为布尔值的地方会将非布尔值自动转为布尔值, 内部调用Boolean()
自动转换为字符串:JavaScript遇到预期为字符串的地方会将非布尔值自动转为字符串
先将复合类型值转为原始类型, 再转为字符串(调用String()
)
自动转换为数值:JavaScript遇到预期为数值的地方会将非布尔值自动转为数值, 内部调用Number()
注意:Number(null)
为0
, Number(undefined)
为NaN
错误处理机制
Error实例对象
ES标准中Error
实例对象必须有message
属性,表示出错时的提示信息
(非ES标准属性)大多数JavaScript引擎对Error
实例提供name
和stack
属性, 分别表示错误的名称和错误的堆栈
原生错误类型
JavaScript定义了6中错误对象:SyntaxError
, ReferenceError
, RangeError
, TypeError
, URIError
,EvalError
SyntaxError
: 语法错误
ReferenceError
:引用一个不存在的变量时发生的错误
RangeError
:一个值超出了有效范围时发生的错误
主要有:数组长度为负数, Number
对象的参数超出范围, 函数堆栈超过最大值
TypeError
:变量或参数不是预期类型时发生的错误
如:字符串, 布尔值等使用new
命令
URIError
:URI
相关函数的参数不正确时抛出的错误
主要涉及encodeURI()
、decodeURI()
、encodeURIComponent()
、decodeURIComponent()
、escape()
和unescape()
这六个函数
EvalError: eval
函数没有被正确执行时发生的错误(已不再使用, 保留是为了与遗留代码兼容)
处理错误
try...catch...finally
语句可以用来处理错误
当try
代码块中出现错误时停止执行后面的代码, catch
代码块会捕获错误, 我们通常在catch
中处理捕获的错误, finally用来处理最后的动作, finally
代码块中的代码不管有没有错都会执行。