【范围】typeof返回值范围:
类型 | 结果 |
String | "string" |
Number | "number" |
Boolean | "boolean" |
Undefined | "undefined" |
Object | "object" |
function函数对象 | "function" |
Symbol(ES6新增) | "symbol" |
宿主对象(由JS环境提供) | Implementation-dependent |
【typeof为什么要区分object和function?】
- 答案一:《JavaScript高级程序设计》:从技术角度讲,函数在ECMAScript中是对象,不是一种数据类型。然而,函数也确实有一些特殊的属性,因此通过typeof操作符来区分函数和其他对象是有必要的。
- 答案二:在实际的使用过程中有必要区分Object和Function,所以在typeof这里实现了
【typeof的不足之处】
- 不能区分对象、数组、正则,对它们操作都返回"object";(正则特殊一点后面说)
- Safar5,Chrome7之前的版本对正则对象返回 'function'
- 在IE6,7和8中,大多数的宿主对象是对象,而不是函数;如:typeof alert; //object
- 而在非ID浏览器或则IE9以上(包含IE9),typeof alert; //function
【记忆行为】
- 根据JS数据类型记忆String/Number/Boolean/Undefined/Object/function返回的字符串形式分别为:string/number/boolean/undefined/object/function
- 特殊记忆:
- Symbol(ES6新增)=> "symbol"
- 不能区分对象、数组、正则,返回"object",正则在Safar5,Chrome7之前的版本中返回"function"
- 宿主对象,IE6/7/8返回"object",其他浏览器返回"function"
- 特殊中的特殊
typeof 1/0; //NaN(这个NaN不是字符串类型,是数值类型) typeof typeof 1/0; //NaN(这个NaN不是字符串类型,是数值类型) typeof(1/0); //"number" typeof typeof(1/0); //"string" typeof(typeof 1/0); //"number"
【题目和答案】
// 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 declaredButUndefinedVariable === 'undefined'; typeof undeclaredVariable === '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'; //NaN typeof 1/0 === 'NaN';
我们会发现一个问题,就是typeof来判断数据类型其实并不准确。比如数组、正则、日期、对象的typeof返回值都是object,这就会造成一些误差。
所以在typeof判断类型的基础上,我们还需要利用Object.prototype.toString方法来进一步判断数据类型。
我们来看看在相同数据类型的情况下,toString方法和typeof方法返回值的区别:
数据 | toString | typeof |
---|---|---|
“foo” | 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等类型。
所以我们一般通过该方法来进行数据类型的验证
真题检测
但是既然今天说到了typeof,那这里就列出几道题目,来看看自己是否真正掌握了typeof的用法。
第一题:
var y = 1, x = y = typeof x;
x;
- 1
- 2
- 3
第二题:
(function f(f){
return typeof f();
})(function(){ return 1; });
- 1
- 2
- 3
- 4
第三题:
var foo = {
bar: function() { return this.baz; },
baz: 1
};
(function(){
return typeof arguments[0]();
})(foo.bar);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
第四题:
var foo = {
bar: function(){ return this.baz; },
baz: 1
}
typeof (f = foo.bar)();
- 1
- 2
- 3
- 4
- 5
- 6
第五题:
var f = (function f(){ return "1"; }, function g(){ return 2; })();
typeof f;
- 1
- 2
- 3
第六题:
var x = 1;
if (function f(){}) {
x += typeof f;
}
x;
- 1
- 2
- 3
- 4
- 5
- 6
第七题:
(function(foo){ return typeof foo.bar; })({ foo: { bar: 1 } });
- 1
- 2
- 3
- 4
下面公布答案了,这七题的答案分别是:
"undefined","number","undefined","undefined","number","1undefined","undefined"
- 1
做对了几道呢?是不是很大的困惑呢?这几题虽然都有typeof,但是考察了很多javascript的基础噢。下面我们来一一详解。
第一题:
var y = 1, x = y = typeof x;
x;//"undefined"
- 1
- 2
- 3
表达式是从右往左的,x由于变量提升,类型不是null,而是undefined,所以x=y=”undefined”。
变量提升我在这篇文章中提到过,可以看看。
第二题:
(function f(f){
return typeof f();//"number"
})(function(){ return 1; });
- 1
- 2
- 3
- 4
传入的参数为f也就是function(){ return 1; }这个函数。通过f()执行后,得到结果1,所以typeof 1返回”number”。这道题很简单,主要是区分f和f()。
第三题:
var foo = {
bar: function() { return this.baz; },
baz: 1
};
(function(){
return typeof arguments[0]();//"undefined"
})(foo.bar);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
这一题考察的是this的指向。this永远指向函数执行时的上下文,而不是定义时的(ES6的箭头函数不算)。当arguments执行时,this已经指向了window对象。所以是”undefined”。对this执行不熟悉的同学可以看看这篇文章:深入理解this,对刚刚提到的箭头函数感兴趣的同学可以看看初步探究ES6之箭头函数。
第四题:
var foo = {
bar: function(){ return this.baz; },
baz: 1
}
typeof (f = foo.bar)();//undefined
- 1
- 2
- 3
- 4
- 5
- 6
如果上面那一题做对了,那么这一题也应该不会错,同样是this的指向问题。
第五题:
var f = (function f(){ return "1"; }, function g(){ return 2; })();
typeof f;//"number"
- 1
- 2
- 3
这一题比较容易错,因为我在遇到这道题之前也从来没有遇到过javascript的分组选择符。什么叫做分组选择符呢?举一个例子就会明白了:
var a = (1,2,3);
document.write(a);//3,会以最后一个为准
- 1
- 2
所以上面的题目会返回2,typeof 2当然是”number”啦。
第六题:
var x = 1;
if (function f(){}) {
x += typeof f;
}
x;//"1undefined"
- 1
- 2
- 3
- 4
- 5
- 6
这是一个javascript语言规范上的问题,在条件判断中加入函数声明。这个声明语句本身没有错,也会返回true,但是javascript引擎在搜索的时候却找不到该函数。所以结果为”1undefined”。
第七题:
(function(foo){ return typeof foo.bar; })({ foo: { bar: 1 } });
- 1
- 2
- 3
- 4
这题其实是一个考察心细程度的题目。形参的foo指向的是{ foo: { bar: 1 } }这个整体。
instanceof
接下来该说说instanceof方法了。instanceof运算符可以用来判断某个构造函数的prototype属性是否存在于另外一个要检测对象的原型链上。
如果对原型不太了解,可以看看深入理解原型。
下面我们看看instanceof的实例:
// 定义构造函数
function C(){}
function D(){}
var o = new C();
// true,因为 Object.getPrototypeOf(o) === C.prototype
o instanceof C;
// false,因为 D.prototype不在o的原型链上
o instanceof D;
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
但是这里我们需要注意一个问题:
function f(){ return f; }
document.write(new f() instanceof f);//false
function g(){}
document.write(new g() instanceof g);//true
- 1
- 2
- 3
- 4
- 5
第一个为什么返回false呢?因为构造函数的原型被覆盖了,我们可以看看new f和new g的区别: