筆記: js構造函數與原型

@java

構造函數與原型介紹

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)ecmascript

(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給實例添加的屬性,是不會反映到原型上的,屬於實例的自己的屬性.this

  • 三種引用是否能夠被更改的測試

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

    //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的方案

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

相關文章
相關標籤/搜索