1. 程式人生 > >JavaScript深入之從原型到原型鏈

JavaScript深入之從原型到原型鏈

對於這篇文章,真的是愛不釋手,看的我手舞足蹈,對原型和原型鏈有了更深的理解,分享一下

建構函式建立物件

我們先使用建構函式建立一個物件:

function Person() {

}
var person = new Person();
person.name = 'Kevin';
console.log(person.name) // Kevin

在這個例子中,Person 就是一個建構函式,我們使用 new 建立了一個例項物件 person。

很簡單吧,接下來進入正題:

prototype

每個函式都有一個 prototype 屬性,就是我們經常在各種例子中看到的那個 prototype ,比如:

function
Person() { } // 雖然寫在註釋裡,但是你要注意: // prototype是函式才會有的屬性 Person.prototype.name = 'Kevin'; var person1 = new Person(); var person2 = new Person(); console.log(person1.name) // Kevin console.log(person2.name) // Kevin

那這個函式的 prototype 屬性到底指向的是什麼呢?是這個函式的原型嗎?

其實,函式的 prototype 屬性指向了一個物件,這個物件正是呼叫該建構函式而建立的例項的原型,也就是這個例子中的 person1 和 person2 的原型。

那什麼是原型呢?你可以這樣理解:每一個JavaScript物件(null除外)在建立的時候就會與之關聯另一個物件,這個物件就是我們所說的原型,每一個物件都會從原型"繼承"屬性。

讓我們用一張圖表示建構函式和例項原型之間的關係:

建構函式和例項原型的關係圖

在這張圖中我們用 Object.prototype 表示例項原型。

那麼我們該怎麼表示例項與例項原型,也就是 person 和 Person.prototype 之間的關係呢,這時候我們就要講到第二個屬性:

__proto__

這是每一個JavaScript物件(除了 null )都具有的一個屬性,叫__proto__,這個屬性會指向該物件的原型。

為了證明這一點,我們可以在火狐或者谷歌中輸入:

function Person() {

}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true

於是我們更新下關係圖:

例項與例項原型的關係圖

既然例項物件和建構函式都可以指向原型,那麼原型是否有屬性指向建構函式或者例項呢?

constructor

指向例項倒是沒有,因為一個建構函式可以生成多個例項,但是原型指向建構函式倒是有的,這就要講到第三個屬性:constructor,每個原型都有一個 constructor 屬性指向關聯的建構函式。

為了驗證這一點,我們可以嘗試:

function Person() {

}
console.log(Person === Person.prototype.constructor); // true

所以再更新下關係圖:

例項原型與建構函式的關係圖

綜上我們已經得出:

function Person() {

}

var person = new Person();

console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
// 順便學習一個ES5的方法,可以獲得物件的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true

瞭解了建構函式、例項原型、和例項之間的關係,接下來我們講講例項和原型的關係:

例項與原型

當讀取例項的屬性時,如果找不到,就會查詢與物件關聯的原型中的屬性,如果還查不到,就去找原型的原型,一直找到最頂層為止。

舉個例子:

function Person() {

}

Person.prototype.name = 'Kevin';

var person = new Person();

person.name = 'Daisy';
console.log(person.name) // Daisy

delete person.name;
console.log(person.name) // Kevin

在這個例子中,我們給例項物件 person 添加了 name 屬性,當我們列印 person.name 的時候,結果自然為 Daisy。

但是當我們刪除了 person 的 name 屬性時,讀取 person.name,從 person 物件中找不到 name 屬性就會從 person 的原型也就是 person.__proto__ ,也就是 Person.prototype中查詢,幸運的是我們找到了 name 屬性,結果為 Kevin。

但是萬一還沒有找到呢?原型的原型又是什麼呢?

原型的原型

在前面,我們已經講了原型也是一個物件,既然是物件,我們就可以用最原始的方式建立它,那就是:

var obj = new Object();
obj.name = 'Kevin'
console.log(obj.name) // Kevin

其實原型物件就是通過 Object 建構函式生成的,結合之前所講,例項的 __proto__ 指向建構函式的 prototype ,所以我們再更新下關係圖:

原型的原型關係圖

原型鏈

那 Object.prototype 的原型呢?

null,我們可以列印:

console.log(Object.prototype.__proto__ === null) // true

然而 null 究竟代表了什麼呢?

null 表示“沒有物件”,即該處不應該有值。

所以 Object.prototype.__proto__ 的值為 null 跟 Object.prototype 沒有原型,其實表達了一個意思。

