題目
實(shí)現(xiàn)一個函數(shù)typeof(),輸入一個數(shù)據(jù),返回?cái)?shù)據(jù)的基本類型。
如:
typeof([]) => array
typeof({}) => object
typeof("") => string
等等
解析
由于javascript這門語言輝(keng)煌(die)的歷史,所以連這種簡單的需求都需要自己來實(shí)現(xiàn),唉,說多了,都是淚啊。
這兒題目相對來說應(yīng)該是比較簡單的,但是也是有不少坑,想要真正實(shí)現(xiàn)的很好,還是需要用到不少知識的。
一開始,肯定有人想到使用typeof,顧名思義嘛,就是判斷數(shù)據(jù)的類型,但是,可是,實(shí)際真的是這樣嗎?
typeof操作符
typeof 操作符(和 instanceof 一起)或許是 JavaScript 中最大的設(shè)計(jì)缺陷, 因?yàn)閹缀醪豢赡軓乃鼈兡抢锏玫较胍慕Y(jié)果。 --javascript秘密花園
盡管instanceof 還有一些極少數(shù)的應(yīng)用場景,typeof 只有一個實(shí)際的應(yīng)用,就是用來檢測一個對象是否已經(jīng)定義或者是否已經(jīng)賦值,而這個應(yīng)用卻不是用來檢查對象的類型。(好吧,這個其實(shí)貌似也并沒有什么卵用。。。)
在下面表格中,Type 一列表示 typeof 操作符的運(yùn)算結(jié)果。其中,JavaScript 標(biāo)準(zhǔn)文檔中定義: [[Class]] 的值只可能是下面12個字符串中的一個: Arguments, Array, Boolean, Date, Error, Function, JSON, Math, Number, Object, RegExp, String??梢钥吹?,這個值在大多數(shù)情況下都返回 "object"。
| Value | Class | Type |
|---|---|---|
| "foo" | String | string |
| new String("foo") | String | object |
| 1.2 | Number | number |
| new Number(1.2) | Number | object |
| true | Boolean | boolean |
| new Boolean(true) | Boolean | object |
| new Date() | Date | object |
| new Error() | Error | object |
| [1,2,3] | Array | object |
| new Array(1, 2, 3) | Array | object |
| new Function("") | Function | function |
| /abc/g | RegExp | object (function in Nitro/V8) |
| new RegExp("meow") | RegExp | object (function in Nitro/V8) |
| {} | Object | object |
| new Object() | Object | object |
測試為定義變量
typeof foo !== 'undefined'
上面代碼會檢測 foo 是否已經(jīng)定義;如果沒有定義而直接使用會導(dǎo)致 ReferenceError 的異常。 這是 typeof 唯一有用的地方。
instanceof 操作符
剛說完,typeof,肯定又有人想用instanceof,但是,instanceof真的有用嗎?
instanceof 操作符用來比較兩個操作數(shù)的構(gòu)造函數(shù),instanceof 運(yùn)算符與 typeof 運(yùn)算符相似,用于識別正在處理的對象的類型。具體的可以看看這個JavaScript instanceof 運(yùn)算符深入剖析。
因此,instanceof在判斷一個對象是不是一個類的實(shí)例只有在比較自定義的對象時才有意義。 如果用來比較內(nèi)置類型,將會和 typeof 操作符 一樣用處不大。
比較自定義對象
function Foo() {}
function Bar() {}
Bar.prototype = new Foo();
new Bar() instanceof Bar; // true
new Bar() instanceof Foo; // true
// 如果僅僅設(shè)置 Bar.prototype 為函數(shù) Foo 本身,而不是 Foo 構(gòu)造函數(shù)的一個實(shí)例。
Bar.prototype = Foo;
new Bar() instanceof Foo; // false
instanceof 比較內(nèi)置類型
但是,不是通過構(gòu)造函數(shù)創(chuàng)建的對象使用instanceof比較,那得到的,可能就不是你想要的結(jié)果。
new String('foo') instanceof String; // true
new String('foo') instanceof Object; // true
'foo' instanceof String; // false
'foo' instanceof Object; // false
注意
還有有一點(diǎn)需要注意,instanceof 用來比較屬于不同 JavaScript 上下文的對象(比如,瀏覽器中不同的文檔結(jié)構(gòu))時將會出錯, 因?yàn)樗鼈兊臉?gòu)造函數(shù)不會是同一個對象。
看到這里,是不是很震驚?你所知道的知道的方法,都是錯的。。。唉,當(dāng)初我知道了這個也是淚流滿面啊。。。
解決方法
Object.prototype.toString
javascript對象的內(nèi)部屬性 [[Class]] 的值就包含有j其對象的類型,為了獲取對象的 [[Class]],我們需要使用定義在 Object.prototype 上的方法 toString。
function is(type, obj) {
var clas = Object.prototype.toString.call(obj).slice(8, -1);
return obj !== undefined && obj !== null && clas === type;
}
is('String', 'test'); // true
is('String', new String('test')); // true
Object.prototype.toString 返回一種標(biāo)準(zhǔn)格式字符串,所以上例可以通過 slice 截取指定位置的字符串,如下所示:
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call(2) // "[object Number]"
看看大神的解決方案
如果我沒記錯,在jQuery和underscore等庫中都有判斷數(shù)據(jù)類型的函數(shù),可能平時大家也就用用,沒有仔細(xì)了解過它們的底層是怎么實(shí)現(xiàn)的,
我們要會使用框架,但不要依賴框架
以后大家再碰到類似的問題的時候,不妨查看一下這些成熟框架或庫的實(shí)現(xiàn)源碼,這里,我拋出jQuery的實(shí)現(xiàn)源碼,拋磚引玉。
var class2type = {} ;
"Boolean Number String Function Array Date RegExp Object Error".split(" ").forEach(function(e,i){
class2type[ "[object " + e + "]" ] = e.toLowerCase();
}) ;
//當(dāng)然為了兼容IE低版本,forEach需要一個polyfill,不作細(xì)談了。
function _typeof(obj){
if ( obj == null ){
return String( obj );
}
return typeof obj === "object" || typeof obj === "function" ?
class2type[ Object.prototype.toString.call(obj) ] || "object" :
typeof obj;
}
結(jié)論:
看源碼是程序員快速成長的重要方式。