持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情
类型
1. 八种内建值类型
nullundefinedbooleannumberstringobjectsymbolbigint
注意: 除了 object 所有这些类型都被称为“基本类型(primitives)”。
变量没有类型,但是值有类型
2. typeof
typeof 操作符可以检测给定值的类型,而且总是返回八种字符串值中的一种 (没有null,但是有function)
特别:typeof null === "object"; // true
function:对象(object)的“子类型”。一个函数(function)被称为“可调用对象” —— 一个拥有 [[Call]] 内部属性、允许被调用的对象。class也是function
function type(value) {
// null单独处理
if (value === null) {
return "null";
}
const baseType = typeof value;
// 基本类型,直接返回
if (!["object", "function"].includes(baseType)) {
return baseType;
}
// 对应的属性值应该为字符串类型,这个字符串用来表示该对象的自定义类型标签,通常只有内置的
// Object.prototype.toString() 方法会去读取这个标签并把它包含在自己的返回值里。
const tag = value[Symbol.toStringTag];
if (typeof tag === "string") {
return tag;
}
// 判断是否为class
if (
baseType === "function" &&
Function.prototype.toString.call(value).startsWith("class")
) {
return "class";
}
// 构造函数名,例如`Array`, `GeneratorFunction`,
// `Number`, `String`, `Boolean` or `MyCustomClass`(自定义的class)
const className = value.constructor.name;
if (typeof className === "string" && className !== "") {
return className;
}
// 兜底
return baseType;
}
3. undefined vs "undeclared"
var a;
a; // undefined
b; // ReferenceError: b is not defined
var a;
typeof a; // "undefined"
typeof b; // "undefined"
安全检查
// 噢,这将抛出一个错误!
if (DEBUG) {
console.log( "Debugging is starting" );
}
// 这是一个安全的存在性检查
if (typeof DEBUG !== "undefined") {
console.log( "Debugging is starting" );
}
4. Array
4.1 “稀散”的 array (坑,尽量避免)
留下或创建空的/丢失的值槽
// 1:只指定长度,不赋值
var arr1 = new Array(5)
arr1.length == 5 // true
// 2:指定下标赋值,未赋值的为空值槽
var arr2 = []
arr2[4] = undefined
arr2.length == 5 // true
console.log(arr2) // [empty × 4, undefined]
// 3: 通过delete删除,(不会改变length)
var arr3 = [0,1,2,3,4]
delete arr3[4]
arr3.length == 5 // true
console.log(arr3) // [0, 1, 2, 3, empty]
// 4: 直接改变length
var arr4 = []
arr4.length = 5
console.log(arr4) // [empty × 5]
var a = new Array(5)
a.join('-').length == 4 // true
4.2 字符串属性 (坑,尽量避免)
- 添加字符串属性不会改变Array的length
- 如果是10进制数字字符串,会被转成数字,会被当做number引索
var arr = []
arr['a'] = 1
arr.length == 0
arr['0'] = 0
arr.length == 1
// 只打印0,字符串属性 遍历不了
arr.map(item => {
console.log(item)
})
5. string
5.1 string与Array
var str = '012'
str[0] == '0' //(老板ie不支持)
str.charAt(0) == '0' // 推荐
var str = '𠮷' // 两个码点
str.length == 2
str[0] == str.charAt(0) // \uD842
str.split('') // ['\uD842', '\uDFB7']
Array.from(str) // ['𠮷']
6. number
6.1 精度损失
IEEE 754 标准,使用64位固定长度来表示,也就是标准的 double 双精度浮点数
符号位S:第 1 位是正负数符号位(sign),0代表正数,1代表负数
指数位E:中间的 11 位存储指数(exponent),用来表示次方数
尾数位M:最后的 52 位是尾数(mantissa),超出的部分自动进一舍零
0.1+0.2过程
十进制的0.1和0.2会被转换成二进制的,0.1和0.2二进制小数是无限不循环的,截取53?位
相加后再转成10进制,就有了精度损失
/**
* 单精度 32位
* 十进制转二进制: 123.456
* 整数部分:不断除2,取余数,直到商为0
* 小数部分:不断乘2,取整数,直到积为0
*
* 计算过程
* 整数
* 123/2 = 61(1);
* 61/2 = 30(1);
* 30/2 = 15(0);
* 15/2 = 7(1);
* 7/2 = 3(1);
* 3/2 = 1(1);
* 1/2 = 0(1);
* 逆向取值,所以整数转为二进制:1111011
*
* 小数
* 0.456*2 = 0.912(0);
* 0.912*2 = 1.824(1);
* 0.824*2 = 1.648(1);
* 0.648*2 = 1.296(1);
*
* 0.296*2 = 0.592(0);
* 0.592*2 = 1.184(1);
* 0.184*2 = 0.368(0);
* 0.368*2 = 0.736(0);
* ... 无限不循环
*
* 正向取值,所以小数转化为二进制:0111 0100 1011 1100 0110 1010 0111 1110 1111 1001 1101 1011
*
* 故该十进制对应的二进制数为:1111011.011101001011110001101010011111101111100111011011
* 转成"尾数+阶码"的格式为:1.111011011101001011110001101010011111101111100111011011 * 2^6,所以阶码E应该为 6 + 127 = 133
* 精度损失之后保留23位:1.11101101110100101111000 * 2^6
*
* 符号为正,故是0
* 阶码为6,而存储阶码时,32位偏移量为127(64位为1023),所以实际应该存 127+6 = 133,转成二进制为 1000 0101
* 尾数直接取二进制小数 11101101110100101111000
* 最后转成单精度的值为 0 10000101 11101101110100101111000
* 参考:https://juejin.cn/post/6875257293257048078
*/
ES6 中,容差值 Number.EPSILON
function numbersCloseEnoughToEqual(n1,n2) {
return Math.abs( n1 - n2 ) < Number.EPSILON;
}
var a = 0.1 + 0.2;
var b = 0.3;
numbersCloseEnoughToEqual( a, b ); // true
numbersCloseEnoughToEqual( 0.0000001, 0.0000002 ); // false
6.2 精确计算
类库:Math.js、decimal.js、big.js
实现:
- 乘10的n次方在计算,35.41 * 100 = 3540.9999999999995
- 记录小数点位置,替换掉小数点,算完后添加小数点
6.3 安全整数范围
Number.MAX_SAFE_INTEGER、Number.MIN_SAFE_INTEGER
判断:Number.isSafeInteger()
6.4 NaN
var a = NaN;
var b = "foo";
window.isNaN( a ); // true
window.isNaN( b ); // true -- 噢!
Number.isNaN( a ) // true
Number.isNaN( b ) // false
Object.is( a, NaN ); // true
6.5 +0 -0
-0 == 0; // true
-0 === 0; // true
function isNegZero(n) {
n = Number( n );
return (n === 0) && (1 / n === -Infinity);
}
isNegZero( -0 ); // true
Object.is( -0, -0 ); // true
7. instance of
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
基础用法
const auto = new Car('Honda', 'Accord', 1998);
console.log(auto instanceof Car); // true
console.log(auto instanceof Object); // true
class继承
class A {}
class B extends A {}
const o2 = new B();
// true, because Object.getPrototypeOf(Object.getPrototypeOf(o2)) === A.prototype
o2 instanceof A;
// true, because Object.getPrototypeOf(o2) === B.prototype
o2 instanceof B;
Symbol.hasInstance 重写
class Forgeable {
static isInstanceFlag = Symbol("isInstanceFlag");
static [Symbol.hasInstance](obj) {
return Forgeable.isInstanceFlag in obj;
}
}
const obj = { [Forgeable.isInstanceFlag]: true };
console.log(obj instanceof Forgeable); // true
bind
class Base {}
const BoundBase = Base.bind(null, 1, 2);
console.log(new Base() instanceof BoundBase); // true
多frame/windows问题
// 会返回 `false,因为 Array.prototype !== window.frames[0].Array.prototype
[] instanceof window.frames[0].Array
Object.create
function Shape() {}
function Rectangle() {
Shape.call(this); // call super constructor.
}
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
const rect = new Rectangle();
rect instanceof Object; // true
rect instanceof Shape; // true
rect instanceof Rectangle; // true
优先级
!mycar instanceof Car // false, !优先级大于 instanceof