js如何檢測變量

了解到了JavaScript的變量主要有基本類型(undefined、null城须、boolean扒吁、number和string, ES6中還新增了Symbol)和引用類型(對象稳摄、數(shù)組、函數(shù))泼橘。但在JavaScript中用戶定義的類型(object)并沒有類的聲明涝动,因此繼承關(guān)系只能通過構(gòu)造函數(shù)和原型鏈接來檢查。而在這篇文章中炬灭,主要整理了在JavaScript中如何檢測一個(gè)變量的類型醋粟。

在JavaScript中常見的類型檢查手段主要有:typeof、instanceof重归、constructor和toString幾種米愿。接下來主要看看這幾種類型檢查手段的使用與區(qū)別之處。

1:type of(操作符返回的是字符串提前,它的返回值如下)

    Undefined>>>>Undefined    Null>>>"object"  
    布爾值>>>>"boolean"      數(shù)值 >>>"number"
    字符串>>>"string"        Symbol>>>"Symbol" 
    函數(shù)對象>>>"function"    任何其他對象>>"object"  
    宿主對象(JS環(huán)境提供的吗货,比如瀏覽器) >>>> Implementation-dependent


  typeof ["w3cplus","大漠"];
    typeof new Date();
    typeof new String("w3cplus");
    typeof new function (){};
    typeof /test/i;  返回的都是object

另外對于Null,typeof檢測返回的值也是一個(gè)object:
這是typeof的一個(gè)知名Bug狈网。先忽略其是不是typeof的bug宙搬,在JavaScript中笨腥,null也是基本數(shù)據(jù)類型之一,它的類型顯然是Null勇垛。其實(shí)這也反映了null的語義脖母,它是一個(gè)空指針表示對象為空,而undefined才表示什么都沒有

根據(jù)上面的內(nèi)容闲孤,簡單的對typeof做一個(gè)歸納:

   typeof只能檢測基本數(shù)據(jù)類型谆级,對于null還有一個(gè)Bug。

在實(shí)際開發(fā)中使用typeof時(shí)需要養(yǎng)成一個(gè)好的習(xí)慣讼积。比如肥照,使用typeof一個(gè)較好的習(xí)慣是寫一個(gè)多種狀態(tài)的函數(shù):

function f (x) { if (typeof x == "function") { ... // 當(dāng)x是一個(gè)函數(shù)時(shí),做些什么... } else { ... // 其它狀態(tài) } }

勤众,前面使用typeof 對一個(gè)數(shù)組做檢測的時(shí)候也返回object

那么在JavaScrit中舆绎,可以通過創(chuàng)建一個(gè)函數(shù),并且通過一些正則表達(dá)式们颜,讓這個(gè)函數(shù)實(shí)現(xiàn)一個(gè)改進(jìn)版本的typeof吕朵。如下所示:

 function toType (obj) { 
   return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
 }

之后我們做測試,則如下:

    toType({name: "大漠"}); // => "object"
    toType(["W3cplus","大漠"]); // => "array"
    (function() {console.log(toType(arguments))})(); // => arguments
    toType(new ReferenceError); // => "error"
    toType(new Date); // => "date"
    toType(/a-z/); // => "regexp"
    toType(Math); // => "math"
    toType(JSON); // => "json"
    toType(new Number(4)); // => "number"
    toType(new String("abc")); // => "string"
    toType(new Boolean(true)); // => "boolean"
    toType(function foo() {console.log("Test")}); // =>"function"

2:instanceof(操作符用于檢測某個(gè)對象的原型鏈?zhǔn)欠癜硞€(gè)構(gòu)造函數(shù)的prototype屬性)

function C(){}
function D(){}
var o = new C();
// true窥突,因?yàn)?Object.getPrototypeOf(o) === C.prototype
o instanceof C;
// false努溃,因?yàn)?D.prototype不在o的原型鏈上
o instanceof D;

o對象的原型鏈上有很多對象(成為隱式原型),比如o.proto阻问,o.proto.proto等等梧税。因?yàn)?Object.getPrototypeOf(o) === C.prototype所以返回的是true,而D.prototype不在o的原型鏈上,所以返回的是false。

