《JavaScript高級程序設計》OOP章節的總結

從去年開始閱讀紅寶書,從第一遍看不懂,到後來慢慢理解,這個過程仍是很痛苦的(已經粗略翻看了6遍了o(╥﹏╥)o),因此忽然打算記錄下看書學習過程當中總結的筆記,並抽這個空再一次回顧下基礎知識,對於我來講,每看一遍感受都是不同的,都會有很大的收貨(前端路漫漫o(╥﹏╥)o)javascript

p爲Person的實例化對象 / var p =new Person()前端

方法 做用
instanceof 檢測對象是否爲另外一個對象的實例 p instanceof Person
isPrototypeOf 沒法訪問[[Prototype]] 可使用isPrototyprOf()來肯定是否存在這種關係 Person.prototype.isPrototypeOf(p)
Object.getPrototypeOf() 能夠獲取一個實例對象的原型 Object.prototypeOf(p)==Person.prototype
hasOwnProperty p.hasOwnProperty('name') true來自實例 false說明來自原型
Object.getOwnPropertyDescriptor() 只能用於實例屬性獲取,要想取得原型屬性的描述符,必須直接在原型對象上調用此方法
Object.keys() 接受一個對象的參數,返回一個包含全部可枚舉屬性的字符串數組
Object.getOwnPropertyNames() 返回的全部實例屬性,枚舉和不可枚舉都有 Object.getOwnPropertyNames(Person.prototype)
Object.create() 參數是一個做爲新對象原型的對象和一個爲新對象定義額外屬性的對象
更多 更多

1. ECMA兩種屬性類型

數據屬性
configurable  默認true
    1.表示能都否經過delete刪除屬性而從新定義屬性,
    2.可否修改屬性的特性,
    3.可否把屬性修改成數據屬性  
enumerable  
     表示可否使用for-in循環返回屬性   默認true
get   
     表示讀取屬性時調用	默認 undefined
set  
	 表示寫入屬性時調用   默認undefined

複製代碼

vue.js中就是採用此方法,因此不支持IE8vue

當對象裏面有屬性,採用Object.defineProperty方法只是修改特性和值時,默認值都爲true,java

訪問器屬性
configurable  默認true
    1.表示能都否經過delete刪除屬性而從新定義屬性,
    2.可否修改屬性的特性,
    3.可否把屬性修改成數據屬性  
enumerable
    表示可否使用for-in循環返回屬性  默認true
get 
    表示讀取屬性時調用	默認 undefined
set 
    表示寫入屬性時調用 默認undefined

複製代碼
案例
/* 數據屬性:定義了一個對象Person 其中name的屬性指定的值就是[[value]]的值 ,這個值的任何修改情況都將反映在value數據屬性的特性上 */

var Person={
	name:"梵高先生"
};

/* 要修改默認的特性,必須使用ES5的Object.defineProperty()方法 arguments參數爲 1.屬性所在的對象, 2.屬性的名字, 3.一個描述符(descriptor)對象(屬性必須是其中之一或多個) Object.defineProperty(Person,'name',{ value:"達芬奇" // /提示:咱們能夠手動進行修改,此時Person.name="達芬奇" }) */

Object.defineProperty(Person,'name',{
	writable:false,   //表示可否修改屬性值
    // configurable:false 一旦把屬性定義爲不可配置的,就再也沒法改變回來; 可屢次調用方法修改同一個屬 性,可是configural設置爲false後就會有限制了
}) 

console.log(Person.name)  //梵高先生
Person.name="莫扎特先生"  //若是上面設置了writable爲false後,屬性value的值不會被修改
console.log(Person.name)  //梵高先生


/* 訪問器屬性 _variable表示只能經過對象訪問的屬性 */

var book={
	_year:2004,
	edition:1
}
Object.defineProperty(book,'year',{
	get:function(){
		return this._year;
	},
	set:function(newValue){
		if(newValue>2004){
			this._year=newValue;
			this.edition=this.edition+(newValue-2004);
		}
	}
})

