typeof
typeof
一般被用于判断一个变量的类型,我们可以利用 typeof
来判断number
, string
, object
, boolean
, function
, undefined
, symbol
这七种类型,这种判断能帮助我们搞定一些问题
看下实例
// Numbers
typeof 37 === 'number';
typeof 3.14 === 'number';
typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // 尽管NaN是"Not-A-Number"的缩写,意思是"不是一个数字"
typeof Number(1) === 'number'; // 不要这样使用!
// Strings
typeof "" === 'string';
typeof "bla" === 'string';
typeof (typeof 1) === 'string'; // typeof返回的肯定是一个字符串
typeof String("abc") === 'string'; // 不要这样使用!
// Booleans
typeof true === 'boolean';
typeof false === 'boolean';
typeof Boolean(true) === 'boolean'; // 不要这样使用!
// Symbols
typeof Symbol() === 'symbol';
typeof Symbol('foo') === 'symbol';
typeof Symbol.iterator === 'symbol';
// Undefined
typeof undefined === 'undefined';
typeof blabla === 'undefined'; // 一个未定义的变量,或者一个定义了却未赋初值的变量
// Objects
typeof {a:1} === 'object';
// 使用Array.isArray或者Object.prototype.toString.call方法可以从基本的对象中区分出数组类型
typeof [1, 2, 4] === 'object';
typeof new Date() === 'object';
// 下面的容易令人迷惑,不要这样使用!
typeof new Boolean(true) === 'object';
typeof new Number(1) ==== 'object';
typeof new String("abc") === 'object';
// 函数
typeof function(){} === 'function';
typeof Math.sin === 'function';
我们会发现在判断不是 object 类型的数据的时候,typeof
能比较清楚的告诉我们具体是哪一类的类型。但是,很遗憾的一点是,typeof
在判断一个 object的数据的时候只能告诉我们这个数据是 object, 而不能细致的具体到是哪一种 object, 比如
let s = new String('abc');
typeof s === 'object'// true
s instanceof String // true
所以在 typeof
判断类型的基础上,我们还需要利用 Object.prototype.toString
方法来进一步判断数据类型。
Object.prototype.toString
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('hi') // "[object String]"
Object.prototype.toString.call({a:'hi'}) // "[object Object]"
Object.prototype.toString.call([1,'a']) // "[object Array]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(() => {}) // "[object Function]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
数据 | toString | typeof |
---|---|---|
‘string’ | String | string |
new String(‘foo’) | String | object |
new Number(1.2) | Number | object |
true | Boolean | boolean |
new Boolean(true) | Boolean | object |
new Date() | Date | object |
new Error() | Error | object |
new Array(1, 2, 3) | Array | object |
/abc/g | RegExp | object |
new RegExp(‘meow’) | RegExp | object |
可以看到利用toString方法可以正确区分出Array、Error、RegExp、Date等类型。
所以我们一般通过该方法来进行数据类型的验证
instanceof
instanceof
主要的作用就是判断一个实例是否属于某种类型,instanceof
主要的实现原理就是只要右边变量的 prototype
在左边变量的原型链上即可。因此,instanceof
在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype
,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。
function new_instance_of(leftVaule, rightVaule) {
let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值
leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值
while (true) {
if (leftVaule === null) {
return false;
}
if (leftVaule === rightProto) {
return true;
}
leftVaule = leftVaule.__proto__
}
}
总结
简单来说,我们使用 typeof
来判断基本数据类型是 ok 的,不过需要注意当用 typeof
来判断 null
类型时的问题,如果想要判断一个对象的具体类型可以考虑用 instanceof
,但是 instanceof
也可能判断不准确,比如一个数组,他可以被 instanceof
判断为 Object。所以我们要想比较准确的判断对象实例的类型时,可以采取 Object.prototype.toString.call
方法。
面试题
# 1
var y = 1, x = y = typeof x;
x; // 表达式是从右往左的,x由于变量提升,类型为undefined,所以x = y = 'undefined'。
# 2
(function f(f) {
return typeof f();
})(function () {
return 1;
});
// 传入的参数为f也就是function(){ return 1; }这个函数。通过f()执行后,得到结果1,
// 所以typeof 1 返回'number'。
# 3
var foo = {
bar: function () {
return this.baz;
},
baz: 1
};
(function () {
return typeof arguments[0]();
})(foo.bar);
// this永远指向函数执行时的上下文,
// 而不是定义时的(ES6的箭头函数不算)。
// 当arguments执行时,this已经指向了window对象。所以是'undefined'。
# 4
var foo = {
bar: function () {
return this.baz;
},
baz: 1
}
typeof (f = foo.bar)(); // 同上
# 5
var f = (function f() {
return "1";
}, function g() {
return 2;
})();
typeof f;
// 分组选择父执行的最后一个 'number'
# 6
var x = 1;
if (function f() {}) {
x += typeof f;
}
x;
// 在条件判断中只有null,undefined, 0, NaN,false为false,所以传入函数时为true
// 但是javascript引擎在搜索的时候却找不到该函数。所以结果为'1undefined'。