需要注意的是则拷,如果表達(dá)式 o instanceof C 返回true贡蓖,則并不意味著該表達(dá)式會(huì)永遠(yuǎn)返回ture,因?yàn)镃.prototype屬性的值有可能會(huì)改變煌茬,改變之后的值很有可能不存在于o的原型鏈上斥铺,這時(shí)原表達(dá)式的值就會(huì)成為false。另外一種情況下坛善,原表達(dá)式的值也會(huì)改變晾蜘,就是改變對象o的原型鏈的情況,雖然在目前的ES規(guī)范中眠屎,我們只能讀取對象的原型而不能改變它剔交,但借助于非標(biāo)準(zhǔn)的proto魔法屬性,是可以實(shí)現(xiàn)的改衩。比如執(zhí)行o.proto = {}之后岖常,o instanceof C就會(huì)返回false了。

    instanceof是通過原型鏈來檢查類型的葫督。所謂的“類型”是一個(gè)構(gòu)
    造函數(shù)竭鞍。例如:
    // 比如直接原型關(guān)系
    function Animal () {};
    var a =  new Animal();
    a instanceof Animal; // => true
    // 原型鏈上的間接原型
    function Cat() {};
    Cat.prototype = new Animal;
    var b = new Cat();
    b instanceof Animal; // =>

instanceof除了適用于任何object的類型檢查之外板惑,也可以用來檢測內(nèi)置兌現(xiàn),比如:Array偎快、RegExp冯乘、Object、Function:

        [1, 2, 3] instanceof Array // true
        /abc/ instanceof RegExp // true
        ({}) instanceof Object // true

instanceof對基本數(shù)據(jù)類型檢測不起作用晒夹,主要是因?yàn)榛緮?shù)據(jù)類型沒有原型
但是還可以用的:如下:

 new Number(3) instanceof Number // true
new Boolean(true) instanceof Boolean // true
new String('abc') instanceof String // true

不過這個(gè)時(shí)候裆馒,都知道數(shù)據(jù)類型了,再使用instanceof來做檢測就毫無意義了丐怯。

簡單總結(jié)一下

   instanceof適用于檢測對象喷好,它是基于原型鏈運(yùn)作的。

3:constructor(返回一個(gè)指向創(chuàng)建了該對象原型的函數(shù)引用读跷。需要注意的是绒窑,該屬性的值是那個(gè)函數(shù)本身)

  function Animal () {};
    var a = new Animal;
    a.constructor === Animal; // => true

constructor不適合用來判斷變量類型。首先因?yàn)樗且粋€(gè)屬性舔亭,所以非常容易偽造:

 var a = new Animal;
a.constructor === Array;
a.constructor === Animal; // => false

另外constructor指向的是最初創(chuàng)建當(dāng)前對象的函數(shù),是原型鏈最上層的那個(gè)方法:

function Cat () { };
Cat.prototype = new Animal;
function BadCat () { };
BadCat.prototype = new Cat;
var a = new BadCat;
a.constructor === Animal;  // => true
Animal.constructor === Function; // => true

與instanceof類似蟀俊,constructor只能用于檢測對象钦铺,對基本數(shù)據(jù)類型無能為力。而且因?yàn)閏onstructor是對象屬性肢预,在基本數(shù)據(jù)類型上調(diào)用會(huì)拋出TypeError異常:

 null.constructor; // => TypeError
 undefined.constructor; // => TypeError

和instanceof不同的是矛洞,在訪問基本數(shù)據(jù)類型的屬性時(shí),JavaScript會(huì)自動(dòng)調(diào)用其構(gòu)造函數(shù)來生成一個(gè)對象烫映,如:

.constructor === Number // true
true.constructor === Boolean // true
'abc'.constructor === String // true
// 相當(dāng)于
(new Number(3)).constructor === Number
(new Boolean(true)).constructor === Boolean
(new String('abc')).constructor === String

另外沼本,使用constructor有兩個(gè)問題。第一個(gè)問題它不會(huì)走原型鏈:

      function Animal () {};
    function Cat () {};
    Cat.prototype = new Animal;
    Cat.prototype.constructor = Cat;
    var felix = new Cat;
    felix.constructor === Cat; // => true
    felix.constructor === Animal; // => false

就是null和undefined使用constructor會(huì)報(bào)異常

同樣對constructor做一個(gè)簡單的總結(jié)

constructor指向的是最初創(chuàng)建者锭沟,而且易于偽造抽兆,不適合做類型判斷。

跨窗口問題

