JavaScript instanceof深度剖析以及Object.prototype.toString.call()使用

来源:http://www.cnblogs.com/ylweb/archive/2017/09/10/7499953.html
-Advertisement-
Play Games

本文由segementfalt上的一道instanceof題引出: var str = new String("hello world"); console.log(str instanceof String);//true console.log(String instanceof Functio ...


本文由segementfalt上的一道instanceof題引出:

var str = new String("hello world");
console.log(str instanceof String);//true
console.log(String instanceof Function);//true
console.log(str instanceof Function);//false
 

先拋開這道題,我們都知道在JS中typeof可以檢測變數的基本數據類型。

let s = "abcd";
let b = true;
let i = 22;
let u;
let n = null;
let o = new Object();

alert(typeof s); //string
alert(typeof b); //boolean
alert(typeof i); //number
alert(typeof u); //undefined
alert(typeof n); //object
alert(typeof o); //object

從上面的信息可以看到,如果我們想知道某個值是什麼類型的對象時,typeof就無能為力了!ECMAScript引入了instanceof來解決這個問題。instanceof用來判斷某個構造函數的prototype是否在要檢測對象的原型鏈上

function Fn(){};
var fn = new Fn();
console.log(fn instanceof Fn) //true

//判斷fn是否為Fn的實例,並且是否為其父元素的實例
function Aoo();
function Foo();
Foo.prototype = new Aoo();
  
let foo = new Foo();
console.log(foo instanceof Foo);  //true
console.log(foo instanceof Aoo);  //true

//instanceof 的複雜用法

console.log(Object instanceof Object)      //true
console.log(Function instanceof Function)  //true
console.log(Number instanceof Number)      //false
console.log(Function instaceof Function)   //true
console.log(Foo instanceof Foo)            //false

看到上面的代碼,你大概會有很多疑問吧。有人將ECMAScript-262 edition 3中對instanceof的定義翻譯如下:

function instance_of(L, R) {//L 表示左表達式,R 表示右表達式
    var O = R.prototype;// 取 R 的顯示原型
    L = L.__proto__;// 取 L 的隱式原型
    while (true) { 
        if (L === null) 
            return false; 
        if (O === L)// 這裡重點:當 O 嚴格等於 L 時,返回 true 
            return true; 
        L = L.__proto__; 
    } 
}

我們知道每個對象都有proto([[prototype]])屬性,在js代碼中用**__proto__**來表示,它是對象的隱式屬性,在實例化的時候,會指向prototype所指的對象;對象是沒有prototype屬性的,prototype則是屬於構造函數的屬性。通過proto屬性的串聯構建了一個對象的原型訪問鏈,起點為一個具體的對象,終點在Object.prototype。

Object instanceof Object :

// 區分左側表達式和右側表達式
ObjectL = Object, ObjectR = Object; 
O = ObjectR.prototype = Object.prototype;
L = ObjectL.__proto__ = Function.prototype 
// 第一次判斷
O != L 
// 迴圈查找 L 是否還有 __proto__ 
L = Function.prototype.__proto__ = Object.prototype 
// 第二次判斷
O == L 
// 返回 true

Foo instanceof Foo :

FooL = Foo, FooR = Foo; 
// 下麵根據規範逐步推演
O = FooR.prototype = Foo.prototype 
L = FooL.__proto__ = Function.prototype 
// 第一次判斷
O != L 
// 迴圈再次查找 L 是否還有 __proto__ 
L = Function.prototype.__proto__ = Object.prototype 
// 第二次判斷
O != L 
// 再次迴圈查找 L 是否還有 __proto__ 
L = Object.prototype.__proto__ = null 
// 第三次判斷
L == null 
// 返回 false

現在我們回到開始的題目。
對於第一條判斷:

console.log(str.__proto__ === String.prototype); //true
console.log(str instanceof String);//true

第二條:

console.log(String.__proto__ === Function.prototype) //true
console.log(String instanceof Function);//true

第三條:

console.log(str__proto__ === String.prototype)//true
console.log(str__proto__.__proto__. === Function.prototype) //true
console.log(str__proto__.__proto__.__proto__ === Object.prototype) //true
console.log(str__proto__.__proto__.__proto__.__proto__ === null) //true
console.log(str instanceof Function);//false

總結以上:

str的原型鏈:

str ---> String.prototype ---> Object.prototype

String原型鏈:

String ---> Function.prototype ---> Object.prototype

由上面的討論我們可以得知對於基本類型的判斷我們可以使用typeof,而對於引用類型判斷要用到instanceof,instanceof無法對原始類型進行判斷。當然用constructor也可以來判斷數據類型,但是要註意的是其值是可以被人為修改的,我們用另外一道面試題來談論constuctor:

var A = function() {}; 
A.prototype = {}; 
 
var B = {}; 
console.log(A.constructor);//Function 
console.log(B.constructor);//Object 
 
var a = new A(); 
A.prototype = {}; 
 
