typeof
typeof 操作符返回一个字符串,表示未经计算的操作数的类型。
语法: typeof 运算符后接操作数:
typeof operand
typeof(operand)
参数:
operand
:一个表示对象或原始值的表达式,其类型将被返回。
下表总结了 typeof 可能的返回值。
类型 | 结果 |
---|---|
Undefined | “undefined” |
Null | “object” (见下文) |
Boolean | “boolean” |
Number | “number” |
BigInt(ECMAScript 2020 新增) | “bigint” |
String | “string” |
Symbol (ECMAScript 2015 新增) | “symbol” |
宿主对象(由 JS 环境提供) | 取决于具体实现 |
Function 对象 (按照 ECMA-262 规范实现 [[Call]]) | “function” |
其他任何对象 | “object” |
typeof null
// JavaScript 诞生以来便如此
typeof null === 'object';
在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null
代表的是空指针(大多数平台下值为 0x00
),因此,null 的类型标签是 0,typeof null
也因此返回 "object"
。
使用 new 操作符
// 除 Function 外的所有构造函数的类型都是 'object'
var str = new String('String');
var num = new Number(100);
typeof str; // 返回 'object'
typeof num; // 返回 'object'
var func = new Function();
typeof func; // 返回 'function'
正则表达式
对正则表达式字面量的类型判断在某些浏览器中不符合标准:
typeof /s/ === 'function'; // Chrome 1-12 , 不符合 ECMAScript 5.1
typeof /s/ === 'object'; // Firefox 5+ , 符合 ECMAScript 5.1
错误
在 ECMAScript 2015 之前,typeof
总能保证对任何所给的操作数返回一个字符串。即便是没有声明的标识符,typeof
也能返回 'undefined'
。使用 typeof
永远不会抛出错误。
if(typeof a!="undefined"){
}
但在加入了块级作用域的 let
和 const
之后,在其被声明之前对块中的 let
和 const
变量使用 typeof
会抛出一个 ReferenceError
。块作用域变量在块的头部处于“暂存死区”,直至其被初始化,在这期间,访问变量将会引发错误。
typeof undeclaredVariable === 'undefined';
typeof newLetVariable; // ReferenceError
typeof newConstVariable; // ReferenceError
typeof newClass; // ReferenceError
let newLetVariable;
const newConstVariable = 'hello';
class newClass{
};
typeof 各类数据 结果
Value | Class | Type |
---|---|---|
“foo” | String | string |
new String(“foo”) | String | object |
1.2 | Number | number |
new Number(1.2) | Number | object |
true | Boolean | boolean |
new Boolean(true) | Boolean | object |
new Date() | Date | object |
new Error() | Error | object |
[1,2,3] | Array | object |
new Array(1, 2, 3) | Array | object |
new Function("") | Function | function |
/abc/g | RegExp | object (function in Nitro/V8) |
new RegExp(“meow”) | RegExp | object (function in Nitro/V8) |
{} | Object | object |
new Object() | Object | object |
null | Null | object |
undefined | Undefined | undefined |
BigInt(123) | Bigint | bigint |
Symbol() | Symbol | symbol |
上面表格中,Type 一列表示 typeof 操作符的运算结果。可以看到,这个值在大多数情况下都返回 “object”。
Class 一列表示对象的内部属性 [[Class]]
的值。
JavaScript 标准文档中定义: [[Class]]
的值只可能是下面字符串中的一个: Arguments
, Array
, Boolean
, Date
, Error
, Function
, JSON
, Math
, Number
, Object
, RegExp
, String
,Symbol
,Bigint
。
结论:
typeof几乎不可能得到它们想要的结果。
instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
语法: object instanceof constructor
参数: object(某个实例对象)constructor(某个构造函数)
描述: instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
instanceof 只有在比较自定义的对象时才有意义。 如果用来比较内置类型,将会和 typeof 操作符 一样用处不大。
比较自定义对象
function Foo() {
}
function Bar() {
}
Bar.prototype = new Foo();
new Bar() instanceof Bar; // true, new Bar().__proto__ === Bar.prototype
new Bar() instanceof Foo; // true, new Bar().__proto__.__proto__ === Foo.prototype
// 如果仅仅设置 Bar.prototype 为函数 Foo 本身,而不是 Foo 构造函数的一个实例
Bar.prototype = Foo;
new Bar() instanceof Foo; // false, new Bar().__proto__.__proto__ !== Foo.prototype
instanceof 比较内置类型
new String('foo') instanceof String; // true, new String('foo').__proto__ === String.prototype
new String('foo') instanceof Object; // true, new String('foo').__proto__.__proto__ === Object.prototype
'foo' instanceof String; // false, 'foo'不为对象
'foo' instanceof Object; // false,'foo'不为对象
有一点需要注意,instanceof 用来比较属于不同 JavaScript 上下文的对象(比如,浏览器中不同的文档结构)时将会出错, 因为它们的构造函数不会是同一个对象。
// 定义构造函数
function C(){
}
function D(){
}
var o = new C();
o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype
o instanceof D; // false,因为 D.prototype 不在 o 的原型链上
o instanceof Object; // true,因为 Object.prototype.isPrototypeOf(o) 返回 true
C.prototype instanceof Object // true,同上
C.prototype = {
};
var o2 = new C();
o2 instanceof C; // true
o instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上.
D.prototype = new C(); // 继承
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true 因为 C.prototype 现在在 o3 的原型链上
需要注意的是,如果表达式 obj instanceof Foo 返回 true,则并不意味着该表达式会永远返回 true,因为 Foo.prototype 属性的值有可能会改变,改变之后的值很有可能不存在于 obj 的原型链上,这时原表达式的值就会成为 false。另外一种情况下,原表达式的值也会改变,就是改变对象 obj 的原型链的情况,虽然在目前的ES规范中,我们只能读取对象的原型而不能改变它,但借助于非标准的 _proto_ 伪属性,是可以实现的。比如执行 obj._proto_ = {} 之后,obj instanceof Foo 就会返回 false 了。
正确获取数据类型
JavaScript 标准文档只给出了一种获取上文 [[Class]] 值的方法,那就是使用 Object.prototype.toString
。
Object.prototype.toString
返回一种标准格式字符串,
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call({
}) // "[object Object]"
Object.prototype.toString.call(2) // "[object Number]"
所以可以通过 slice 截取指定位置的字符串,如下所示:
Object.prototype.toString.call([]).slice(8, -1); // Array
上面例子中,Object.prototype.toString 方法被调用,this 被设置为了需要获取 [[Class]] 值的对象。
为了更加明确地检查类型,将typeof 在生产级代码中使用的包装器如下:
/**
* @param data 必填 需要获取类型的数据
* @param { boolean } isGetfullClass 选填 是否获取完整数据类型结构
**/
function getDataType(data, isGetfullClass) {
// get toPrototypeString() of data (handles all types)
// Early JS environments return '[object Object]' for null, so it's best to directly check for it.
if (isGetfullClass) {
return (data === null) ? '[object Null]' : Object.prototype.toString.call(data);
}
if (data == null) {
return (data + '').toLowerCase(); } // implicit toString() conversion
var deepType = Object.prototype.toString.call(data).slice(8,-1).toLowerCase();
if (deepType === 'generatorfunction') {
return 'function' }
// Prevent overspecificity (for example, [object HTMLDivElement], etc).
// Account for functionish Regexp (Android <=2.3), functionish <object> element (Chrome <=57, Firefox <=52), etc.
// String.prototype.match is universally supported.
return deepType.match(/^(arguments|array|bigint|boolean|date|error|function|json|math|number|generator|regexp|symbol|object)$/) ? deepType :
(typeof data === 'object' || typeof data === 'function') ? 'object' : typeof data;
}