js的数据类型检测typeof、instanceof、constructor区别(面试高频考点)

123 阅读4分钟

1.typeof

下表总结了 typeof 可能的返回值。有关类型和基本类型的更多信息,可查看 JavaScript 数据结构 页面。

类型结果
Undefined"undefined"
Null"object"原因
Boolean"boolean"
Number"number"
BigInt"bigint"
String"string"
Symbol"symbol"
Function(在 ECMA-262 中实现 [[Call]];classes也是函数)"function"
其他任何对象(数组、对象、null)"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';

// JavaScript 诞生以来便如此
typeof null === "object";

2.instanceof (object instanceof constructor)

object:某个实例对象,constructor:某个构造函数;
instanceof 运算符用来检测(构造函数的 prototype 属性) constructor.prototype 是否存在于参数 (实例对象)object` 的原型链上。

//eg:
// 定义构造函数
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,同上

//eg:
C.prototype = {};
var o2 = new C();

o2 instanceof C; // true
o instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上。

//eg:
D.prototype = new C(); // 继承
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true 因为 C.prototype 现在在 o3 的原型链上

要检测对象不是某个构造函数的实例时,你可以这样做
if (!(mycar instanceof Car)) {}

`String` 和 `Date` 对象同时也属于`Object` 类型(他们是由 `Object` 类派生出来的)
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

类型 `Car`,以及该类型的对象实例 `mycar``instanceof` 运算符表明了这个 `mycar` 对象既属于 `Car` 类型,又属于 `Object` 类型。
function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
var mycar = new Car("Honda", "Accord", 1998);
var a = mycar instanceof Car;    // 返回 true
var b = mycar instanceof Object; // 返回 true

3.constructor

Object.prototype.constructorconstructor 属性返回 Object 的构造函数(用于创建实例对象)。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。

原始类型的值是只读的,如:1true 和 "test"

所有对象(使用 Object.create(null) 创建的对象除外)都将具有 constructor 属性。在没有显式使用构造函数的情况下,创建的对象(例如对象和数组文本)将具有 constructor 属性,这个属性指向该对象的基本对象构造函数类型。

const o = {}
o.constructor === Object // true

const o = new Object
o.constructor === Object // true

const a = []
a.constructor === Array // true

const a = new Array
a.constructor === Array // true

const n = new Number(3)
n.constructor === Number // true

function Tree(name) {
  this.name = name
}
const theTree = new Tree('Redwood')
console.log( theTree.constructor)  // function Tree(name) {this.name = name}


可以为除了 `null` 和 `undefined`(因为这两者没有相应的构造函数)之外的任何类型指定 `constructor` 属性(如 `String``Number``Boolean` 等),但基本类型不会保留这些更改(也不会抛出异常)。也是同样的原因,基本类型允许设置任何属性(除了 `null` 和 `undefined`),而不会产生副作用。就是说,每当把这样的基本类型当成对象使用时,其对应的构造函数的实例就会在语句执行后立即被创建和丢弃。
let val = null;
val.constructor = 1; // TypeError: val is null

val = 'abc';
val.constructor = Number; // val.constructor === String

val.foo = 'bar'; // An implicit instance of String('abc') was created and assigned the prop foo
val.foo === undefined; // true, since a new instance of String('abc') was created for this comparison, which doesn't have the foo property


因此,基本上除了上面的提到的基本类型外,任何对象都可以更改 `constructor` 属性的值,**请注意,改变** `constructor` **的属性不会影响** `instanceof` **运算符**:
let a = [];
a.constructor = String
a.constructor === String // true
a instanceof String // false
a instanceof Array // true

a = new Foo();
a.constructor = 'bar'
a.constructor === 'bar' // true

如果对象被密封或冻结,那么更改 constructor 将不会起作用,也不会抛出异常:
let a = Object.seal({});
a.constructor = Number;
a.constructor === Object; // true

大多数情况下,此属性用于定义一个**函数的构造函数(function-constructor)** ,并使用 **new** 和原型链继承进一步使用它。
function Parent() { /* ... */ }
Parent.prototype.parentMethod = function parentMethod() {}

function Child() {
  Parent.call(this) // Make sure everything is initialized properly
}
Child.prototype = Object.create(Parent.prototype) // re-define child prototype to Parent prototype

Child.prototype.constructor = Child // return original constructor to Child