摘要:而原型對象都會獲得一個構造函數屬性,這是一個指向屬性所在函數的指針。所以組合使用構造函數模式和原型模式簡單來說就是構造函數里面定義實例屬性,原型模式定義共享屬性。
一、創建對象
創建對象的發展史:
最早
var person = new Object() person.name = "Green"
對象字面量
var person = { name = "Green", age = "25", sayName: function(){ alert("this.name") } }
以上兩種都會有大量重復性的代碼,于是乎:
工廠模式
function createPerson(name, age, job){ var o = new Object(); // 這個叫做顯式的創造對象 o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert("this.name") }; return o; } var example = createPerson("Green",25,"無業!");
雖然解決了重復代碼問題,但沒有解決對象識別(工廠模式無從識別對象的類型,因為全部都是Object,不像Date、Array等,本例中,得到的都是o對象,對象的類型都是Object,因此出現了構造函數模式)
構造函數模式
function Person(name,age,family) { this.name = name; this.age = age; this.family = family; this.say = function(){ alert(this.name); } } var person1 = new Person("lisi",21,["lida","lier","wangwu"]); var person2 = new Person("lisi",21,["lida","lier","lisi"]); /* 這是在創建Person的實例,必須用到new - 創建一個新對象 - 將構造函數的作用域賦過去(this指向新對象) - 執行構造函數的代碼 - 返回新對象 */ console.log(person1 instanceof Object); //true console.log(person1 instanceof Person); //true
算是構造函數的特點?
。沒有顯式的創建對象(拗口)
。將屬性方法賦給了this對象
。沒有return
instanceof: 識別對象類型
在全局作用域中調用一個函數時,this永遠指向window (踩坑了~)
構造函數模式內的方法每次都會在實例上重建一遍,里面的方法在做同一件事,但是實例化后卻產生了不同的對象,方法是函數 ,函數也是對象。但如果相同的方法都寫在全局作用域里,會產生很多全局函數,失去了這個引用類型的封裝性。所以就產生了:
原型模式
function Person() { } Person.prototype.name = "lisi"; Person.prototype.age = 21; Person.prototype.family = ["lida","lier","wangwu"]; Person.prototype.say = function(){ alert(this.name); }; console.log(Person.prototype); //Object{name: "lisi", age: 21, family: Array[3]} var person1 = new Person(); //創建一個實例person1 console.log(person1.name); //lisi var person2 = new Person(); //創建實例person2 person2.name = "wangwu"; person2.family = ["lida","lier","lisi"]; console.log(person2); //Person {name: "wangwu", family: Array[3]} // console.log(person2.prototype.name); //報錯 console.log(person2.age); //21
~ 每個函數有一個prototype屬性,指向一個對象(該函數的原型對象),用途是包含了一些屬性和方法等信息,可以被一些由調用該函數創建的實例所共享。這些信息不必定義在構造函數內,只要添加到原型對象上即可。
~ 而原型對象都會獲得一個constructor(構造函數)屬性,這是一個指向prototype屬性所在函數的指針。(prototype和constructor屬性在函數與原型之間互相指)
~ 而創建出的實例內部,又有一個指針,指向原型對象(和構造函數里的prototype指的一樣,其實實例與構造函數無關,與他的原型有關),是你嗎__proto__?
檢測屬性
使用 hasOwnProperty() 方法可以檢測一個屬性是存在于實例還是他的原型中。給定屬性存在于實例中會返回true。
in操作符:多帶帶使用時,無論屬性存在于哪里,只要有就是true
for-in循環使用時,返回所有能夠通過對象訪問的可枚舉屬性,實例和原型的都包括。入所需要取得對象上所有可枚舉的實例屬性,推薦Object.key()方法。【深拷貝用過】
原型模式的優點是共享,缺點也是共享(過度)。比如兩個實例由調用同一個構造函數得來,其中一個實例修改了原型對象上屬性值 ,另一個實例也會共享這個修改。所以:
組合使用構造函數模式和原型模式
簡單來說就是構造函數里面定義實例屬性,原型模式定義共享屬性。
原型鏈簡單描述,就是將一個構造函數的實例賦值給另一個構造函數的原型對象。層層套在一起成為一個鏈條。是實現繼承的方法。
原型鏈的繼承仍然存在共享過度的問題,除此之外子類型實例不能給超類型傳遞參數。于是我們就要用到:
借用構造函數
基本思想: 在子類型構造函數內部調用超類型構造函數(通過call apply方法)
但是這樣方法又必須全定義在構造函數里,又不能復用了。于是就又有了:
組合繼承
基本思想:使用原型鏈實現對原型屬性和方法的繼承,通過借用構造函數來實現對實例屬性的繼承。這樣既通過在原型上定義方法實現了函數的復用,又能夠保證每個函數都有自己的屬性。
function SuperType(name) { this.name = name this.color = ["red", "blue"] } SuperType.prototype.getName = function() { console.log(this.name) } function SubType(name, age) { SuperType.call(this, name) // 繼承屬性 this.age = age } SubType.prototype.getAge = function() { console.log(this.age) } SubType.prototype = new SuperType() // 繼承方法 SubType.prototype.constructor = SubType var instance1 = new SubType("zhangsan", 18) instance1.colors.push("black") console.log(instance1.colors) // "red", "blue", "black" console.log(instance1.getName) // "zhangsan" console.log(instance1.getAge) // 18 var instance2 = new SubType("lisi", 20) console.log(instance2.colors) // "red", "blue" console.log(instance2.getName) // "lisi" console.log(instance2.getAge) // 20屬性類型 1、數據屬性
有四個描述特性:
configurable 能否刪除屬性 (*置為false后就無法再改變)
enumerable 能否通過for-in循環返回屬性
writable 能否修改屬性值
value 從這里讀取或者寫入屬性值
前三項默認值都為true,如果需要修改,需要調用大名鼎鼎的Object.defineProperty()方法
var person = {} Object.defineProperty(person,"name",{ // 三個參數 writable: false, // 這里如果不指定都默認為false value: "Green" }) alert(person.name); // Green person.name = "Blue" alert(person.name); // Green2、訪問器屬性
包含一對getter(讀取訪問器屬性時調用)和setter(寫入訪問器屬性時調用)函數
有四個描述特性:
configurable 一樣
enumerable 一樣
get 讀取屬性時調用的函數
set 寫入屬性時調用的函數
仍需調用Object.defineProperty()方法來定義
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 += newValue - 2004; } } }) book.year = 2005 alert(book.edition); // 2
這是使用訪問器屬性的常見方式,即設置一個屬性的值會導致其他屬性發生變化。
*_year的下劃線表示只能通過對象方法訪問(不懂,等我再查查)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103763.html
摘要:深入之繼承的多種方式和優缺點深入系列第十五篇,講解各種繼承方式和優缺點。對于解釋型語言例如來說,通過詞法分析語法分析語法樹,就可以開始解釋執行了。 JavaScript深入之繼承的多種方式和優缺點 JavaScript深入系列第十五篇,講解JavaScript各種繼承方式和優缺點。 寫在前面 本文講解JavaScript各種繼承方式和優缺點。 但是注意: 這篇文章更像是筆記,哎,再讓我...
摘要:是完全的面向對象語言,它們通過類的形式組織函數和變量,使之不能脫離對象存在。而在基于原型的面向對象方式中,對象則是依靠構造器利用原型構造出來的。 JavaScript 函數式腳本語言特性以及其看似隨意的編寫風格,導致長期以來人們對這一門語言的誤解,即認為 JavaScript 不是一門面向對象的語言,或者只是部分具備一些面向對象的特征。本文將回歸面向對象本意,從對語言感悟的角度闡述為什...
摘要:通常有這兩種繼承方式接口繼承和實現繼承。理解繼承的工作是通過調用函數實現的,所以是寄生,將繼承工作寄托給別人做,自己只是做增強工作。適用基于某個對象或某些信息來創建對象,而不考慮自定義類型和構造函數。 一、繼承的概念 繼承,是面向對象語言的一個重要概念。通常有這兩種繼承方式:接口繼承和實現繼承。接口繼承只繼承方法簽名,而實現繼承則繼承實際的方法。 《JS高程》里提到:由于函數沒有簽名,...
摘要:一基礎接口的意義百度規范擴展回調抽象類的意義我的前端面試經歷百度前端掘金博主就讀于電子科技大學,大三狗一枚面試是個漫長的過程,從海投到收獲電話面試,一面二面三面,一個步驟出錯那么后面就宣告終結。 一道常被人輕視的前端 JS 面試題 - 前端 - 掘金 目錄前言第一問第二問變量聲明提升函數表達式第三問第四問第五問第六問構造函數的返回值第七問最后前言 年前剛剛離職了,分享下我曾經出過的一道...
摘要:創建構造函數后,其原型對象默認只會取得屬性至于其他的方法都是從繼承來的。上圖展示了構造函數的原型對象和現有的兩個實例之間的關系。所有原生的引用類型都在其構造函數的原型上定義了方法。 第6章我一共寫了3篇總結,下面是相關鏈接:讀《javaScript高級程序設計-第6章》之理解對象讀《javaScript高級程序設計-第6章》之繼承 工廠模式 所謂的工廠模式就是,把創建具體對象的過程抽象...
閱讀 4270·2021-09-26 10:11
閱讀 2666·2021-07-28 00:37
閱讀 3223·2019-08-29 15:29
閱讀 1178·2019-08-29 15:23
閱讀 3124·2019-08-26 18:37
閱讀 2467·2019-08-26 10:37
閱讀 597·2019-08-23 17:04
閱讀 2346·2019-08-23 13:44