判断数据类型
<h3>JS判断数据类型</h3>
<p>在ECMAScript规范中,共定义了7种数据类型,分为基本数据类型和引用数据类型两大类,如下所示:</p>
<blockquote>
<h5>基本数据类型:String、Number、Boolean、Symbol、Undefined、Null</h5>
<h5>引用类型:Object、Function、Array、RegExp、Date</h5>
</blockquote>
<h3>1.typeof</h3>
<hr />
<p>typeof是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的类型。
包括以下 7 种:number、boolean、symbol、string、object、undefined、function 等。</p>
<pre><code> typeof ''; //string有效
typeof 1; //number有效
typeof Symbol(); //symbol有效
typeof true; //boolean有效
typeof undefined;//undefined 有效
typeof null; //object无效
typeof []; //object 无效
typeof new Function(); // function 有效
typeof new Date(); //object 无效
typeof new RegExp(); //object 无效</code></pre>
<ul>
<li>除了null以外,均可以返回正确的结果。</li>
<li>对于引用类型,除 function 以外,一律返回 object 类型。</li>
<li>对于 null ,返回 object 类型。</li>
<li>对于 function 返回 function 类型。</li>
</ul>
<p>其中,null 有属于自己的数据类型 Null , 引用类型中的 数组、日期、正则 也都有属于自己的具体类型,而 typeof 对于这些类型的处理,只返回了处于其原型链最顶端的 Object 类型,没有错,但不是我们想要的结果。</p>
<h3>2、instanceof</h3>
<p>instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。 在这里需要特别注意的是:instanceof 检测的是原型,我们用一段伪代码来模拟其内部执行过程:</p>
<pre><code>instanceof (A,B) = {
var L = A.__proto__;
var R = B.prototype;
if(L === R) {
// A的内部属性 __proto__ 指向 B 的原型对象
return true;
}
return false;
}
//判断数组对象类型
var a = {};
var b = [];
function c () {}
console.log(Object.prototype.toString.call(a)); //'[Object Object]'
console.log(Object.prototype.toString.call(b)); //'[Object Array]'
console.log(Object.prototype.toString.call(c)); //"[object Function]"</code></pre>
<h3>3.constructor</h3>
<p>当一个函数 F被定义时,JS引擎会为F添加 prototype 原型,然后再在 prototype上添加一个 constructor 属性,并让其指向 F 的引用。如下所示:
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/6871d8b5699d3ebed52f97fb9f3a6e73?showdoc=.jpg" alt="" />
可以看出,F 利用原型对象上的 constructor 引用了自身,当 F 作为构造函数来创建对象时,原型上的 constructor 就被遗传到了新创建的对象上, 从原型链角度讲,构造函数 F 就是新对象的类型。这样做的意义是,让新对象在诞生以后,就具有可追溯的数据类型。
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/ba1d419e49757ed5853d92fa60786b28?showdoc=.jpg" alt="" /></p>
<ol>
<li>
<p>null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。</p>
</li>
<li>函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/fd6411099d66447d723c98492ca70172?showdoc=.jpg" alt="" />
为什么变成了 Object?</li>
</ol>
<p>因为 prototype 被重新赋值的是一个 { }, { } 是 new Object() 的字面量,因此 new Object() 会将 Object 原型上的 constructor 传递给 { },也就是 Object 本身。</p>
<p>因此,为了规范开发,在重写对象原型时一般都需要重新给 constructor 赋值,以保证对象实例的类型不被篡改。</p>
<h3>4.toString</h3>
<p>对于 Object 对象,直接调用 toString() 就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。</p>
<pre><code class="language-javascript">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 的引用
</code></pre>