book.year=2008;
console.log(book.edition)  //5


複製代碼
定義多個屬性defineProperties
/* 定義多個屬性 Object.defineProperties(obj,{props}) 參數一爲要修改屬性的對象,參數二爲要修改的屬性對象 讀取屬性的特徵 Object.getOwnPropertyDescriptor(obj,描述符對象的屬性) */

var book={};
Object.defineProperties(book, {
	_year:{
		// writable:true,
		value:2004
	},
	edition:{
		writable:true,
		value:1
	},
	year:{
		get:function(){
			return this._year
		},
		set:function(newValue){
			if(newValue>2004){
				this._year=newValue;
				this.edition+=newValue-2004
			}
		}
	}
})

book.year=2008;
console.log(book.edition) //5
var descriptor1=Object.getOwnPropertyDescriptor(book, "_year")
console.log(descriptor1)

/* {value: 2004, writable: false, enumerable: false, configurable: false} configurable: false enumerable: false value: 2004 writable: false __proto__: Object */
var descriptor2=Object.getOwnPropertyDescriptor(book, "edition")
console.log(descriptor2)

/* {value: 5, writable: true, enumerable: false, configurable: false} configurable: false enumerable: false value: 5 writable: true __proto__: Object */
var descriptor3=Object.getOwnPropertyDescriptor(book,"year")
console.log(descriptor3)

/** {get: ƒ, set: ƒ, enumerable: false, configurable: false} configurable: false enumerable: false get: ƒ () set: ƒ (newValue) __proto__: Object */

複製代碼

2. 建立對象

對象字面量和Object()
var person=new Object();
person.name="cc"

//便於收編屬性,
//方便函數傳參調用
var person={
    name:"cc"
}

複製代碼

3. 工廠模式(常見)

ECMA沒法建立類(ES6支持Class),利用函數封裝一特定接口建立對象的細節,雖然解決了建立對象的問題,可是沒有解決如何知道對象的類型python

/* 工廠模式:抽象了建立具體對象的一過程 缺點:沒法知道一個對象的類型(對象識別的問題) */
function createPerson(name,age){
	var obj= new Object();
	obj.name=name;
	obj.age=age;
	obj.say=function(){
		console.log(this.name)
	}
	return obj
}
var person=createPerson("梵高先生",20);
person.say()

複製代碼

4. 構造函數(常見)

  1. 沒有顯示的建立對象
  2. 直接將屬性和方法賦給this
  3. 沒有return對象
/* 使用構造函數會經歷如下四個過程 1.建立一個新對象 2.將構造函數的做用域賦給新的對象 所以this就指向這個對象 3.執行構造函數中的代碼 也就是給新對象添加屬性 4.返回新的對象 缺點: 1.每一個方法都要在不一樣的對象實例中建立一次,形成沒必要要的資源浪費 (函數也是對象Function,每次new的時候都會建立一次) */

function Person(name,age,food){
	this.name=name;
	this.age=age;
    this.food=food;
    this.say=function(){  //每次建立實例都會調用,形成沒必要要的資源浪費 
        console.log(this.name)
    }
    this.eat=eat;   //會致使定義多個全局函數
}
/* obj1和obj2共享一個全局做用域中的函數eat 問題思考:調用時候是被對象調用,全局做用域名副其實,會致使定義多個全局函數 */
function eat(){
	console.log(this.food)
}
var obj1=new Person("我是對象一",10,'米粉');
var obj2=new Person("我是對象二",20'花甲')
obj1.eat()
obj2.eat()
console.log(obj2.constructor===Person)


