Javascript原型繼承容易忽略的錯誤

来源:https://www.cnblogs.com/weshare/archive/2018/02/12/8445300.html
-Advertisement-
Play Games

編寫Javascript的開發者都知道,JS雖然沒有類(ES6添加了class語法),但是可以模擬出OOP語言的類和麵向對象的概念,比如我們都知道的一句話,Javascript中處處是對象,而面向對象語言的特性是繼承,封裝,多態,抽象,而本文討論的是Javascript的繼承,Javascript的 ...


  編寫Javascript的開發者都知道,JS雖然沒有類(ES6添加了class語法),但是可以模擬出OOP語言的類和麵向對象的概念,比如我們都知道的一句話,Javascript中處處是對象,而面向對象語言的特性是繼承,封裝,多態,抽象,而本文討論的是Javascript的繼承,Javascript的繼承方式有原型繼承,組合繼承,寄生繼承等等,在日常開發中,哪種繼承方式更好用在於開發者對於程式的結果以及性能的考慮。筆者在下麵列舉出原型繼承中經常容易被忽略的錯誤。

  常見錯誤一:

    

     function Fa(name){
            this.name=name;
        }
        Fa.prototype.myname=function(){  //設置Fa的原型方法
            console.log(this.name);
        }
        
        function Son(name){
            Fa.call(this,name);   //將Fa的this顯示綁定給Son,這裡的this顯示綁定機制後續筆者會更新
        }
        Son.prototype=Fa.prototype;   //將son的原型對象引用到fa的原型對象,其實就是c++裡面的指針概念,直接指向fa的原型
        
        //註意!!!!!:當你修改Son.prototype的constructor時  

   //   Son.prototype.constructor=Son;
// 這樣會導致Fa創建出來的對象的constructor也指向了Son,會使對象的類型變的很混亂 Son.prototype.sayhello=function(){ //設置son的原型方法 console.log("this is son"); } //創建對象 var son=new Son("son"); son.sayhello(); //"this is son" console.log(son); var fa=new Fa("fa"); fa.sayhello(); //"this is son" console.log(fa); //這裡發現在由Fa創建的對象中也存在sayhello方法,這是因為son.prototype直接引用了Fa.prototype // Son.prototype=Fa.prototype; 並不會創建一個關聯到 Son.prototype 的新對象,它只 //是讓 Son.prototype 直接引用 Fa.prototype 對象。因此當你執行類似 Son.prototype. //sayhello = ... 的賦值語句時也會直接修改 Fa.prototype 對象本身。顯然這不是你想要的結 //果,否則你根本不需要 Son 對象,直接使用 Fa就可以了,這樣一來代碼也會更簡單一些。

    常見錯誤二:

function Fa(name){
            this.name=name;
        }
        Fa.prototype.myname=function(){
            console.log(this.name);
        }
        
        function Son(name){
            Fa.call(this,name);
        }
        
        Son.prototype=new Fa("fa");  //調用Fa的構造函數new一個新的對象關聯給Son.prototype
        
        
        Son.prototype.sayhello=function(){
            console.log("this is son");
        }
        
        var son=new Son("son");
        console.log(son);
        son.myname();  //son
        var fa=new Fa("fa");
        console.log(fa);
        fa.sayhello(); //這裡會報錯。Uncaught TypeError: fa.sayhello is not a function
        //我們發現在son的原型創建的方法並沒有影響到Fa的原型。但是在Son.prototype = new Fa()後,
//        var son=new Son("son");我們輸出son.name的值為son,在原型上有一個Fa實例對象,這個實例對象也有name屬性
    //而輸出son是因為原型鏈上的隱式屏蔽,這一層的屬性會屏蔽上一層相同屬性的值。
        
        
//        Son.prototype = new Fa() 的確會創建一個關聯到 Son.prototype 的新對象。但是它使用
//了 Fa(..) 的“構造函數調用”,如果函數 Foo 有一些副作用(比如寫日誌、修改狀態、註
//冊到其他對象、給 this 添加數據屬性,等等)的話,就會影響到 Son() 的“後代”,後果
//不堪設想   也會讓原型變的臃腫
        

  正確做法:

function Fa(name){
            this.name=name;
        }
        Fa.prototype.myname=function(){
            console.log(this.name);
        }
        
        function Son(name){
            Fa.call(this,name);
        }