JavaScript是運(yùn)行在宿主環(huán)境下的族淮,而每個(gè)宿主環(huán)境都會(huì)提供一套標(biāo)準(zhǔn)的內(nèi)置對象辫红,以及宿主對象(如window,document),一個(gè)新的窗口即是一個(gè)新的宿主環(huán)境祝辣。不同的窗口下的內(nèi)置對象是不同的實(shí)例贴妻,擁有不同的內(nèi)存地址。

而instanceof和constructor都是通過比較兩個(gè)Function是否相等來進(jìn)行類型判斷的蝙斜。 此時(shí)顯然會(huì)出問題名惩,例如:

  var iframe = document.createElement('iframe');
    var iWindow = iframe.contentWindow;
    document.body.appendChild(iframe);
     
    iWindow.Array === Array         // false
    // 相當(dāng)于
    iWindow.Array === window.Array  // false

因此iWindow中的數(shù)組arr原型鏈上是沒有window.Array的

toString

最簡單的數(shù)據(jù)類型檢測方法應(yīng)當(dāng)算是toString,不過其看起來像是一個(gè)黑魔法:

toString屬性定義在Object.prototype上孕荠,因而所有對象都擁有toString方法娩鹉。默認(rèn)情況之下攻谁,調(diào)用{}.toString()(一個(gè)object),將會(huì)得到[object object]底循。

我們可以通過.call()來改變這種情況(因?yàn)樗鼘⑵鋮?shù)轉(zhuǎn)換為值類型)巢株。例如,通過使用.call(/test/i)(正則表達(dá)多),這個(gè)時(shí)候[object object]將變成[object RegExp]熙涤。

    Object.prototype.toString.call([]); // => [object Array]
    Object.prototype.toString.call({}); // => [object Object]
    Object.prototype.toString.call(''); // => [object String]
    Object.prototype.toString.call(new Date()); // => [object Date]
    Object.prototype.toString.call(1); // => [object Number]
    Object.prototype.toString.call(function () {}); // => [object Function]
    Object.prototype.toString.call(/test/i); // => [object RegExp]
    Object.prototype.toString.call(true); // => [object Boolean]
    Object.prototype.toString.call(null); // => [object Null]
    Object.prototype.toString.call(); // => [object Undefined]

不過toString也不是十全十美的阁苞,因?yàn)樗鼰o法檢測用戶自定義類型。主要是因?yàn)镺bject.prototype是不知道用戶會(huì)創(chuàng)造什么類型的祠挫,它只能檢測ECMA標(biāo)準(zhǔn)中的那些內(nèi)置類型那槽。

    function Animal () {};
    Object.prototype.toString.call (Animal); // => [object Function]
    Object.prototype.toString.call (new Animal); // => [object Object]

和Object.prototype.toString類似,F(xiàn)unction.prototype.toString也有類似功能等舔,不過它的this只能是Function骚灸,其它類型(如基本數(shù)據(jù)類型)都會(huì)拋出異常。

自定義檢測數(shù)據(jù)類型的函數(shù)

通過前面的內(nèi)容介紹慌植,我們可以獲知:
typeof只能檢測基本數(shù)據(jù)類型甚牲,對于null還有Bug;
instanceof適用于檢測對象蝶柿,它是基于原型鏈運(yùn)作的丈钙;
constructor指向的是最初創(chuàng)建者,而且容易偽造交汤,不適合做類型判斷雏赦;
toString適用于ECMA內(nèi)置JavaScript類型(包括基本數(shù)據(jù)類型和內(nèi)置對象)的類型判斷;
基于引用判等的類型檢查都有跨窗口問題芙扎,比如instanceof和constructor星岗。

總之,如果你要判斷的是基本數(shù)據(jù)類型或JavaScript內(nèi)置對象戒洼,使用toString俏橘; 如果要判斷的是自定義類型,請使用instanceof圈浇。

其實(shí)敷矫,為了便于使用,可以在toString的基礎(chǔ)上封閉一個(gè)函數(shù)汉额。比如@toddmotto寫的axis.js:

    (function (root, factory) {
      if (typeof define === 'function' && define.amd) {
        define([], factory);
      } else if (typeof exports === 'object') {
        module.exports = factory();
      } else {
        root.axis = factory();
      }
    }(this, function () {
      'use strict';
     
      var axis = {};
     
      var types = 'Array Object String Date RegExp Function Boolean Number Null Undefined'.split(' ');
     
      function type() {
        return Object.prototype.toString.call(this).slice(8, -1);
      }
     
      for (var i = types.length; i--;) {
        axis['is' + types[i]] = (function (self) {
          return function (elem) {
            return type.call(elem) === self;
          };
        })(types[i]);
      }
     
      return axis;
     
    }));

