第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

全部開發(fā)者教程

JavaScript 入門教程

JavaScript 原型

JavaScript 常被描述為一種基于原型的語言 (prototype-based language)——每個對象擁有一個原型對象,對象以其原型為模板、從原型繼承方法和屬性。原型對象也可能擁有原型,并從中繼承方法和屬性,一層一層、以此類推。這種關系常被稱為原型鏈 (prototype chain),它解釋了為何一個對象會擁有定義在其他對象中的屬性和方法。(MDN)

每個對象都有一個標簽,這個標簽指向他的原型對象,對象基于一種機制,可以訪問到原型對象上的屬性。

在標準中,一個對象的原型是使用 [[prototype]] 表示的,chrome 對其的實現是使用 __proto__ 屬性表示。

1. 什么是原型

1.1 屬性的訪問機制

在 JavaScript 中,除了幾種基礎類型,剩下的幾乎都是對象。

當我們使用對象自面量創(chuàng)建一個對象的時候,可以訪問到對象的 toString 方法。

var obj = { empty: true };

console.log(obj.toString()); // 輸出:[object Object]

在書寫這個自面量的時候,并沒有提供 toString 這個方法,卻可以被成功調用。

這就涉及到了原型。

當在訪問一個對象的屬性時,如果當前對象沒有這個屬性,就會繼續(xù)往這個對象的原型對象上去找這個屬性。

如果原型對象上沒有這個屬性,則繼續(xù)從這個 對象 的 原型對象 的 原型對象 找這個屬性。

這就是屬性查找的機制,直到查到原型的末端,也就是 null ,就會停止查找,這個時候已經確定沒有這個屬性了,就會返回 undefined。

例子中的變量 obj 的原型可以通過 __proto__ 訪問。

var obj = { empty: true };

console.log(obj.__proto__);

在輸出的原型對象中可以找到 toString 方法。

圖片描述

可以通過相等運算符來判斷調用的 toString 方法是不是原型上的方法。

var obj = { empty: true };

console.log(
  obj.toString === obj.__proto__.toString,
); // 輸出:true

1.2 原型是怎么出現在一個對象上的

到這里有個問題,到底什么是原型,原型是怎么來的。

首先看一段代碼:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

var point = new Point(1, 2);

console.log(point.__proto__);

圖片描述

這樣打印出來的 point 的原型對象,除了 constructor__proto__ 屬性,就什么都沒有了。

接下來做個改寫:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.info = function() {
  console.log('x: ' + this.x + ', y: ' + this.y);
};

var point = new Point(1, 2);

point.info(); // 輸出:"x: 1, y: 2"

console.log(point.__proto__);

圖片描述

這樣輸出的 point 的原型對象,就具有了一個 info 方法。

從這就可以看出對象的原型,和他的構造函數的 prototype 屬性是有關的。

所有函數都具有一個 prototype 屬性,翻譯過來也是 原型的意思。

當一個函數作為構造函數被調用的時候,就會把這個函數的 prototype 屬性,作為構造函數生成的對象的原型。

使用相等運算符,就可以驗證上面這個規(guī)則:

console.log(
  point.__proto__ === Point.prototype,
); // 輸出:true

這就是一個對象原型的由來。

如果要知道對象由哪個構造函數生成,可以從 constructor 屬性獲取到,原型對象的 constructor 屬性則指向這個原型所處的函數。

這一點也可以由相等運算符驗證,point 對象的 constructor 屬性和其原型對象下的 constructor 應該都指向同一個,也就是 Point 函數。

console.log(
  point.constructor === point.__proto__.constructor, // 輸出:true
  point.constructor === Point, // 輸出:true
  point.__proto__.constructor === Point, // 輸出:true
);

事實上對象的 constructor 屬性就是直接從原型上繼承的。

圖片描述

1.3 原型鏈

前面有提到訪問對象屬性的機制。

function Point(x, y) {
  this.x = x;
  this.y = y;
}

var point = new Point(1, 2);

console.log(point.toString());

假如要訪問 point 對象的 toString 方法,首先會去 point 類里找,很顯然是沒有這個方法的。

然后回去 point 類的原型對象上找,也就是 Point 函數的 prototype 屬性上,很顯然也是沒有的。

然后會再往上一層找,也就是找到了 Point.prototype.__proto__ 上 (等同于 point.__proto__.__proto__),這個時候就找到了 toString,隨后被返回并且調用。

Point.prototype.__proto__ 其實就是 Object.prototype

console.log(
  Point.prototype.__proto__ === Object.prototype,
); // 輸出:true

假如檢查到 Object.prototype 還沒有目標屬性,則在往上就找不到了,因為 Object.prototype.__proto__null。

也就是說原型查找的末端是 null,碰到 null 就會終止查找。

這些原型環(huán)環(huán)相扣,就形成了原型鏈。

有些同學會有疑問,為什么 Point.prototype 的原型是 Object.prototype。其實 Point.prototype 也是一個對象,可以理解成這個對象是通過 new Object 創(chuàng)建的,所以原型自然是 Object.prototype。

圖片描述

2. proto 屬性

Chrome瀏覽器 下通過訪問對象的 __proto__ 屬性可以取到對象的原型對象,這是所有對象都具備的屬性。

var date = new Date();

console.log(date.__proto__);

__proto__ 具有兼容性問題,因此開發(fā)中盡量不要使用到,他不在 ES6 之前的標準中,但是許多舊版瀏覽器也對他進行了實現。

ES6__proto__ 屬性 被定制成了規(guī)范。

3. Object.getPrototypeOf 方法

由于 __proto__ 存在一定兼容性的問題,可以使用 Object.getPrototypeOf 方法代替 __ptoto__ 屬性。

var date = new Date();

var dateProto = Object.getPrototypeOf(date);

console.log(dateProto);
console.log(dateProto === date.__proto__); // 輸出:true

4. JavaScript 中沒有類

JavaScript 中是沒有類的概念的。

有其他面向對象開發(fā)經驗的同學可能會被 new 關鍵字誤導。

JavaScript 中采用的是原型的機制,很多文獻會稱其為 原型代理,但個人認為對于初學者使用 原型繼承 的方式會更好理解一點,日常討論中其實是一個意思,不需要過多糾正其說法。

原型是兩種不同的機制。

有關于類的內容,篇幅很大,如果不熟悉但又感興趣,可以嘗試著接觸一下其他面向對象的語言,如 Python、JavaC++。

ES6 提供了 class 關鍵字,引入了一些類相關的概念,但其底層運行機制依然是原型這一套,所以即便是有了 class 關鍵字來幫助開發(fā)者提升開發(fā)體驗,但其本質依然不是類,只是一種原型寫法的語法糖。

5. 小結

原型的概念至關重要,利用原型的機制可以開發(fā)出更加靈活的 JavaScript 應用。

利用原型,可以很好的復用一些代碼,雖然在 JavaScript 中沒有類,但是我們可以利用原型這個特性來模擬類的實現,達到繼承、多態(tài)、封裝的效果,實現代碼邏輯的復用,同時可以更好的組織代碼結構。