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