变量和类型
JavaScript规定了几种语言类型
| 名称 | 类别 | 内置对象 |
|---|---|---|
| String | 值类型 | String |
| Number | 值类型 | Number |
| Boolean | 值类型 | Boolean |
| Null | 值类型 | |
| Undefined | 值类型 | |
| Symbol | 值类型 | Symbol |
| BigInt | 值类型 | BigInt |
| Object | 引用类型 | Object |
| Function | 引用类型 | |
| Array | 引用类型 | Array |
装箱拆箱是什么
-
装箱
-
显示装箱
- 通过内置对象String、Boolean、Number等可以对基本类型进行显示装箱
-
隐式装箱
- 当读取一个基本类型值时,后台会为该基本类型创建一个对应的基本类型包装对象。在这个基本类型上调用方法,实际上是在这个基本类型包装对象上调用方法。这个基本类型包装对象是临时的,它只存在于方法调用那一行代码的瞬间,一旦调用完毕就立即销毁。
num.toFixed(1); // 在后台的具体执行如下: const c = new Number(12.345); c.toFixed(1); c = null; ```
-
-
拆箱
-
拆箱与装箱相反,将对象转变成基本类型的值。
-
拆箱过程内部调用了抽象操作 ToPrimitive 。该操作接受两个参数,第一个参数是要转变的对象,第二个参数 PreferredType 是对象被期待转成的类型。第二个参数不是必须的,默认该参数为 number,即对象被期待转为数字类型。有些操作如 String(obj) 会传入 PreferredType 参数。有些操作如 obj + " " 不会传入 PreferredType。
-
具体转换过程是这样的。默认情况下,ToPrimitive 先检查对象是否有 valueOf 方法,如果有则再检查 valueOf 方法是否有基本类型的返回值。如果没有 valueOf 方法或 valueOf 方法没有返回值,则调用 toString 方法。如果 toString 方法也没有返回值,产生 TypeError 错误。
PreferredType 影响 valueOf 与 toString 的调用顺序。如果 PreferrenType 的值为 string。则先调用 toString ,再调用 valueOf。
-
值类型和引用类型有什么区别
-
值类型:
- 存储位置:栈内存中,栈内存中包含了变量的标识符和指向堆内存中该对象的指针
- 内存占用:栈是自动分配相对固定大小的内存空间,并由系统自动释放
- 复制方式:直接赋值是深拷贝
- 无法添加属性和方法
- 比较:比较值
-
引用类型:
- 存储位置:堆内存中,堆内存中包含了对象的内容,是按引用访问
- 内存占用:动态分配内存,内存大小不一,也不会自动释放
- 复制方式:直接赋值是传递引用,是浅拷贝
- 可以添加属性和方法
- 比较:比较引用地址
Number
有哪些内置方法
| 方法 | 用途 |
|---|---|
Number.isNaN() | 确定传递的值是否是 NaN |
Number.isFinite() | 确定传递的值类型及本身是否是有限数 |
Number.isInteger() | 确定传递的值类型是“number”,且是整数 |
Number.isSafeInteger() | 确定传递的值是否为安全整数 ( -(253 - 1) 至 253 - 1之间) |
Number.toInteger() | 计算传递的值并将其转换为整数 (或无穷大) |
Number.parseFloat() | 和全局对象 parseFloat() 一样 |
Number.parseInt() | 和全局对象 parseInt() 一样 |
有哪些内置属性
| 属性 | 含义 |
|---|---|
Number.EPSILON | 两个可表示(representable)数之间的最小间隔 |
Number.MAX_SAFE_INTEGER | JavaScript 中最大的安全整数 (2^53 - 1) |
Number.MAX_VALUE | 能表示的最大正数。最小的负数是 -MAX_VALUE |
Number.MIN_SAFE_INTEGER | JavaScript 中最小的安全整数 (-(2^53 - 1)). |
Number.MIN_VALUE | 能表示的最小正数即最接近 0 的正数 (实际上不会变成 0)。最大的负数是 -MIN_VALUE |
Number.NaN | 特殊的“非数字”值 |
Number.NEGATIVE_INFINITY | 特殊的负无穷大值,在溢出时返回该值 |
Number.POSITIVE_INFINITY | 特殊的正无穷大值,在溢出时返回该值 |
Number.prototype (en-US) | Number 对象上允许的额外属性。 |
-
最大数字:2^53(9007199254740992)
-
最大安全数字:2^53(9007199254740992)-1
-
为什么 0.1 + 0.2 != 0.3
-
那为什么 x=0.1 能得到 0.1?
- 标准中规定尾数f的固定长度是52位,再加上省略的一位,这53位是JS精度范围。它最大可以表示2^53(9007199254740992), 长度是 16,所以可以使用 toPrecision(16) 来做精度运算,超过的精度会自动做凑整处理。 0.10000000000000000555.toPrecision(16) // 返回 0.1000000000000000,去掉末尾的零后正好为 0.1
-
tofixed()对于小数最后一位为5时进位不正确
- firefox/chrome中,对于小数最后一位为5时进位不正确,修复方式即判断最后一位为5的,改成6,再调用toFixed
-
避免精度丢失的方法
-
扩大整数,再缩小
-
ES6在Number对象上新增了一个极小的常量——Number.EPSILON
目的在于为浮点数计算设置一个误差范围,如果误差能够小于Number.EPSILON,我们就可以认为结果是可靠的。
-
也可以用成熟的库来解决此问题
- math.js
- number-precision
-
-
JavaScript处理大数字的方法- 参考网上常用的一种方案是将
Number转为String,然后将String转为Array,并且注意补齐较短的数组,将他们的长度标称一样再一一相加得到一个新数组,再讲和组成的新数组转为数字就可以了。
function sumString(a, b) { a = '0' + a; b = '0' + b; //加'0'首先是为了转为字符串,而且两个数相加后可能需要进位,这样保证了和的长度就是a、b中长的那个字符的长度 var arrA = a.split(''), //将字符串转为数组 arrB = b.split(''), res = [], //相加结果组成的数组 temp = '', //相同位数相加的值 carry = 0, //同位数相加结果大于等于10时为1,否则为0 distance = a.length - b.length, //计算两个数字字符串的长度差 len = distance > 0 ? a.length : b.length; //和的长度 // 在长度小的那个值前加distance个0,保证两个数相加之前长度是想等的 if(distance > 0) { for(let i = 0; i < distance; i++) { arrB.unShift('0'); } }else{ for(let i = 0; i < distance; i++) { arrA.unShift('0'); } } // 现在得到了两个长度一致的数组,需要做的就是把他们想通位数的值相加,大于等于10的要进一 // 最终得到一个和组成的数组,将数组转为字符串,去掉前面多余的0就得到了最终的和 for(let i = len-1; i >= 0; i--) { temp = Number(arrA[i]) + Number(arrB[i]) + carry; if(temp >= 10) { carry = 1; res.unshift((temp + '')[1]) } else{ carry = 0; res.unshift(temp) } } res = res.join('').replace(/^0/, ''); console.log(res); } ``` - 参考网上常用的一种方案是将
BigInt
用法
-
可以用在一个整数字面量后面加
n的方式定义一个BigInt,如:10n,或者调用函数BigInt() -
注意点
- 不能用于Math对象中的方法
- 不能和任何Number实例混合运算,两者必须转换成同一类型。
- 在两种类型来回转换时要小心,因为
BigInt变量在转换成Number变量时可能会丢失精度。 - 使用
typeof测试时,BigInt对象返回 "bigint" - 使用
Object包装后,BigInt被认为是一个普通 "object" BigInt和Number不是严格相等的,但是宽松相等的。- 对任何
BigInt值使用JSON.stringify()都会引发TypeError,因为默认情况下BigInt值不会在JSON中序列化。但是,如果需要,可以实现toJSON方法
Null | Undefined 的区别
-
undefined:希望表示一个变量最原始的状态,而非人为操作的结果。
- 出现undefined的场景
- 使用void对表达式求值
- 函数默认返回值
- 函数定义了形参数,但没有传递实参
- 获取对象不存在的key
- 获取对象不存在的下标
- 声明变量,不初始化
- 未声明的变量执行typeof操作符返回了undefined值
- 出现undefined的场景
-
null:希望表示一个对象被人为的重置为空对象,而非一个变量最原始的状态。 在内存里的表示就是,栈中的变量没有指向堆中的内存对象。
- 如果定义的变量在将来用于保存对象,那么最好将该变量初始化为null,而不是其他值。
- 当一个数据不再需要使用时,我们最好通过将其值设置为null来释放其引用,这个做法叫做解除引用。不过解除一个值的引用并不意味着自动回收改值所占用的内存。解除引用的真正作用是让值脱离执行环境,以便垃圾收集器在下次运行时将其回收。解除引用还有助于消除有可能出现的循环引用的情况。这一做法适用于大多数全局变量和全局对象的属性,局部变量会在它们离开执行环境时(函数执行完时)自动被解除引用。
- 注意⚠️:当我们使用typeof操作符检测null值,我们理所应当地认为应该返"Null"类型呀,但是事实返回的类型却是"object"。
- 一方面从逻辑角度来看,null值表示一个空对象指针,它代表的其实就是一个空对象,所以使用typeof操作符检测时返回"object"也是可以理解的。
- 另一方面,其实在JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的(对象的类型标签是 0)。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null的类型标签也成为了 0,typeof null就错误的返回了"object"。在ES6中,当时曾经有提案为历史平反, 将type null的值纠正为null, 但最后提案被拒了,所以还是保持"object"类型。
Symbol
参考资料:www.dazhuanlan.com/tomj/topics…
- 含义:它是一切非字符串的对象 key 的集合
- 用法:
Symbol(value)
用途
- 每个Symbol都不相等,可以作为对象的属性名,可以保证属性不重名。
- 使用 Symbol定义常量,这样就可以保证这一组常量的值都不相等。
- 定义类的私有属性/方法,Symbol是唯一的,因此在外部无法访问到这个属性。 可以用static替代。
注意点
- Symbol 可以具有字符串类型的描述,但是即使描述相同,Symbol 也不相等。
- Symbol 作为对象属性名时不能用`.`运算符,要用`[]`。因为.运算符后面是字符串,所以取到的是字符串 sy 属性,而不是 Symbol 值 sy 属性。
- Symbol 值作为属性名时,该属性是公有属性不是私有属性,可以在类的外部访问。但是不会出现在 for...in 、 for...of 的循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回。如果要读取到一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到。
常用API
- Symbol.for()
类似单例模式,首先会在全局搜索被登记的 Symbol 中是否有该字符串参数作为名称的 Symbol 值,如果有即返回该 Symbol 值,若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并登记在全局环境中供搜索。如果我们希望使用同一个 Symbol 值,可以用 for。
- Symbol.keyFor()
返回一个已登记的 Symbol 类型值的 key ,用来检测该字符串参数作为名称的 Symbol 值是否已被登记。
常见手写面试题
手动实现一个简单的Symbol
(function(){
var root = this
var generateName = (function(){
var flag = 0
return function(name){
flag++
return 'troubledot'+ name +'_'+ flag
}
})()
var Generatesymbol = function (description){
var descString = description == undefined ? undefined : String(description)
if (this instanceof Generatesymbol) {
throw new TypeError('Symbol is not a constructor')
}
var symbol = Object.create({
toString : function(){
return this.__Name__
},
valueOf: function(){
return this
}
})
Object.defineProperties(symbol, {
__Description__:{
value: descString,
writable: false,
enumerable: false,
configurable: false
},
__Name__:{
value: generateName(descString),
writable: false,
enumerable: false,
configurable: false
}
})
return symbol
}
var symbolMap = {}
Object.defineProperties(Generatesymbol, {
'for': {
value: function(description){
var descString = description == undefined ? undefined : String(description)
return symbolMap[descString] ? symbolMap[descString] : symbolMap[descString] = Generatesymbol(description)
},
writable: true,
enumerable: false,
configurable: true
},
'keyFor': {
value: function(symbol){
for (var key in symbolMap) {
if (symbolMap[key] === symbol){
return key
}
}
},
writable: true,
enumerable: false,
configurable: true
}
})
root.Generatesymbol = Generatesymbol
})()
Object
Object
Array
Function
类型判断
-
typeof
- 优点:使用简单
- 缺点:只能检测基本类型(除了null)
- 用法:
typeof 值
-
instanceof
- 优点:能检测引用类型
- 缺点:不能检测出基本类型,且不能跨iframe
- 用法:
值 instanceof 类型
-
constructor
- 优点:基本能检测所有类型(除null、undefined)
- 缺点:易被修改,也不能跨iframe
- 用法:
值.constructor === 类型
-
Object.prototype.toString
- 优点:检测出所有的类型
- 缺点:IE6下,null、undefined为Object
- 用法:
Object.prototype.toStringO() === 类型
类型转换
类型转换
-
转换为字符串:Boolean 值、数字和字符串的原始值都是伪对象,ECMAScript 定义所有对象都有 toString() 方法,无论它是伪对象,还是真对象。
-
转换为数字:ECMAScript 提供了两种把非数字的原始值转换成数字的方法,即 parseInt() 和 parseFloat() 。 只有对 String 类型调用这些方法,它们才能正确运行;对其他类型返回的都是 NaN。
-
转换为布尔:
参数 结果 undefined false null false boolean 无需转换 number +0,-0转换为false,其他为true string ''为false,其他为true [] true {} true Symbol true
隐式转换
-
自动转换Boolean 。例如 if 语句 或者其他需要 Boolean 的地方
-
运算符。非 Numeber 类型进行数学运算符 - * / 时,会先将非 Number 转换成 Number 类型。
+运算符要考虑字符串的情况,在操作数中存在字符串时,优先转换成字符串。 -
对象。只有在 JavaScript 表达式或语句中需要用到数字或字符串时,对象才被隐式转换。
- 调用 valueOf()。如果结果是原始值(不是一个对象),则将其转换为一个数字。
- 否则,调用 toString() 方法。如果结果是原始值,则将其转换为一个数字。
- 否则,抛出一个类型错误。