筆記: js構造函數與原型

来源:https://www.cnblogs.com/peterzhangsnail/archive/2018/11/22/10002798.html
-Advertisement-
Play Games

@[toc] 構造函數與原型介紹 1.函數與函數的原型對象(prototype object): 在JavaScript中,創建一個函數A, 瀏覽器就會在記憶體中創建一個對象B,而且該函數預設會有一屬性 prototype 指向這個對象(即:prototype屬性的值) 這個對象B就是函數A的 原型對 ...


目錄

@

構造函數與原型介紹

1.函數與函數的原型對象(prototype object):

  • 在JavaScript中,創建一個函數A, 瀏覽器就會在記憶體中創建一個對象B,而且該函數預設會有一屬性 prototype 指向這個對象(即:prototype屬性的值)
  • 這個對象B就是函數A的原型對象,簡稱函數的原型。原型對象B也預設會有一個屬性 constructor 指向了這個函數A (即:constructor屬性的值是函數A)
  • 凡是以函數A為構造函數而創建的對象A1,A2,A3等等,也都有一個內部的[[prototype]]屬性,也指向這個對象B.
    構造函數,原型對象,實例對象之間的三種重要引用:

(1) 構造函數-->原型對象 (A.prototype-->B)

(2) 原型對象-->構造函數 (B.constructor-->A)

(3) 實例對象-->原型對象 (A1.[[Prototype]]/A1._ proto _-->B)

涉及三種引用的操作

2.基於三種引用的操作
如上圖,我們基於這三種引用會有許多的操作(修改,替換,刪除),我們來進行一個分析總結.

  • 構造函數創建實例的具體過程
    參考資料:
    英文版--new [[Construct]] [[Call]]
    中文版--new [[Construct]] [[Call]]
    new A();
    1.令ref = 構造函數A的引用(地址)
    2.令變數constructor = GetValue(ref): 按照ref找到函數對象A的存儲單元
    3.調用constructor的[[Construct]]內部方法:

    • 創建一個新的原生javascript對象obj
    • 按照規範設定好obj對象的一系列內部屬性和方法
    • 設置obj的[[Prototype]] (設置obj的原型)
      • 如果A.prototype是一個對象,令obj.[[Prototype]]=A.prototype
      • 如果A.prototype不是一個對象,令obj.[[Prototype]]=Object.prototype
    • 令變數result = A.[[Call]] (其中,this值為obj)
      (就是以obj為this的值,執行A的函數體中的代碼,目的是對obj進行初始化)

    總結: 由上面的分析可知,如果在構造函數A的函數體內用this給實例添加的屬性,是不會反映到原型上的,屬於實例的本身的屬性.

  • 三種引用是否可以被更改的測試

    //TEST:三種引用是否都可以修改替換
    function A (){}
    var B = A.prototype;
    var A1 = new A();

    //A.prototype與B.constructor
    console.log(Object.getOwnPropertyDescriptor(A,'prototype'));//可修改
    console.log(Object.getOwnPropertyDescriptor(B,'constructor'));//可修改


    //[[Prototype]]
    console.log('prototype' in A1); //false,內部屬性不屬於原型屬性
    console.log(A1.hasOwnProperty('prototype'));//false,內部屬性不屬於自身屬性
    //只有獲取方法,沒有手動修改方法


    //__ proto __
    console.log(' __ proto __ ' in A1);
    console.log(A1.hasOwnProperty(' __ proto __ '));//false, __ proto __ 屬於原型屬性
    console.log(Object.prototype.hasOwnProperty(' __ proto __ '));//true,__ proto __ 定義在Object.prototype上
    console.log(Object.getOwnPropertyDescriptor(Object.prototype, ' __ proto __ '));//configurable:true enumerable:false


    //利用 __ proto __ 間接修改[[prototype]] (不推薦)
    function C() {}
    var D = C.prototype;
    console.log(Object.getPrototypeOf(A1));
    A1. __ proto __ = D; //利用非規範屬性 __ proto __ 間接修改[[prototype]]
    console.log(Object.getPrototypeOf(A1));

    總結: __ proto __屬性是非標準的,是定義在Object.prototype上的一個暴露實例內部[[prototype]]屬性的訪問器屬性.如果我們考慮到代碼的安全和性能,我們可以在代碼開始位置用delete Objet.prototype. _ _ proto _ _ 來刪除掉.

  • 替換構造函數A的原型--修改A.prototype的值
    預計的影響:
    1.已有實例的原型不變,但無法再用A.prototype添加或修改原型屬性
    2.新實例的原型是A.prototype修改後的值,並且可以用A.prototype添加或修改原型屬性

    //TEST:構造函數替換原型對象的影響
    function A() {}
    function B() {}
    var A1 = new A();
    var B1 = new B();


    A.prototype.say = function (){
    alert('A鏈的方法');
    }
    B.prototype.say = function (){
    alert('B鏈的方法');
    }


    var temp = B.prototype;
    B.prototype = A.prototype;
    A.prototype = temp;


    var A2 = new A();
    var B2 = new B();


    //檢測A1 A2 B1 B2 各自的原型鏈
    A1.say();
    B1.say();


    A2.say();
    B2.say();


    //嘗試通過原有構造函數向A1添加原型屬性
    A.prototype.say2 = function (){
    alert('仍可以通過A向A1添加原型屬性');
    }
    A.prototype.say3 = function (){
    alert('可以通過A向A2添加原型屬性');
    }


    alert('say2' in A1);//false,A1.say2方法不存在.不能再通過A向A1添加原型屬性
    A2.say3();//添加成功

  • 替換已有實例的原型
    預計影響:
    1.接上了另一條原型鏈
    2.無法再用A.prototype添加或修改原型屬性

    //TEST:已有實例對象修改原型對象的影響
    function A() {}
    function B() {}


    var A1 = new A();
    var B1 = new B();


    A.prototype.say = function (){
    alert('A鏈的方法');
    }
    B.prototype.say = function (){
    alert('B鏈的方法');
    }


    //測試是否接到另一條原型鏈
    var A2 = Object.create(A1);
    A2.say();


    A2.__ proto __ = B1;
    A2.say();


    //測試是否不能再用原來的構造函數添加原型屬性
    var A3 = new A();
    A3.__ proto __ = B1;
    A.prototype.say2 = function (){
    alert('仍然可用構造函數添加原型屬性');
    }
    A3.say2(); //出錯,A3中找不到方法say2()

  • 替換原型的構造函數--A.prototype.constructor
    影響:
    1.只是無法再用A.prototype.constructor獲取構造函數A,無法便捷地添加'靜態方法'了
    2.仍能正常用A創建實例,用A.prototype添加或修改原型屬性

