我不知道的JavaScript - 类型

198 阅读3分钟

这是一篇读书笔记
出自《你不知道的JavaScript中卷》

一:类型

1、七中内置类型
Number、String、Boolean、Object、Undefined、Null、Symbol
除对象外,其他统称为“基本类型”
2、typeof运算符

typeof 1 // "number"
typeof '1' // "string"
typeof true // "boolean"
typeof {} // "object"
typeof undefined // "undefined"
typeof Symbol() // "symbol"
typeof function(){} // "function"

typeof null // "object"
typeof [] // "object"

判断null

var a = null
!a && typeof a === 'object'

typeof运算符总会返回一个字符串

typeof typeof 42 // string

3、undefined和undeclared
undefined: 声明未赋值
undeclared: 未声明

var a;
a; // undefined
b; // ReferenceError: b is not defined

二:值

1、数组

  • 可以容纳任何类型的值;不需要预先设定大小。
  • 使用delete可以删除数组中的元素,但是length属性不变:
var a = [1,2,3]
delete a[2] // [1, 2, empty]
a.length // 3
  • 可以包含字符串键值和属性,但这些不计算在数组长度内
var arr = [1]
arr['name'] = 'ming'
arr.length // 1
arr // [1, name: "ming"]
  • 如果字符串的键值能够被强制转换成十进制数字的话,会被当做数字索引:
arr['10']=3
arr.length // 11

2、类数组
2.1 类数组的定义:

  • 拥有length属性,索引为非负整数;
  • 不具有数组所具有的方法;

2.2 常见的类数组有
arguments对象和 DOM方法的返回结果(document.getElementsByTagName())。

2.3 将类数组转为数组:
1)通过数组工具函数 Array.prototype.slice.call(arguments)
2)Array.from(arguments)

3、字符串

字符串是不可变的,创建并返回一个新的字符串;
数组是在原始值上进行操作。

var a = 'foo'
var b = ['f', 'o', 'o']

d = a.toUpperCase()
a === d // false

b.push('!')
b // ["f", "o", "o", "!"]

4、数字

  • 特别大和特别小的数字默认用指数格式显示
var a = 5E10
a // 50000000000

var b = 1/a
b // 2e-11
  • .运算符会被优先识别为数字字面量的一部分
42.toFixed(2) // SyntaxError 视为42.的一部分
(42).toFixed(2) // "42.00"
0.42.toFixed(2) // "0.42"
42..toFixed(2) // "42.00" 第一个.是42.的一部分;第二个.属性访问运算符
  • 进制
0b // 2进制
0o // 8进制
0x // 16进制
  • 较小的数值 -- 不精确
0.1 + 0.2 === 0.3 // false
  • 怎么判断0.1 + 0.2 === 0.3?

设置一个误差范围-机器精度:

if(!Number.EPSILON){
  Number.EPSILON = Math.pow(2,-52)
}
function compareEqual(a,b){
  return Math.abs(1-b) < Number.EPSILON
}
var a = 0.1 + 0.2
var b = 0.3
compareEqual(a,b) // true
compareEqual(0.0000001,0.0000002) // false
  • 整数的安全范围:Number.MIN_SAFE_INTEGER ~ Number.MAX_SAFE_INTEGER。

整数检测、安全整数检测:

Number.isInteger(42) // true
Number.isSafeInteger(Math.pow(2, 53)) // false
Number.isSafeInteger(Math.pow(2, 53) - 1) // true
  • 不是数字的数字NaN:
var a = 1/'a'
a // NaN
typeof a // "number"

NaN == NaN // false 跟自身不相等
isNaN(a) // true

if(!Number.isNaN){ // 唯一一个不等于自身的值
  Number.isNaN = function(n){
    return n !== n
  }
}
  • 零值
var a = 0/-3 // -0

为什么需要-0呢?数字的符号代表其他信息:移动的方向

  • 特殊等式
var a = 1/'name'
var b = -3 * 0

Object.is(a, NaN) // true
Object.is(b, -0) // true
Object.is(b, 0) // false

6、值和引用

简单值-基本类型值:通过值复制的方式来赋值/传递(null、undefined、字符串、数字、布尔值、symbol);

复合值-对象(数组和封装对象)和函数:通过引用复制的方式来赋值/传递。

三:原生函数

1、常见原生函数

String()
Number()
Boolean()
Array()
Object()
Function()
RegExp()
Date()
Error()
Symbol()

封装了基本类型的封装对象:

var a = new String('abc')
typeof a // "object"
a instanceof String // true
Object.prototype.toString.call(a) // "[object String]"

2、封装对象包装

JS会自动为基本类型值包装一个封装对象

var a = 'abc'
a.length // 3
a.toUpperCase() // "ABC"

3、拆封

想要得到封装对象的基本类型值,可以使用valueOf()函数

var a = new String('a')
var b = new Number(1)
var c = new Boolean(true)

a.valueOf() // "a"
b.valueOf() // 1
c.valueOf() // true

Array构造函数只有一个参数时,会被作为数组的预设长度:

var arr = Array(10)
arr // (10) [empty × 10]

-- 至少包含一个空单元的数组称为稀松数组。

4、Symbol

符号-Symbol是具有唯一性的特殊值,用它命名对象属性不容易导致重名。

5、原生原型

String.prototype.XYZ可以简写为String#XYZ。

String#indexOf(...) 查找字符串中指定字符串的位置。
String#charAt(...) 获得字符串指定位置上的字符。
String#substr(...)/String#substring(...)/String#slice(...) 获得字符串的指定部分。

