GG資源網

前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)

前端進階:javascript認識並理解構造函數,原型和原型鏈

一、前言

介紹構造函數,原型,原型鏈。比如說經常會被問道:symbol是不是構造函數;constructor屬性是否只讀;prototype、[[Prototype]]和__proto__的區別;什麼是原型鏈?等等問題

前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)

二、構造函數

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

前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)

這就是一個典型的構造函數,構造函數本身也是個函數,與普通區別不大,主要區別就是:構造函數使用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);

前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)

對於基本類型來說是只讀的,比如: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屬性上,而不是對象實例本身上。

前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)

上圖發現:

1、Parent對象有一個原型對象Parent.prototype,原型對象上有兩個屬性,分別為:constructor和__proto__,其中__proto__已被棄用。

2、構造函數Parent有一個指向原型的指針constructor;原型Parent.prototype有一個指向構造函數的指針Parent.prototype.constrcutor,其實就是一個循環引用。

前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)

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

前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)

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之間的關係,如下圖所示:

前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)

注意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:

前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)

有圖可以知道,實例化對象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

下圖展示原型鏈運行機制。

前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)

五、總結

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

前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)

什麼是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原型鏈是什麼的詳細內容,更多請關注網站的其它相關文章!

前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)

由於網站搬家,部分鏈接失效,如無法下載,請聯繫站長!謝謝支持!
1. 帶 [親測] 說明源碼已經被站長親測過!
2. 下載後的源碼請在24小時內刪除,僅供學慣用途!
3. 分享目的僅供大家學習和交流,請不要用於商業用途!
4. 本站資源售價只是贊助,收取費用僅維持本站的日常運營所需!
5. 本站所有資源來源於站長上傳和網路,如有侵權請郵件聯繫站長!
6. 沒帶 [親測] 代表站長時間緊促,站長會保持每天更新 [親測] 源碼 !
7. 盜版ripro用戶購買ripro美化無擔保,若設置不成功/不生效我們不支持退款!
8. 本站提供的源碼、模板、插件等等其他資源,都不包含技術服務請大家諒解!
9. 如果你也有好源碼或者教程,可以到審核區發布,分享有金幣獎勵和額外收入!
10.如果您購買了某個產品,而我們還沒來得及更新,請聯繫站長或留言催更,謝謝理解 !
GG資源網 » 前端進階:javascript認識並理解構造函數,原型和原型鏈(什麼是js原型鏈)

發表回復

CAPTCHAis initialing...