所以查詢屬性的時候查到 Object.prototype 就可以停止查找了。

最後一張關係圖也可以更新為:

原型鏈示意圖

順便還要說一下,圖中由相互關聯的原型組成的鏈狀結構就是原型鏈,也就是藍色的這條線。

補充

最後,補充三點大家可能不會注意的地方:

constructor

首先是 constructor 屬性,我們看個例子:

function Person() {

}
var person = new Person();
console.log(person.constructor === Person); // true

當獲取 person.constructor 時,其實 person 中並沒有 constructor 屬性,當不能讀取到constructor 屬性時,會從 person 的原型也就是 Person.prototype 中讀取,正好原型中有該屬性,所以:

person.constructor === Person.prototype.constructor

__proto__

其次是 __proto__ ,絕大部分瀏覽器都支援這個非標準的方法訪問原型,然而它並不存在於 Person.prototype 中,實際上,它是來自於 Object.prototype ,與其說是一個屬性,不如說是一個 getter/setter,當使用 obj.__proto__ 時,可以理解成返回了 Object.getPrototypeOf(obj)。

真的是繼承嗎?

最後是關於繼承,前面我們講到“每一個物件都會從原型‘繼承’屬性”,實際上,繼承是一個十分具有迷惑性的說法,引用《你不知道的JavaScript》中的話,就是:

繼承意味著複製操作,然而 JavaScript 預設並不會複製物件的屬性,相反,JavaScript 只是在兩個物件之間建立一個關聯,這樣,一個物件就可以通過委託訪問另一個物件的屬性和函式,所以與其叫繼承,委託的說法反而更準確些。

原文地址:https://github.com/mqyqingfeng/Blog/issues/2    大家可以去star一下

相關推薦

JavaScript 深入原型原型(轉載)

typeof 藍色 標準 hub 不知道 函數 col 瀏覽器 rip 構造函數創建對象 我們先使用構造函數創建一個對象: function Person() { } var person = new Person(); person.name = ‘name‘; co

JavaScript深入原型原型

對於這篇文章,真的是愛不釋手,看的我手舞足蹈,對原型和原型鏈有了更深的理解,分享一下建構函式建立物件我們先使用建構函式建立一個物件:function Person() { } var person = new Person(); person.name = 'Kevin';

換個角度看 JavaScript 中的 (this) => { 整理 (JavaScript 深入 ECMAScript 規範解讀 this ) }

前言 這篇文章的產生,是基於冴羽大大的JavaScript 深入之從 ECMAScript 規範解讀 this的思考,這是對應掘金鍊接,文中詳細的論述了前因後果,建議各位都可以去了解一下,很有幫助,並且這篇文章在寫作時,也有冴羽大大的幫助,再次表示感謝~ 文中的 ES5 規範是參考 顏海鏡大大 的譯本,也

javascript深入理解-作用域理解閉包

contex num 位置 返回 ron 自由 spa 其中 alc 一、概要 紅寶書(P178)對於閉包的定義:閉包就是有權訪問另外一個函數作用域中變量的函數。 MDN,對於閉包的定義:閉包就是指能夠訪問自由變量的函數。 那麽什麽是自由變量?自由變量就是在函數中使用

Javascript深入創建對象的多種方式以及優缺點

