xiaokeqi / i-learned

1 stars 0 forks source link

判断js数据类型的方法 #59

Open xiaokeqi opened 3 years ago

xiaokeqi commented 3 years ago

typeof

其能识别出的类型有:

number string function object boolean undefined symbol

缺点是:typeof null; //object 无效 typeof[] ; //object 无效 typeof newDate(); //object 无效 typeof newRegExp(); //object 无效

typeof 操作符会返回一些令人迷惑但技术上却正确的值:

对于基本类型,除 null 以外,均可以返回正确的结果。 对于引用类型,除 function 以外,一律返回 object 类型。 对于 null ,返回 object 类型。 对于 function 返回 function 类型。 其中,null 有属于自己的数据类型 Null , 引用类型中的 数组、日期、正则 也都有属于自己的具体类型,而 typeof 对于这些类型的处理,只返回了处于其原型链最顶端的 Object 类型,没有错,但不是我们想要的结果

instanceof

其内部实现过程如下:

instanceof (A,B) = {
    varL = A.__proto__;
    varR = B.prototype;
    if(L === R) {
        // A的内部属性 __proto__ 指向 B 的原型对象
        return true;
    }
    return false;
}

从上述过程可以看出,当 A 的 proto 指向 B 的 prototype 时,就认为 A 就是 B 的实例,我们再来看几个例子:

[] instanceof Array; // true
{} instanceof Object;// true
new Date() instanceof Date;// true

function Person(){};
new Person() instanceof Person;

[] instanceof Object; // true
new Date() instanceof Object;// true
new Person instanceof Object;// true

我们发现,虽然 instanceof 能够判断出 [ ] 是Array的实例,但它认为 [ ] 也是Object的实例,为什么呢?

我们来分析一下 [ ]、Array、Object 三者之间的关系:

从 instanceof 能够判断出 [ ].proto 指向 Array.prototype,而 Array.prototype.proto 又指向了Object.prototype,最终 Object.prototype.proto 指向了null,标志着原型链的结束。因此,[]、Array、Object 就在内部形成了一条原型链:

img

从原型链可以看出,[] 的 proto 直接指向Array.prototype,间接指向 Object.prototype,所以按照 instanceof 的判断规则,[] 就是Object的实例。依次类推,类似的 new Date()、new Person() 也会形成一条对应的原型链 。因此,instanceof 只能用来判断两个对象是否属于实例关系**, 而不能判断一个对象实例具体属于哪种类型。**

constructor

当一个函数 F被定义时,JS引擎会为F添加 prototype 原型,然后再在 prototype上添加一个 constructor 属性,并让其指向 F 的引用。如下所示:

img

当执行 var f = new F() 时,F 被当成了构造函数,f 是F的实例对象,此时 F 原型上的 constructor 传递到了 f 上,因此 f.constructor == F

img

可以看出,F 利用原型对象上的 constructor 引用了自身,当 F 作为构造函数来创建对象时,原型上的 constructor 就被遗传到了新创建的对象上, 从原型链角度讲,构造函数 F 就是新对象的类型。这样做的意义是,让新对象在诞生以后,就具有可追溯的数据类型。

同样,JavaScript 中的内置对象在内部构建时也是这样做的:

img

细节问题:

\1. null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。

\2. 函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object

img

为什么变成了 Object?

因为 prototype 被重新赋值的是一个 { }, { } 是 new Object() 的字面量,因此 new Object() 会将 Object 原型上的 constructor 传递给 { },也就是 Object 本身。

因此,为了规范开发,在重写对象原型时一般都需要重新给 constructor 赋值,以保证对象实例的类型不被篡改。

toString

toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。

对于 Object 对象,直接调用 toString() 就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。

Object.prototype.toString.call(``''``) ;  ``// [object String]``Object.prototype.toString.call(1) ;   ``// [object Number]``Object.prototype.toString.call(``true``) ;``// [object Boolean]``Object.prototype.toString.call(Symbol());``//[object Symbol]``Object.prototype.toString.call(undefined) ;``// [object Undefined]``Object.prototype.toString.call(``null``) ;``// [object Null]``Object.prototype.toString.call(``new``Function()) ;``// [object Function]``Object.prototype.toString.call(``new``Date()) ;``// [object Date]``Object.prototype.toString.call([]) ;``// [object Array]``Object.prototype.toString.call(``new``RegExp()) ;``// [object RegExp]``Object.prototype.toString.call(``new``Error()) ;``// [object Error]``Object.prototype.toString.call(document) ;``// [object HTMLDocument]``Object.prototype.toString.call(window) ;``//[object global] window 是全局对象 global 的引用