摘要:中又兩種屬性數(shù)據(jù)屬性包含一個數(shù)據(jù)值的位置叫做數(shù)據(jù)屬性和訪問器屬性和就是訪問器屬性數(shù)據(jù)屬性而數(shù)據(jù)屬性又有四個這樣的特性是否可配置編輯刪除屬性默認(rèn)是否可以被枚舉即可被默認(rèn)是否可寫默認(rèn)寫不代表刪除這里僅限修改屬性的數(shù)據(jù)值默認(rèn)是設(shè)置了一個空對象定
ECMAScript 中又兩種屬性:數(shù)據(jù)屬性(包含一個數(shù)據(jù)值的位置叫做數(shù)據(jù)屬性)和訪問器屬性(getter() 和 setter()就是訪問器屬性)
數(shù)據(jù)屬性而數(shù)據(jù)屬性又有四個這樣的特性:
[Configurable] 是否可配置,編輯,刪除屬性,默認(rèn)true
[Enumberable]是否可以被枚舉,即可被for-in,默認(rèn)true
[Writable] 是否可寫,默認(rèn)true,寫不代表刪除,這里僅限修改
[Value] 屬性的數(shù)據(jù)值,默認(rèn)是undefined
var person = {};//設(shè)置了一個空對象 //定義了對象的默認(rèn)屬性 name 的一些屬性的特性 Object.defineProperty(person,"name",{ writable:false,// 不可以編輯 value:"niconico" //默認(rèn)的值就是 niconico }); console.log(person.name);//返回niconico,因為默認(rèn)值,不用設(shè)置也有值 person.name = "gggg";//即使設(shè)置了name 新值,因為不可編輯,所以沒變化 console.log(person.name);//返回niconico
需要注意的是[Configurable]被改 false 之后沒辦法改回來 true
訪問器屬性(getter() 和 setter()),而他們又有這樣的特性:
[Configurable] 跟之前一樣
[Enumberable] 跟之前一樣
[Get] 在讀取屬性時調(diào)用的函數(shù),默認(rèn)是 undefined
[Set] 在寫入屬性時調(diào)用的函數(shù),默認(rèn)是 undefined
var book = { _year: 2004, //常用語法,代表只能通過對象方法訪問的屬性 edition: 1 }; Object.defineProperty(book, "year", { get: function () { //定義一個 getter return this._year; //直接讀取屬性 }, //如果不設(shè)置 setter的話那么這個對象的屬性就沒辦法修改 set: function (newValue) { //定義一個 setter if (newValue > 2004) { this._year = newValue; //如果注釋掉,那么_ year 不會改變 this.edition += newValue - 2004; } } }); book.year = 2005; //因為這個函數(shù)的 setter 里面也一起把_year修改了,所以能夠看到被修改 console.log(book.year); //返回2005 console.log(book.edition);//返回2
數(shù)據(jù)屬性和訪問器屬性的區(qū)分
var book = {}; Object.defineProperties(book, { //這里用了defineProperties定義多個屬性 _year: { //數(shù)據(jù)屬性 value: 2004 }, edition: { //數(shù)據(jù)屬性 value: 1 }, year: {//訪問器屬性,判斷的標(biāo)準(zhǔn)就是是否有 getter 或者 setter get: function () { return this._year; }, set: function (newValue) { if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004; } } } }) //獲取屬性的特性 var descriptor = Object.getOwnPropertyDescriptor(book,"_year"); console.log(descriptor.value); //獲取值這個特性 console.log(descriptor.configurable); //獲取 configurable 這個特性創(chuàng)建對象
工廠模式:用函數(shù)封裝以特定的接口創(chuàng)建對象,沒法創(chuàng)建特定類型的對象
構(gòu)造函數(shù)模式: 構(gòu)造函數(shù)可以用來創(chuàng)建特定類型的對象,但是每個成員無法復(fù)用
原型模式:使用構(gòu)造函數(shù)的 prototype 屬性來指定那些應(yīng)該共享的屬性和方法
組合繼承: 使用構(gòu)造函數(shù)模式和原型模式時,使用構(gòu)造函數(shù)定義實例屬性,而使用原型定義共享的屬性和方法
動態(tài)原型模式:可以在不必預(yù)先定義構(gòu)造函數(shù)的情況下實現(xiàn)繼承,其本質(zhì)是執(zhí)行給指定對象的淺復(fù)制
寄生構(gòu)造函數(shù)模式:基于某個對象或某些信息創(chuàng)建一個對象,然后增強(qiáng)對象,最后返回對象
穩(wěn)妥構(gòu)造函數(shù)模式:集寄生式繼承和組合繼承的優(yōu)點
工廠模式這種模式抽象了創(chuàng)建具體對象的過程,因為 ECMAScript 中無法創(chuàng)建類,所以用函數(shù)封裝以特定的接口創(chuàng)建對象
function createPerson(name,age,job) { var o = new Object(); //代替創(chuàng)建對象 o.name = name;//代替設(shè)置屬性 o.age = age; o.job = job; o.sayName = function () { // 代替設(shè)置方法 console.log(this.name); }; return o; //返回是一個對象 } var person1 = createPerson("nico",29,"soft"); var person2 = createPerson("gg",30,"dog"); console.log(person1);//返回Object {name: "nico", age: 29, job: "soft"} console.log(person2);
構(gòu)造函數(shù)模式優(yōu)點:
1.創(chuàng)建對象的方式簡單了
缺點:
1.沒有解決對象類型識別的問題,因為都是直接new Object, 都是 Object,所以沒法區(qū)分是具體哪一個對象類型
實際上構(gòu)造函數(shù)經(jīng)歷了以下過程:
創(chuàng)建一個新對象
將構(gòu)造函數(shù)的作用域賦給新對象(因此this指向了這個新對象)
執(zhí)行構(gòu)造函數(shù)中的代碼(為這個新對象添加屬性)
返回新對象
function Person(name,age,job) { //標(biāo)準(zhǔn)寫法,構(gòu)造函數(shù)名第一個大寫 this.name = name; this.age = age; this.job = job; this.sayName = function () { console.log(this.name); } //不需要return,因為會自動返回新對象,如果使用了 return 就會改變了返回的內(nèi)容 } var person1 = new Person("nico",29,"soft");//用new var person2 = new Person("gg",30,"dog"); console.log(person1);//返回Person {name: "nico", age: 29, job: "soft"} console.log(person2); //這些實例都是Object 對象,也是Person 對象 console.log(person1 instanceof Object);//返回 true console.log(person1 instanceof Person);//返回 true //person1和 person2分別保存著Person 一個不同的實例,這兩個實例都有一個constructor(構(gòu)造函數(shù))屬性,都指向Person, 說明他們都是同一個構(gòu)造函數(shù)創(chuàng)建的 console.log(person1.constructor == Person);//返回 true console.log(person2.constructor == Person);//返回
構(gòu)造函數(shù)與其他函數(shù)的唯一區(qū)別就在于調(diào)用他們的方式不同,任何函數(shù),只要通過 new 來調(diào)用,那他就可以作為構(gòu)造函數(shù),而任何函數(shù),如果不通過 new 來調(diào)用,那他就跟普通函數(shù)也不會有兩樣.
構(gòu)造函數(shù)也可以作為普通函數(shù)使用
function Person(name, age, job) { this.name = name; //直接賦值給 this, 即直接設(shè)置當(dāng)前對象的屬性 this.age = age; this.job = job; this.sayName = function () { console.log(this.name); } //不需要return, 也不需要返回對象 } // 作為構(gòu)造函數(shù)調(diào)用 var person = new Person("pp", 10, "kk"); person.sayName();//返回 pp //作為普通函數(shù)調(diào)用 Person("yy", 20, "gg");//這里添加到 window 對象了,因為默認(rèn)全局作用域 window.sayName();//返回 yy //在另外一個對象的作用域中調(diào)用 var o = new Object(); Person.call(o, "aa", 25, "bb"); //因為是被 o 調(diào)用,所以 this 指向 o o.sayName();//返回 aa
原型模式優(yōu)點:
1.可以知道對象實例是是哪個對象類型,即構(gòu)造函數(shù)是誰(通過 instanceOf() 或者 constructor 來驗證)
缺點:
1.每個方法都要在每個實例上重新創(chuàng)建一遍,會導(dǎo)致不同的作用域鏈和標(biāo)示符解析問題,例如兩個實例之間的方法并不能用== 來判斷,例如 person1.sayName == person2.sayName 是返回 false 的,因為都是新創(chuàng)建的實例,都是獨立的
我們創(chuàng)建的每個函數(shù)都有一個 prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的共享的屬性和方法,
換句話說,不必再構(gòu)造函數(shù)中定義對象實例的信息,而是可以將這些信息直接添加到原型對象中
function Person() {} //初始化一個空對象 Person.prototype.name = "nico"; //直接將屬性寫到原型里面 Person.prototype.sayName = function () {//直接將方法寫到原型里面 console.log(this.name); }; //原型的所有屬性和方法被所有實例共享 var person1 = new Person(); person1.sayName();//返回 nico var person2 = new Person(); person2.sayName();//返回 nico //他們其實都指向同一個原型的同一個方法,所以 true console.log(person1.sayName() == person2.sayName());//返回true
理解原型對象優(yōu)點:
1.可以讓所有對象實例共享它所包含的屬性和方法
缺點:
1.實例都需要有只屬于自己的屬性,而原型對象是完全共享的,所以很少有人多帶帶使用原型模式
在腳本中沒有標(biāo)準(zhǔn)的方式訪問[prototype],但是firefox,safari,chrome在每個對象上都支持一個_proto_
創(chuàng)建了自定義的構(gòu)造函數(shù)之后,其原型對象默認(rèn)只會取得constructor屬性,當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個新實例后,該實例的內(nèi)部將包含一個指針指向構(gòu)造函數(shù)的運(yùn)行對象.
?
Person 是構(gòu)造函數(shù),Person.prototype是原型對象,person1 和 person2 是實例
Person.prototype的constructor指向Person,因為原型對象是構(gòu)造函數(shù)創(chuàng)建的,所以 constructor 指向Person
Person的prototype 指向了原型對象,而又因為默認(rèn)情況下,所有的原型對象的 constructor 都是在被創(chuàng)建的時候指向構(gòu)造函數(shù)
person1和person2 有一個內(nèi)部屬性[prototype],指向Person.prototype,實例的prototype 指向原型對象很正常
通過isPrototypeOf()來確定對象之間是否存在實例和原型對象的關(guān)聯(lián)關(guān)系
//如果[prototype]指向調(diào)用isPrototypeOf方法的對象的話,那么就會返回 true console.log(Person.prototype.isPrototypeOf(person1)); //返回 true console.log(Person.prototype.isPrototypeOf(person2)); //返回 true
通過 getPrototypeOf 方法來獲取原型的屬性
//getPrototypeOf返回的對象是原型對象 console.log(Object.getPrototypeOf(person1) == Person.prototype);//返回 true console.log(Object.getPrototypeOf(person1).name); //即使這個實例沒有設(shè)置屬性 name, 也可以獲取原型對象的屬性 name
用 hasOwnProperty() 方法檢測一個屬性是否存在實例中(返回 true),還是存在與原型中(返回 false)
function Person() {} //初始化一個空對象 Person.prototype.name = "nico"; var person1 = new Person(); var person2 = new Person(); //沒有這個屬性也會返回 false console.log(person1.hasOwnProperty("name"));//返回 false person1.name="bbb";//設(shè)置 person1的name console.log(person1.name); //返回 bbb console.log(person1.hasOwnProperty("name"));//返回true //沒有設(shè)置,使用的是原型的 name,即使不存在實例中的時候 console.log(person2.name);//返回 nico console.log(person2.hasOwnProperty("name"));//返回 false
每當(dāng)代碼讀取某個對象的某個屬性的時候,都會執(zhí)行一次搜搜,搜索搜索對象實例本身,如果沒有,就去搜索原型對象
同時使用 in 和hasOwnProperty就能確定該屬性是存在對象中還是存在原型中
只能確定是否存在實例中,但區(qū)分不了是對象還是原型,hasOwnProperty只能確認(rèn)是否存在實例中,所以兩者結(jié)合可以實現(xiàn)判斷
function hasPrototypeProperty(object,name) { //屬性不存在于實例中 并且屬性存在于對象中就返回 true return !object.hasOwnProperty(name) && (name in object); }
在 for-in 循環(huán)時,返回的是所有能夠通過對象訪問的,可枚舉的屬性,其中包括實例中的屬性和原型中的屬性
用 Object.keys() 方法返回所有可枚舉的屬性, Object.getOwnPropertyNames可以返回所有屬性,包括不可枚舉的屬性
function Person() { } Person.age = 19; Person.prototype.name = "nico"; var keys1 = Object.keys(Person);//Person 的屬性 console.log(keys1); //返回["age"],數(shù)組 var keys2 = Object.keys(Person.prototype);//Person的原型對象屬性 console.log(keys2);//返回["name"],數(shù)組 //getOwnPropertyNames可以返回所有屬性,包括不可枚舉的屬性,例如constructor var keys3 = Object.getOwnPropertyNames(Person); console.log(keys3); //返回["length", "name", "arguments", "caller", "prototype", "age"] var keys4 = Object.getOwnPropertyNames(Person.prototype); console.log(keys4); //返回["constructor", "name"]
Object.keys()和Object.getOwnPropertyNames()都可不同程度的代替for-in, 不過需要比較新的瀏覽器
更簡單的原型語法,封裝原型
function Person() { } //字面量創(chuàng)建對象語法 Person.prototype = { constructor: Person, name: "nico", age: 18, sayName: function () { console.log(this.name); } }
原型的動態(tài)性需要注意的是這種寫法的話, constructor屬性不再指向Person,因為沒創(chuàng)建一個函數(shù),就會同時創(chuàng)建他的 prototype 對象,這個對象自動獲得 constructor 屬性,而字面量語法會重寫這個 prototype 對象,因此 constructor 屬性也就變成了新的對象的 constructor 屬性(指向 Object),所以需要另外指定一個 constructor
由于在原型中查找值的過程是一次搜索,因此我們隊原型對象所做的任何修改都能夠立即從實例上反映出來
function Person1() {}; var friend = new Person1(); //先與修改原型前創(chuàng)建了實例,但也能使用這個原型方法 Person1.prototype.sayHi = function () { console.log("hi"); }; //先找自己,然后找原型 friend.sayHi();//返回 hi
原因:實例與原型之間的松散鏈接關(guān)系
當(dāng)我們調(diào)用 friend.sayHi( )時,首先會在實例中搜索名為 sayHi 的屬性,沒找到之后會繼續(xù)搜索原型,因為實例與原型之間的鏈接只不過是一個指針,而非一個副本,因此就可以在原型中找到新的 sayHi 屬性并返回保存在那里的函數(shù)
重寫原型切斷了現(xiàn)有原型與任何之前已經(jīng)存在的對象實例之間的聯(lián)系
調(diào)用構(gòu)造函數(shù)時會為實例添加一個指向最初原型的[prototype]指針,而把原型修改為另外一個對象就等于切斷了構(gòu)造函數(shù)與最初原型之間的聯(lián)系
function Person() { } //重寫原型之前 var friend = new Person(); Person.prototype.sayName = function () { console.log("hi"); }; friend.sayName();//返回 hi, //重寫原型之后(注釋了) // Person.prototype = { // constructor: Person, // name: "nico", // age: 18, // sayName: function () { // console.log(this.name); // } // }; friend.sayName();//直接報錯
?
組合使用構(gòu)造函數(shù)模式和原型模式(常用)1.字面量寫法修改原型對象會重寫這個原型對象
2.實例中的指針僅指向原型,而不指向構(gòu)造函數(shù)
3.因為他會創(chuàng)建一個新的原型對象,原有的實例會繼續(xù)指向原來的原型,但是所有的屬性和方法都存在于新的原型對象里面,所以沒辦法使用這些屬性和方法
4.并不推薦在產(chǎn)品化得程序中修改原生對象的原型,可能會影響了其他使用原生對象的原型的代碼
構(gòu)造函數(shù)用于定義實例屬性,原型模式用于定義方法和共享的屬性.
每個實例都會有自己的一份實例屬性的副本,但同時又共享著對方法的引用,這種模式還支持向構(gòu)造函數(shù)傳遞參數(shù)
function Person(name, age, job) { //實例屬性在構(gòu)造函數(shù)中定義 this.name = name; this.age = age; this.job = job; this.friends = ["tom", "jim"]; } Person.prototype = { //共享的方法在原型中定義 constructor: Person, sayName: function () { console.log(this.name); } }; var person1 = new Person("Nico", 29, "software eng"); var person2 = new Person("Greg", 30, "doctor"); person1.friends.push("Vivi");//多帶帶添加 person1實例的數(shù)組數(shù)據(jù) console.log(person1.friends);//返回["tom", "jim", "Vivi"] console.log(person2.friends);//返回["tom", "jim"] console.log(person1.friends === person2.friends); //返回 false,沒共享 friends 數(shù)組 console.log(person1.sayName === person2.sayName); //返回 true ,共享了其他方法動態(tài)原型模式
通過檢查某個應(yīng)該存在的方法是否有效,來決定是否需要初始化原型.
把所有信息都封裝在構(gòu)造函數(shù),通過在構(gòu)造函數(shù)中初始化原型
function Person(name, age, job) { //實例屬性在構(gòu)造函數(shù)中定義 this.name = name; this.age = age; this.job = job; //只在sayName方法不存在的時候才添加原型中 if (typeof this.sayName != "function") { Person.prototype.sayName = function () { console.log(this.name); } } } var friend = new Person("jack", 29, "soft ware eng"); friend.sayName();
寄生構(gòu)造函數(shù)模式(parasitic)(不建議使用)對原型修改的話,不能使用字面量重寫,因為會斷開跟原型的關(guān)聯(lián)
創(chuàng)建一個函數(shù),該函數(shù)的作用僅僅是封裝創(chuàng)建對象的代碼,然后再返回新創(chuàng)建的對象.
這個代碼幾乎跟工廠模式一樣,唯一區(qū)別是如何調(diào)用,工廠模式?jīng)]有 new, 這個有 new
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); }; //在構(gòu)造函數(shù)里面添加一個 return 會重寫調(diào)用構(gòu)造函數(shù)時返回的值 //不寫 return 的話,默認(rèn)會返回新對象實例 return o; } //用 new 方式來調(diào)用 var friend = new Person("jack", 29, "soft ware eng"); friend.sayName(); //返回的實例就是 Person 函數(shù)里面新創(chuàng)建的那個指定實例,所以有這個實例的所有屬性和方法
穩(wěn)妥構(gòu)造函數(shù)模式(較少使用)返回的對象與構(gòu)造函數(shù)或者構(gòu)造函數(shù)原型屬性之間沒有關(guān)系,所以不能使用 instanceof 來確定對象類型,不建議使用這種模式
穩(wěn)妥對象durable object指的是沒有公共屬性,而且其方法也不引用 this 的對象,主要用在一些安全的環(huán)境中,禁止使用this 和 new 之類的,或者在防止數(shù)據(jù)被其他應(yīng)用程序改動時使用
function Person(name, age, job) { //創(chuàng)建要返回的對象 var o = new Object(); // 這個就是一個穩(wěn)妥對象,因為多帶帶獨立 //可以在這里定義私有變量和函數(shù) //添加方法 o.sayName = function () { console.log(name); }; //返回對象 return o; } var friend = Person("nico", 29, "software eng"); friend.sayName(); //返回 nico繼承
實現(xiàn)繼承:表示一個類型派生于一個基類型,擁有該基類型的所有成員字段和函數(shù)。
接口繼承:表示一個類型只繼承了函數(shù)的簽名,沒有繼承任何實現(xiàn)代碼。
一個函數(shù)由這么幾部分組成,函數(shù)名、參數(shù)個數(shù)、參數(shù)類型、返回值,函數(shù)簽名由參數(shù)個數(shù)與其類型組成。函數(shù)在重載時,利用函數(shù)簽名的不同(即參數(shù)個數(shù)與類型的不同)來區(qū)別調(diào)用者到底調(diào)用的是那個方法!函數(shù)簽名由函數(shù)的名稱和它的每一個形參(按從左到右的順序)的類型和種類(值、引用或輸出)組成。
因為 ECMAScript 中函數(shù)沒有簽名,所以無法實現(xiàn)接口繼承
ECMAScript 只支持實現(xiàn)繼承,而且其實現(xiàn)繼承主要是依靠原型鏈來實現(xiàn).
原型鏈實現(xiàn)繼承主要是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法
構(gòu)造函數(shù),原型和實例的關(guān)系:
每個構(gòu)造函數(shù)都有一個原型對象
原型對象都包含一個指向構(gòu)造函數(shù)的指針
實例都包含一個指向原型對象的內(nèi)部指針
假如我們讓原型對象等于另外一個類型的實例,此時,原型對象將包含一個指向另一個原型的指針,相應(yīng)地,另一個原型中也包含著一個指向另外一個構(gòu)造函數(shù)的指針,如此類推
?
(我把 SuperType 的 prototype 屬性換了一個名字testprototype,方便理解)
instance 指向 SubType 的原型, SubType 的原型又指向了 SuperType 的原型, getSuperValue 方法仍然還在 SuperType.prototype 中,但是property(testprototype) 則位于 SubType.prototype 中,這是因為 prototype(testprototype)是一個實例屬性,而 getSuperValue() 則是一個原型方法.既然 SubType.prototype 現(xiàn)在是 SuperType 的實例,那么 property(testprototype)就位于該實例中.
instance.constructor 現(xiàn)在指向SuperType, 這是因為 SubType 的原型指向了另外一個對象-- SuperType 的原型,而這個原型對象的 constructor 屬性指向 SuperType
//假設(shè)這個類型要被繼承方 function SuperType() { //屬性 this.testSuperprototype = true; } //原型的方法 SuperType.prototype.getSuperValue = function () { return this.testSuperprototype; }; //假設(shè)這個類型是繼承方 function SubType() { //屬性 this.subproperty = false; } //SubType繼承于SuperType,將實例賦給SubType.prototype(Subtype的原型), //實現(xiàn)的本質(zhì)就是重寫了SubType的原型對象 SubType.prototype = new SuperType(); //集成之后,設(shè)置SubType的原型的方法 SubType.prototype.getSubValue = function () { return this.subproperty;//獲取subproperty屬性,如果沒有繼承的話,那么這里是 false //繼承之后就改變了 }; var instance = new SubType(); console.log(instance.getSuperValue()); //返回 true
繼承通過創(chuàng)建 SuperType 的實例,然后賦給 Subtype.prototype 原型實現(xiàn)的,原來存在于 SuperType 的實例的所有屬性和方法,現(xiàn)在也存在于 SubType.prototype 中了
確立繼承關(guān)系之后,我們給 Subtype.prototype 添加了一個方法,這樣就在繼承了 SuperType 的屬性和方法的基礎(chǔ)上有添加了一個新方法
這是完整的原型鏈圖,因為還要包含 Object, 不過總的來說基本差不多,例如,如果調(diào)用 instance的 toString()方法,其實就是調(diào)用 Object 的 toString()
?
//因為原型鏈的關(guān)系, instance都是Object 或者SuperType 或者SubType 任何一個類型的實例 console.log(instance instanceof Object);//true console.log(instance instanceof SuperType);//true console.log(instance instanceof SubType);//true //只要在原型鏈出現(xiàn)過的原型,都可以說是該原型鏈所派生的實例的原型 console.log(Object.prototype.isPrototypeOf(instance));//true console.log(SuperType.prototype.isPrototypeOf(instance));//true console.log(SubType.prototype.isPrototypeOf(instance));//true謹(jǐn)慎地定義方法
給原型添加方法的代碼一定要放在替換原型的語句之后,不然就會覆蓋了超類中的方法了.
//假設(shè)這個類型要被繼承方 function SuperType() { //屬性 this.testSuperprototype = true; } //原型的方法 SuperType.prototype.getSuperValue = function () { return this.testSuperprototype; }; //假設(shè)這個類型是繼承方 function SubType() { //屬性 this.subproperty = false; } //SubType繼承于SuperType,將實例賦給SubType.prototype(Subtype的原型) SubType.prototype = new SuperType(); //繼承之后,設(shè)置SubType的原型的方法 SubType.prototype.getSubValue = function () { return this.subproperty;//獲取subproperty屬性,如果沒有繼承的話,那么這里是 false //繼承之后就改變了 }; //重寫超類型(被繼承的類型)中的方法 SubType.prototype.getSuperValue = function () { return false; //返回的是這個,而不是 true(被繼承的類型中是 true) }; var instance = new SubType(); console.log(instance.getSuperValue()); //返回 false
借用構(gòu)造函數(shù)constructor stealing(很少用)在通過原型鏈實現(xiàn)繼承時,不能使用對象字面量創(chuàng)建原型方法,因為這樣會重寫原型鏈的
基本思想:在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù).
function SuperType() { this.colors = ["red", "blue", "green"]; } function SubType() { ///call 的方式以SubType的身份來調(diào)用SuperType的構(gòu)造函數(shù), //這么做可以將SuperType的構(gòu)造函數(shù)的屬性傳到SubType上, SuperType.call(this); } var instance1 = new SubType(); instance1.colors.push("black"); console.log(instance1.colors);//返回["red", "blue", "green", "black"] var instance2 = new SubType(); console.log(instance2.colors);//返回["red", "blue", "green"]
組合繼承combination inheritance(常用)優(yōu)點:
1.能實現(xiàn)繼承
缺點:
1.因為使用 call 的方式即使可以調(diào)用超類來實現(xiàn)繼承,但是超類的原型屬性和方法都不能使用,因為 call 只是改變 this, 沒有改變 constructor 指向
將原型鏈和借用構(gòu)造函數(shù)技術(shù)組合到一起
基本思想是:使用原型鏈實現(xiàn)對原型屬性和方法的繼承,而通過借用構(gòu)造函數(shù)來實現(xiàn)對實例屬性的繼承
既通過原型上定義方法實現(xiàn)了函數(shù)復(fù)用,又能夠保證每個實例都有它自己的屬性
//設(shè)置一個超類,即SuperType的構(gòu)造函數(shù),里面有2個屬性 function SuperType(name) { this.name = name; this.colors = ["red", "blue"]; } //設(shè)置一個超類,即SuperType的原型方法 sayName() SuperType.prototype.sayName = function () { console.log(this.name); }; //設(shè)置一個子類,即SubType的構(gòu)造函數(shù) function SubType(name, age) { //call 的方式以SubType的身份來調(diào)用SuperType的構(gòu)造函數(shù), //這么做可以將SuperType的構(gòu)造函數(shù)的屬性傳到SubType上,但是因為call 只能改變 this 指向,改變不了constructor, 所以沒辦法獲得超類的原型方法 //這樣的話就將超類的屬性放到子類里面,所以在實例化子類之后,即使改變了其中一個子類實例的屬性,也不會影響其他的子類實例 SuperType.call(this, name);////第二次調(diào)用超類SuperType this.age = age; //也設(shè)置了自己本身的屬性(方便區(qū)分) } //將超類SuperType實例化,并賦值給SubType的原型 //SubType的原型被改寫了,現(xiàn)在就是SuperType實例了,這樣就可以獲取到SuperType的原型方法了 SubType.prototype = new SuperType();//第一次調(diào)用超類SuperType //定義一個自己的原型方法(方便區(qū)分) //這個需要在原型被改寫完成后才能做,不然的話會被覆蓋 SubType.prototype.sayAge = function () { console.log(this.age); }; var instance1 = new SubType("nico", 20); instance1.colors.push("black"); //instance1改變了,不過 instance2不會改變 console.log(instance1.colors); //返回["red", "blue", "black"] instance1.sayName();//返回 nico,這是超類的原型方法,拿到子類用 instance1.sayAge();//返回20,這是子類自定義的原型方法,一樣可以用 var instance2 = new SubType("greg", 29); console.log(instance2.colors);//返回["red", "blue"] instance2.sayName();//返回 greg instance2.sayAge();//返回29
備注:
需要理解原型鏈的知識
需要理解構(gòu)造函數(shù)的執(zhí)行過程
使用這種方式實現(xiàn)繼承, 子類能夠調(diào)用超類的方法和屬性,因為超類的原型也賦值給子類了,真正實現(xiàn)了復(fù)用和繼承,而且也能夠保證各自實例的屬性互不干涉,因為屬性都在new 構(gòu)建的時候生成,每個實例都多帶帶生成
第一次調(diào)用超類會得到兩個屬性 name 和 colors,他們是超類 SuperType 的屬性,不過現(xiàn)在位于子類 SubType 的原型中,第二次調(diào)用超類會創(chuàng)建新的兩個屬性 name 和 colors, 他們會覆蓋掉子類 SubType原型中的兩個同名屬性
原型式繼承缺點:
1.會調(diào)用兩次超類型構(gòu)造函數(shù)
2.不得不在調(diào)用子類型構(gòu)造函數(shù)時重寫屬性
必須有一個對象作為另外一個對象的基礎(chǔ)
在沒必要創(chuàng)建構(gòu)造函數(shù),只想讓一個對象與另外一個對象保持類似的情況下,可以使用這個方式,需要注意的就是共享的問題
function object(o) { function F() { //創(chuàng)建一個臨時性的構(gòu)造函數(shù) } F.prototype = o;//將傳入的對象作為這個構(gòu)造函數(shù)的原型 return new F();//返回這個臨時構(gòu)造函數(shù)的新實例 } var person = { name: "nico", friends: ["a", "b"] }; //傳入 person 對象,返回一個新的實例,這個實例也是傳入的 person 對象作為原型的 //所以可以使用它的屬性和方法 var anotherPerson = object(person); anotherPerson.name = "gg"; anotherPerson.friends.push("rr"); //因為是使用同一個對象作為原型,所以跟原型鏈差不多,會共享這個原型對象的東西 var yetAnotherPerson = object(person); yetAnotherPerson.name = "ll"; yetAnotherPerson.friends.push("kk"); console.log(person.friends);//返回["a", "b", "rr", "kk"] console.log(person.name);//返回nico, 因為基本類型值是不會變化的
在 ECMAScript 5下有一個 Object.create 方法跟他差不多
var person = { name: "nico", friends: ["a", "b"] }; var anotherPerson = Object.create(person); anotherPerson.name = "gg"; anotherPerson.friends.push("rr"); var yetAnotherPerson = Object.create(person); yetAnotherPerson.name = "ll"; yetAnotherPerson.friends.push("kk"); //結(jié)果一樣 console.log(person.friends);//返回["a", "b", "rr", "kk"]
另外Object.create 支持第二個參數(shù),可以指定任何屬性覆蓋原型對象的同名屬性
var person = { name: "nico", friends: ["a", "b"] }; var anotherPerson = Object.create(person, { name: { //以傳入一個對象的方式, key 就是屬性名 value: "lala" } }); console.log(anotherPerson.name);//返回 lala寄生式繼承
寄生式繼承的思路與寄生構(gòu)造函數(shù)和工廠模式類似,即創(chuàng)建一個僅用于封裝繼承過程的函數(shù),該函數(shù)在內(nèi)部以某種方式來增強(qiáng)對象,最后再像真的是他做了所有工作一樣返回對象
任何能夠返回新對象的函數(shù)都適用于此模式
跟構(gòu)造函數(shù)模式類似,不能做到函數(shù)復(fù)用.
function object(o) { function F() { //創(chuàng)建一個臨時性的構(gòu)造函數(shù) } F.prototype = o;//將傳入的對象作為這個構(gòu)造函數(shù)的原型 return new F();//返回這個臨時構(gòu)造函數(shù)的新實例 } //相當(dāng)于扔了兩次,第一次扔給一個臨時的構(gòu)造函數(shù),生成一個實例 //第二次再扔給一個固定變量,然后在這里去給予屬性和方法 function createAnother(original) { var clone = object(original); clone.sayHi = function () { //可以自己添加方法 console.log("hi"); }; return clone; } var person = { name: "nico", friends: ["a", "b"] }; var anotherPerson = createAnother(person); anotherPerson.sayHi();//返回 hi寄生組合式繼承
通過借用構(gòu)造函數(shù)來繼承屬性,通過原型鏈的混成形式來繼承方法
基本思路,使用寄生式繼承來繼承超類型的原型,然后再將結(jié)果指定給予子類型的原型
function object(o) { function F() { //創(chuàng)建一個臨時性的構(gòu)造函數(shù) } F.prototype = o;//將傳入的對象作為這個構(gòu)造函數(shù)的原型 return new F();//返回這個臨時構(gòu)造函數(shù)的新實例 } //兩個參數(shù),一個是子類型函數(shù),一個是超類型構(gòu)造函數(shù) function inheritPrototype(subType, superType) { //創(chuàng)建一個超類型原型的一個副本 var prototype = object(superType.prototype);//創(chuàng)建對象 //為創(chuàng)建的副本添加 constructor 屬性,彌補(bǔ)因重寫原型而失去默認(rèn)的 constructor 屬性 prototype.constructor = subType;//增強(qiáng)對象 //將新創(chuàng)建的對象賦值給子類型的原型,這樣子類型就完成了繼承了 subType.prototype = prototype;//指定對象 } function SuperType(name) { this.name = name; this.colors = ["red", "blue"]; } SuperType.prototype.sayName = function () { console.log(this.name); }; function SubType(name, age) { SuperType.call(this, name); this.age = age; } //可以看到,這里少調(diào)用了一次超類的構(gòu)造函數(shù) inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function () { console.log(this.age); }; var test = new SubType("aa", 100); test.colors.push("white"); console.log(test.colors); //["red", "blue", "white"] test.sayName();//aa test.sayAge();//100 var test1 = new SubType("pp", 1); test1.colors.push("black"); console.log(test1.colors);//["red", "blue", "black"] test1.sayName();//pp test1.sayAge();//1
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/88084.html
摘要:繼承傳統(tǒng)的面向?qū)ο笳Z言,繼承是類與類之間的關(guān)系。原型繼承原型定義原型就是指構(gòu)造函數(shù)的屬性所引用的對象。創(chuàng)建構(gòu)造函數(shù)創(chuàng)建的實例對象張三李四就是對象的原型也是的原型在原型上創(chuàng)建一個屬性運(yùn)行和,并對比是否為同一個方法。 原文鏈接:http://www.hansmkiii.com/2018/07/06/javascript-node-1/ 面向?qū)ο?、原型、繼承 1、面向?qū)ο?1.1 什么...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數(shù)式編程語言,它的代碼運(yùn)行在之上。它通過編輯類工具,帶來了先進(jìn)的編輯體驗,增強(qiáng)了語言服務(wù)。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經(jīng)到來了,總結(jié)過去的 2017,相信小伙們一定有很多收獲...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數(shù)式編程語言,它的代碼運(yùn)行在之上。它通過編輯類工具,帶來了先進(jìn)的編輯體驗,增強(qiáng)了語言服務(wù)。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經(jīng)到來了,總結(jié)過去的 2017,相信小伙們一定有很多收獲...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數(shù)式編程語言,它的代碼運(yùn)行在之上。它通過編輯類工具,帶來了先進(jìn)的編輯體驗,增強(qiáng)了語言服務(wù)。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經(jīng)到來了,總結(jié)過去的 2017,相信小伙們一定有很多收獲...
摘要:寫在前面金三銀四又到了一年一度的跳槽季相信大家都在準(zhǔn)備自己面試筆記我也針對自己工作中所掌握或了解的一些東西做了一個目錄總結(jié)方便自己復(fù)習(xí)詳細(xì)內(nèi)容會在之后一一對應(yīng)地補(bǔ)充上去有些在我的個人主頁筆記中也有相關(guān)記錄這里暫且放一個我的面試知識點目錄大家 寫在前面: 金三銀四, 又到了一年一度的跳槽季, 相信大家都在準(zhǔn)備自己面試筆記, 我也針對自己工作中所掌握或了解的一些東西做了一個目錄總結(jié),方便自...
摘要:寫在前面金三銀四又到了一年一度的跳槽季相信大家都在準(zhǔn)備自己面試筆記我也針對自己工作中所掌握或了解的一些東西做了一個目錄總結(jié)方便自己復(fù)習(xí)詳細(xì)內(nèi)容會在之后一一對應(yīng)地補(bǔ)充上去有些在我的個人主頁筆記中也有相關(guān)記錄這里暫且放一個我的面試知識點目錄大家 寫在前面: 金三銀四, 又到了一年一度的跳槽季, 相信大家都在準(zhǔn)備自己面試筆記, 我也針對自己工作中所掌握或了解的一些東西做了一個目錄總結(jié),方便自...
閱讀 2418·2021-11-16 11:44
閱讀 1877·2021-10-12 10:12
閱讀 2160·2021-09-22 15:22
閱讀 3008·2021-08-11 11:17
閱讀 1505·2019-08-29 16:53
閱讀 2653·2019-08-29 14:09
閱讀 3474·2019-08-29 14:03
閱讀 3301·2019-08-29 11:09