丟失 創建對象 工廠 pre 使用 OS 不能 屬性和方法 一次 1.工廠模式 function createPerson(name) { var o = new Object(); o.name = name; o.getName = function() {

【進階1-4期】JavaScript深入帶你走進記憶體機制

本期的主題是呼叫堆疊,本計劃一共28期,每期重點攻克一個面試重難點,如果你還不瞭解本進階計劃,文末點選檢視全部文章。 如果覺得本系列不錯,歡迎點贊、評論、轉發,您的支援就是我堅持的最大動力。 JS記憶體空間分為棧(stack)、堆(heap)、池(一般也會歸類為棧中)。 其中棧存放變數,堆存放複雜物件

【進階1-3期】JavaScript深入記憶體空間詳細圖解

本期的主題是呼叫堆疊,本計劃一共28期,每期重點攻克一個面試重難點,如果你還不瞭解本進階計劃,文末點選檢視全部文章。 如果覺得本系列不錯,歡迎點贊、評論、轉發,您的支援就是我堅持的最大動力。 堆疊的內容和執行順序我就不說了,前面兩篇已經介紹過了。 但是今天補充一個知識點:某些情況下,呼叫堆疊中函式呼

【進階1-2期】JavaScript深入執行上下文棧和變數物件

本期的主題是呼叫堆疊,本計劃一共28期,每期重點攻克一個面試重難點,如果你還不瞭解本進階計劃,文末點選檢視全部文章。 如果覺得本系列不錯,歡迎點贊、評論、轉發,您的支援就是我堅持的最大動力。 JS是單執行緒的語言,執行順序肯定是順序執行,但是JS 引擎並不是一行一行地分析和執行程式,而是一段一段地分析

【進階2-3期】JavaScript深入閉包面試題解

這是我在公眾號(高階前端進階)看到的文章,現在做筆記  https://github.com/yygmind/blog/issues/19 作用域指的是一個變數和函式的作用範圍,JS中函式內宣告的所有變數在函式體內始終是可見的,在ES6前有全域性作用域和區域性作用域,但是沒有塊級作用域(catch

JavaScript深入 執行上下文(一):作用域

作用域 作用域是指程式原始碼中定義變數的區域。 作用域規定了如何查詢變數,也就是確定當前執行程式碼對變數的訪問許可權。下面我們來看看作用域的兩種解析方式。 作用域兩種解析方式: 靜態作用域:js使用

10.JavaScript深入call和apply的模擬實現

call 一句話介紹 call: call() 方法在使用一個指定的 this 值和若干個指定的引數值的前提下呼叫某個函式或方法。 舉個例子: var foo = { value: 1 }; function bar() { console.l

JavaScript繼承(原型

call() 的人 spa 環境 isp 而且 依賴 .html console   我們知道繼承是oo語言中不可缺少的一部分,對於JavaScript也是如此。一般的繼承有兩種方式:其一,接口繼承,只繼承方法的簽名;其二,實現繼承,繼承實際的方法。JavaScript不支

接:深入理解javascript構造函數和原型對象

原型對象 函數 href 深入理解java asc 構造 rip spa tar 鏈接:深入理解javascript構造函數和原型對象http://www.jb51.net/article/55539.htm 鏈接:深入理解javascript構造函數和原型對象

零開始學 Web JavaScript 高階(一)原型,貪吃蛇案例

一、複習 例項物件和建構函式之間的關係: 1、例項物件是通過建構函式來建立的,建立的過程叫例項化。 2、如何判斷一個物件是不是某種資料型別? 通過構造器的方法。例項物件.constructor === 建構函式名字 (推薦使用)例項物件 instanceof 建構函式名字 二、原型 1、原型的引入 由

JavaScript繼承基礎講解,原型、借用構造函數、混合模式、原型式繼承、寄生式繼承、寄生組合式繼承

push 需要 覆蓋 pan 只需要 童鞋 java var 自定義 說好的講解JavaScript繼承,可是遲遲到現在講解。廢話不多說,直接進入正題。   既然你想了解繼承,證明你對JavaScript面向對象已經有一定的了解,如還有什麽不理解的可以參考《面向對象JS基礎

JavaScript踩坑筆記06---原型物件、prototype、隱式原型、__proto__、原型

原型、prototype: 每個函式都有一個prototype屬性,這個屬性就是原型,它指向該函式對應的原型物件。這個物件包含由該函式建立的所有例項共享的屬性和方法。 舉例說明。 // 定義一個函式fn function fn() {} // 建立函式fn的例項 var fn1 =

javascript 原型 原型 繼承

每個函式物件都有一個prototype屬性,但普通物件沒有prototype屬性,prototype下面有一個constructor,指向這個函式 每個人物件有一個名為[[prototype]]

JavaScript Prototype原型原型詳解

Prototype屬於比較底層的知識,學習之前我要先說一句話. 建構函式.prototype 是個物件(有唯一例外,Function.prototype),__proto__指向建立他的建構函式的原型物件。 如果沒理解這句話,就說明沒有理解原型。

Javascript基礎-原型(prototype)

首先呢,prototype是物件裡的一個內建屬性,並且呢,這個屬性是對於其他物件的一個引用。所以呢,思考下面的例子: var obj = { a: 2 } var myObj = Object.create(obj); console.log(myObj.a); // 2 console.log(myO

零開始設計一款APP如何做原型

@Sophia的玲瓏閣 :這個系列的文章把整個設計過程的經驗總結成文,逐點分享,上期是概述+立項,這期聊聊低保真和高保真原型圖的作用、處理工具和檔案要求等。 Low-fi,即低保真原型圖,整個APP設計階段,設計師真正開始上手的環節。待PM製作好PRD文件和邏輯流程圖之後,