var b = new A(); 
b.constructor = A.constructor; 
console.log(a.constructor == A);//false 
console.log(a.constructor == b.constructor);//false 
console.log(a instanceof A);//false 
console.log(b instanceof A);//true 

在JS高程設計裡面,作者是這樣寫的:無論什麼時候,只要創建一個新函數,就會為該函數創建一個prototype屬性,這個屬性指向函數原型對象。在預設情況下,所有的原型都會自動獲得一個constructor屬性,這個屬性指向prototype所在函數的指針。

我們先來看下麵這個例子:

function Foo() {
// ...
}
Foo.prototype.constructor === Foo; // true
var a = new Foo();
a.constructor === Foo; // true

a本身並沒有constructor屬性,雖然a.constructor確實指向Foo,但是這個屬性並不是a由Foo“構造”的。實際上,.constructor 引用同樣被委托給了Foo.prototype,而Foo.prototype.constructor 預設指向Foo。思考以下代碼便知:

function Foo() { /* .. */ }
Foo.prototype = { /* .. */ }; // 創建一個新原型對象
var a1 = new Foo();
a1.constructor === Foo; // false!
a1.constructor === Object; // true!

我們回到拋出的第二道面試題:

var A = function() {};  
A.prototype = {};  //此時對構造函數對象A的prototype屬性重新複製,constructor屬性不見了
 
var B = {}; 
console.log(A.constructor);//Function 函數的構造函數為 function Function() 
console.log(B.constructor);//Object 普通object的構造函數為 function Object() 
 
var a = new A();       new一個新對象
A.prototype = {};      
 
var b = new A(); 
b.constructor = A.constructor; 
console.log(a.constructor == A);//false  a.constructor 為 Object 
console.log(a.constructor == b.constructor);//false b.constructor 為 Function
console.log(a instanceof A);//false 
console.log(b instanceof A);//true 

上面的三種方式都有弊端,接下來引出我們的Object.prototype.toString.call()

Object.prototype.toString()可以通用的來判斷原始數據類型和引用數據類型。

console.log(Object.prototype.toString.call(123)) //[object Number]
console.log(Object.prototype.toString.call('123')) //[object String]
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]
console.log(Object.prototype.toString.call(true)) //[object Boolean]
console.log(Object.prototype.toString.call({})) //[object Object]
console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call(function(){})) //[object Function]

參考:

1: JS高程設計 第六章
2: 你不知道的JavaScript(上捲) 第五章
3: https://www.ibm.com/developerworks/cn/web/1306_jiangjj_jsinstanceof/
4: https://segmentfault.com/q/1010000003872816?_ea=403162
5: http://mobile.51cto.com/web-487689.htm

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>checkbox</title> <script src="jquery.js"></script> </head> <body> <form action=" ...
  • 【正則表達式】1、正則表達式包括兩部分: ① 定義正則表達式的規則 ② 定義正則表達式的模式(i/g/m)2、聲明正則表達式: ① 字面聲明 : var reg = /表達式規則/表達式模式 ② 使用new關鍵字: var reg = new RegExp("表達式規則","表達式模式") eg: ...
  • 上文我們講到了options的配置和獲取數據的方式,本文,我們繼續深入options的配置 一、html-webpack-plugin插件中的options除了自己定義了一些基本配置外,我們是可以任意的添加自定義的數據 webpack.dev.config.js文件: 我們在webpack.dev. ...
  • 一、概述 和select下拉列表相比,jquery+css實現的下拉列表具有更好的靈活性,第二部分的代碼為下拉列表的實現。 二、代碼 下拉列表效果如下: 下拉列表的選項為動態追加,使用on方法,採用事件委派機制,響應選項的單擊事件。 經過測試,以下代碼在Firefox 55.0和Safari 10. ...
  • 還記得我們上文中的index.html文件嗎? 那裡面的script標簽還是寫死的index.bundle.js文件,那麼怎麼把他們變成動態的index.html文件,這個動態生成的index.html文件會動態引入我們打包後生成的js文件呢?,我們可以使用插件html-webpack-plugin ...
  • 一、定義 用來描述或匹配一系列符合某個規則的字元串信息的字元串。 通常用來檢驗字元串是否符合某些規則,或者匹配(替換)被操作的字元串中的某些信息。 二、基本語法 1.以“/”開始,以另一個“/”結束 2.結束符“/”後可以有g(global-在整個字元串中搜索)、i(ignore-忽略大小寫)、m( ...
  • 本文繼續接著上文,繼續寫下webpack.config.js的其他配置用法. 一、把兩個文件打包成一個,entry怎麼配置? 在上文中的webpack.dev.config.js中,用數組配置entry webpack.dev.config.js文件代碼: 然後在src/js目錄下麵新建一個calc ...
  • 在標準流中,一個塊級元素在水平方向會自動伸展,直到包含它的元素的邊界;而在豎直方向和兄弟元素依次排列,不能併排。使用“浮動”方式後,塊級元素的表現就會有所不同。 CSS中有一個float屬性,預設為none,也就是標準流通常的情況。如果將float屬性的值設置為left或right,元素就會向其父元 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...