有關原型及原型鏈的一些相關方法總結

1.instanceof運算符
參考資料:instanceof [[HasInstance]](V)
instaceof運算符,是將左邊對象的原型作為參數,來調用右邊函數的[[HasInstance]] (V)內部方法,所得返回值即為運算符的結果.
[[HasInstance]] (V)方法大致過程:(以A.[[HasInstance]] (V)為例)

  • 1.令V=V.prototype
  • 2.如果V不是null,比較A.prototype與V的值
    • 如果相等,返回true
    • 如果不等,從1重新開始,直到V為null

    //TEST: instanceof原理測試:向上搜索實例的原型鏈,看由構造函數所指向的原型對象是否在其中
    function A() {}
    var A1 = new A();
    var B = A.prototype;


    console.log(A1 instanceof A);//true


    B.constructor = null;
    console.log(A1 instanceof A);//true


    A.prototype = {};
    console.log(A1 instanceof A);//false

2.屬性遍歷

  • 1.自身可枚舉一般屬性遍歷:(enumerable為true) "Object.keys()+迴圈語句"或"hasOwnProperty()/getOwnPropertyNames()+propertyIsEnumerable()+迴圈語句"
  • 2.所有可枚舉一般屬性遍歷:for...in迴圈
  • 3.自身所有一般屬性: hasOwnProperty()/getOwnPropertyNames()+迴圈語句
  • 4.原型鏈所有一般屬性: 用一個迴圈遍歷整個原型鏈,對裡面的每一個原型都應用3的方案

註:歡迎轉載,轉載請註明出處


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

-Advertisement-
Play Games
更多相關文章
  • Vue源碼解析之nextTick 前言 nextTick是Vue的一個核心功能,在Vue內部實現中也經常用到nextTick。但是,很多新手不理解nextTick的原理,甚至不清楚nextTick的作用。 那麼,我們就先來看看nextTick是什麼。 nextTick功能 看看官方文檔的描述: 在下 ...
  • 摘要: 本插件基於layui.layedit,增加了HTML源碼模式,片插入功能添加alt屬性(layupload),視頻插入功能,全屏功能,段落格式,字體顏色設置,右鍵菜單操作,插入錨點,水平線功能。 所有拓展功能菜單按鈕圖標均引用自layui自帶圖標。 演示地址:kz.layedit 一、基礎拓 ...
  • RegExp語法(包含ES2018標準) 註意:本次所有代碼都僅在Chrome 70中進行測試 1. 正則表達式是什麼? 正則表達式是用於匹配字元串中字元組合的模式。(mdn) 簡單來說,正則表達式是用來提取、捕獲文本(匹配字元)的。 2. 創建: 字面量: 構造函數: 3. 實例屬性: 每個正則表 ...
  • 安裝 cordova 新建項目 運行項目 編譯項目 修改編譯輸出 打開vue項目目錄下麵的index.html,添加 打開/config/index.js 編譯打包 先刪除 cordova項目下的www文件夾里的東西 執行編譯vue項目將輸出到 cordova 項目目錄下的www文件內 添加andr ...
  • 寫JS時,不斷翻看HTML,確保`querySelector`能取到期望的元素。 改HTML時,一個個排查JS文件,確保其沒受影響。 類似的情況很影響我們工作效率。 ...
  • 基於CANVAS的簡單畫圖組件讓你用類似於dom的方式,在canvas上畫圖,感覺會不會很爽。 主頁:http://graph.jm47.com/示例:http://graph.jm47.com/example/index.html 安裝 直接從github下載 https://github.com ...
  • 有時候富文本渲染到頁面的時候 會連帶標簽一起渲染出來。 解決辦法: 首先引用 <script src="https://cdn.jsdelivr.net/npm/[email protected]/fuwenben.js"></script> 然後把富文本數據用 htmlDecode() 轉換一下 就可以 ...
  • input:focus{ outline: none; border: 1px solid #fff; } 或者 input[type=text]:focus{ outline: none; border: 1px solid #fff; } ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...