1. typeof
typeof
运算符返回一个字符串,表示操作数的类型。
原理: js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息。而 typeof 关键字检测数据类型的原理就是通过检测这1-3位机器码实现检测不同数据类型。 如下:
object | 000 |
---|---|
int | 1 |
double浮点数 | 010 |
string | 100 |
boolean | 110 |
undefined | -2^30 |
null | 全零 |
其次:typeof 在检测出其为引用数据类型后,还会再检测这个引用数据类型是否有实现[[call]]方法。不是,则返回object ,是,则返回function。
[[Call]] 是什么
“执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个 this 值和一个包含调用表达式传递给函数的参数的列表。实现此内部方法的对象是可调用的。”
这是翻译的原话,简单点说,一个对象如果支持了内部的 [[Call]] 方法,那么它就可以被调用,就变成了函数,所以叫做函数对象。
相应地,如果一个函数对象支持了内部的 [[Construct]] 方法,那么它就可以使用 new 或 super 来调用,这时我们就可以把这个函数对象称为:构造函数。
1.1 语法
typeof operand
参数说明:
- operand:表示要返回类型的对象或 基本类型 的表达式。
1.2 返回值
下表总结了 typeof
可能的返回值:
类型 | 结果 |
---|---|
Undefined | "undefined" |
Null | "object" |
Boolean | "boolean" |
Number | "number" |
BigInt (ES11新增) | "bigint" |
String | "string" |
Symbol | "symbol" |
Function(在 ECMA-262 中实现 [[Call]];classes也是函数) | "function" |
其他任何对象 | "object" |
// 数值
typeof 37 === 'number';
typeof 3.14 === 'number';
typeof(42) === 'number';
typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // 尽管它是 "Not-A-Number" (非数值) 的缩写
typeof Number(1) === 'number'; // Number 会尝试把参数解析成数值
typeof Number("shoe") === 'number'; // 包括不能将类型强制转换为数字的值
typeof 42n === 'bigint';
// 字符串
typeof '' === 'string';
typeof 'bla' === 'string';
typeof `template literal` === 'string';
typeof '1' === 'string'; // 注意内容为数字的字符串仍是字符串
typeof (typeof 1) === 'string'; // typeof 总是返回一个字符串
typeof String(1) === 'string'; // String 将任意值转换为字符串,比 toString 更安全
// 布尔值
typeof true === 'boolean';
typeof false === 'boolean';
typeof Boolean(1) === 'boolean'; // Boolean() 会基于参数是真值还是虚值进行转换
typeof !!(1) === 'boolean'; // 两次调用 !(逻辑非)运算符相当于 Boolean()
// Symbols
typeof Symbol() === 'symbol';
typeof Symbol('foo') === 'symbol';
typeof Symbol.iterator === 'symbol';
// Undefined
typeof undefined === 'undefined';
typeof declaredButUndefinedVariable === 'undefined';
typeof undeclaredVariable === 'undefined';
// 对象
typeof { a: 1 } === 'object';
// 使用 Array.isArray 或者 Object.prototype.toString.call
// 区分数组和普通对象
typeof [1, 2, 4] === 'object';
typeof new Date() === 'object';
typeof /regex/ === 'object';
// 下面的例子令人迷惑,非常危险,没有用处。避免使用它们。
typeof new Boolean(true) === 'object';
typeof new Number(1) === 'object';
typeof new String('abc') === 'object';
// 函数
typeof function() {} === 'function';
typeof class C {} === 'function'
typeof Math.sin === 'function';
1.3 需要注意的情况
1.3.1 typeof null
// JavaScript 诞生以来便如此
typeof null === "object";
在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于
null
代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null
也因此返回"object"
。(参考来源)曾有一个 ECMAScript 的修复提案(通过选择性加入的方式),该提案会导致
typeof null === 'null'
,但是被拒绝了。
1.3.2 使用 new 操作符
所有使用 new
调用的构造函数都将返回非基本类型("object"
或 "function"
)。大多数返回对象,但值得注意的例外是 Function
,它返回一个函数。
const str1 = "String";
const str2 = new String("String");
typeof str1; // "string"
typeof str2; // "object"
typeof String; // "function"
/* 补充说明:
当我们 new 一个构造函数时, 结果肯定是实例化了一个对象。所以其肯定是引用数据类型。
接着我们只需要判断这个引用数据类型是否实现[[call]]方法,不是 则返回object ,是 则返回function。
*/
const func = new Function();
typeof func; // "function"
1.3.3 语法中需要圆括号
typeof
操作符的优先级高于加法(+
)等二进制操作符。因此,需要用括号来计算加法结果的类型。
// 括号有无将决定表达式的类型。
const someData = 99;
typeof someData + " Wisen"; // "number Wisen"
typeof (someData + " Wisen"); // "string"
1.3.4 对未声明和未初始化变量使用
typeof
通常总是保证为它提供的任何操作数返回一个字符串。即使使用未声明的标识符,typeof
也会返回 "undefined"
,而不是抛出错误。
typeof undeclaredVariable; // "undefined"
1.3.5 document.all 的异常行为
typeof document.all === 'undefined';
document.all
也是假值,与 undefined
非严格相等,但它不是 undefined
。在 Web 标准中,document.all
具有 "undefined"
类型的情况被归类为“故意违反”原始 ECMAScript Web 兼容性标准。
2. instanceof
instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
2.1 语法
object instanceof constructor
参数说明:
- object:某个实例对象;
- constructor: 某个构造函数。
2.2 返回值
返回值为布尔值true
或false
。
// 定义构造函数
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 的原型链上
2.3 需要注意的情况
2.3.1 表达式的返回值有可能会改变
需要注意的是,如果表达式 obj instanceof Foo
返回 true
,则并不意味着该表达式会永远返回 true
,因为 Foo.prototype
属性的值有可能会改变,改变之后的值很有可能不存在于 obj
的原型链上,这时原表达式的值就会成为 false
。
另外一种情况下,原表达式的值也会改变,就是改变对象 obj
的原型链的情况,虽然在目前的 ES 规范中,我们只能读取对象的原型而不能改变它,但借助于非标准的 __proto__
伪属性,是可以实现的。比如执行 obj.__proto__ = {}
之后,obj instanceof Foo
就会返回 false
了。
2.3.2 使用字面量创建的实例与使用 new 操作符创建对象
使用字面量创建的实例instanceof constructor
返回 false
。但是,使用对象文字符号创建的对象在这里是一个例外:虽然原型未定义,但 instanceof Object
返回 true
。
var simpleStr = "This is a simple string";
var myString = new String();
var newStr = new String("String created with constructor");
var myDate = new Date();
var myObj = {};
var myNonObj = Object.create(null);
simpleStr instanceof String; // 返回 false,非对象实例,因此返回 false
myString instanceof String; // 返回 true
newStr instanceof String; // 返回 true
myString instanceof Object; // 返回 true
myObj instanceof Object; // 返回 true,尽管原型没有定义
({}) instanceof Object; // 返回 true,同上
myNonObj instanceof Object; // 返回 false,一种创建非 Object 实例的对象的方法
myString instanceof Date; //返回 false
myDate instanceof Date; // 返回 true
myDate instanceof Object; // 返回 true
myDate instanceof String; // 返回 false
2.3.3 检测不是...的实例
要检测对象不是某个构造函数的实例时,你可以这样做
if (!(mycar instanceof Car)) {// Do something, like mycar = new Car(mycar)}
这和以下代码完全不同
if (!mycar instanceof Car) {// Do something, like mycar = new Car(mycar)}
这段代码永远会得到 false
(!mycar
将在 instanceof
之前被处理,所以你总是在验证一个布尔值是否是 Car
的一个实例)。