/* 問題詳細補充: 1.把構造函數當作函數, - 也就是說任何函數只要經過new操做符調用,咱們均可以稱之爲構造函數 - 若是不使用new做爲普通函數調用,則調用結果會指向全局window對象 - 在另外一個對象做用域中調用 Person.call(實例,屬性1,屬性2....) 2. 構造函數的問題 this.sayName=new Function() 以這種方式建立函數,會致使不一樣的做用域鏈和標識符解析。建立機制相同,因此不一樣實例的sayName是不相等的,因此開銷也比較大,咱們能夠在外部定義方法(咱們將對象方法寫在了全局做用域中),一樣帶來了問題,在全局中的方法實際是被某個對象調用,這讓全局做用域有點 對不上號,若是須要定義不少方法,就要定義不少全局函數,因此面向對象絲毫沒有封裝可言,因此咱們能夠經過下一節的原型模式解決 */
複製代碼

5.原型模式(常見+重點)

  1. 每一個函數都有一個原型屬性prototype,這個屬性是一個指針,指向一個對象數組

  2. 這個對象包含有特定類型的全部實例共享的屬性和方法瀏覽器

理解原型對象
/* 1. 原型對象默認會擁有constructor屬性指向構造函數 Person.prototype.constructor==Person //true 2. __proto__ 隱式屬性 存在實例和構造函數的原型對象之間 ,而不是實例和構造函數之間 p.__proto__==Person.prototype //true 3. 一旦給實例設置了屬性,就會阻止咱們訪問原型對象中的同名屬性 一旦使用delete刪除了屬性,就會恢復原型對象的連接 */
function Person(name,age,sex){
    this.name=name;
    this.age=age;
    this.sex=sex;
    this.sayName=function(){
        console.log(this.name,this.sex,this.sex);
    }
}
Person.prototype.address="aaa"
var p=new Person('cc',18,'男');
p.address="bbb"  //就會阻止咱們訪問原型對象中的同名屬性 
console.log(p.addesss) //bbb而不是aaa
delete p.address;
console.log(p.addesss)  //恢復原型對象的連接 aaa
/* 換句話說,添加的屬性只會阻止咱們訪問原型中的屬性,但不會修改那個屬性,即便是添加屬性設置爲null,,也 只在實例中設置,並不會恢復指向原型的連接,咱們可使用delete徹底刪除實例屬性 */
複製代碼
原型與in操做符
  1. in操做符安全

    //肯定一個屬性是原型屬性
    function hasPrototypeProperty(object,name){
        return !object.hasOwnProperty(name)  && (name in object)
    }
    var person=new Person("cc");
    Person.prototype.id=123
    hasPrototypeProperty(person,'id') // 存在原型中
    複製代碼
  2. for inapp

    返回全部可以經過對象訪問和可枚舉的屬性,其中包括存在實例和原型中的屬性函數

    ie8中及更早版本,存在bug:屏蔽不可枚舉屬性的實例屬性不會出如今for..in循環中

更簡單的原型語法(重寫原型)

前面每次添加原型屬性都要使用 Person.prototype,每次都要寫一遍,爲了減小沒必要要的輸入,咱們能夠經過對象字面量來重寫原型對象(收編),從視覺上更好的封裝原型的功能

注意點: 這種作法會致使 constructor屬性再也不指向Person了,由於原型對象被重寫了,儘管instanceof能檢查實例,可是constructor以及沒法肯定對象類型了

function Person(name,age,sex){
    this.name=name;
    this.age=age;
    this.sex=sex;
    this.sayName=function(){
        console.log(this.name,this.sex,this.sex);
    }
}
/* 原型對象被重寫,constructor屬性再也不指向Person了,咱們能夠手動重置constructor 可是這種重設置會致使 [[Enumerable]]特性被設置爲true,默認constructor屬性是不可枚舉的 */
Person.prototype={
    // constructor:Person,
    address:"地址"
}
var p=new Person('cc',18,'男');


/* 若是設置兼容性則可使用Object.defineProperty(),重設構造函數,只適用用ES5兼容的瀏覽器 */
Object.defineProperty(Person.prototype,"constructor",{
    enumerable:false,   //默認constructor屬性是不可枚舉的
    value:Person
})
複製代碼
原型的動態性

