typeof和instanceof原理

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 返回值

返回值为布尔值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()

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354

推荐阅读更多精彩内容