JS53 判断变量类型

总结了几种判断JavaScript中变量类型的方法。

学到了instanceofiframe中的问题,去美团面试又被拒了吧。(2018.12.13)

知其然不知其所以然,只知道可以用intanceofconstructor,区别又不知道,去头条面试又被拒了吧(2018.12.3)

总结

最常用的判断方法是Object.prototype.slice.call(),其他判断的方法都有着各种问题:

  1. Array.isArray判断数组,需要ES6的支持
  2. typeof有各种特殊情况
  3. instanceof对于字面量不适用,并且在iframe场景中有问题
  4. constructor是可以手动改写的

Object.prototype.toString也有着风险:Object.prototype.toString的结果可以使用Symbol.toStringTag定义(2019.02.01)

方法1 typeof

不太靠谱的方法,对于基本类型是可以的

1
2
3
4
5
6
7
8
9
10
11
typeof 123
// "number"

typeof '123'
// "string"

typeof true
// "boolean"

typeof undefined
// "undefined"

对于引用类型行不通,判断结果都是object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typeof []
// "object"

typeof new Array()
// "object"

typeof {}
// "object"

typeof null
// "object"

typeof JSON
// "object"

typeof Math
// "object"

方法2:instanceof

instanceof运算符用来验证原型对象与实例对象之间的关系。

==当a.___proto___ === A.prototype成立时,instanceof返回true==

1
2
3
4
5
6
7
var a = [],
b = '21',
c = {};

a instanceof Array; // true
b instanceof Array; // false
c instanceof Array; // false

但是注意:

1 instanceof只对引用类型有效,==对于基本类型字面量是无效的==,因为基本类型不是对象,不存在原型链的继承关系

1
2
3
4
5
6
7
8
9
10
11
[] instanceof Array;
// true

123 instanceof Number;
// false

Number(123) instanceof Number
// false

new Number(123) instanceof Number
// true

2 instanceof存在继承关系,==对于引用类型都是Object的实例==

1
2
3
4
5
[] instanceof Array;
// true

[] instanceof Object;
// true

3 instanceof在iframe的场景中有可能出现误判

1
2
3
4
5
6
const a = [1, 2, 3];
const iframe = document.querySelector('#iframe');

console.log(a instanceof Array); // ture
console.log(a instanceof iframe.contentWindow.Array); // false
console.log(iframe.contentWindow.Array === Array); // false

可以认为宿主和iframe是两套JS的执行环境,环境中的Array是不同的,所以判断会出现问题。

方法3:constructor

实质上实例是没有constructor属性的,其constructor属性是继承自原型的:

1
2
3
4
5
6
7
function Person(){};
let p = new Person();

p.constructor === p.__proto__.constructor;
// ture
p.constructor === Person.prototype.constructor
// true

应用时:

1
2
3
4
5
6
7
var a = [],
b = '21',
c = {};

console.log(a.constructor === Array); // true
console.log(b.constructor === Array); // false
console.log(c.constructor === Array); // false

它的主要问题是:constructor是可以手动更改的,当通过原型继承时,constructor会被改写

1
2
3
4
5
6
7
8
9
10
function Person() {};
function Man() {};

Man.prototype.constructor === Man;
// true

Man.prototype = new Person();

Man.prototype.constructor === Person;
// true

instacnceofconstructor的区别

  1. instanceof是操作符,而constructor是继承构造函数的原型的一个属性
  2. instanceof对于基本类型的字面量是无效的(返回false),基本类型的字面量的constructro不存在
  3. instancoeof存在继承性,引用类型都是Object的实例,而constructor是可以认为改写的
1
2
3
4
5
6
7
undefined instanceof Object;

null instanceof Object;

null.constructor

undefined.constructo

方法4 Array.isArray()

ES6的Array.isArray()方法可以来判断数组的类型

1
2
3
4
5
6
var a = [],
b = '21',
c = {};
console.log(Array.isArray(a)); // true
console.log(Array.isArray(b)); // false
console.log(Array.isArray(c)); // false

方法5 toString方法

首先要知道,调用Object.prototype.toString返回结果都是[object xxx]的格式,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Object.prototype.toString.call({})
"[object Object]"

Object.prototype.toString.call('')
"[object String]"

Object.prototype.toString.call([])
"[object Array]"

Object.prototype.toString.call(function (){})
"[object Function]"

Object.prototype.toString.call(new Date())
"[object Date]"

Object.prototype.toString.call(true)
"[object Boolean]"

构造一个函数,isType,传入要验证的类型,返回值是一个新的函数:

1
2
3
4
5
6
7
8
9
10
11
function isType(type) {
return function(obj) {
return Object.prototype.toString.call(obj) === `[object ${type}]`
}
}

const isArray = isType('Array')
const isString = isType('String')

console.log(isArray[1, 2, [3]]); // true
console.log(isString({})); // false

这是一个比较稳妥的判断方法。

Object.prototype.toString也有着风险:Object.prototype.toString的结果可以使用Symbol.toStringTag定义:

1
2
var o = { [Symbol.toStringTag]: "MyObject" };
console.log(Object.prototype.toString.call(o)); // [object MyObject]

参考