typeof

typeof 一般被用于判断一个变量的类型,我们可以利用 typeof 来判断number, string, object, boolean, function, undefined, symbol 这七种类型,这种判断能帮助我们搞定一些问题

看下实例

  1. // Numbers
  2. typeof 37 === 'number';
  3. typeof 3.14 === 'number';
  4. typeof Math.LN2 === 'number';
  5. typeof Infinity === 'number';
  6. typeof NaN === 'number'; // 尽管NaN是"Not-A-Number"的缩写,意思是"不是一个数字"
  7. typeof Number(1) === 'number'; // 不要这样使用!
  8. // Strings
  9. typeof "" === 'string';
  10. typeof "bla" === 'string';
  11. typeof (typeof 1) === 'string'; // typeof返回的肯定是一个字符串
  12. typeof String("abc") === 'string'; // 不要这样使用!
  13. // Booleans
  14. typeof true === 'boolean';
  15. typeof false === 'boolean';
  16. typeof Boolean(true) === 'boolean'; // 不要这样使用!
  17. // Symbols
  18. typeof Symbol() === 'symbol';
  19. typeof Symbol('foo') === 'symbol';
  20. typeof Symbol.iterator === 'symbol';
  21. // Undefined
  22. typeof undefined === 'undefined';
  23. typeof blabla === 'undefined'; // 一个未定义的变量,或者一个定义了却未赋初值的变量
  24. // Objects
  25. typeof {a:1} === 'object';
  26. // 使用Array.isArray或者Object.prototype.toString.call方法可以从基本的对象中区分出数组类型
  27. typeof [1, 2, 4] === 'object';
  28. typeof new Date() === 'object';
  29. // 下面的容易令人迷惑,不要这样使用!
  30. typeof new Boolean(true) === 'object';
  31. typeof new Number(1) ==== 'object';
  32. typeof new String("abc") === 'object';
  33. // 函数
  34. typeof function(){} === 'function';
  35. typeof Math.sin === 'function';

我们会发现在判断不是 object 类型的数据的时候,typeof能比较清楚的告诉我们具体是哪一类的类型。但是,很遗憾的一点是,typeof 在判断一个 object的数据的时候只能告诉我们这个数据是 object, 而不能细致的具体到是哪一种 object, 比如

  1. let s = new String('abc');
  2. typeof s === 'object'// true
  3. s instanceof String // true

所以在 typeof 判断类型的基础上,我们还需要利用 Object.prototype.toString 方法来进一步判断数据类型。

Object.prototype.toString

  1. Object.prototype.toString.call(1) // "[object Number]"
  2. Object.prototype.toString.call('hi') // "[object String]"
  3. Object.prototype.toString.call({a:'hi'}) // "[object Object]"
  4. Object.prototype.toString.call([1,'a']) // "[object Array]"
  5. Object.prototype.toString.call(true) // "[object Boolean]"
  6. Object.prototype.toString.call(() => {}) // "[object Function]"
  7. Object.prototype.toString.call(null) // "[object Null]"
  8. Object.prototype.toString.call(undefined) // "[object Undefined]"
  9. 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'。