在原型中查找值的過程是一次搜索,因此對原型對象作任何修改都會當即從實例上反映出來,

例:在生成實例代碼後面(代碼順序),咱們在原型對象上建立一個方法,結果依然能調用,由於實例和原型之間的鬆散鏈接關係

var p=new Person();//生成實例
Person.prototype.say=function(){   //生成實例代碼後面添加
    console.log(this.name)
}
p.say() //結果依然能調用
複製代碼

當咱們調用構造函數建立實例時,js會給實例添加一個指向最初原型的 [[prototype]]指針,若是對象字面量重寫原型,就切斷了這個實例和原型的關係,而指向Object

原生對象的原型 Object,Array,String

不推薦修改原型以及擴展方法,會致使衝突,也有可能 重寫原生對象的方法

原型模式的問題

問題: 因爲原型對象的共享性致使的,函數(方法)可使用,基本值類型也說得過去,可是使用引用類型值的屬性時,問題就比較突出了 (原型對象中使用引用數據類型的問題 )

要共享相同基本數據(如數組)沒什麼問題,可是實例通常都是有本身單獨的屬性的,這也就是沒人單獨使用原型模式的問題的緣由所在

6. 組合使用構造函數模式和原型模式(常見)

建立自定義類型最多見的方式就是 組合使用構造函數模式和原型模式 ,這是在ECMAScript中使用最爲普遍,認同度最高的一種建立自定義類型的方法,也能夠說是定義引用類型的一種默認模式

  1. 構造函數用於定義實例屬性
  2. 原型模式用於定義方法和共享的屬性
/* 好處: 1.每個實例都會有本身的一份實例屬性的副本,但同時有共享者對方法的引用,最大限度的節省了內存 2.這種混合模式還支持向構造函數中傳遞參數,可謂是集兩者之長 */

function Person(name,age,obj){
    this.name=name;
    this.age=age;
    this.sex=obj;
    this.friends=['cc01','cc02'];
}
Person.prototype={  //重寫了原型Person.prototype,
    constructor:Person,  //須要手動重置constructor (Person.prototype.constructor==Person)
    sayName:function(){
        console.log(this.name)
    }
}
var person01=new Person("張三",18,'前端開發');
var person02=new Person("李四",20,'java後臺');
person01.friends.push("cc03");
console.log(person01.friends); // ["cc01", "cc02","cc03"]
console.log(person02.friends) // ["cc01", "cc02"]
複製代碼

7.動態原型模式(常見)

以前組合使用構造函數和原型模式時,兩者都是分別單獨定義的 。使用動態原型模式能夠將全部信息都封裝在構造函數中的,而經過在構造函數中初始化原型(必要狀況下),又同時使用構造函數和原型的優勢

function Person(name,age,job){
    this.name=name;
    this.age=age;
    this.job=job;
    if(typeof this.sayName != 'function'){   //添加方法
        Person.prototype.sayName=function(){
            console.log(this.name)
            //注意點: 使用此模式不能使用對象字面量重寫原型對象,已經建立了實例的狀況下而後去重寫原型,會切斷新的原型對象和實例之間的聯繫
        }
    }
}
var person=new Person("cc",18,'前端開發')
person.sayName() //cc

複製代碼

8.寄生構造函數模式

場景: 前面幾種模式都不適用的狀況下可使用此模式

基本思想:建立一個函數,做用僅僅是封裝建立對象的代碼,而後返回新建立的對象

/* 注意點: 寄生構造函數模式,返回的對象和構造函數或者與構造函數的原型對象沒有任何關係,也就是說 構造函數返回的對象與在構造函數以外建立的對象沒有什麼不一樣, instanceof檢測無心義, 因此因爲這些問題,建議在使用其餘模式的狀況下不要使用這種模式 */

