Skip to content
On this page

类型与值

原始类型

  • null空值
    • 表示一个空对象指针
    • 定义将来要保存对象值的变量时,建议使用null来初始化
    • undefined是由null派生而来的,undefined == null
  • undefined未定义
    • 一般声明变量的同时进行初始化,这样使用typeof时可以知道给定的变量尚未声明
  • boolean布尔值
  • number数值
  • string字符串
  • symbol符号(ES6+新增)
  • bigint大数(ES6+新增)

使用typeof

  • 可能的值
    • undefined(未声明或未定义)、booleanstringnumberobject(对象或null)、functionsymbol
  • 作用
    • 检测值类型
    • 判断变量是否存在
  • typeof返回值的类型是string
  • 检查null
    • !a && typeof a === "object"
    • 在 javascript 最初的实现中,javascript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 "object"
  • 安全防范机制
    • 对于没有声明的变量,typeof该变量,返回undefined,而不会报ReferenceError错误。

类型转化

image

三种情况:

  • 转化为布尔值
    • 除了undefinednullfalseNaN-00''以外,其他都返回true,包括所有对象。
  • 转化为数字
  • 转化为字符串

对象转原始类型

  • 调用内置的Symbol.toPrimitive函数,如果存在的话,调用优先级最高
    • 该函数接受一个hint参数,表示要转换到的原始值的预期类型
    • 取值是 numberstringdefault 中的任意一个。
  • 否则,如果 hint 是 string —— 尝试 obj.toString()obj.valueOf(),无论哪个存在。
  • 否则,如果 hint 是 numberdefault —— 尝试 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字符索引来比较
javascript
let a = {
  valueOf() {
    return 0 
  },
  toString() {
    return '1'
  } 
}
a > -1 // true,通过 valueOf 转换为原始类 型再比较值

== VS ===

对于 == 来说,如果对比双方的类型不一样的话,就会进行类型转换

  • 判断类型是否相等,相等则判断大小
  • 类型不相等,先判断是不是比对nullundefined,是的话返回true
  • 判断两者类型是不是string或者number,是的话将string转化为number
  • 判断是否有一方是boolean,是的话将boolean转化为number
  • 判断有一方是NaN,则相等操作符==返回false,注意:NaN == NaN也为false
  • 判断其中一方是否为object且另一方为stringnumber 或者 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
  • 正确判断是否相等(在误差范围内则认为相等)
javascript
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 行为的东⻄

javascript
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():执行时的日期和时间毫秒数,可以用于代码分析中。

重写方法

toString():返回带时区信息的日期和时间。

toLocaleString():返回本地环境一致的日期和时间。

valueOf():返回日期的毫秒表示,使用日期可以直接比较大小,确定日期的先后顺序。

日期的格式化方式

toDateString()toTimeString()toLocaleDateString()toLocaleTimeString()toUTCString()

日期/时间组件方法

get/set(Time/Day)() get/getUTC/set/setUTC(FullYear/Month/Date/Hours/Minutes/Seconds/Milliseconds)()

RegExp

原始值包装类型

对原始值调用相关方法,后台都会执行这三步:

  1. 创建对应的对象实例
  2. 调用实例上的特定方法
  3. 销毁实例

自动创建原始值的包装对象只存在于执行某行代码时的那个期间,不代表可以对原始值添加属性或方法。

Object()构造函数作为一个工厂方法,能够根据传入值的类型返回相应原始值包装类型的实例。

一般没有使用boolean和number的构造函数创建

Boolean

Boolean的实例会重写valueOf()方法,返回一个原始值true或者false,toString()也会返回字符串的true或者false

Number

  • Number方法
    • isNAN()是否是NaN
    • toFixed()格式化数值,用于补位
    • toExponential()格式化数值,返回科学计数法
    • toPrecision()格式化数值,自动返回合适的格式
    • isFinite()是否是有限数
    • isInteger()是否是整数
    • isSafeInteger()是否是安全整数( -(2^53 - 1)2^53 - 1之间)
    • toInteger()转化为整数(或无穷大)
    • parseFloat()函数解析一个参数(必要时先转换为字符串)并返回一个浮点数。
    • parseInt(string, radix) 解析一个字符串并返回指定基数的十进制整数或NaNradix 是2-36之间的整数,表示被解析字符串的基数。

String

  • 字符串是不可变的,而字符串数组是可变的。
  • 在每行末尾使用反斜杠字符\,以指示字符串将在下一行继续

字符相关

  • charAt(index)从一个字符串中返回指定的字符
  • charCodeAt(index)返回 065535 之间的整数,表示给定索引处的 UTF-16 代码单元
  • fromCharCode(x1,x2,x3,...)通过Unicode编码创建字符,最大支持16位的数字,兼容性好
  • fromCodePoint多达21位数字,是ES6新加入的方法,是为了能够处理所有合法的Unicode
  • codePointAt(index)方法返回 一个 Unicode 编码点值的非负整数。如果在索引处没找到元素则返回 undefined

操作相关

  • concat将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回
  • substring(start,?end)提取某个字符串的一部分,并返回一个新的字符串,且不会改动原字符串,负数会被转化为0
  • slice(begin,?end)提取某个字符串的一部分,并返回一个新的字符串,且不会改动原字符串,支持负数
  • substr(start,?num)截取指定起始位置和长度的字符串

位置相关

  • indexOf(substr,?start)fromIndex 处进行搜索,返回第一次出现的指定值的索引,如果未找到该值,则返回 -1
  • lastIndexOf(substr,?end)fromIndex处从后向前搜索,返回最后一次出现的索引,如果未找到该值,则返回 -1

包含相关

  • startsWith(substr,?pos)
  • endsWith(substr,?pos)判断当前字符串是否是以另外一个给定的子字符串“结尾”的,根据判断结果返回 truefalse
    • 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对象属性

undefinedNaNInfinitity等特殊值或像NumberArray等内置对象都是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计算
javascript
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)根据值找到第一个索引,没有返回-1
  • lastIndexOf()根据值找到最后索引,没有返回-1
  • includes()判断一个数组是否包含一个指定的值,如果包含则返回 true,否则返回 false
  • find(fn)根据条件返回第一个目标值,找不到返回undefined
  • findIndex(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构造对象传一个可迭代对象,需要包含键值对数组。

js
const m1 = new Map([
	["key1", "val1"],
	["key2", "val2"]
])

const m2 = new Map({
	[Symbol.iterator]:function *(){
		yield ["key1", "value1"];
		yield ["key2", "value2"];
	}
})

Map可以使用任何数据类型作为键。内部使用SameValueZero比较操作,意味着独立实例不冲突。

js
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对象
js
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

如何使用

给对象打标签

js
const disabledElements = new WeakSet();

const loginButton = document.querySelector("#login");

disabledElements.add(loginButton);

当loginButton在DOM中被删除,垃圾回收程序也会忽视它的存在,从而释放内存。

MIT Licensed | Copyright © 2021 - 2022