类型与值
原始类型
null
空值- 表示一个空对象指针
- 定义将来要保存对象值的变量时,建议使用null来初始化
- undefined是由null派生而来的,
undefined == null
undefined
未定义- 一般声明变量的同时进行初始化,这样使用typeof时可以知道给定的变量尚未声明
boolean
布尔值number
数值string
字符串symbol
符号(ES6+新增)bigint
大数(ES6+新增)
使用typeof
- 可能的值
undefined
(未声明或未定义)、boolean
、string
、number
、object
(对象或null)、function
、symbol
- 作用
- 检测值类型
- 判断变量是否存在
typeof
返回值的类型是string
- 检查
null
!a && typeof a === "object"
- 在 javascript 最初的实现中,javascript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于
null
代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null
也因此返回 "object"
- 安全防范机制
- 对于没有声明的变量,
typeof
该变量,返回undefined
,而不会报ReferenceError
错误。
- 对于没有声明的变量,
类型转化
三种情况:
- 转化为布尔值
- 除了
undefined
、null
、false
、NaN
、-0
、0
、''
以外,其他都返回true
,包括所有对象。
- 除了
- 转化为数字
- 转化为字符串
对象转原始类型
- 调用内置的
Symbol.toPrimitive
函数,如果存在的话,调用优先级最高- 该函数接受一个
hint
参数,表示要转换到的原始值的预期类型 - 取值是
number
、string
和default
中的任意一个。
- 该函数接受一个
- 否则,如果 hint 是
string
—— 尝试obj.toString()
和obj.valueOf()
,无论哪个存在。 - 否则,如果 hint 是
number
或default
—— 尝试obj.valueOf()
和obj.toString()
,无论哪个存在。
在实践中,为了便于进行日志记录或调试
,对于应该返回一种“可读性好”的对象的表示形式的转换,只需要实现 obj.toString()
作为字符串转换的“全能”方法就足够了。
四则运算符
- 加法运算符
- 运算中其中一方为字符串,那么就会把另一方也转换为字符串
1 + '1' => '11'
- 如果一方不是字符串或者数字,那么会将它转换为数字或者字符串
true + true => 2
4 + [1,2,3] => '41,2,3'
'a' + +'b' => 'aNaN'
:+'b'
为NaN
,如果是+ '1'
,则为数字1
- 运算中其中一方为字符串,那么就会把另一方也转换为字符串
- 其他运算符
- 能正常转化为数字的就可以运算
比较运算符
- 如果是对象,就通过toPrimitive转换对象
- 如果是字符串,就通过unicode字符索引来比较
let a = {
valueOf() {
return 0
},
toString() {
return '1'
}
}
a > -1 // true,通过 valueOf 转换为原始类 型再比较值
== VS ===
对于 ==
来说,如果对比双方的类型不一样的话,就会进行类型转换
- 判断类型是否相等,相等则判断大小
- 类型不相等,先判断是不是比对
null
和undefined
,是的话返回true
- 判断两者类型是不是
string
或者number
,是的话将string
转化为number
- 判断是否有一方是
boolean
,是的话将boolean
转化为number
- 判断有一方是
NaN
,则相等操作符==
返回false
,注意:NaN == NaN
也为false
- 判断其中一方是否为
object
且另一方为string
、number
或者symbol
,是的话就会把object 转为原始类型
再进行判断
分析
[] == ![]
- 首先,
!
的优先级比==
的高,所以![]
会被转化为false
=>[] == false
- 接着,当有一边是
boolean
时,将boolean
转化为number
=>[] == 0
- 接着,当有一边是
object
和另一边是number
时,object
转化为原始类型,[]
调用valueOf
返回自身,不可行,调用toString
,转化为字符串''
=>'' == 0
- 接着,
string
转化为number
=>0 == 0
,得到答案true
分析
{} == !{}
- 与上述同理
- 不同的点在于,如果有一个操作数是NaN,则相等操作符返回 false
{} == ! {}
=>{} == false
=>{} == 0
=>NaN == 0
=>false
对于===
来说,则不会进行转化,当类型和值都相等时,返回true
,反之为false
数字类型
Number类型使用IEEE 754格式表示整数和浮点值(双精度值)。
不同的数值类型有相应的数值字面量类型。
- 八进制:第一个数字必须是0,当数字超出范围时,会去掉前置0,后面的数当成十进制处理。在严格模式下无效。
- 十六进制:0x开头,后面是0-9和a-f,不区分大小写
- 由于javascript保存数值的方式,存在-0和+0,在所有情况下是等同的
浮点数
数值中必须有小数点,小数点后至少1位,前面可以没有数字但不推荐。
存储浮点值使用的内存空间是存储整数值的两倍,所以ECMAScript总是想方设法把值转换为整数。1.
和10.0
都会被认为是整数。
科学记数法:一个数值(整数或浮点数)后跟一个大写或小写的字母e,再加上一个要乘的10的多少次幂。
浮点值的精确度最高可达17位小数,但在算术计算中远不如整数精确。二进制浮点数的问题:
0.1 + 0.2 === 0.3
- 实际上0.1和0.2并不精确,得到的实际结果是0.3000...04,所以结果为
false
- 正确判断是否相等(在误差范围内则认为相等)
function numbersCloseEnoughToEqual(n1, n2){
return Math.abs( n1 - n2 ) < Number.EPSILON;
}
32位带符号整数
a | 0
可以将变量a中的数值转化为32位带符号的整数
值的范围
- 机器精度:
Number.EPSILON
2^-52 - 最大浮点数:
Number.MAX_VALUE
1.798e+308 - 最小浮点数:
Number.MIN_VALUE
5e-324,不是负数,无限接近0 - 最大整数:
Number.MAX_SAFE_INTEGER
2^53 - 1 - 最小整数:
Number.MIN_SAFE_INTEGER
-2^53+1
判断一个数是否是无限大的函数:isFinite
NaN
表示本来要返回数值的操作失败了。
0、+0或-0相除会返回NaN。
如果分子是非0值,分母是有符号0或无符号0,则会返回Infinity或-Infinity。
NaN不等于包括NaN在内的任何值。NaN == NaN // false
isNaN()
函数接收一个参数,该函数会尝试把它转换为数值。某些非数值的值可以直接转换成数值,如字符串"10"或布尔值。任何不能转换为数值的值都会导致这个函数返回true,反之返回false。
转化为数值
Number()
可用于任何类型- null返回0
- undefined返回NaN
- 字符串(忽略前导0)
- 八进制或十进制 -> 十进制
- 十六进制 -> 相对应的十进制
- 有效浮点数 -> 浮点数
- 空字符串 -> 0
- 其他 -> NaN
parseInt(str, radix)
- 转化数值优先使用parseInt
- 转化原理:从第一个非格字符开始,如果第一个不是数值或+/-号,返回NaN;如果是,继续检测知道末尾或者遇到非数值字符。
- 第二个参数是进制数,字符串以
0x
开头为16,0
开头为8 - 例子
- "1234blue" -> 1234
- "" -> NaN
- "22.5" -> 22
parseFloat
- 类似parseInt
- 始终忽略前导0
- 只解析是进制数
- 例子
- "0xA" -> 0
- "22.34.5" -> 22.34
- "3.125e7" -> 31250000
String类型
- 字符字面量
- 字符串的特点
- 不可变
- 转化为字符串
- 对数值使用toString(),可带参数表示底数
- 模板字面量
- 插入的值会调用toString()
- 模板字面量标签函数
- 接受参数依次是原始字符串 和 对每个表达式求值的结果
- 原始字符串
- String.raw
Symbol类型
符号是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。
- typeof Symbol() -> symbol
- 可以接收一个字符串作为符号的描述
- 不能使用new
- 使用Symbol.for()在全局注册表中创建和复用symbol
- 符号作为属性
- Object.getOwnPropertyNames()返回对象实例的常规属性数组
- Object.getOwnPropertySymbols()返回对象实例的符号属性数组
- Object.getOwnPropertyDescriptors()会返回同时包含常规和符号属性描述符的对象
- Reflect.ownKeys()会返回两种类型的键
- 常用内置符号
- Symbol.iterator,由for...of...使用,实现迭代API的函数
- Symbol.asynclterator实现异步迭代器API的函数
- Symbol.hasInstance,由instanceof使用
- Symbol.isConcatSpreadable表示是否应该打平数组
- false或假值会导致整个对象被追加到数组末尾。类数组对象默认情况下会被追加到数组末尾,
- true或真值会导致这个类数组对象被打平到数组实例。
- Symbol.match,用于正则匹配
- Symbol.toStringTag,获取自定义类名,由Object.prototype.toString()使用。
- ...
对象类型
一组数据和方法的集合。
属性和方法
- constructor构造函数
- hasOwnProperty(protetyName)判断实例上是否存在该属性
- isPrototypeOf(object):用于判断当前对象是否为另一个对象的原型。
- propertyIsEnumerable(propertyName):用于判断给定的属性是否可以使用for-in语句枚举。属性名必须是字符串。
- toLocaleString():返回对象的字符串表示,该字符串反映对象所在的本地化执行环境。
- toString():返回对象的字符串表示。
- valueOf():返回对象对应的字符串、数值或布尔值表示。通常与toString()的返回值相同。
使用instanceof
如果我们想判断一个对象的正确类型,这时候可以考虑使用 instanceof
,因为内部机制是通过原型链来判断的
。
对于原始类型来说,你想直接通过 instanceof 来判断类型是不行的,当然我们还是有办法让 instanceof 判断原始类型的Symbol.hasInstance
就是一个 能让我们自定义 instanceof 行为的东⻄
class PrimitiveString {
static [Symbol.hasInstance](x) {
return typeof x === 'string'
}
}
console.log('hello world' instanceof PrimitiveString)
// 等同于typeof 'hello world' === 'string'
基本引用类型
Date
可以精确表示1970年1月1日之前及之后285616年的日期。
new Date()参数接收
- 不传参:创建当前日期和时间
- 传参数:基于其他日期创建,必须传入毫秒表示
- Date.parse():接收一个表示日期的字符串参数,转化为毫秒数。
- 参数直接传给构造函数,会隐式转化。
- Date.UTC():接收多个参数:年、零起月(0-11)、日、时(0-23)、分、秒、毫秒,转化为毫秒。
- 参数直接传给构造函数,会隐式转化。但创建的不是GMT标准时间,是本地时间。
- Date.now():执行时的日期和时间毫秒数,可以用于代码分析中。
- Date.parse():接收一个表示日期的字符串参数,转化为毫秒数。
重写方法
toString()
:返回带时区信息的日期和时间。
toLocaleString()
:返回本地环境一致的日期和时间。
valueOf()
:返回日期的毫秒表示,使用日期可以直接比较大小,确定日期的先后顺序。
日期的格式化方式
toDateString()
、toTimeString()
、toLocaleDateString()
、toLocaleTimeString()
、toUTCString()
日期/时间组件方法
get/set(Time/Day
)() get/getUTC/set/setUTC(FullYear/Month/Date/Hours/Minutes/Seconds/Milliseconds
)()
RegExp
原始值包装类型
对原始值调用相关方法,后台都会执行这三步:
- 创建对应的对象实例
- 调用实例上的特定方法
- 销毁实例
自动创建原始值的包装对象只存在于执行某行代码时的那个期间,不代表可以对原始值添加属性或方法。
Object()
构造函数作为一个工厂方法,能够根据传入值的类型返回相应原始值包装类型的实例。
一般没有使用boolean和number的构造函数创建
Boolean
Boolean的实例会重写valueOf()
方法,返回一个原始值true或者false,toString()
也会返回字符串的true
或者false
Number
Number
方法isNAN()
是否是NaNtoFixed()
格式化数值,用于补位toExponential()
格式化数值,返回科学计数法toPrecision()
格式化数值,自动返回合适的格式isFinite()
是否是有限数isInteger()
是否是整数isSafeInteger()
是否是安全整数( -(2^53 - 1)
至2^53 - 1之间
)toInteger()
转化为整数(或无穷大)parseFloat()
函数解析一个参数(必要时先转换为字符串)并返回一个浮点数。parseInt(string, radix)
解析一个字符串并返回指定基数的十进制整数或NaN
,radix
是2-36之间的整数,表示被解析字符串的基数。
String
- 字符串是不可变的,而字符串数组是可变的。
- 在每行末尾使用反斜杠字符
\
,以指示字符串将在下一行继续
字符相关
charAt(index)
从一个字符串中返回指定的字符charCodeAt(index)
返回0
到65535
之间的整数,表示给定索引处的 UTF-16 代码单元fromCharCode(x1,x2,x3,...)
通过Unicode编码创建字符,最大支持16位的数字,兼容性好fromCodePoint
多达21位数字,是ES6新加入的方法,是为了能够处理所有合法的UnicodecodePointAt(index)
方法返回 一个 Unicode 编码点值的非负整数。如果在索引处没找到元素则返回undefined
操作相关
concat
将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回substring(start,?end)
提取某个字符串的一部分,并返回一个新的字符串,且不会改动原字符串,负数会被转化为0
slice(begin,?end)
提取某个字符串的一部分,并返回一个新的字符串,且不会改动原字符串,支持负数
substr(start,?num)
截取指定起始位置和长度的字符串
位置相关
indexOf(substr,?start)
从fromIndex
处进行搜索,返回第一次出现的指定值的索引,如果未找到该值,则返回 -1lastIndexOf(substr,?end)
从fromIndex
处从后向前搜索,返回最后一次出现的索引,如果未找到该值,则返回 -1
包含相关
startsWith(substr,?pos)
endsWith(substr,?pos)
判断当前字符串是否是以另外一个给定的子字符串“结尾”的,根据判断结果返回true
或false
length
作为str
的长度。默认值为str.length
includes(substr,?start)
判断一个字符串是否包含在另一个字符串中,根据情况返回 true 或 false
正则匹配相关
match
返回一个字符串匹配正则表达式的结果matchAll
返回一个包含所有匹配正则表达式的结果及分组捕获组的数组replace(regexp|substr, newSubStr|function)
用指定的内容替换掉匹配的结果replaceAll
search
对正则表达式进行匹配搜索,返回首次匹配项的索引,否则,返回-1
。
其他
normalize()
按照Unicode的格式转化为正常字符串padEnd(targetLength,?padString)
用一个字符串填充当前字符串,从右侧开始,返回填充后的字符串padStart(targetLength,?padString)
用一个字符串填充当前字符串,从左侧开始,返回填充后的字符串repeat(count)
重复字符串多次,返回新的字符串split(regexp|substr,limit)
使用指定的分隔符字符串
将字符串割成子字符串
数组,limit
限制返回分割的数量localeCompare()
比较字符串,返回1/0/-1,按字母表顺序。toLowerCase()
字符串值转为小写形式,并返回。toUpperCase()
字符串值转为大写形式,并返回。toLocaleLowerCase
会考虑区域语言环境设置toLocaleUpperCase
会考虑区域语言环境设置trim()
删除两端的所有空白字符trimEnd()
和trimStart()
单例内置对象
Global
URL编码方法
encodeURI()
和encodeURIComponent()
用于编码统一资源标识符,以便传给服务器。作用主要是对特别的字符进行编码,使浏览器可以理解。主要使用encodeURIComponent()
eval()
传入js代码,直接解释执行。
通过eval()
定义的任何变量和函数都不会提升,这些代码只是被执行的时候才被创建。
严格模式下,在eval内部创建的变量和函数无法被外部访问。
Global对象属性
像undefined
、NaN
、Infinitity
等特殊值或像Number
、Array
等内置对象都是Global
属性。
window对象
浏览器中的windows作为global对象的代理,但window对象的实现远不止于此。
调用一个简单返回this的函数是在任何执行上下文中获取Global对象的通用方法。
Math
一些对象属性
- Math.E:自然对数的基数e的值
- Math.PI:π的值
- ...
一些方法
- min、max比较大小
- ceil、floor、round、fround
- random返回0-1范围内的随机数
- 编写selectFrom(low, high)函数
- pow、abs、log、sin、cos等
集合引用类型
Object
一般使用字面量方式创建,点语法是首选的属性存取的方式,除非访问属性时必须使用变量。
Array
创建方式
Array()
- 数组字面量
[]
Array.of()
:接收一组参数转化为数组Array.from()
:第一个参数接收可迭代的结构,或者有length属性和可索引元素的结构,第二个接收一个映射函数,可以对元素进行处理
数组空位
字面量创建可以使用,
来创建空位,ES6方法会普遍把空位认为是存在的元素,只不过值为undefined
在使用中应该避免使用空位,或者显性的用undefined
值代替。
数组索引
数组length属性不是只读的,修改length属性,可以从数组末尾删除或添加元素。
length
属性- 不需要预设大小
- 使用
delete
运算符,length
不会发生改变
- 空白字符会被计入
length
的计算中,值为undefined
,但与显式赋值为undefined
不同 - 当key为数字或者能被强制转化为十进制字符串的话,就会被当做数字索引处理,计入
length
计算
let a = [];
a[0] = 1;
a[2] = 1;
a[1]; // undefined
a.length; // 3
a["footer"] = 2;
a.length; // 3
a["13"] = 1
a.length; // 14
检测数组
value instanceof Array
Array.isArray()
:确定一个值是不是数组,不管是从哪个全局上下文创建的。
复制和填充方法
fill
方法:向一个已有的数组中插入全部或部分相同的值。接收参数:填充内容,开始位置,结束位置copyWithin()
方法:按照指定范围复制数组中的部分内容,插入到指定位置。接收参数:复制开始的位置,覆盖开始位置,覆盖结束位置
转换方法
valueOf()
返回原数组toString()
返回每个值的等效字符串拼接成的一个逗号分隔的字符串。- 想要其他分隔符可以使用
join()
- 想要其他分隔符可以使用
栈方法
pop()
从数组中删除最后一个元素,并返回该元素的值push()
将一个或多个元素添加到数组的末尾,并返回该数组的新长度
队列方法
push()
shift()
从数组中删除第一个元素,并返回该元素的值unshift()
将一个或多个元素添加到数组的头部,并返回该数组的新长度
排序方法
sort()
对数组元素进行原地排序并返回此数组- 默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的。
compareFn(a,b)
小于0,a在b前面,反之相反。相等这相对位置不变
reserve()
将数组中元素的位置颠倒,并返回该数组。该方法会改变原数组
操作方法
concat()
合并多数组,返回一个新数组- 底层可以通过
[Symbol.isConcatSpreadable]
控制数组是否可以打平
- 底层可以通过
flat()
按指定深度扁平化数组flatMap(fn)
:map()
+flat(1)
,例如将语句数组转化为单词数组slice()
提取源数组的一部分并返回一个新数组splice(start,?deleteCount,?add1,?add2,...)
通过删除或替换现有元素或者原地添加新的元素来修改数组,以数组形式返回被删除的内容,没有返回空数组,会改变源数组。
搜索和位置方法
at
接受一个正数或负数作为索引,返回对应的项,负数则从后面开始数indexOf(x)
根据值找到第一个索引,没有返回-1lastIndexOf()
根据值找到最后索引,没有返回-1includes()
判断一个数组是否包含一个指定的值,如果包含则返回true
,否则返回false
find(fn)
根据条件返回第一个目标值,找不到返回undefinedfindIndex(fn)
根据条件返回第一个目标值索引,找不到返回-1
迭代方法
every(fn)
所有元素是否都能通过某个指定函数的测试,返回一个布尔值some(fn)
测试数组中是不是至少有一个元素通过了被提供的函数测试,返回一个布尔值filter()
过滤出通过fn
的所以元素,返回一个新数组forEach
对数组的每个元素执行一次给定的函数map(fn)
对每个元素调用fn
,返回新的数组
归并方法
reduce(callback(cur,val,idx,arr),initVal)
对数组中的每个元素执行一个函数(升序执行),将其结果汇总为单个返回值。cur
累计值val
当前值idx
当前索引arr
源数组initVal
初始值,没有则默认数组的第一个元素
reduceRight()
同上,相反方向
定型数组(skip)
Map
ES6提供的新的集合类型,用于键值存储。
如果现在创建的时候初始化实例,可以给Map构造对象传一个可迭代对象,需要包含键值对数组。
const m1 = new Map([
["key1", "val1"],
["key2", "val2"]
])
const m2 = new Map({
[Symbol.iterator]:function *(){
yield ["key1", "value1"];
yield ["key2", "value2"];
}
})
Map可以使用任何数据类型作为键。内部使用SameValueZero
比较操作,意味着独立实例不冲突。
const m = new Map();
const functionKey = function(){};
m.set(functionKey, "functionValue");
console.log(m.get(functionKey)) // "functionValue"
console.log(m.get(function(){})) // "undefined"
常用方法
- get、set、has、delete、clear
- keys、values(以插入顺序生成键和值的迭代器)
- entries(默认的迭代器)
Object和Map怎么选
- 给定固定大小的内存,Map大约比Object多存储50%的键值对
- 如果涉及大量的插入操作,Map性能更佳
- 如果涉及大量查找操作,可能Object更好(在把Object当成数组使用时,浏览器引擎会做优化)
- 如果涉及大量删除操作,选择Map
WeakMap
键只能是引用类型。使用方法跟Map类似。
如果某个键,没有指向这个键的引用,当代码执行完后,这个对象键就会被当做垃圾回收。
键不可迭代。
如何使用
- 实现私有变量
- 私有变量存储在弱引用中,以对象实例为键,私有成员的字典为值。使用闭包把WeakMap包装起来,避免外部访问。
- 保存关键元数据,比如存储DOM对象
const wm = new WeakMap();
const loginButton = document.querySelector(’#login‘);
wm.set(loginButton, {disabled: true});
当结点从DOM树中删除时,垃圾回收程序就会立即释放内存。
Set
与Map使用方式类似,像是加强版Map。
Set会维护值插入时的顺序。
常用方法
- add、has、delete、clear
- add和delete的操作是幂等的
- values(keys)、entries
定义集合操作(待完善)
WeakSet
如何使用
给对象打标签
const disabledElements = new WeakSet();
const loginButton = document.querySelector("#login");
disabledElements.add(loginButton);
当loginButton在DOM中被删除,垃圾回收程序也会忽视它的存在,从而释放内存。