function Person(name,age,job){
    var o=new Object();
    o.name=name;
    o.age=age;
    o.job=job;
    o.sayName=function(){
        console.log(this.name)
    }
    return o;
}
var person01=new Person("cc",18,'java')


// 在特殊場景下用來建立構造函數,假設增長一個數組方法,不能修改Array的構造函數狀況下,咱們能夠本身使用這個模式建立
function SpecialArray(){
        //建立數組
        var values=new Array();
        //添加值 
        values.push.apply(values,arguments);  //用構造函數接受到的全部參數 這裏使用arguments對象
        //添加方法
        values.toPipedString=function(){
            return this.join("|")
        }
        //返回數組
        return values
    }
var colors=new SpecialArray('red','green','blue')
console.log(colors.toPipedString())  //red|green|blue
複製代碼

9.穩妥構造函數模式

所謂穩妥就是 沒有公共屬性,並且其方法也不引用this的對象 ,最適合在一些安全的環境中(會禁用this和new),或者在防止數據被其餘應用程序改動時使用

/* 穩妥構造函數模式遵循與寄生構造函數模式相似的模式 不一樣之處: 1.新建立對象的實例不引用this 2.不適用new操做符調用構造函數 和寄生構造函數模式同樣,返回的對象和構造函數或者與構造函數的原型對象沒有任何關係, 因此instanceof操做符沒意義 */

function Person(name,sex,job){
        var o=new Object();
        //這裏能夠添加定義 私有變量和函數
        o.name='cc'
        o.sayName=function(){ 
            console.log(name)  //只有經過此方法才能訪問到name
        }

        return o;
    }
var person=Person("cc",12,'python')  //person保存的是一個穩妥對象
person.sayName()
複製代碼

10. 繼承(重點)

OOP預語言都支持兩種方式的繼承:

  1. 實現繼承:是指繼承實際的方法
  2. 接口繼承 :只繼承方法的簽名,ECMAScript沒有函數簽名,因此只能支持實現繼承
原型鏈繼承

基本思想是利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法

原型,構造函數,實例三者關係 :

  1. 每一個構造函數都是一個原型對象,原型對象都包含一個指向構造函數的指針(constructor)
  2. 每一個實例都包含一個指向原型對象的內部指針(proto)
function SuperType() {
      this.property = "我是父類屬性"
  }
SuperType.prototype.getSuperValue = function() {
    return this.property;
}
function SubType() {
    this.property = "我是子類屬性"
}
/* SubType繼承了SuperType,實現繼承的本質是重寫原型對象,用新類型的實例代替(替換原型) 新原型擁有SuperType實例所擁有的全部屬性和方法,其中內部指針__proto__也指向SuperType的原型對象 new SuperType()-->SubType.prototype-->SuperType.prototype */
SubType.prototype = new SuperType(); 
//添加新的方法
SubType.prototype.getSubProperty = function() {  
    return this.property; //實例屬性和原型方法
}
//重寫父類型中的方法
SuperType.prototype.getSuperValue = function() {
    return false
}
var instance = new SubType();
console.log(instance)   //SubType{property}
console.log(instance.__proto__)  //SuperType{property,getSubProperty}
console.log(instance.constructor) //指向SuperType是由於SubType.prototype中的constructor被重寫了
console.log(instance.getSubProperty()) //我是子類屬性
console.log(instance.getSuperValue()) //false
複製代碼

總結:

  1. 別忘了默認的原型是Object,SubType繼承了SuperType.SuperType又繼承了Object

  2. 肯定原型和實例的關係

  3. 謹慎定義方法 覆蓋和添加方法時必定要放在替換原型的語句以後 ,也不要使用對象字面量建立原型的方法,

    這樣會重寫原型鏈

  4. 原型鏈的問題

    1.包含引用原型的原型屬性會被全部實例共享,(爲何定義在構造函數中,而再也不原型中定義屬性)
    2.建立子類型的實例時,不能向父類型的構造函數中傳遞參數,因此實際不多會單獨使用原型鏈
    複製代碼
