一、前言
了解JavaScript面向對象,需要先了解三個名詞: 構造函數,實例對象和原型對象。
注意:JavaScript中沒有類(class)的概念,取而代之的是構造函數,兩者類似卻又有很大的差別。
先上代碼,最常用的:
function Person(name, age) { this.name = name; this.age = age; this.eat= function() { alert('吃西紅柿') } } var person1 = new Person('小米', 28); var person2 = new Person('大米', 23);
Chrome打印測試,上圖:
上圖分別是:
- 圖一打印perspn1實例對象,
- 圖二打印Person構造函數,
- 圖三打印構造函數的prototype(即Person的原型對象)和person1的__proto__
通過上面的打印,我們可以發現幾個問題:
- 實例對象和構造函數一點也不像,
- person1.__proto__和Person.prototype一模一樣
我們先來看看第一個問題:實例對象和它的構造函數——打印出來的內容一點也不像
這個問題就大了呀!
我們都知道,在java中,類和它的實例對象之間有很緊密的關系,你的(屬性和方法)是我的,我的還是我的!
可是到了js這里,Person構造函數中並沒有體現出他本該有的屬性和方法
也就是說,無論我們實例化出來多少個person,他們的屬性和方法都是不一樣的。屬性不一樣還可以理解,方法不一樣就意味着:每個實例出來的person的方法並不是共用的(並不指向同一個地址空間),那我們要構造函數還有什么意義?
我們要的還是類和實例對象的關系那樣,能夠共享數據,節省內存空間
這就引出了我們今天要講的關鍵:原型
二、正文
(一)、使用原型對象造共用屬性和方法
前面已經講到,js的構造函數和實例對象之間,並不能夠實現共享數據,節省內存空間的作用,所以我們就引入了原型這一概念
再上代碼:這次我們添加了原型方法play()
//構造函數
function Person(name, age) { this.name = name; this.age = age; this.eat= function() { alert('吃西紅柿') } } //添加原型方法
Person.prototype.play = function() { alert("玩溜溜球")} //實例化對象
var person1 = new Person('小米', 28); var person2 = new Person('大米', 23);
在僅限Chrome測試:
上圖分別是:
- 圖一打印perspn1實例對象,
- 圖二打印Person構造函數,
- 圖三打印構造函數的prototype(即Person的原型對象)和person1的__proto__
通過上圖可以知道:構造函數中定義的方法,實例化后並不一樣,而原型對象prototype中定義的方法確實相等的(指向同一地址)
添加了原型方法后,實例對象person1和構造函數Person上並沒有直觀體現,反而在Person.prototype和person1.__proto__中顯示了出來
由此,我們可以知道,JS中給同一構造函數的實例對象 添加共用屬性和方法,需要使用prototype這一屬性,也就是原型對象來實現
(二)、prototype和__proto__和constructor構造器
上圖表現出:Person.prototype === person1.__proto__
即:實例對象的__proto__和構造函數的prototype相等(指向同一地址),完全一樣
上圖,圖一打印Person.prototype;圖二打印person1.__proto__;圖三打印Person構造函數
通過上面三張圖,我們可以發現:Person.prototype.constructor和person1.__proto__.constructor以及Person一模一樣
上圖表現出:Person.prototype.constructor === Person
即:構造函數的原型對象(prototype)的構造器(constructor)指向該構造函數
通過之前的打印和上圖,我們可以發現,
- 實例對象中都有__proto__屬性,而構造函數中都有prototype屬性,
- prototype和__proto__都有構造器constructor,其實實例對象的__proto__和構造函數的prototype是一樣的(Person.prototype === person1.__proto__)
- 構造函數的原型對象(prototype)的構造器(constructor)指向該構造函數(Person.prototype.constructor === Person)
(三)、使用原型的注意事項
原型屬性和方法統一定義時,需要定義構造器constructor,即將構造函數的原型對象中的構造器指向該構造函數,否則原型屬性和方法定義失敗
//添加原型方法
Person.prototype.job= "程序員" Person.prototype.address = "蘇州" Person.prototype.study= function() { alert("學JavaScript")} //可以這樣定義嗎?
Person.prototype = { job: "程序員", address: "蘇州" , study: function() { alert("學JavaScript")} } //上面的原型對象定義出錯,需要加上constructor--手動修改構造器的指向
Person.prototype = { constructor: Person, job: "程序員", address: "蘇州" , study: function() { alert("學JavaScript")} }
分別將兩種添加原型屬性和方法的方式打印看看:
上圖分別是:
- 圖一為錯誤示范,表示未手動修改構造器指向,結果打印顯示Person.prototype丟失構造器constructor,被新添加的對象覆蓋
- 圖二為正確示范,表示手動修改構造器指向,即加上constructor: Person,
三、結束
加油哦,最后來張圖