創建對象的設計模式
工廠模式
抽象了創建具體對象的過程,用函數封裝以特定接口創建對象的細節
解決了創建多個相似對象的問題,沒有解決對象識別的問題
function createPerson(name,age){ var obj = new Object(); obj.name = name; obj.age = age; obj.sayName = function(){ alert(this.name) } return obj; } var person = createPerson("aa",12);
構造函數模式
構造函數可以用來創建特定類型的對象,可以用instanceof檢測類型
使用new操作符會經歷以下四個步驟
創建一個新對象
將構造函數的作用域賦給新對象(因此this指向了這個新對象)
執行構造函數中的代碼(為這個新對象添加屬性)
返回新對象
缺點:
每個方法都要在每個實例上重新創建一遍
每個方法(函數)就是一個對象,不同實例上的同名函數是不相等的
將函數抽離到外部使多個對象共共享全局作用域的函數雖然可以解決上面問題,但讓this.sayName = sayName全局方法會破壞了對象的封裝性
function Person(name,age){ this.name = name; this.age = age; this.sayName = function(){ alert(this.name) } } var person = new Person("aa",12); person instanceof Person => true原型模式
prototype
只要創建了一個新函數,就會為函數創建一個prototype屬性,這個屬性指向函數的原型對象
包含了特定類型的所有對象實例共享的方法和屬性
prototype是通過調用構造函數而創建的那個對象實例的原型對象
默認情況下所有原型對象會獲得一個constructor(構造函數屬性),這個屬性指向prototype屬性所在函數的指針
當調用構造函數創建一個新實例時,內部將包含一個指針,指向原型對象,連接實例與原型對象
function Person(name,age){ } Person.prototype.name = name; Person.prototype.age = age; Person.prototype.sayName = function(){ alert(this.name) } var person = new Person("aa",12); person instanceof Person => true
判斷原型對象
Person.prototype.isPropertyOf(person) => true
Object.getPropertyOf(person) == Person.prototype => true (es5)
多個對象實例共享原型所保存的屬性和方法的基本原理
每當代碼讀取某個對象的某個屬性時,搜索從對象實例本身開始,如果在實例中找到具有給定名字的屬性則返回該屬性的值
如果沒有找到則繼續搜索指針指向的原型對象,如果在原型對象中找到給定名字的屬性,則返回這個值
雖然可以通過對象實例訪問保存在原型實例中的值,但是不能通過對象實例重寫原型中的值,如果在實例中添加一個與原型實例中的一個屬性同名,訪問時實例屬性會屏蔽原型中這個屬性的值,只能訪問到這個實例上屬性的值。可以通過delete操作符刪除實例屬性,重新訪問原型中的屬性
使用hasOwnProperty()可以檢測一個屬性是存在實例中還是原型中,只有存在實例中才會返回true;person.hasOwnProperty("name")
"name" in person 無論name是存在實例中還是原型對象中都會返回true
//判斷對象原型上是否有這個屬性 function hasPrototypeProperty(obj, attr){ return (attr in obj) && !obj.hasOwnProperty(attr); }
在使用for-in 循環時,返回的是能夠通過對象訪問的,可枚舉的屬性,包括了實例上的屬性和原型上的屬性。原型上不可枚舉的屬性,但是在實例中定義了也可以獲取得到,如在實例中定義toString
Object.keys() (es5)
取得對象上所有可枚舉的實例屬性,返回一個包含所有可枚舉的字符串數組
也可以傳入一個原型對象,Object.keys(Person.prototype),但不會沿著原型鏈往上尋找,只返回當前prototype下的屬性
Object.getOwnPropertyNames()(ie9+)
枚舉所有實例屬性,不管是否可枚舉
原型的動態性在原型中查找值的過程是一次搜索,對原型 對象所做的任何修改都能立即從實例上反映出來--即使是先創建了實例后修改原型也照樣如此
var friend = new Person(); Person.prototype.sayHi = function(){ alert("hi") } friend.sayHi() //hi
實例和原型之間松散的連接關系,可以隨時為原型添加屬性和方法,并且修改能夠立即在所有對象實例中反映出來
但是重寫整個原型對象就不一樣了,調用構造函數時會為實例添加一個指向最初原型的[[prototype]]指針,而把原型修改為另外一個對象就等于切斷了構造函數與最初原型的聯系
實例中的指針僅指向原型對象,而不指向構造函數
var friend = new Person(); Person.prototype = { sayHi:function(){ alert("hi") } } friend.sayHi() //error
原型模式的缺點
省略了為構造函數傳遞初始化參數,所有實例在默認情況下都將取得相同的值
當使用原型屬性時會只要在一個實例上修改都會影響到所有的實例,例如在一個實例上修改數組
組合使用構造函數和原型模式構造函數定義實例屬性,原型模式定義方法和屬性
動態原型模式通過檢查某個應該存在的方法是否有效,來決定是否需要初始化原型
function Person(name,age){ this.name = name; this.age = age; **if( typeof this.sayName != "funcction"){ Person.prototype.sayName = function(){ alert("hi") } }** }
只有初次調用構造函數時才會執行將函數添加到原型中
寄生構造函數模式類似于工廠模式,封裝創建對象的代碼,然后返回新創建對象,return語句可以重寫調用構造函數時返回的值
function Person(name,age){ var o = new Object(); o.name = name; o.age = age; o.sayName = function(){ alert("hi"); } return o; }
使用場景:特殊情況下用來為對象創造構造函數。
假設創建一個具有額外方法的特殊數組,由于不能直接修改Array構造函數
function SpecialArray(){ var arr = new Array(); arr.push.apply(values,arguments); arr.toPipedString = function(){ return this.join("|"); } return arr;
colors = new SpecialArray("green","red");
colors.toPipedString()
缺點:返回的對象和構造函數或者與構造函數的原型屬性之間沒有什么關系,不能使用instanceof來確定對象類型
穩妥構造函數模式
特點
新創建對象的實例方法不引用this
不使用new操作符調用構造函數
除了返回對象上的屬性和方法,沒有其他辦法訪問到構造函數內的數據
function Person(name,age){ var o = new Object(); //這里可以定義私有數據 o.sayName = function(){ alert(name) } return o;
person = Person("green",12);
person.sayName() //只能通過sayName()方法去訪問name的值
js以原型鏈作為實現繼承的主要方法
基本思想是利用原型鏈讓一個引用類型繼承另一個引用類型的屬性和方法
構造函數、原型、實例的關系
每個構造函數都有一個原型對象
原型對象都有一個指向構造函數的方法
實例都包含一個指向原型對象的一個指針
function SuoperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; } function SubType(){ this.subproperty = false; } SubType.prototype = new SuperType(); SubType.prototype.getSubType = function(){ return this.subprototype; } var instance = new SubType() instance.getSuperValue();//true
SubType不僅具有作為一個SuperType的實例所擁有的全部屬性和方法,而且擁有一個指向SuperType原型的指針
instance指向SubType原型,SubType原型又指向SuperType的原型
確定實例與原型的關系
instanceof操作符 只要用這個操作符來測試實例與原型中出現過的構造函數,結果就會返回true
isPropertyOf() 只要是原型鏈中出現的原型,都可以說是該原型鏈所派生的實例的原型,都會返回true
缺點:
原型屬性會被所有實例共享,如果在父構造函數中有一個this.colors=[]的數組,子構造函數繼承后的實例可以修改這個存在于子構造函數原型對象上的原型屬性
在創建子類型的實例時,不能向超類型的構造函數傳遞參數。實際上是沒有辦法在不影響所有實例的情況下給超類型的構造函數傳遞參數
借用構造函數在子類型構造函數的內部調用超類型構造函數,通過call,apply 改變對象的this指向
function SuoperType(name){ this.colors = ["red"]; this.name = name; } function SubType(name){ SuperType.call(this,name); } SubType.prototype = new SuperType(); var instance = new SubType("1") instance.colors.push("green") => ["red","green"] var instance2 = new SubType("2") instance2.colors.push("black") => ["red","black"]組合繼承
將原型鏈和借用構造函數的技術組合到一起
使用原型鏈實現對原型屬性和方法的繼承
使用借用構造函數實現對實例屬性的繼承
function SuperType(name){ this.colors = ["red"]; this.name = name; } SuperType.prototype.sayName = function(){ alert(this.name) } function SubType(name,age){ SuperType.call(this,name); this.age = age; } SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayName = function(){ alert(this.name) } var instance = new SubType("1",12) instance.sayName() => 1 instance.colors => ["red"] var instance2 = new SubType("2",21) instance2.sayName() => 2 instance2.colors => ["red"]原型式繼承
基于一個對象上,這個對象相當于作為原型,再根據需求對得到的對象加以修改
在沒有必要興師動眾創建構造函數,而只想讓一個對象與另一個對象保持類似的情況下使用
function object(o) { function F(){} F.prototype = o; return new F(); } var person = { name:"n", friends:["a","b"] } var anotherPerson = object(person); anotherPerson.name = "y"; =>y anotherPerson.colors.push("c"); => ["a","b","c"] var person = object(person); person.name = "yy"; =>yy person.color; =>["a","b","c"]
Object.create() es5新增規范原型式繼承
var person = { name:"n", friends:["a","b"] } var anotherPerson = Object.create(person); anotherPerson.name = "y"; =>y anotherPerson.colors.push("c"); => ["a","b","c"] var person = Object.create(person); person.name = "yy"; =>yy person.color; =>["a","b","c"]寄生式繼承
創建一個僅用于封裝繼承過程的函數,在函數內部以某種方式增強對象
function object(o) { function F(){} F.prototype = o; return new F(); } function createAnother(original){ var clone = object(original); clone.sayHi = function(){ alert("hi") } return clone; } var person = { name:"n", friends:["a","b"] } var anotherPerson = createAnother(person); anotherPerson.sayHi(); =>hi寄生組合式繼承
組合繼承最大的問題是無論什么情況下都會調用兩次超類型構造函數
一次是創建子類型原型時,一次是子函數的內部構造函數
寄生組合繼承通過借用構造函數來繼承屬性,通過原型鏈混成形式來繼承方法。
思路是不必為了指定的子類型原型而調用超類型的構造函數。
使用寄生式繼承來繼承超類型的原型,然后再將結果指定給子類型的原型
function inheritPrototype(subType,superType){ var prototype = object(superType); prototype.constructor = subType; subType.prototype = prototype; } function SuperType(name){ this.colors = ["red"]; this.name = name; } SuperType.prototype.sayName = function(){ alert(this.name) } function SubType(name,age){ SuperType.call(this,name); this.age = age; } SubType.prototype =inheritPrototype(subType,superType) ; SubType.prototype.sayName = function(){ alert(this.name) }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/87944.html
摘要:繼承的是超類型中構造函數中的屬性,如上繼承了屬性,但沒有繼承原型中的方法。上述造成的結果是子類型實例中有兩組超類型的構造函數中定義的屬性,一組在子類型的實例中,一組在子類型實例的原型中。 ECMAScript只支持實現繼承,主要依靠原型鏈來實現。與實現繼承對應的是接口繼承,由于script中函數沒有簽名,所以無法實現接口繼承。 一、原型鏈 基本思想:利用原型讓一個引用類型繼承另一個引用...
摘要:的繼承方式屬于原型式繼承,非常靈活。當使用關鍵字執行類的構造函數時,系統首先創建一個新對象,這個對象會繼承自構造函數的原型對象新對象的原型就是構造函數的屬性。也就是說,構造函數用來對生成的新對象進行一些處理,使這個新對象具有某些特定的屬性。 繼承這個東西在Javascript中尤其復雜,我掌握得也不好,找工作面試的時候在這個問題上栽過跟頭。Javascript的繼承方式屬于原型式繼承,...
摘要:高程讀書筆記第六章理解對象創建自定義對象的方式有創建一個實例,然后為它添加屬性和方法。創建了自定義的構造函數之后,其原型對象默認只會取得屬性至于其他方法都是從繼承而來的。 JS高程讀書筆記--第六章 理解對象 創建自定義對象的方式有創建一個Object實例,然后為它添加屬性和方法。還可用創建對象字面量的方式 屬性類型 ECMAScript在定義只有內部采用的特性時,描述了屬性的各種特征...
摘要:解耦優勢代碼復用,單元測試。常用比較誤區可同時判斷,可用來判斷對象屬性是否存在。使用作判斷無法進行充分的類型檢查。文件中應用常量參考文檔高級程序設計作者以樂之名本文原創,有不當的地方歡迎指出。 showImg(https://segmentfault.com/img/bVburXw?w=500&h=400); 編寫可維護性代碼 可維護的代碼遵循原則: 可理解性 (方便他人理解) 直觀...
摘要:使用構造器有個嚴重的危害,如果在調用構造器函數的時候忘記使用前綴,不僅不會綁定到新對象,還會污染全局變量原型模式原型模式中,我們采用對象來繼承。 構造器調用模式 當一個函數對象被創建時,Function構造器會運行類似這樣的代碼: this.prototype = {constructor: this} new一個函數事會發生: Function.method(new, functio...
閱讀 3232·2021-11-02 14:44
閱讀 3732·2021-09-02 15:41
閱讀 1676·2019-08-29 16:57
閱讀 1796·2019-08-26 13:38
閱讀 3304·2019-08-23 18:13
閱讀 2117·2019-08-23 15:41
閱讀 1680·2019-08-23 14:24
閱讀 3039·2019-08-23 14:03