有了這個(gè)函數(shù)曹仗,咱們只需要像下面這樣使用,就可以檢測數(shù)據(jù)類型:

   axis.isArray([]); // true
    axis.isObject({}); // true
    axis.isString(''); // true
    axis.isDate(new Date()); // true
    axis.isRegExp(/test/i); // true
    axis.isFunction(function () {}); // true
    axis.isBoolean(true); // true
    axis.isNumber(1); // true
    axis.isNull(null); // true
    axis.isUndefined(); // true

總結(jié)

    typeof  instanceof  constructor toString
    避免字符串比較 No  Yes Yes No
    常用的 Yes Yes No  No
    檢查自定義類  No  Yes Yes No
    直接檢查null    No  No  No  Yes
    直接檢查undefined   Yes No  No  Yes
    跨窗口工作   Yes No  No  Yes

我們總結(jié)為一句口訣:如果你要判斷的是基本數(shù)據(jù)類型或JavaScript內(nèi)置對象蠕搜,使用toString怎茫; 如果要判斷的時(shí)自定義類型,請使用instanceof。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末轨蛤,一起剝皮案震驚了整個(gè)濱河市蜜宪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌祥山,老刑警劉巖圃验,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異缝呕,居然都是意外死亡澳窑,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進(jìn)店門供常,熙熙樓的掌柜王于貴愁眉苦臉地迎上來摊聋,“玉大人,你說我怎么就攤上這事栈暇÷椴茫” “怎么了?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵源祈,是天一觀的道長煎源。 經(jīng)常有香客問我,道長香缺,這世上最難降的妖魔是什么薪夕? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮赫悄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘馏慨。我一直安慰自己埂淮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布写隶。 她就那樣靜靜地躺著倔撞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪慕趴。 梳的紋絲不亂的頭發(fā)上痪蝇,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天,我揣著相機(jī)與錄音冕房,去河邊找鬼躏啰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛耙册,可吹牛的內(nèi)容都是我干的给僵。 我是一名探鬼主播,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼帝际!你這毒婦竟也來了蔓同?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤蹲诀,失蹤者是張志新(化名)和其女友劉穎斑粱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脯爪,經(jīng)...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡则北,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了披粟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咒锻。...
    茶點(diǎn)故事閱讀 40,769評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖守屉,靈堂內(nèi)的尸體忽然破棺而出惑艇,到底是詐尸還是另有隱情,我是刑警寧澤拇泛,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布滨巴,位于F島的核電站,受9級特大地震影響俺叭,放射性物質(zhì)發(fā)生泄漏恭取。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一熄守、第九天 我趴在偏房一處隱蔽的房頂上張望蜈垮。 院中可真熱鬧,春花似錦裕照、人聲如沸攒发。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惠猿。三九已至,卻和暖如春负间,著一層夾襖步出監(jiān)牢的瞬間偶妖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工政溃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留趾访,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓董虱,卻偏偏與公主長得像腹缩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 1.通過typeof可以判斷處幾種基本數(shù)據(jù)類型Boolean,number,string,null,undefin...
    舟漁行舟閱讀 647評論 0 1
  • ??面向?qū)ο螅∣bject-Oriented,OO)的語言有一個(gè)標(biāo)志盘寡,那就是它們都有類的概念楚殿,而通過類可以創(chuàng)建任意...
    霜天曉閱讀 2,112評論 0 6
  • 第3章 基本概念 3.1 語法 3.2 關(guān)鍵字和保留字 3.3 變量 3.4 數(shù)據(jù)類型 5種簡單數(shù)據(jù)類型:Unde...
    RickCole閱讀 5,132評論 0 21
  • 【大楚】的情書 大楚寶貝, 前段時(shí)間我們讀了個(gè)物理故事繪本竿痰,故事本身你不感興趣脆粥,卻對里面提到的物理變化和化學(xué)變...
    浮沉浮沉閱讀 126評論 0 0
  • 最放松的周末,生物鐘就是這么強(qiáng)大影涉,6:00前自然醒了变隔,原本計(jì)劃晨跑的,但想到晩上20:10-21:00要去游泳蟹倾,而...
    好心情2816閱讀 173評論 1 1