Son.prototype=Object.create(Fa.prototype); //註意這一句 //當修改son的原型時。son的constructor也會指向fa,這裡需要手動修改constructor //ES6方法 屬性描述符 //IE8以下不相容 Object.defineProperty(Son.prototype,"constructor",{ writable:true, //讀寫屬性 ,為false時為只讀,外界無法修改 configurable:true, //配置屬性,為false時,外界無法刪除該屬性,比如delete Son.prototype.constructor會失效 在嚴格模式下會報錯
enumerable:false, //枚舉屬性,此時為false,在外界的for in訪問方法不會列舉出該屬性,為true時反之; value:Son //將constructor關聯到Son }); var son=new Son("son"); console.log(son); son.myname(); console.log(son instanceof Son); //true // 這段代碼的核心部分就是語句 son.prototype = Object.create( fa.prototype ) 。調用 // Object.create(..) 會憑空創建一個“新”對象並把新對象內部的 Prototype 關聯到你 // 指定的對象(本例中是 fa.prototype )。 // 換句話說,這條語句的意思是:“創建一個新的 son.prototype 對象並把它關聯到 fa. // prototype ”。

     這裡我們對比一下三個方法:

    

   和你想要的機制不一樣!
       Son.prototype = Fa.prototype;
   基本上滿足你的需求,但是可能會產生一些副作用 :(
      Son.prototype = new Fa();

   

  使用 Object.create(..) 而不是使用具有副

  作用的 Fa(..) 。這樣做唯一的缺點就是需要創建一個新對象然後把舊對象拋棄掉,不能
  直接修改已有的預設對象。


       如果能有一個標準並且可靠的方法來修改對象的 Prototype關聯就好了。在 ES6 之前,
  我們只能通過設置 .__proto__ 屬性來實現,但是這個方法並不是標準並且無法相容所有瀏
  覽器。ES6 添加了輔助函數 Object.setPrototypeOf(..) ,可以用標準並且可靠的方法來修
  改關聯。

 

  

  ES6 之前需要拋棄預設的 Son.prototype
   Son.ptototype = Object.create( Fa.prototype );


    ES6 開始可以直接修改現有的 Son.prototype
   Object.setPrototypeOf( Son.prototype,Fa.prototype );


       如果忽略掉 Object.create(..) 方法帶來的輕微性能損失(拋棄的對象需要進行垃圾回收)
它實際上比 ES6 及其之後的方法更短而且可讀性更高。不過無論如何,這是兩種完
 全不同的語法。

 

      以上是筆者對於原型繼承中常常忽略的錯誤的總結以及更好的解決方法,至於寄生繼承之類的方法並不是本文討論的範圍,後續筆者也會更新寄生繼承的方法,如果忽視這些細節上的錯誤,對後續程式運行的結果也會產生一些不可預知的結果,細節決定成敗。

 


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

-Advertisement-
Play Games
更多相關文章
  • 位置是從1 開始 substr("ABCDEFG", 0); //返回:ABCDEFG,截取所有字元 substr("ABCDEFG", 1); //返回:ABCDEFG,截取所有字元 substr("ABCDEFG", -0); //返回:ABCDEFG,截取所有字元 -0=0,都是從左邊截 su ...
  • 一,assign 代表設置時候直接賦值,而不是複製或者保留它。 二,retain. 會在賦值的時候把新值保留。此屬性只能用於Object-C對象類型。 三,copy 在賦值時,將新值複製一份,複製工作由copy執行,此屬性只對那些實行了NSCopying協議的對象類型有效。 參考資料:《iPhone ...
  • 簡介 在開發過程中,經常會使用到第三框架,我們通過一個pod install命令,很方便的就將第三方框架加到我們自己的項目中。 如果我們也想將自己寫的組件或庫開源出去,讓別人也可以通過pod install命令安裝自己的框架該怎麼做呢?下麵,我就教大家一步一步的將自己的pods發佈到CocoaPod ...
  • iOS開發過程中很大一部分內容就是界面佈局和跳轉,iOS的佈局方式也經歷了 顯式坐標定位方式 --> autoresizingMask --> iOS 6.0推出的自動佈局(Auto Layout)的逐步優化,至於為什麼推出自動佈局,肯定是因為之前的方法不好用(哈哈 簡直是廢話),具體如何不好用以及 ...
  • 越來越多公司,開始了組件化,你還要等到什麼時候...... 說到開發模式,我們最熟知的開發模式 MVC 或者最近比較熱門的MVVM。但是我今天說的組件化的開發,其實MVC不是一類的。它其實是····· 在實際工作中,尤其是參與過比較大的項目,同時,參與開發的成員比較多的情況下,隨著時間的不停的開發, ...
  • 這一次要繪製出波浪效果,也是小白的我第一次還望輕噴。首先當然是展示效果圖啦: 一.首先來說說實現思路。 想到波浪效果,當然我第一反應是用正餘弦波來設計啦(也能通過貝塞爾曲線,這裡我不提及這個方法但是在demo里這種方法也實現了),肯定要繪製一個靜態的波,然後通過不斷的對它平移刷新,這樣最簡單的波浪效 ...
  • Android Button Make 右側設置按鈕的屬性,可以即時看到效果,並即時生成對應的.xml 代碼,非常高效(當然熟練的話 自己手寫代碼更快) ...
  • 結論:使用合法屬性名,使用 . 和 [] 訪問都可以; 如果屬性名是數字,則必須用“”包圍,並且用 [] 方括弧訪問。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...