借用構造函數

解決原型對象中包含引用類型值所帶來的問題,使用一種叫作 借用構造函數的模式 (僞造對象或經典繼承)

實現方式:在子類型構造函數的內部調用父類型構造函數 (記住:函數只不過是在特定環境中執行代碼的對象),而後使用apply()和call()能夠在未來建立的對象上執行構造函數

/* 好處: 相對與原型鏈來說,借用構造函數很大的優點是能夠 在子類型中構造函數中能夠向父類型構造函數中傳遞參數 問題: 若是僅借用構造函數,那麼就沒法避免構造函數模式所存在的問題----方法都在構造函數中定義,函數的複用就無從談起,因此不多是單獨使用的 */

function Person(name) {
      this.colors = ['a', 'b', 'c'];
      this.name = name //還能夠經過call傳遞參數
 }

function Student() {
    Person.call(this, 'EastBoat') 
   // Person.apply(this, ['EastBoat']) 
    //表示在將來將要建立的新對象(實例)person01和person02的各自環境中調用Person的構造函數,各自都會有本身的副本了,互不影響,同時傳遞了參數
}

var person01 = new Student()
person01.colors.push("fff", 'ggg');
console.log(person01.colors) //["a", "b", "c", "fff", "ggg"]
console.log(person01.name)
var person02 = new Student();
console.log(person02.colors) //["a", "b", "c"]
console.log(person02.name)
複製代碼
組合繼承

也叫做僞經典繼承:將原型鏈和構造函數組合在一塊兒使用,從而發揮兩者之長的一種繼承模式

/* 思想: 使用原型鏈實現對原型屬性和方法的繼承, 經過借用構造函數實現對實例屬性的繼承 做用: 經過在原型對象上定義方法實現了函數複用,又能保證每一個實例都有他本身的屬性 優勢: 避免了原型鏈和借用構造函數的缺陷,融合了他們的優勢,成爲javascript中最經常使用的繼承模式 instanceof和isPrototypeOf()也可以識別基於組合繼承建立的對象 缺點 不管什麼狀況下,都會調用兩次父類型的構造函數, 第一次:建立子類的原型的時候 SubType.prototype=new SuperType() 第二次:在子類的構造函數內部 SuperType.call(this) 第一次調用SubType.prototype會獲得兩個屬性,自己他們是SupperType的實例屬性,如今位於子類原型SubType.prototype中 第二次調用SubType構造函數時----call(this),此時又會調用SuperType構造函數,此時生成SubType屬性,就自動屏蔽了第一次SubType的原型 */

function SuperType(name, age) {
     this.name = name;
     this.age = age;
     this.hobby = ['籃球', '唱歌'];
 }
SuperType.prototype.sayHobby = function() {
    console.log(this.hobby)
}
function SubType(name, age) {
    SuperType.call(this, name) //繼承父類屬性
    this.age = age;
}
SubType.prototype = new SuperType(); //繼承方法
SubType.prototype.sayAge = function() {
     console.log("我是SubType的原型方法" + this.age)
}
var p1 = new SubType('p1', 22);
p1.hobby.push("羽毛球")
p1.sayHobby();
p1.sayAge()

var p2 = new SubType("p1", 18);
console.log(p2.hobby);
p2.sayHobby();
p2.sayAge()
複製代碼
原型式繼承

沒有使用嚴格意義上的構造函數,藉助原型能夠基於已有的對象建立新對象,同時還沒必要所以建立自定義類型

整個過程和Object.create(person)同樣,是建立了對象的一個副本

這種方式比較少 ,緣由就是和原型鏈繼承同樣,引用數據類型容易改變

function object(o) {
    function F() {} //臨時性構造函數
    F.prototype = o; //傳入的對象做爲這個構造函數的原型
    return new F() //返回臨時類型的一個新實例
}

