首页 > 代码库 > JS 对基本类型和引用类型的判断

JS 对基本类型和引用类型的判断

啰啰嗦嗦写了很多,把原本几句话就可以交待的东西扯得那么长。。

但在我曾经对这个问题感到困惑时,就希望能找到让我知其然并知其的讲解。

限于自己的知识和文章篇幅,我无法讲得多么细致,甚至不能保证正确性。

我只能把自己对js的理解写出来,大家可以批判着看。

着急找答案的朋友可以直接翻到后面看总结。。

 

 ------------------------------------------------------------------------------------------------------------

 

对基本类型的检测:

  JS有 6 种基本类型的数据:

    Undefined、Null、Boolean、Number、String,和相对复杂的 Object。

    其中,Undefined 和 Null 比较特殊:都只有一个值,都表示“无”,但前者表示未定义,后者表示空指针(对象)。

    如果用 typeof 操作符对 6 种类型的值进行检测:

typeof undefined;            // 得到 "undefined"typeof null;                 // 得到 "object"typeof false;                // 得到 "boolean"typeof (1);                  // 得到 "number"typeof "str";                // 得到 "string"typeof {};                   // 得到 "object"    


    就会发现 typeof null 得到的不是 “null” 而是 “object”。

    从技术上说 null 属于 object,因为它包含的是一个对象指针。然而与普通对象指针不同的是:这个指针又不指向任何对象。它既存在又不存在,js 用它来描述一个“还没存在或即将不存在的对象”。因为 null 在 object 类型中的特殊性,js 将它独立于普通的Object类型而成为一个新的类型,又因为它与 Object 的本质关联,它的typeof结果还是 "object"。

    说到特殊性,函数(Function)也是具有特殊性的一个对象。

    它属于对象(js是面向对象的语言,除了基本类型,自然就是对象了),但又不局限于对象,甚至高于对象。函数用来描述“行为”,既可以是专属于某个对象的行为,又可以泛指整个程序的行为。它有自己的特殊属性和应用方式,也因此跟其他对象不同。

    null 再特殊充其量也只是个特殊的对象,因此 typeof null === "object";而函数因为其特殊性已经超脱于Object,因此

typeof (function(){});        // 得到 "function"


    函数拥有属于自己的特殊标识 "function",而不是 "object".


对引用类型的判断:
    
  js 是面向对象的语言,对象的封装和使用必不可少。

    对于具体的对象,如果仍旧使用 typeof,往往得不到想要的结果,如:

typeof new Array();                   // 得到 "object",而不是 "array"typeof new Date();                    // 得到 "object",而不是 "date"。

    结果可能跟你所意料的有所出入,但并不能就直接说是错的——数组和正则确实是对象,这何错之有?

    之所以产生这样的结果,我个人觉得是因为 typeof 只负责从大方向上告诉你这是什么类型,于是面对一个对象的时候它只会告诉你这是一个对象而不管这个对象叫什么名字——"array" 和 "date" 确实就是一个名字,他们可以是 "brray" 和 "cate", js并不限制也限制不了对象叫什么(尤其是自定义对象),它只确定能确定的,对不确定的则会给出一个虽然笼统但仍能确保正确的答案,这种设计是合理的。

    typeof 操作符只适用于检测基本类型和函数而不再负责对具体引用类型的判断。

    那么对于具体对象,又要怎么判断其类型?

    用 instanceof 操作符:

    var arr = new Array();    arr instanceof Array;                    // 得到 true    function Sup() {}    var s1 = new Sup();    s1 instanceof Sup();                     // 得到 true,对于自定义对象也能正确检测。


    或者用对象的 constructor 属性

var arr = new Array();arr.constructor === Array;                // 得到 truefunction Sup() {}var s1 = new Sup();s1.constructor === Sup;                   // 对于自定义对象,也能返回正确结果 true

    两种方法看似效果一致,但却存在不同的弊端:

      instanceof 用以检测“一个对象是否某个对象原型的实例”,正常情况下(同一域里) var arr = new Array(), arr自然是Array的实例,检测结果当然准确。但具体应用中,如果涉及到跨域问题,比如某个网页里有不同的frame,代码需要在不同的frame中穿梭运行。那使用instanceof的时候就要注意了。因为每个frame都有自己的window(Global)对象,每个window又有自己的Array对象,它们虽然同名,却不指向同一个对象。说起来拗口,其实道理很简单:在中国你说家在首都,谁都知道指的是北京。在美国如果你也跟人说家在首都,别人又会觉得你在讲华盛顿。“首都(对象原型)”虽然同名,在不同的域(frame)中却指向不同的位置(对象)。

      至于 constructor 的问题更大,它返回一个“当前对象的构造函数”。不仅存在跨域问题,还可能因为实现对象的继承时的疏忽导致错误结果:
      

function Super() {}function Sub() {}Sub.prototype = new Super();// Sub.prototype.constructor = Sub;        // 除非在这里进行指定,否则下一句代码结果为 false。new Sub().constructor === Sub;

    解决的方法并不是没有:

    首先,js对Array类型提供了一个Array.isArray()方法,但需要 ie9+、firefox4+、safari5+、 chrome 或 opera10.5+ 的版本支持,适用性不强。

    此外,在ie之外的浏览器上使用 函数.name 也是个不错的方法,但不推荐使用:
       

arr.constructor.name === "Array";

    最后,书上还提供了一个跨域方法:

Object.prototype.toString.call(对象).indexOf("对象原型") >= 0;var type = Object.prototype.toString.call(arr);        // type 得到 "[object Array]", 再对字符串中的"Array"进行判断即可。

    不过这方法无法使用在对自定义对象上,它对所有的自定义对象都返回 "[object Object]"。对于这点我们只能退而求其次去使用 instanceof 操作符了,所幸的是一般很少会碰到这问题,注意一下就行。

总结:

  1,对于 6 大基本类型和函数引用类型的判断:使用 typeof 操作符可以胜任。

  2,对js内置的引用类型的判断:使用 Object.prototype.toString.call() 方法可以避免跨域问题

  3,对于自定义类型,就只能用 instanceof 了