以上方法不改变原字符串的值,返回新的字符串。

四:强制类型转换

1、值类型转换
类型转换:将值从一种类型转换为另一种类型。

var a = 42
var b = a + '' // 隐式
var c = String(a) // 显式

2、抽象值操作
2.1 toString:会调用Object.prototype.toString()方法

var a = 1.1 * 1000* 1000* 1000* 1000* 1000* 1000* 1000
a.toString() // "1.1e+21"

数组的toString()方法进行了重新定义,先用,连接

var a = [1,2,3]
a.toString() // "1,2,3"

JSON.stringify()在对象中遇到undefined、function、symbol会忽略;
对包含循环引用的对象执行JSON.stringify()会报错。

JSON.stringify()可选参数replacer:

var a = {b: 42, c: "42", d: [1,2,3]}
JSON.stringify(a, ['b', 'c']) // "{"b":42,"c":"42"}" ,除'b'\'c'属性外忽略
JSON.stringify(a, function(k,v){if(k!=='c') return v}) // "{"b":42,"d":[1,2,3]}" 对对象本身调用一次

JSON.stringify()另一个可选参数space,指定输出的缩进格式:

JSON.stringify(a, null, 3)
"{
   "b": 42,
   "c": "42",
   "d": [
      1,
      2,
      3
   ]
}"
JSON.stringify(a, null, '---')
"{
---"b": 42,
---"c": "42",
---"d": [
------1,
------2,
------3
---]
}"

2.2 toNumber

Number(true) // 1
Number(false) // 0
Number(undefined) // NaN
Number(null) // 0

对象(包含数组)首先会被转换成响应的基本类型;如果返回的是非数字,再将其强制转换为数字。
先检查valueof()方法;再检查toString()方法,如果均不返回基本类型,则报错。

var a = {valueOf: function(){return '42'}}
var b = {toString: function(){return '42'}}
var c = [4,2]
c.toString = function(){return this.join("")}
Number(a) // 42
Number(b) // 42
Number(c) // 42
Number('') // 0
Number([]) // 0
Number(['abc']) // NaN
Number(['42']) // 42

2.3 toBoolean
假值

Boolean(undefined) // false
Boolean(null) // false
Boolean(false) // false
Boolean('') // false
Boolean(0) // false
Boolean(NaN) // false

假值列表以外的都是真值。
假值对象:

var a = new Boolean(false)
var b = new Number(0)
var c = new String('')
var d = Boolean(a && b && c) // true

3、显示强制类型转换
字符串和数字间的转换:

var a = 2
String(a) // "2"
var b = '1'
Number(b) // 1
var c = +b
c // 1 一元运算符

~运算符:~X 即 -(X+1)

var str = 'abcd'
~str.indexOf('bc') // -2
~str.indexOf('cb') // 0

显式解析数字字符串

var a = '22px'
parseInt(a) // 22
Number(a) // NaN

解析允许字符串中有非数字字符;
转换不允许,否则会失败并返回NaN。
parseInt(string, radix) 解析一个字符串并返回指定基数的十进制整数, radix 是2-36之间的整数,表示被解析字符串的基数。

parseInt(11, 2) // 3

解析非字符串

parseInt(1/0, 19) // 18
// 即:
parseInt(Infinity, 19) // 18

显式转换为布尔值

Boolean('0') // true 显式
Boolean('') // false 显式
!!'0' // true 显式
!!'' // true 显式
if('0'){...} // if语句中
var a = 42? true: false // 三元运算符

4、隐式强制类型转换
• 作用是为了减少冗余,让代码更简洁
• 字符串与数字之间的隐式强制类型转换

var a = 1
var b = '1'
var c = a + b
c // "11"
var a = [1,2]
var b = [3,4]
var c = a + b // '1,2' + '3,4' => 1,23,4
c // "1,23,4"

一个常见的坑:

[]+{} // "[object Object]" => '' + "[object Object]"
[]+[] // "" => '' + ''
{}+[] // 0 => 代码块 +'' => 代码块 0
{}+{} // "[object Object][object Object]"

其他: - * /

var a = '1'
a-0 // 1
var a = '2'
a*1 // 2
var a = '3'
a/1 // 3

• 布尔值到数字的强制类型转换

// 判断条件a/b/c/d/e只有一个为真时
function(a,b,c,d,e){
  var sum = 0
  // 将条件转为数字,true=>1; false=>0,如果sum为1则符合条件
  return sum===1
}

• 转换为布尔值

if(...) // 语句
for(..., ..., ...) // 第二个判断条件
while(...) 和 do...while(...) // 表达式
? ... : ... // 三元运算符
|| 和 && // 逻辑或、逻辑与

• 选择器运算符 || &&:返回的是两个操作符中的一个

var a = 22
var b = null
a || b // 22
a && b // null

5、宽松相等和严格相等
• '==' 和 '===' 相比:允许在比较中进行强制类型转换
• 字符串和数字之间的比较

var a = 2
var b = '2'
a == b // true 将string转为number

• 其他类型和布尔类型之间的比较

var a = '2'
var b = true
a == b // false, 因为b会转为1。如果是布尔类型,则ToBumber(布尔)

• null和undefined之间的相等比较

null == undefined

• 对象和非对象之间的相等比较

var a = 2
var b = [2]
a == b // true 对象会调用ToPrimitive(...)
• 比较少见的情况
[] == ![] // true  (![]=>false) => ([] == false) => true
0 == '\n' // true
0 == ' ' // true 会被转成0