摘要:屬性每個(gè)函數(shù)默認(rèn)有屬性方法返回的函數(shù)除外,其值為構(gòu)造函數(shù)創(chuàng)建對象繼承的對象。其思路使用原型鏈實(shí)現(xiàn)原型屬性和方法的繼承通過借用構(gòu)造函數(shù)實(shí)現(xiàn)實(shí)例屬性繼承。
1 類和模塊
每個(gè)獨(dú)立的JavaScript對象都是一個(gè)屬性的集合,獨(dú)立對象間沒有任何關(guān)系
ES5中的類是基于原型繼承實(shí)現(xiàn)的:如果兩個(gè)對象從同一個(gè)原型對象繼承屬性,稱兩個(gè)對象為同一個(gè)類的實(shí)例。r instanceof Range.prototype操作符是檢查對象r是否繼承自Range.prototype
JavaScript中的類可以動(dòng)態(tài)繼承
1.1 類和原型JavaScript中所有類的實(shí)例都從同一個(gè)原型對象上繼承屬性。原型對象是函數(shù)的prototype屬性,每個(gè)函數(shù)都有。Function.bind()方法返回的函數(shù)沒有prototype屬性。
工廠方法:顯式創(chuàng)建一個(gè)對象,并將其作為返回值
1.2 類和構(gòu)造函數(shù)構(gòu)造函數(shù)用來初始化新創(chuàng)建的對象,每個(gè)新創(chuàng)建對象都繼承了構(gòu)造函數(shù)的prototype屬性指向的原型對象。
關(guān)于構(gòu)造函數(shù)的約定:
構(gòu)造函數(shù)的首字母大寫;
構(gòu)造函數(shù)必須通過new關(guān)鍵字調(diào)用才能創(chuàng)建對象,否則與普通函數(shù)無異;
原型對象必須通過Range.prototype引用
通過new關(guān)鍵字調(diào)用構(gòu)造函數(shù)時(shí),先創(chuàng)建一個(gè)空對象,將構(gòu)造函數(shù)的this綁定到該對象;然后利用構(gòu)造函數(shù)初始化該對象
function Range(from, to) { this.from = from; this.to = to; } // 新創(chuàng)建的所有對象都繼承這個(gè)原型對象 Range.prototype = { // 重置原型對象的constructor屬性 // 判斷x是否在范圍之內(nèi) includes: function(x) {return this.from <= x && x <= this.to;}, // 對于范圍內(nèi)的整數(shù)調(diào)用一次f方法 foreach: function(f) { for(var x=Math.ceil(this.from); x<=this.to; x++) { f(x); } }, toString: function() {return "(" + this.from + "..." + this.to + ")";} }; var r = new Range(1, 3); console.log(r instanceof Range); // true r.foreach(console.log); // 1 2 3 console.log(Range.prototype.constructor); // 原型對象的constructor屬性被重置,不再指向Range()1.3 構(gòu)造函數(shù)和類的標(biāo)識
原型對象是類的唯一標(biāo)識:當(dāng)且僅當(dāng)兩個(gè)對象繼承自同一個(gè)原型對象時(shí),他們才屬于同一個(gè)類的實(shí)例。
r instanceof Range.prototype操作符是檢查對象r是否繼承自Range.prototype
1.4 constructor屬性原型對象中的constructor屬性是構(gòu)造函數(shù)的引用,但如果直接用字面量對象重寫Range.prototype,新對象中沒有constructor屬性,會(huì)默認(rèn)指向Object()構(gòu)造函數(shù)。
重置constructor屬性指向的方法:
// 重置constructor屬性的方法: // 1 顯式為原型添加一個(gè)構(gòu)造函數(shù)屬性 Range.prototype = { constructor: Range, // 顯式增加指向Range的constructor屬性 includes: function(x) {return this.from <= x && x <= this.to;}, foreach: function(f) { for(var x=Math.ceil(this.from); x<=this.to; x++) { f(x); } }, toString: function() {return "(" + this.from + "..." + this.to + ")";} }; // 2 依次為原型對象添加方法 Range.prototype.includes = function(x) { return this.from <= x && x <= this.to; }; Range.prototype.foreach = function(a) { for(var a=Math.ceil(this.from); a<=this.to; a++) { f(a); } }; Range.prototype.toString = function(x) { return "(" + this.from + "..." + this.to + ")"; };2 類的補(bǔ)充 2.1 JavaScript中的函數(shù)
JavaScript中類中的函數(shù)以值的形式出現(xiàn),如果一個(gè)屬性值是函數(shù),稱其為方法。
類的三種對象:
構(gòu)造函數(shù)對象:定義類名,任何添加到構(gòu)造函數(shù)對象本身的屬性都是類字段或類方法
原型對象:原型對象的所有屬性都被實(shí)例對象繼承。
實(shí)例對象:類的每個(gè)實(shí)例對象都是獨(dú)立對象,直接為每個(gè)實(shí)例對象定義的屬性不會(huì)被其他實(shí)例共享。實(shí)例方法與屬性
/* * Complex用于描述復(fù)數(shù)類 * 復(fù)數(shù)是實(shí)數(shù)與虛數(shù)之和,虛數(shù)i的平方為-1 */ function Complex(real, imaginary) { if(isNaN(real) || isNaN(imaginary)) { // 確保兩個(gè)參數(shù)都是數(shù)字 throw new TypeError(); } this.r = real; this.i = imaginary; } // 兩個(gè)復(fù)數(shù)對象之和為一個(gè)新的復(fù)數(shù)對象,使用this代表當(dāng)前復(fù)數(shù)對象 Complex.prototype.add = function(that) { return new Complex(this.r + that.r, this.i + that.i); }; Complex.prototype.multiply = function(that) { return new Complex(this.r * that.r - this.i * that.i, this.r * that.i + this.i * that.r); }; // 復(fù)數(shù)對象的模:原點(diǎn)(0, 0)到復(fù)平面的距離 Complex.prototype.mag = function() { return Math.sqrt(this.r * this.r + this.i * this.i) }; // 復(fù)數(shù)求負(fù)運(yùn)算 Complex.prototype.neg = function() { return new Complex(-this.r, -this.i); }; // 將復(fù)數(shù)轉(zhuǎn)化為字符串 Complex.prototype.toString = function() { return "{" + this.r + "," + this.i + "}"; }; // 當(dāng)前復(fù)數(shù)對象是否與另外一個(gè)復(fù)數(shù)對象值相等 Complex.prototype.equal = function(that) { return that != null && that.constructor === Complex && this.r === that. r && this.i === that.i; }; // 類屬性 Complex.ZERO = new Complex(0, 0); Complex.ONE = new Complex(1, 0); Complex.I = new Complex(0, 1); // 類方法:將實(shí)例對象toString()方法返回的字符串解析為一個(gè)Complex對象 // 或拋出類型錯(cuò)誤異常 Complex._format = /^{([^,]+),([^}]+)}$/; Complex.parse = function(s) { try { // 假設(shè)解析成功 var m = Complex._format.exec(s); return new Complex(parseFloat(m[1]), parseFloat(m[2]); } catch(e) { throw new TypeError("can"t parse " + s + "as a complex number"); } };2.2 類的擴(kuò)充
JavaScript中基于原型對象的繼承機(jī)制是動(dòng)態(tài)的:原型對象的屬性發(fā)生變化,會(huì)影響所有繼承該原型對象的實(shí)例對象,即使實(shí)例對象已經(jīng)定義。(原理應(yīng)該是實(shí)例對象中只是保存指向原型對象的引用)
2.3 類和類型不推薦直接在prototype對象上添加屬性或方法,ES5之前不能設(shè)置添加的屬性和方法為不可枚舉,會(huì)被for-in循環(huán)遍歷,ES5中通過Object.defineProperty()方法設(shè)置對象屬性。
使用typeof操作符可以區(qū)分基本數(shù)據(jù)類型:undefined、null、number、string、function、object和boolean。要區(qū)分?jǐn)?shù)組,有兩種方法:
ES5中的Array.isArray()方法
typeof o === "object" && Object.prototype.toString.call(o).slice(8, -1) === "Array"
區(qū)分自定義類型使用typeof操作符并不能區(qū)分自定義類型:instanceof操作符、constructor屬性和構(gòu)造函數(shù)名稱三種方式可以區(qū)分自定義類型,但各自與各自的缺點(diǎn)
1 instanceof操作符如果對象o繼承自對象c.prototype,o instanceof c返回true,缺點(diǎn)是不能返回類名稱,只能檢測對象是否屬于某個(gè)類。其中c.prototype可以是原型鏈上的對象
使用c.prototype.isPrototypeOf(o)方法可以檢測o繼承的原型鏈上是否有原型對象c.prototype。
2 constructor屬性每個(gè)函數(shù)默認(rèn)有prototype屬性(bind()方法返回的函數(shù)除外),其值為構(gòu)造函數(shù)創(chuàng)建對象繼承的對象。原型對象constructor屬性指向構(gòu)造函數(shù)。
缺點(diǎn)是并非所有對象都帶有constructor屬性。
function typeAndValue(x) { if(x == null || x == undefined) { return ""; //null和undefined沒有構(gòu)造函數(shù) } switch (x.constructor) { case Number: return "Number: " + x; // 原始類型 case String: return "String: " + x; case Date: return "Date: " + x; // 內(nèi)置類型 case RegExp: return "RegExp: " + x; case Complex: return "Complex: " + x; // 自定義類型 } };3 構(gòu)造器函數(shù)的名稱
在多個(gè)執(zhí)行上下文中都存在構(gòu)造器函數(shù)的副本時(shí),instanceof操作符與constructor屬性檢測結(jié)果會(huì)出錯(cuò),但是構(gòu)造器函數(shù)本身的名稱沒有改變,可以作為標(biāo)識
4 鴨子類型可以向鴨子一樣走路、游泳并且嘎嘎叫的鳥就是鴨子。
以部分特征屬性來描述一類對象(關(guān)注對象能做什么,弱化對象的類型)
3 繼承許多OO語言支持接口繼承與實(shí)現(xiàn)繼承。但是ECMAScript沒有函數(shù)簽名,只支持實(shí)現(xiàn)繼承,繼承的實(shí)現(xiàn)主要依賴于原型鏈
3.1 原型鏈原型鏈?zhǔn)紼CMAScript實(shí)現(xiàn)繼承的主要方法:子類的原型對象是父類的實(shí)例對象。
構(gòu)造函數(shù)、原型與實(shí)例的關(guān)系:
構(gòu)造函數(shù)的prototype屬性指向原型對象;
原型對象的constructor屬性指向構(gòu)造函數(shù);
實(shí)例的__proto__屬性指向原型對象,實(shí)例與構(gòu)造函數(shù)沒有直接聯(lián)系
將SubType的原型重寫為SuperType的實(shí)例對象,新原型對象作為SuperType一個(gè)實(shí)例擁有全部屬性和方法,內(nèi)部__proto__屬性指向SuperType的原型。
instance指向SubType的原型,SubType的原型指向SuperType的原型。形成一條原型鏈:原型鏈的搜索機(jī)制。先搜索實(shí)例對象instance,再搜索Subype的原型,再搜索SuperType的原型,依次向上
// 父類 function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; } // 子類 function SubType() { this.subProperty = false; } // 子類的原型對象是父類的實(shí)例對象(其__proto__屬性指向父類的原型對象) SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function() { return this.subProperty; } var instance = new SubType(); instance.getSuperValue(); // true,子類調(diào)用父類的方法
注:實(shí)例對象instance的原型的構(gòu)造函數(shù)不是SubType,而是SuperType。因?yàn)橹刂?b>SubType.prototype的指向,但是沒有重置construtor的指向
console.log(instance.__proto__.constructor); // function SuperType() {native code}1原型鏈末端
所有引用類型都繼承自Object,函數(shù)的默認(rèn)原型是Object的實(shí)例,默認(rèn)原型內(nèi)包含指向Object.prototype的引用,這是所有自定義類型都會(huì)繼承toString()、valueOf()等方法的根本原因
Object.prototype沒有原型,其原型為null,即
Object.prototype.__proto__ === null; // true
2原型與實(shí)例的關(guān)系Object.prototype.__proto__是原型鏈的末端,出口
使用instanceof操作符與isPrototypeOf()方法:
instance instanceof Object; // true instance instanceof SuperType; // true instance instanceof SubType; // true Object.prototype.isPrototypeOf(instance); // true SuperType.prototype.isPrototypeOf(instance); // true SubType.prototype.isPrototypeOf(instance); // true3 謹(jǐn)慎定義子類中方法的位置
如果在子類中定義新方法或者重寫父類的方法,必須子類替換原型語句SubType.prototype = new SuperType();之后,否則不起作用。
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; } // 子類 function SubType() { this.subProperty = false; } // 子類的原型對象是父類的實(shí)例對象(其__proto__屬性指向父類的原型對象) SubType.prototype = new SuperType(); // 添加新方法 SubType.prototype.getSubValue = function() { return this.subProperty; } // 重寫父類中的方法 SuperType.prototype.getSuperValue = function() { return false; } var instance = new SubType(); instance.getSuperValue(); // false
在使用原型鏈實(shí)現(xiàn)繼承時(shí),不能使用字面量方式創(chuàng)建原型對象,否則會(huì)切斷原型鏈,將原型對象重行指向字面量對象
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; } // 子類 function SubType() { this.subProperty = false; } // 子類的原型對象是父類的實(shí)例對象(其__proto__屬性指向父類的原型對象) SubType.prototype = new SuperType(); // 使用字面量方式添加新方法,使上一行代碼無效 SubType.prototype = { getSubValue: function() { return this.subProperty; }, getSuperValue: function() { return false; } } var instance = new SubType(); instance.getSuperValue(); // false4 原型鏈的問題
對于包含引用類型值的原型對象:所有勢力共享原型的屬性,如果其屬性值是引用類型:在一個(gè)實(shí)例上修改該引用類型的值,會(huì)體現(xiàn)在所有的實(shí)例對象上 。-----所以需要將引用類型值定義在構(gòu)造函數(shù)中,而非原型對象中。
function SuperColor() { this.color = ["red", "blue"]; } function SubColor() { } SubColor.prototype = new SuperColor(); // 子類原型定義為父類的實(shí)例,但是color屬性值為引用類型 var col1 = new SubColor(); col1.color.push("green"); console.log(col1.color); // ["red", "blue", "green"] // 注意,所有的實(shí)例對象的color都改變 var col2 = new SubColor(); console.log(col2.color); // ["red", "blue", "green"]
沒有辦法在不影響所有對象實(shí)例的情況下,向父類的構(gòu)造函數(shù)傳遞參數(shù)。
基于上述2點(diǎn)原因,很少多帶帶使用原型鏈
3.2 借用構(gòu)造函數(shù)constructor stealing在子類中,利用創(chuàng)建的對象,以方法的形式調(diào)用父類構(gòu)造器函數(shù),父類構(gòu)造器函數(shù)僅用于初始化子類中創(chuàng)建的對象
基本思想:在子類構(gòu)造函數(shù)內(nèi)部調(diào)用父類構(gòu)造函數(shù)。因?yàn)楹瘮?shù)只是特定環(huán)境中執(zhí)行代碼的對象,可以使用call()、apply()方法在新創(chuàng)建對象上執(zhí)行構(gòu)造函數(shù)
1.通過new調(diào)用SubColor():本質(zhì)先創(chuàng)建一個(gè)對象,將其綁定到this,再利用this調(diào)用函數(shù)SuperColor(),設(shè)置this.color屬性值
2.每次調(diào)用new SubColor()創(chuàng)建的都是獨(dú)立的對象,所以不影響
function SuperColor() { this.color = ["red", "blue"]; } function SubColor() { // 繼承SuperColor // 使用新創(chuàng)建的對象this來調(diào)用SuperColor()函數(shù),設(shè)置this.color屬性值 // 每次調(diào)用new SubColor()創(chuàng)建的都是獨(dú)立的對象,所以不影響 SuperColor.call(this); } var col3 = new SubColor(); col1.color.push("green"); console.log(col3.color); // ["red", "blue", "green"] var col4 = new SubColor(); console.log(col4.color); // ["red", "blue"]傳遞參數(shù)
通過借用構(gòu)造器函數(shù)可以向父類構(gòu)造函數(shù)傳遞參數(shù)。將參數(shù)掛載在call()或apply()方法中:將父類構(gòu)造器哈數(shù)僅用作初始化對象用
function SuperType(name) { this.name = name; } function SubType() { // 繼承SuperType,同時(shí)傳遞參數(shù)"Tracy" SuperType.call(this, "Tracy"); this.age = 23; // 實(shí)例屬性 } var kyxy = new SubType(); console.log(kyxy.name); // "Tracy" console.log(kyxy.age); // 23借用構(gòu)造器函數(shù)的問題
3.3 組合繼承如果僅僅使用借用構(gòu)造函數(shù)模式,只能講方法都定義在構(gòu)造函數(shù)中,不能復(fù)用函數(shù),所以借用構(gòu)造函數(shù)模式很少多帶帶使用
將原型鏈模式與借用構(gòu)造函數(shù)模式組合,發(fā)揮二者的長處。其思路:
使用原型鏈實(shí)現(xiàn)原型屬性和方法的繼承;通過借用構(gòu)造函數(shù)實(shí)現(xiàn)實(shí)例屬性繼承。組合繼承避免原型鏈與借用構(gòu)造函數(shù)的缺點(diǎn),融合優(yōu)點(diǎn),是ECMAScript中最常用的的繼承模式
首先使用借用構(gòu)造函數(shù)模式繼承實(shí)例屬性
再使用原型鏈模式繼承原型的屬性與方法
借用構(gòu)造函數(shù)模式:利用子類創(chuàng)建空對象,將父類的實(shí)例屬性拷貝到子類中。因?yàn)槊總€(gè)子類是獨(dú)立的對象,所以享有父類的拷貝也是相互獨(dú)立的
實(shí)例間共享的原型對象中的屬性依然通過原型鏈模式實(shí)現(xiàn)
function SuperType(name) { this.name = name; this.color = ["red", "blue"]; } SuperType.prototype.sayName = function() { coonsle.log(this.name); } function SubType(name, age) { // 實(shí)例屬性的繼承(不再是引用,而是多帶帶一份拷貝) SuperType.call(this, name); this.age = age; } // 原型屬性與方法的繼承 SubType.prototype = new SuperType(); // 重置原型對象constructor的指向 SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function() { console.log(this.age); } var p1 = new SubType("Kyxy", 23); p1.color.push("black"); p1.sayAge(); // 23 p1.sayName(); // "Kyxy" p1.color; // ["red", "blue", "black"] var p2 = new SubType("Tracy", 23); p2.color; // ["red", "blue"]3.4 總結(jié)
ECMAScript中創(chuàng)建對象的模式:
工廠模式
構(gòu)造函數(shù)模式
原型模式
ECMAScript中主要的繼承模式是組合繼承:綜合原型鏈模式與借用構(gòu)造函數(shù)模式的優(yōu)點(diǎn)。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/81989.html
摘要:異步函數(shù)對象接口,包含和兩個(gè)成員方法。哈希計(jì)數(shù)在整個(gè)的源碼中都沒有找到和方法的調(diào)用,這兩個(gè)方法的具體作用是在原生中實(shí)現(xiàn)類式繼承和私有屬性一類的功能。 文件結(jié)構(gòu) utils/HashObject.ts文件:showImg(https://segmentfault.com/img/bVZpuq?w=642&h=472); 首先解釋一下文件結(jié)構(gòu)圖 __extends方法 通過原型對象模擬類...
摘要:類總所周知,不像其他面向?qū)ο笳Z言那樣支持類,但是可以通過函數(shù)和原型來模擬類。如果你學(xué)習(xí)過或者其他面向?qū)ο笳Z言的話,你會(huì)覺得很熟悉。結(jié)論下一個(gè)版本的會(huì)帶來一個(gè)更加簡單更加友好的語法來幫助那些從面向?qū)ο笳Z言轉(zhuǎn)過來的開發(fā)者的學(xué)習(xí)。 原文地址:http://www.frontendjournal.com/javascript-es6-learn-important-features-in-a-...
摘要:又將整個(gè)文藝類閱讀系統(tǒng)的業(yè)務(wù)劃分為兩大部分,分別是面向管理員和合作作者的后臺(tái)管理系統(tǒng)和面向用戶的移動(dòng)端,系統(tǒng)的需求分析將圍繞這兩部分進(jìn)行展開。 效果展示 showImg(https://user-gold-cdn.xitu.io/2018/8/26/16576a709bd02f5f?w=1409&h=521&f=gif&s=30128195); showImg(https://user...
摘要:又將整個(gè)文藝類閱讀系統(tǒng)的業(yè)務(wù)劃分為兩大部分,分別是面向管理員和合作作者的后臺(tái)管理系統(tǒng)和面向用戶的移動(dòng)端,系統(tǒng)的需求分析將圍繞這兩部分進(jìn)行展開。 效果展示 showImg(https://user-gold-cdn.xitu.io/2018/8/26/16576a709bd02f5f?w=1409&h=521&f=gif&s=30128195); showImg(https://user...
閱讀 3229·2021-11-23 10:09
閱讀 2063·2021-10-26 09:51
閱讀 980·2021-10-09 09:44
閱讀 3906·2021-10-08 10:04
閱讀 2745·2021-09-22 15:14
閱讀 3624·2021-09-22 15:02
閱讀 1057·2021-08-24 10:03
閱讀 1727·2019-12-27 12:14