@[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
- 如果A.prototype是一個對象,令obj.[[Prototype]]=A.prototype
- 令變數result = A.[[Call]] (其中,this值為obj)
(就是以obj為this的值,執行A的函數體中的代碼,目的是對obj進行初始化)
總結: 由上面的分析可知,如果在構造函數A的函數體內用this給實例添加的屬性,是不會反映到原型上的,屬於實例的本身的屬性.
- 創建一個新的原生javascript對象obj
三種引用是否可以被更改的測試
//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
- 如果相等,返回true
2.屬性遍歷
- 1.自身可枚舉一般屬性遍歷:(enumerable為true) "Object.keys()+迴圈語句"或"hasOwnProperty()/getOwnPropertyNames()+propertyIsEnumerable()+迴圈語句"
- 2.所有可枚舉一般屬性遍歷:for...in迴圈
- 3.自身所有一般屬性: hasOwnProperty()/getOwnPropertyNames()+迴圈語句
- 4.原型鏈所有一般屬性: 用一個迴圈遍歷整個原型鏈,對裡面的每一個原型都應用3的方案
註:歡迎轉載,轉載請註明出處