1.内置类型

1.1 JavaScript有7种内置类型

序号 类型 含义 typeof返回值
1 null 空值 object
2 undefined 未定义 undefined
3 boolean 布尔值 boolean
4 number 数字 number
5 string 字符串 string
6 object 对象 object
7 symbol 符号 symbol

1.2 注意点

1.2.1 null值检测

由于null值返回object,需要通过复合条件检测null值

  1. var a = null
  2. if(!a && typeof a === 'object') {
  3. console.log('a is null')
  4. }

1.2.2 typeof function(){}

对函数使用typeof运算符会返回’function’

1.2.3 typeof的安全防范机制及应用

对于undeclared(或者not defined)变量,typeof照样返回undefined,虽然变量未声明,但typeof并没有报错,这是因为typeof有一个特殊的安全防范级制。
该安全防范机制对在浏览器中运行的JavaScript代码很有帮助,因为多个脚本文件会在共享的全局命名空间中加载变量,通过typeof的安全防范级制(阻止报错)来检查哪些是未声明的变量。

2. 值

数组,字符串,数字

2.1 数组

2.1.1 delete

使用delete运算符可以将单元从数组中删除,但是单元删除后,数组的length属性不会改变

2.1.2 数组索引

数组通过数字进行索引,但他们也是对象,所以也可以包含字符串键值和属性,如果字符串键值能被强制类型转换为十进制数字的话,它就被当作数字索引来处理。

2.1.3 类数组转换为数组

详情参考数组和类数组及arguments

2.1.4 数组检测

  • Array.isArray 推荐用法
  • instanceof Array 如果存在多个全局环境(多个框架),从而存在多个不同版本的Array构造函数,检测会不准确
  • Object.prototype.toString.call 判断如果返回值是[object Array],说明是数组

2.1.5 数组API

修改原数组的API有:

splice/reverse/fill/copyWithin/sort/push/pop/unshift/shift

不修改原数组的API有:

slice/map/forEach/every/filter/reduce/entries/find

2.1.6 转换方法

  • toString():返回由数组中每个值的字符串形式拼接而成的以逗号分隔的字符串。
  • valueOf():返回数组本身
  • join():使用传入的分隔符构建字符串,如果不给join方法传入任何值,或者给它传入undefined,则使用逗号作为分隔符

2.1.7 栈方法

栈是一种LIFO(last-in-first-out)的数据结构,push和pop可以模拟类似栈的行为。

  1. push():接受任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。
  2. pop():从数组末尾移除最后一项,然后返回移除的项

2.1.8 队列方法

队列是一种先进FIFO(先进先出)的数据结构,结合使用shift和push可以模拟队列的行为,另外同时使用unshift和pop可以从相反的方向来模拟队列。
方向从右向左

  1. shift():移除数组的第一项
  2. push():接受任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。

方向从左向右

  1. unshift():数组前端添加任意项并返回数组的长度。
  2. pop():从数组末尾移除最后一项,然后返回移除的项

2.1.9 操作方法

定义 参数 说明
操作方法
concat(value1[, value2[, …[, valueN]]]) 数组和/或值,将被合并到一个新的数组中。如果省略了所有 valueN 参数,则 concat 会返回调用此方法的现存数组的一个浅拷贝 基于当前数组的所有项创建一个新数组,不会更改现有数组。如果传递的参数是一个数组,则会将这些数组中的每一项都添加到结果数组中,如果传递的不是数组,这些值会被简单的添加到结果数组末尾
slice([begin[, end]]) begin:如果是负数,则从倒数第几个元素开始取,如果大于数组length,则返回空数组。
end:如果为负数,则它表示在原数组中的倒数第几个元素结束抽取,如果被省略,取到数组结尾,如果大于数组长度,也取到数组结尾
slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。
splice(start[, deleteCount[, item1[, item2[, …]]]]) start:指定修改的开始位置,如果超出数组长度,从末尾开始添加元素,如果是负值,则表示从数组末位开始的第几位,如果负数的绝对值大于数组长度,则开始位置为0
item1, item2, … 可选要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。
位置方法
indexOf(searchElement[, fromIndex]) searchElement:要查找的项
fromIndex:开始查找的位置
lastIndexOf(searchElement[, fromIndex]) (searchElement[, fromIndex])
迭代方法
map 返回每次函数调用的结果组成的数组
fliter 对数组每一项运行给定函数,返回该函数返回true的项构成的数组
forEach 对数组中每一项运行给定函数,无返回值
every 对数组中的每一项运行给定函数,如果都返回true则返回true
some 对数组中的每一项运行给定函数,如果函数对任一项返回true,则返回true
并归方法
reduce 从左向右遍历
reduceRight 从右向左遍历