/* 本質上,object()對傳入其中的對象執行了一次淺複製, 要求你必須有一個對象能夠做爲另外一個對象的基礎, 下面的案例中,能夠做爲另外一個對象基礎的是Person對象,咱們將其傳入到object函數中,而後就返回出一個新的對象 */
var person={
    name:"cc",
    food:['a','b','c']
}

var p1=object(person)
p1.name='p1';
p1.food.push('dddd');
console.log(person.food);  //["a", "b", "c", "dddd"]

var p2=object(person);
p2.name="p2";
p2.food.push("eeee")
console.log(person.food);  //["a", "b", "c", "dddd", "eeee"]
複製代碼
寄生式繼承

寄生式繼承在原型式繼承的基礎上增長了本身的方法。

/* 與原型式繼承密切相關的一種思路 相似: 寄生構造函數和工廠模式 缺點: 爲對象添加函數,因爲沒法作到函數複用而下降效率,這一點和構造函數模式相似 */
function object(o) {
     function F() {}
     F.prototype = o;
     return new F();
 }

function createAnother(original) {
    //調用函數建立一個新對象,此處object不是必須的,任何可以返回新對象的函數都適用此模式
    var cloneObj = object(original); 
    cloneObj.sayHi = function() {  
        console.log('hihihiih')
    }
    return cloneObj;
}

var person = {
    name: "cc",
    hobby: ['a', 'b', 'c']
}

var anotherPerson = createAnother(person);
console.log(anotherPerson)  //hihihiih
anotherPerson.sayHi()
複製代碼
寄生組合式繼承(最理想 )

組合繼承並非最好的繼承,由於經過將構造函數的原型指向父構造函數的實例,會兩次調用構造函數 ,前面已經講了組合繼承的缺點,咱們沒必要爲了指定子類的原型對象而去調用父類的構造函數,咱們無非只須要父類型的一個副本而已,

優勢:

  1. 高效體如今這種模式只調用了一次superType構造函數,而且所以避免了在SubType.prototype上建立沒必要要的,多餘的屬性
  2. 與此同時,還能保證原型鏈不變,還可以正常使用instanceof和isPrototypeOf()
/* 解決組合繼承中兩次調用superType構造函數的問題,其方法是在中間架一座橋樑(加一個空的構造函數) 另外不能直接將SubType.prototype指向SuperType.prototype是由於會引用相同的內存,形成共享,因此要指向實例。 實際過程以下: 中間橋樑爲空的構造函數,後面註釋爲object()函數的實現過程,也可以使用Object.create() function Temp(){ } // function F() { } Temp.prototype=SuperType.prototype; // F.prototype = SuperType.prototype var temp=new Temp(); // var prototype=new F(); ///建立對象 temp.constructor=SubType // prototype.constructor = SubType; //加強對象 SubType.prototype=temp; // SubType.prototype = prototype //指定對象 */

 function object(o) {
     function F() { }
     F.prototype = o;
     return new F();
 }
function inheritPrototype(subType, superType) {
      var prototype = object(superType.prototype); //建立對象 
      //var prototype = Object.create(superType.prototype); //ES5新增方法
      prototype.constructor = subType; //加強對象
      subType.prototype = prototype //指定對象
  }
//父類構造器
function SuperType(name) {
    this.name = name;
    this.colors = ['res', 'yellow', 'green']
}
SuperType.prototype.sayName = function () {
    console.log(this.name)
}

//子類構造器
function SubType(name, age) {
    this.name = name;
    this.age = age;
}
inheritPrototype(SubType, SuperType)  //繼承實現 

SubType.prototype.sayAge = function () {
    console.log(this.age)
}

//重寫原型對象方法sayName
//SubType.prototype.sayName = function () {
// console.log("bb")
//}

var p = new SubType("cc", 18);
p.sayAge(); //18
p.sayName()//cc 若是重寫原型對象方法,打印就是'bb'
複製代碼
相關文章
相關標籤/搜索