前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)
前端進階:javascript認識並理解構造函數,原型和原型鏈
一、前言
介紹構造函數,原型,原型鏈。比如說經常會被問道:symbol是不是構造函數;constructor屬性是否只讀;prototype、[[Prototype]]和__proto__的區別;什麼是原型鏈?等等問題
二、構造函數
1、什麼構造函數
構造函數就是通過new關鍵詞生成實例的函數。
js的構造函數和其他語言不一樣,一般規範都是首字母大寫。
首先我們來看一下這個栗子:
// saucxs
function Parent(age) {
this.age = age;
}
var p = new Parent(30);
console.log(p); //見下圖
console.log(p.constructor); // ƒ Parent(age){this.age = age;}
p.constructor === Parent; // true
p.constructor === Object; // false
這就是一個典型的構造函數,構造函數本身也是個函數,與普通區別不大,主要區別就是:構造函數使用new生成實例,直接調用就是普通函數。
2、constructor屬性
返回創建實例對象的Object構造函數的引用。此屬性的值對函數本身的引用,而不是一個包含函數名稱的字元串。
所有對象都會從它的原型上繼承一個constructor屬性:
var o = {};
o.constructor === Object; // true
var o = new Object;
o.constructor === Object; // true
var a = [];
a.constructor === Array; // true
var a = new Array;
a.constructor === Array // true
var n = new Number(3);
n.constructor === Number; // true
那麼普通函數創建的實例有沒有constructor屬性呢?
// saucxs
// 普通函數
function parent2(age) {
this.age = age;
}
var p2 = parent2(50);
console.log(p2);
// undefined
// 普通函數
function parent3(age) {
return {
age: age
}
}
var p3 = parent3(50);
console.log(p3.constructor); //ƒ Object() { [native code] }
p3.constructor === parent3; // false
p3.constructor === Object; // true
上面代碼說明:
(1)普通函數在內部有return操作的就有constructor屬性,沒有return的沒有constructor屬性;
(2)有constructor屬性的普通函數的constructor屬性值不是普通函數本身,是Object。
3、symbol是構造函數嗎?
MDN 是這樣介紹 Symbol
的
Symbol是基本數據類型,作為構造函數它不完整,因為不支持語法new Symbol(),如果要生成實例直接使用Symbol()就可以的。
// saucxs
new Symbol(123); // Symbol is not a constructor
Symbol(123); // Symbol(123)
雖然Symbol是基本數據類型,但是Symbol(1234)實例可以獲取constructor屬性值。
// saucxs
var sym = Symbol(123);
console.log( sym ); // Symbol(123)
console.log( sym.constructor ); // ƒ Symbol() { [native code] }
sym.constructor === Symbol; //true
sym.constructor === Object; //false
這裡的constructor屬性來自哪裡?其實是Symbol原型上的,默認為Symbol函數。
4、constructor的值是只讀的嗎?
回答:如果是引用類型的constructor屬性值是可以修改的,如果是基本類型的就是只讀的。
引用類型的情況,修改這個很好理解,比如原型鏈繼承的方案中,就是對constructor重新賦值的修正。
// saucxs
function Foo() {
this.value = 42;
}
Foo.prototype = {
method: function() {}
};
function Bar() {}
// 設置 Bar 的 prototype 屬性為 Foo 的實例對象
Bar.prototype = new Foo();
Bar.prototype.foo = \'Hello World\';
Bar.prototype.constructor === Object;
//true
// 修正 Bar.prototype.constructor 為 Bar 本身
Bar.prototype.constructor = Bar;
var test = new Bar() // 創建 Bar 的一個新實例
console.log(test);
對於基本類型來說是只讀的,比如:1, \"saucxs\", true, Symbol, null, undefined。null和undefined也是沒有constructor屬性的。
// saucxs
function Type() { };
var types = [1, \"muyiy\", true, Symbol(123)];
for(var i = 0; i < types.length; i++) {
types[i].constructor = Type;
types[i] = [ types[i].constructor, types[i] instanceof Type, types[i].toString() ];
};
console.log( types.join(\"\\n\") );
// function Number() { [native code] },false,1
// function String() { [native code] },false,muyiy
// function Boolean() { [native code] },false,true
// function Symbol() { [native code] },false,Symbol(123)
為什麼會這樣?因為創建他們的是只讀的原生構造函數(native constructors),這個栗子說明依賴一個對象的constructor屬性並不安全。
三、原型
3.1 prototype屬性
每一個對象都擁有一個原型對象,對象以其原型為模板,從原型集成方法和屬性,這些屬相和方法都在對象的構造器函數的prototype屬性上,而不是對象實例本身上。
上圖發現:
1、Parent對象有一個原型對象Parent.prototype,原型對象上有兩個屬性,分別為:constructor和__proto__,其中__proto__已被棄用。
2、構造函數Parent有一個指向原型的指針constructor;原型Parent.prototype有一個指向構造函數的指針Parent.prototype.constrcutor,其實就是一個循環引用。
3.2 __proto__屬性
上圖中可以看到Parent原型(Parent.prototype)上有一個__proto__屬性,這是一個訪問器屬性(即getter函數和setter函數)。作用:通過__proto__可以訪問到對象的內部[[Prototype]](一個對象或者null)
__proto__
發音 dunder proto,最先被 Firefox使用,後來在 ES6 被列為 Javascript 的標準內建屬性。
[[Prototype]]
是對象的一個內部屬性,外部代碼無法直接訪問。
// saucxs
function Parent(){};
var p = new Parent();
console.log(p);
console.log(Parent.prototype);
1、p.__proto__獲取的是對象的原型,__proto__是每一個實例上都有的屬性;
2、prototype是構造函數的屬性;
3、p.__proto__和Parent.prototype指向同一個對象。
// saucxs
function Parent() {}
var p = new Parent();
p.__proto__ === Parent.prototype
// true
所以構造函數Parent,Parent.prototype和p之間的關係,如下圖所示:
注意1:__proto__
屬性在 ES6
時才被標準化
以確保 Web 瀏覽器的兼容性,但是不推薦使用,除了標準化的原因之外還有性能問題。為了更好的支持,推薦使用 Object.getPrototypeOf()
。
如果要讀取或修改對象的 [[Prototype]]
屬性,建議使用如下方案,但是此時設置對象的 [[Prototype]]
依舊是一個緩慢的操作,如果性能是一個問題,就要避免這種操作。
如果要創建一個新對象,同時繼承另一個對象的 [[Prototype]]
,推薦使用 Object.create()
。
// saucxs
function Parent() {
age: 50
};
var p = new Parent();
var child = Object.create(p);
這裡 child
是一個新的空對象,有一個指向對象 p 的指針 __proto__
。
四、原型鏈
每一個對象擁有一個原型對象,通過__proto__指針指向上一個原型,並從中繼承方法和屬性,同時原型對象也可能擁有原型,這樣一層層的,最終指向null。這種關係成為原型鏈(prototype chain),作用:通過原型鏈一個對象會擁有定義在其他對象中的屬性和方法。
// saucxs
function Parent(age) {
this.age = age;
}
var p = new Parent(50);
p.constructor === Parent; // true
p.constructor指向Parent,那麼是不是意味著p實例化存在constructor屬性呢?並不存在,列印一下p:
有圖可以知道,實例化對象p本身沒有constructor屬性,是通過原型鏈向上查找__proto__,最終找到constructor屬性,該屬性指向Parent
// saucxs
function Parent(age) {
this.age = age;
}
var p = new Parent(50);
p; // Parent {age: 50}
p.__proto__ === Parent.prototype; // true
p.__proto__.__proto__ === Object.prototype; // true
p.__proto__.__proto__.__proto__ === null; // true
下圖展示原型鏈運行機制。
五、總結
1、Symbol是基本數據類型,作為構造函數並不完整,因為不支持語法new Symbol(),但是原型上擁有constructor屬性,即Symbol.prototype.constructor。
2、引用類型constructor屬性值是可以修改的,但是對於基本類型的是只讀的,當然null和undefined沒有constructor屬性。
3、__proto__是每個實例上都有的屬性,prototype是構造函數的屬性,這兩個不一樣,但是p.__proto__和Parent.prototype是指向同一個對象。
4、__proto__屬性在ES6時被標準化,但是因為性能問題並不推薦使用,推薦使用Object.getPropertyOf()。
5、每個對象擁有一個原型對象,通過__ptoto_指針指向上一個原型,並從中繼承方法和屬性,同時原型對象也可能擁有原型,這樣一層已成的,最終指向null,這就是原型鏈。
原文地址:https://www.cnblogs.com/chengxs/p/10862197.html
什麼是js原型鏈
原型鏈是一種機制,指的是JavaScript每個對象包括原型對象都有一個內置的[[proto]]屬性指向創建它的函數對象的原型對象,即prototype屬性。
作用:原型鏈的存在,主要是為了實現對象的繼承。
關於原型鏈的幾個概念:
1、函數對象
在JavaScript中,函數即對象。
2、原型對象
當定義一個函數對象的時候,會包含一個預定義的屬性,叫prototype,這就屬性稱之為原型對象。
//函數對象
function F(){}; console.log(F.prototype) |
圖片1
3、__proto__
JavaScript在創建對象的時候,都會有一個[[proto]]的內置屬性,用於指向創建它的函數對象的prototype。原型對象也有[[proto]]屬性。因此在不斷的指向中,形成了原型鏈。
舉個例子來說,我們將對象F的原型對象修改一下,就可以清楚看到上述的關係
//函數對象
function F(){}; F.prototype = { hello : function(){} }; var f = new F(); console.log(f.__proto__) |
圖片2
4、new
當使用new去調用構造函數時,相當於執行了
var o = {};
o.__proto__ = F.prototype; F.call(o); |
因此,在原型鏈的實現上,new起到了很關鍵的作用。
5、constructor
原型對象prototype上都有個預定義的constructor屬性,用來引用它的函數對象。這是一種循環引用。
function F(){};
F.prototype.constructor === F; |
在實際運用中,經常會有下列的寫法
function F(){};
F.prototype = { constructor : F, doSomething : function(){} } |
這裡要加constructor是因為重寫了原型對象,constructor屬性就消失了,需要自己手動補上。
6、原型鏈的內存結構
function F(){
this.name = \'zhang\'; }; var f1 = new F(); var f2 = new F(); |
以上就是js原型鏈是什麼的詳細內容,更多請關注網站的其它相關文章!
1. 帶 [親測] 說明源碼已經被站長親測過!
2. 下載後的源碼請在24小時內刪除,僅供學慣用途!
3. 分享目的僅供大家學習和交流,請不要用於商業用途!
4. 本站資源售價只是贊助,收取費用僅維持本站的日常運營所需!
5. 本站所有資源來源於站長上傳和網路,如有侵權請郵件聯繫站長!
6. 沒帶 [親測] 代表站長時間緊促,站長會保持每天更新 [親測] 源碼 !
7. 盜版ripro用戶購買ripro美化無擔保,若設置不成功/不生效我們不支持退款!
8. 本站提供的源碼、模板、插件等等其他資源,都不包含技術服務請大家諒解!
9. 如果你也有好源碼或者教程,可以到審核區發布,分享有金幣獎勵和額外收入!
10.如果您購買了某個產品,而我們還沒來得及更新,請聯繫站長或留言催更,謝謝理解 !
GG資源網 » 前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)