注:reduce方法回调函数第一次执行时,accumulatorcurrentValue的取值有两种情况:如果调用reduce()时提供了initialValueaccumulator取值为initialValuecurrentValue取数组中的第一个值;如果没有提供 initialValue,那么accumulator取数组中的第一个值,currentValue取数组中的第二个值。

要累加对象数组中包含的值,必须提供初始值,以便各个item正确通过你的函数。如下所示:

  1. let initialValue = 0
  2. let sum = [{ x: 1 }, { x: 2 }, { x: 3 }].reduce((acc,curr,index,arr) => { return acc + curr.x }, initialValue)

2.2 字符串

2.2.1 字符串转义序列

字面量 含义
\n 换行
\t 制表
\b 退格
\r 回车
\f 进制
\\ 斜杠
\‘ 单引号,在用单引号表示的字符串中使用
\“ 双引号,在用双引号表示的字符串中使用
\xnn 以16进制代码nn表示的一个字符(其中n为0~F)
\unnn 以16进制代码nnnn表示的一个Unicode字符(其中n为0~F)

2.2.2 字符串的特点

ECMAScript中的字符串是不可变的,字符串一旦创建,它们的值就不能改变,要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新知的字符串填充该变量。

2.2.3 通过数组函数处理字符串

需要数组函数处理字符串很方便,可以通过call apply间接调用,示例如下:

  1. var a = 'foo'
  2. var b = Array.prototype.join.call(a,'-')
  3. var c = Array.prototype.map.call(a, ch => ch.toUpperCase() + ".")

注意:字符串反转不能直接使用Array.prototype.reverse.call(),而应该使用变通的方式,如下所示:

  1. var a = "abcdef"
  2. var b = a.split('').reverse().join('')

2.2.4 字符串方法

定义 参数 说明
字符方法
charAt(index) index一个介于0 和字符串长度减1之间的整数。 (0~length-1)如果没有提供索引,charAt() 将使用0。 已单字符字符串形式返回给定位置的字符
charCodeAt(index) index一个介于0 和字符串长度减1之间的整数。 (0~length-1)如果没有提供索引,charAt() 将使用0。 已字符编码形式返回给定位置的字符
字符串操作
concat()
slice()
substring()
substr()
trim() 创建一个字符串副本,删除前置及后缀的所有空格
String.fromCharCode() 接受一个或多个字符编码,将其转换为一个字符串
位置方法
indexOf(element[,startIndex])
lastIndexOf(element[,startIndex])
大小写转换
toUpperCase() toLocalUpperCase()
toLowerCase() toLocalLowerCase()

利用indexOf匹配所有子字符串位置示例代码:

  1. var stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"
  2. var positions = []
  3. var pos = stringValue.indexOf("e")
  4. while(pos > -1){
  5. positions.push(pos)
  6. pos = stringValue.indexOf("e",pos+1)
  7. }
  8. return positions

2.3 数字

JavaScript中的数字类型是基于IEEE 754标准来实现的,该标准也被称为浮点数,JavaScript使用的是”双精度”格式(即64位二进制)

2.3.1 数字的语法

toFixed():指定小数部分的显示位数,如果指定的小数部分的显示位数多于实际位数就用0补齐。
toPrecision():指定有效数位的显示位数。包括整数部分。
注意:42.toFixed(3)是无效语法,因为.被视为常量42.的一部分,可以使用(42).toFixed(3)
_

2.3.2 二进制浮点数误差

二进制浮点数最大的问题是会出现如下情况:

  1. 0.1 + 0.2 === 0.3 //false

二进制浮点数中的0.1和0.2并不是十分精确,它们相加的结果并非刚好等于0.3而是一个比较接近的数字,所以判断条件为false。最常见的方法是设置一个误差范围值,通常称为”机器精度”,对于JavaScript来说这个值通常是 2^-52(Math.pow(2,-52)),该值定义在Number.EPSILON中。

  1. function numberCloseEnoughToEqual(n1,n2) {
  2. return Math.abs(n1 - n2) < Number.EPSILON
  3. }

