typeof和instanceof原理

本文详细解释了JavaScript中typeof运算符的工作原理,包括其返回值和需要注意的情况,以及instanceof用于检测原型链的机制。重点讨论了null的特殊性、new操作符的影响和instanceof的使用注意事项。
摘要由CSDN通过智能技术生成

1. typeof

typeof 运算符返回一个字符串,表示操作数的类型。

原理: js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息。而 typeof 关键字检测数据类型的原理就是通过检测这1-3位机器码实现检测不同数据类型。 如下:

object000
int1
double浮点数010
string100
boolean110
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 返回值

返回值为布尔值truefalse

// 定义构造函数
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 的一个实例)。

3. 拓展阅读:Object.prototype.toString()

  • 24
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值