摘要:深入理解原型與繼承看過不少書籍,不少文章,對于原型與繼承的說明基本上讓人不明覺厲,特別是對于習慣了面向對象編程的人來說更難理解,這里我就給大家說說我的理解。
深入理解:JavaScript原型與繼承
看過不少書籍,不少文章,對于原型與繼承的說明基本上讓人不明覺厲,特別是對于習慣了面向對象編程的人來說更難理解,這里我就給大家說說我的理解。
首先JavaScript是一門基于原型編程的語言,它遵守原型編程的基本原則:
所有的數據都是對象(javascript中除了字符串字面量、數字字面量、true、false、null、undefined之外,其他值都是對象!)
要得到一個對象,不是通過實例化類,而是找到一個對象作為原型并克隆它(new操作符)
對象會記住它的原型(JavaScript中通過隱藏的__proto__屬性)
如果對象無法響應某個請求,它會把該請求委托給它自己的原型
這么說來,JavaScript一定有一個根對象,所有的對象的最終原型都將是它,它就是Object.prototype(有的人說跟對象是null,因為Object.prototype.__proto__為null),Object.prototype也是一個對象,它是一個空的對象。(記住一點:所有的原型都是對象,但不是函數,雖然函數也是對象,Object其實就是一個函數,而Object.prototype是一個對象)
以下代碼創建空對象:
var obj1 = new Object(); var obj2 = {}; Object.getPrototypeOf(obj1) === Object.prototype; //true Object.getPrototypeOf(obj2) === Object.prototype; //true
我們再來看下以下代碼:
function Book(name){ this.name = name; } Book.prototype.getName = function(){ return this.name; } Book.num = 5; var book1 = new Book("javascript"); book1.getName(); //javascript Object.getPrototypeOf(book1) === Book.prototype; //true
我們通常說,使用了new Book()來創建了Book的實例book1,但是JavaScript并沒有類的概念,這里的Book本質上只是一個函數而已,如果用new則把它當著構造函數對待,那么var book1 = new Book("javascript)是怎么個執行過程呢?在這之前,我們來先看下Function與Object的關系
這里有張圖:來源于javascriptcn
console.log(Function); //Function 函數 console.log(Function.constructor); //Function 函數 console.log(Function.__proto__); //function(){} 空函數 console.log(Function.__proto__.__proto__);//{} 空對象,即Object.prototype console.log(Function.prototype); //function(){} 空函數 console.log(Function.constructor.prototype); //function(){} 空函數 console.log(Object); //function Object(){[native code]} Object是個函數 console.log(Object.__proto__); //function(){} 空函數 console.log(Object.prototype); //{} 空對象 console.log(Object.constructor); //Function 函數 console.log(Object.constructor.prototype); //function(){} 空函數
以上測試說明什么?
說明了內置函數Function以它自身為構造函數,且以它自身為原型,這個原型是個空函數;
Object是個函數,Object是以內置函數Function為構造函數的,它自己的原型Object.prototype是個空對象
我總結了這么個東西:在JavaScript中,最原始的東西有兩個,就是function(){}空函數 和 {}空對象,但它們都有內置的屬性、方法,所有的JS對象都是基于它們創建出來的;
Function和Object就像是女媧和伏羲,Object提供種子(Object.prototype),Function負責繁衍(Function.prototype)。Object的實例是由Object.prototype提供的種子經過Function.prototype(function(){} 空函數)打造出來的。Object.prototype這個基因會被一直繼承下去,并一代一代增強。
每一個函數都有一個原型對象(prototype)和隱藏的__proto__屬性,函數的__proto__屬性指向Function.prototype,而原型對象(prototype)是一個對象,符合以下第2點(也有構造函數constructor和隱藏的__proto__屬性);
每一個對象都有一個構造函數(constructor)和隱藏的__proto__屬性,constructor自然指的是它的構造函數,而__proto__指向的是它的構造函數的原型對象prototype,而對象的原型prototype對象同時又包含了該對象構造函數、還具有自己的__proto__屬性;
通過__proto__屬性,每個對象和函數都會記住它的原型,這樣就形成了原型鏈;
console.log(Book); //Book函數自身 console.log(Book.__proto__); //function(){} 空函數 console.log(Book.prototype); //Book的原型對象,包含了構造函數constructor、__proto__ console.log(Book.prototype.__proto__); //{} 等于Object.prototype console.log(Book.prototype.constructor); //Book函數自身 console.log(Book.constructor); //Function函數 console.log(Book.constructor.prototype); //function(){} 空函數(Function函數的原型) console.log(Book.__proto__.__proto__); //{} 等于Object.prototype
每一個函數都是通過Function構造出來的,函數的原型屬性__proto__指向Function.prototype,而函數的原型對象prototype是代表著自身的對象,它的__proto__屬性指向Object.prototype,它的constructor屬性默認指向它自己構造函數(也可改為別的函數,如:Book.prototype.constructor = Person;)。
console.log(book1); //Book { name: "javascript" } console.log(book1.__proto__); //指向Book.prototype對象 console.log(book1.prototype); //undefined console.log(book1.constructor); //指向Book函數 console.log(book1.constructor.prototype); //Book.prototype對象
所以,我們通常說‘一個對象的原型’其實是不準確的,應該是‘一個對象的構造器的原型’,且對象是把它無法響應的請求委托給它的構造器的原型順著原型鏈往上傳遞的。
現在來講解一下var book1 = new Book("javascript)的執行過程,是這樣:new先通過Function.prototype從Object.prototype克隆一個空對象,然后將空對象的構造函數constructor指定為Book,并將該空對象的__proto__屬性指向它的構造函數的原型Book.prototype,之后通過Book構造函數對這個空對象進行賦值操作,最后將這個對象返回給變量book1。
我們再看如下代碼:
var obj = {name: "Sufu"}; var Person = function(){}; Person.prototype = obj; var a = new Person(); console.log(a.name); //Sufu
這段是這樣執行的:
首先嘗試查找對象a的name屬性,找不到,執行第2步
將查找name屬性的這個請求委托給a的構造器的原型,a.__proto__記錄的是Person.prototype,Person.prototype.__proto__記錄的是obj
在對象obj中查找name,找到并返回它的值給a,假如還找不到,它就通過obj.__proto__找到Object.prototype,而Object.prototype.__proto__為null,所以找不到就返回undefined。
總之,對象、函數都有一個隱藏的__proto__屬性,這個屬性就是原型鏈上的環,一環扣一環,從而形成了原型鏈!
好了,就介紹到這里了,以上是個人的理解,有不對的地方歡迎指出!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/80463.html
摘要:深入之繼承的多種方式和優缺點深入系列第十五篇,講解各種繼承方式和優缺點。對于解釋型語言例如來說,通過詞法分析語法分析語法樹,就可以開始解釋執行了。 JavaScript深入之繼承的多種方式和優缺點 JavaScript深入系列第十五篇,講解JavaScript各種繼承方式和優缺點。 寫在前面 本文講解JavaScript各種繼承方式和優缺點。 但是注意: 這篇文章更像是筆記,哎,再讓我...
摘要:原型鏈與繼承當談到繼承時,只有一種結構對象。如果對該圖不怎么理解,不要著急,繼續往下看基于原型鏈的繼承對象是動態的屬性包指其自己的屬性。當使用操作符來作用這個函數時,它就可以被稱為構造方法構造函數。 原型鏈與繼承 當談到繼承時,JavaScript 只有一種結構:對象。每個實例對象(object )都有一個私有屬性(稱之為proto)指向它的原型對象(prototype)。該原型對象也...
摘要:首先,需要來理清一些基礎的計算機編程概念編程哲學與設計模式計算機編程理念源自于對現實抽象的哲學思考,面向對象編程是其一種思維方式,與它并駕齊驅的是另外兩種思路過程式和函數式編程。 JavaScript 中的原型機制一直以來都被眾多開發者(包括本人)低估甚至忽視了,這是因為絕大多數人沒有想要深刻理解這個機制的內涵,以及越來越多的開發者缺乏計算機編程相關的基礎知識。對于這樣的開發者來說 J...
摘要:原文發自我的博客易企秀招聘啦首先我們先來回顧以下中出現的原型繼承原型繼承自如果我們要在上查詢一個定義在的屬性會先在上查找如果沒有查到那么會順著原型鏈去查找所以以下判別式均為如果我們做如下操作原型鏈并沒有被訪問一個新的會被加入到的屬性中去新的 原文發自我的博客 xiaoyu2er.github.io 易企秀招聘啦! JavaScript Prototypal Inheritance 首先...
摘要:深入系列的第一篇,從原型與原型鏈開始講起,如果你想知道構造函數的實例的原型,原型的原型,原型的原型的原型是什么,就來看看這篇文章吧。讓我們用一張圖表示構造函數和實例原型之間的關系在這張圖中我們用表示實例原型。 JavaScript深入系列的第一篇,從原型與原型鏈開始講起,如果你想知道構造函數的實例的原型,原型的原型,原型的原型的原型是什么,就來看看這篇文章吧。 構造函數創建對象 我們先...
閱讀 1012·2021-11-23 10:11
閱讀 3854·2021-11-16 11:50
閱讀 921·2021-10-14 09:43
閱讀 2713·2021-10-14 09:42
閱讀 2710·2021-09-22 16:02
閱讀 1056·2019-08-29 10:57
閱讀 3378·2019-08-29 10:57
閱讀 2268·2019-08-26 13:52