2.3.3 整数的安全范围及最大 最小浮点数

最大浮点数:Number.MAX_VALUE
最小浮点数:Number.MIN_VALUE
能够被安全呈现的最大整数: Number.MAX_SAFE_INTERGER (2^53 - 1)
能够被安全呈现的最小整数:Number.MIN_SAFE_INTEGER
超出JavaScript数值范围的值,会被自动转换成特殊的Infinity值,如果这个值是负数则会转换为-Infinity,确定一个值是不是有穷可以使用isFinite()函数

2.3.4 整数检测

检测一个值是否是整数,使用ES6的Number.isInteger()方法。

  1. Number.isInteger('123') //false
  2. Number.isInteger(123) //true
  3. Number.isInteger(123.3) //false
  4. Number.isInteger(-0) //true

针对ES6之前版本的polyfill方法

  1. if(!Number.isInteger) {
  2. Number.isInteger = function(num) {
  3. return typeof num === 'number' && num % 1 === 0
  4. }
  5. }

检测一个值是否是安全的整数,使用ES6的Number.isSafeInteger()方法.

  1. Number.isSafeInteger(Number.MAX_SAFE_INTEGER) //true
  2. Number.isSafeInteger(Math.pow(2,53)) //false
  3. Number.isSafeInteger(Math.pow(2,53) - 1) //true

针对ES6之前版本的polyfill代码

  1. if(!Number.isSafeInteger) {
  2. Number.isSafeInteger = function(num) {
  3. return Number.isInteger(num) &&
  4. Math.abs(num) <= Math.pow(2,53) -1
  5. }
  6. }

2.3.5 NaN

数学运算的操作数不是数字类型,就无法返回一个有效的数字,这种情况下返回值为NaN.意指”不是一个数字”(not a number)。其实更准确的意思应该是”无效数值” “失败数值”。任何涉及NaN的操作都会返回NaN,NaN仍然是数字类型。

  1. typeof NaN === 'number' //true

检测变量的值是否为NaN,不能和NaN直接比较判断,NaN是一个特殊值,它和自身不相等,是唯一一个非自反的值(NaN != NaN 为true).标准的检测方法为Number.isNaN(),针对ES6之前版本的polyfill代码如下:

  1. //版本1
  2. if(!Number.isNaN) {
  3. Number.isNaN = function(num) {
  4. return typeof num === 'number' &&
  5. window.isNaN(num)
  6. }
  7. }
  8. //版本2 利用NaN非自反的特点直接判断传入参数是否和自身相等
  9. if(!Number.isNaN) {
  10. NUmber.isNaN = function(num) {
  11. return num !== num
  12. }
  13. }

2.3.6 零值

JavaScript有一个常规的0和一个-0.

  1. var a = 0/-3 //-0
  2. var b = 0*-3 //-0
  3. //////////////////////////////
  4. var c = 0
  5. a == c //true
  6. -0 == 0 //true
  7. a === c //true
  8. -0 === 0 //true

要区分-0和0,需要做一些特殊逻辑处理,如下代码所示:

  1. function isNegZero(n) {
  2. n = Number(n)
  3. return (n === 0) && (1 / n === -Infinity)
  4. }

2.3.7 特殊等式

针对以上特殊情况的等式判断,ES6新加入了一个工具方法。Object.is(),来判断两个值是否绝对相等
注意:能够使用 ==和===时尽量不要使用Object.is(),因为前者的效率更高,Object.is()主要用于处理特殊情况

  1. var a = 2 / 'foo'
  2. var b = -3 * 0
  3. Object.is(a,NaN) //true
  4. Object.is(b,-0) //true
  5. Object.is(b,0) //false

3. 复制变量值

3.1 基本类型

如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上,这两个值可以参与任何操作而不会互相影响。

3.2 引用类型

当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到新变量分配的空间,不同的是,这个值实际上是一个指针,这个指针指向存储在堆中的一个对象,复制操作结束后,两个变量实际将引用同一个对象。

4. 检测类型

typeof运算符常用于检测基本类型值,检测引用类型值时,通常会使用instanceof操作符