Appearance
类型检测
typeof
javascript/** * 返回一个字符串,表示未经计算的操作数的类型。 * * @param {exp} operand - 一个表示对象或原始值的表达式 * @returns {string} 返回对应类型,可能值有: * string | number | boolean | undefined | * symbol | bigint | object | function */ typeof 'Lorem' // output: 'string' typeof '1' // output: 'string' typeof '' // output: 'string' typeof (typeof 1) // output: 'string' typeof String(1) // output: 'string' typeof 3.14 // output: 'number' typeof NaN // output: 'number' typeof Infinity // output: 'number' typeof +Infinity // output: 'number' typeof -Infinity // output: 'number' typeof Number(1) // output: 'number' typeof true // output: 'boolean' typeof !!(1) // output: 'boolean' typeof Boolean(1) // output: 'boolean' let declaredButUndefinedVariable typeof undefined // output: 'undefined' typeof declaredButUndefinedVariable // output: 'undefined' typeof undeclareVariable // output: 'undefined' typeof Symbol() // output: 'symbol' typeof Symbol('foo') // output: 'symbol' typeof Symbol.iterator // output: 'symbol' typeof 1n // output: 'bigint' typeof -1n // output: 'bigint' typeof function foo () {} // output: 'function' typeof class Foo {} // output: 'function' typeof {} // output: 'object' typeof [] // output: 'object' typeof /''/ // output: 'object' typeof Math // output: 'object' // new 操作符,除 Function 以外的所有构造函数返回的类型都是 object typeof new Function() // output: 'function' typeof new Number(1) // output: 'object' typeof new String(1) // output: 'object' // 括号 typeof 'Lorem' + ' Lorem' // output: 'string Lorem' typeof ('Lorem' + 'Lorem') // output: 'string' // null typeof null // output: 'object'typeof null
https://2ality.com/2013/10/typeof-null.html
这个错误是JS第一版的遗留物,在这个版本中,JS中的值用 32 位存储。由类型标签(1-3 位) 和实际数据值组成,类型标签存储在单元的低位中。
000:object,实际数据值是对象的引用
1:int,实际数据值是 31 位有符号整数
010:double,实际数据值是对双浮点数的引用
100:string,实际数据值是对字符串的引用
110:boolean,实际数据值是布尔值
undefined 和 null 是特殊的。undefined 是整数 -2 ^ 30,即整数范围外的数字,null是 机器码 null 指针,它的 32位 都是 0 ,可以理解成一个对象类型标签,实际数据值全是 0 的引用。
typeof 的引擎代码
CJS_PUBLIC_API(JSType) JS_TypeOfValue(JSContext *cx, jsval v) { JSType type = JSTYPE_VOID; JSObject *obj; JSObjectOps *ops; JSClass *clasp; CHECK_REQUEST(cx); if (JSVAL_IS_VOID(v)) { // (1) type = JSTYPE_VOID; } else if (JSVAL_IS_OBJECT(v)) { // (2) obj = JSVAL_TO_OBJECT(v); if (obj && (ops = obj->map->ops, ops == &js_ObjectOps ? (clasp = OBJ_GET_CLASS(cx, obj), clasp->call || clasp == &js_FunctionClass) // (3,4) : ops->call != 0)) { // (3) type = JSTYPE_FUNCTION; } else { type = JSTYPE_OBJECT; } } else if (JSVAL_IS_NUMBER(v)) { type = JSTYPE_NUMBER; } else if (JSVAL_IS_STRING(v)) { type = JSTYPE_STRING; } else if (JSVAL_IS_BOOLEAN(v)) { type = JSTYPE_BOOLEAN; } return type; }在(1)处,首先检查值是不是 undefined(void),通过比较值是否相等来判断
C#define JSVAL_IS_VOID(v) ((v) == JSVAL_VOID)在(2)处,检查值是否有对象类型标签,如果有对象类型标签,会通过有没有call判断是不是 可调用的(3)或者通过其内部属性 [[Class]] 判断有没有将其标记为函数(4),如果是可调用 的或者被标记位函数,那么它的类型是 function,否则,是object。 随后检查数字、字符串和布尔值,没有对 null 进行显式检查,所以对于 null 来说,typeof 认为它的类型标签是 object。可以通过C marco执行
C#define JSVAL_IS_NULL(v) ((v) == JSVAL_NULL)来判断 null,这似乎是一个非常明显的错误,但不要忘记完成 JavaScript 的第一个版本的 时间很短。
暂存死区
ES6 之前,typeof 总能保证对任何所给的操作数返回一个字符串,即使是未声明的变量, 使用 typeof 永远不会抛出错误。在加入了块级作用域的 let 和 const 之后,在其被声 明之前对块中的 let 和 const 变量使用 typeof 会抛出一个 ReferenceError。块作用 域变量在块的头部处于“暂存死区”,直至其被初始化,在这期间,访问变量将会引发错误。
javascripttypeof undeclaredVariable // output: undefined typeof newLetVariable; // output: ReferenceError: Cannot access 'newLetVariable' before initialization typeof newConstVariable // output: ReferenceError: Cannot access 'newLetVariable' before initialization typeof newClass // output: ReferenceError: Cannot access 'newLetVariable' before initialization let newLetVariable; const newConstVariable = 'foo' class newClass {}document.all
当前所有浏览器都暴露了一个类型为 undefined 的非标准宿主对象 document.all。
javascripttypeof document.all // output: 'undefined' Object.prototype.toString.call(document.all) // output: '[object HTMLAllCollection]'
instanceof
javascript/** * 用于检测右边的 prototype 属性是否出现在左边的原型链上。 * * @param {Object} object - 某个实例对象 * @param {Function} constructor - 某个构造函数 */ class Foo {} const foo = new Foo() foo instanceof Foo // output: true, because foo.__proto__ === Foo.prototype foo instanceof Object // output: true, because Foo.prototype.__proto__ === Object.prototype // 手动修改原型链指向导致结果变化 foo.__proto__ = {} foo instanceof Foo // output: false // 检测不了基本数据类型,因为 instanceof 要求检测目标必须是实例对象 const simpleStr = 'Lorem' // 非对象实例,类型是 string simpleStr.__proto__ === String.prototype // output: true,包装对象 simpleStr instanceof String // output: false const simpleStr2 = String('Lorem') // 非对象实例,类型是 string simpleStr2.__proto__ === String.prototype // output: true,包装对象 simpleStr2 instanceof String // output: false const newStr = new String('Lorem') // 实例对象,类型是 Object newStr instanceof String // output: true, because newStr.__proto__ === String.prototype newStr instanceof Object // output: true, because String.prototype.__proto__ === Object.prototype const nullProtoObj = Object.create(null) nullProtoObj.prototype // output: undefined nullProtoObj.__proto__ // output: undefined nullProtoObj instanceof Object // output: false, because nullProtoObj.__proto_ === undefined // 如果实例对象不是构造器的实例 if (!(foo instanceof Foo)) { // ... } // 错误写法,!foo 在 instanceof 前被处理,所以总是在验证一个布尔值是不是 Foo 的实例 if (!foo instanceof Foo) {}constructor
所有对象(除Object.create(null)以外)原型上都具有 constructor 属性,指向该对象的构 造函数。
javascript
const o = {}
o.constructor === Object // true
const simpleStr = 'Lorem'
simpleStr.constructor === String // true,包装对象
const simpleStr2 = String('Lorem')
simpleStr2.constructor === String // true,包装对象
const newStr = new String('Lorem')
newStr.constructor === String // true
const o2 = Object.create(null)
o2.constructor === Object // false
o2.constructor === null // false
o2.constructor === undefined // true- Object.prototype.toString()
返回 "[object type]",其中 type 是对象的类型,可以通过这个方法来获取每个对象的类型。
javascript
const o = {}
o.toString() // output: [object Object]
Object.prototype.toString.call('Lorem') // output: [object String]
Object.prototype.toString.apply(1) // output: [object Number]
Object.prototype.toString.call(null) // output: [object Null]
Object.prototype.toString.call(undefined) // output: [object Undefined]- Array.isArray
javascript
/**
* 用于确定传递的值是否是一个 Array
*
* @param {Object} obj - 需要检测的值
* @returns {boolean} 如果值是 Array,返回 true,否则返回 false。
*/
Array.isArray('Lorem') // output: false
Array.isArray([]))// output: true
Array.isArray(new Array()) // output: true
Array.isArray(new Array('foo', 'bar')) // output: true
Array.isArray(Array.prototype) // output: true
Object.prototype.toString.call(Array.prototype) // output: [object Array]
Array.prototype.__proto__ === Array.prototype // output: false
Array.prototype.__proto__ === Object.prototype // output: true- arrayLike
javascript
// JavaScript 权威指南
function isArrayLike(target) {
if (
target && // target 非 null | undefined 等
typeof target === 'object' && // target是对象
target.nodeType !== 3 && // DOM文本节点也有length属性, 需要排除
isFinite(target.length) && // length是有限数值
target.length >= 0 && // length是非负数
target.length === Math.floor(target.length) && // length是整数
target.length < Math.pow(2, 32) // length < 2 ^ 32
) {
return true
} else {
return false
}
}
// Vue.js
function isArrayLike (val) {
return val != null &&
isLength(val.length) &&
!/^function$/.test(typeof val)
}
function isLength (val) {
return typeof val === 'number' &&
val > -1 &&
val % 1 === 0 &&
val <= Number.MAX_SAFE_INTEGER
}