摘要:規(guī)定,每個(gè)函數(shù)都有一個(gè)屬性,指向一個(gè)對(duì)象大毛二毛構(gòu)造函數(shù)的屬性,就是實(shí)例對(duì)象和的原型對(duì)象。這就是所有對(duì)象都有和方法的原因,因?yàn)檫@是從繼承的構(gòu)造函數(shù)的屬性指向一個(gè)數(shù)組,就意味著實(shí)例對(duì)象可以調(diào)用數(shù)組方法。
目錄 [隱藏]
原型對(duì)象概述
構(gòu)造函數(shù)的缺點(diǎn)
prototype 屬性的作用
原型鏈
constructor 屬性
instanceof 運(yùn)算符
構(gòu)造函數(shù)的繼承
多重繼承
模塊
基本的實(shí)現(xiàn)方法
封裝私有變量:構(gòu)造函數(shù)的寫法
封裝私有變量:立即執(zhí)行函數(shù)的寫法
模塊的放大模式
輸入全局變量
參考鏈接
1.原型對(duì)象概述
每新建一個(gè)實(shí)例,就會(huì)新建一個(gè)meow方法。這既沒有必要,又浪費(fèi)系統(tǒng)資源,因?yàn)樗衜eow方法都是同樣的行為,完全應(yīng)該共享。
這個(gè)問題的解決方法,就是 JavaScript 的原型對(duì)象(prototype)
2.prototype 屬性的作用
原型對(duì)象的作用,就是定義所有實(shí)例對(duì)象共享的屬性和方法。這也是它被稱為原型對(duì)象的原因,而實(shí)例對(duì)象可以視作從原型對(duì)象衍生出來的子對(duì)象。
JavaScript 繼承機(jī)制的設(shè)計(jì)思想就是,原型對(duì)象的所有屬性和方法,都能被實(shí)例對(duì)象共享。
JavaScript 規(guī)定,每個(gè)函數(shù)都有一個(gè)prototype屬性,指向一個(gè)對(duì)象
function f() {}
typeof f.prototype // "object"
function Animal(name) {
this.name = name;
}
Animal.prototype.color = "white";
var cat1 = new Animal("大毛");
var cat2 = new Animal("二毛");
cat1.color // "white"
cat2.color // "white
構(gòu)造函數(shù)Animal的prototype屬性,就是實(shí)例對(duì)象cat1和cat2的原型對(duì)象。
原型對(duì)象上添加一個(gè)color屬性,結(jié)果,實(shí)例對(duì)象都共享了該屬性
Animal.prototype.color = "yellow";
cat1.color // "yellow"
cat2.color // "yellow"
上面代碼中,原型對(duì)象的color屬性的值變?yōu)閥ellow,兩個(gè)實(shí)例對(duì)象的color屬性立刻跟著變了。這是因?yàn)閷?shí)例對(duì)象其實(shí)沒有color屬性,都是讀取原型對(duì)象的color屬性。也就是說,
當(dāng)實(shí)例對(duì)象本身沒有某個(gè)屬性或方法的時(shí)候,它會(huì)到原型對(duì)象去尋找該屬性或方法。如果實(shí)例對(duì)象自身就有某個(gè)屬性或方法,它就不會(huì)再去原型對(duì)象尋找這個(gè)屬性或方法。這就是原型對(duì)象的特殊之處。
3.原型鏈
JavaScript 規(guī)定,所有對(duì)象都有自己的原型對(duì)象(prototype)。一方面,任何一個(gè)對(duì)象,都可以充當(dāng)其他對(duì)象的原型;另一方面,由于原型對(duì)象也是對(duì)象,所以它也有自己的原型。因此,就會(huì)形成一個(gè)“原型鏈”(prototype chain):對(duì)象到原型,再到原型的原型
如果一層層地上溯,所有對(duì)象的原型最終都可以上溯到Object.prototype,即Object構(gòu)造函數(shù)的prototype屬性。也就是說,所有對(duì)象都繼承了Object.prototype的屬性。這就是所有對(duì)象都有valueOf和toString方法的原因,因?yàn)檫@是從Object.prototype繼承的
var MyArray = function () {};
MyArray.prototype = new Array();
MyArray.prototype.constructor = MyArray;
var mine = new MyArray();
mine.push(1, 2, 3);
mine.length // 3
mine instanceof Array // true
構(gòu)造函數(shù)的prototype屬性指向一個(gè)數(shù)組,就意味著實(shí)例對(duì)象可以調(diào)用數(shù)組方法。
mine是構(gòu)造函數(shù)MyArray的實(shí)例對(duì)象,由于MyArray.prototype指向一個(gè)數(shù)組實(shí)例,使得mine可以調(diào)用數(shù)組方法(這些方法定義在數(shù)組實(shí)例的prototype對(duì)象上面)
3.constructor 屬性
prototype對(duì)象有一個(gè)constructor屬性,默認(rèn)指向prototype對(duì)象所在的構(gòu)造函數(shù)
function P() {}
P.prototype.constructor === P // true
由于constructor屬性定義在prototype對(duì)象上面,意味著可以被所有實(shí)例對(duì)象繼承
function P() {}
var p = new P();
p.constructor === P // true
p.constructor === P.prototype.constructor // true
p.hasOwnProperty("constructor") // false
上面代碼中,p是構(gòu)造函數(shù)P的實(shí)例對(duì)象,但是p自身沒有constructor屬性,該屬性其實(shí)是讀取原型鏈上面的P.prototype.constructor屬性
3.1作用
3.1.1可以得知某個(gè)實(shí)例對(duì)象,到底是哪一個(gè)構(gòu)造函數(shù)產(chǎn)生的。
3.1.2另一方面,有了constructor屬性,就可以從一個(gè)實(shí)例對(duì)象新建另一個(gè)實(shí)例
function Constr() {}
var x = new Constr();
var y = new x.constructor();
y instanceof Constr // true
3.1.3在實(shí)例方法中,調(diào)用自身的構(gòu)造函數(shù)成為可能
Constr.prototype.createCopy = function () {
return new this.constructor();
};
3.2constructor屬性表示原型對(duì)象與構(gòu)造函數(shù)之間的關(guān)聯(lián)關(guān)系,如果修改了原型對(duì)象,一般會(huì)同時(shí)修改constructor屬性,防止引用的時(shí)候出錯(cuò)。
function Person(name) {
this.name = name;
}
Person.prototype.constructor === Person // true
Person.prototype = {
method: function () {}
};
Person.prototype.constructor === Person // false
Person.prototype.constructor === Object // true
// 壞的寫法
C.prototype = {
method1: function (...) { ... },
// ...
};
// 好的寫法
C.prototype = {
constructor: C,
method1: function (...) { ... },
// ...
};
// 更好的寫法
C.prototype.method1 = function (...) { ... };
上面代碼中,要么將constructor屬性重新指向原來的構(gòu)造函數(shù),要么只在原型對(duì)象上添加方法,這樣可以保證instanceof運(yùn)算符不會(huì)失真。
如果不能確定constructor屬性是什么函數(shù),還有一個(gè)辦法:通過name屬性,從實(shí)例得到構(gòu)造函數(shù)的名稱。
function Foo() {}
var f = new Foo();
f.constructor.name // "Foo"
4.instanceof 運(yùn)算符
instanceof運(yùn)算符返回一個(gè)布爾值,表示對(duì)象是否為某個(gè)構(gòu)造函數(shù)的實(shí)例
instanceof運(yùn)算符的左邊是實(shí)例對(duì)象,右邊是構(gòu)造函數(shù)。它會(huì)檢查右邊構(gòu)建函數(shù)的原型對(duì)象(prototype),是否在左邊對(duì)象的原型鏈上。因此,下面兩種寫法是等價(jià)的。
v instanceof Vehicle
// 等同于
Vehicle.prototype.isPrototypeOf(v)
由于instanceof檢查整個(gè)原型鏈,因此同一個(gè)實(shí)例對(duì)象,可能會(huì)對(duì)多個(gè)構(gòu)造函數(shù)都返回true。
var d = new Date();
d instanceof Date // true
d instanceof Object // true
Null都為false
注意,instanceof運(yùn)算符只能用于對(duì)象,不適用原始類型的值。
var s = "hello";
s instanceof String // false
上面代碼中,字符串不是String對(duì)象的實(shí)例(因?yàn)樽址皇菍?duì)象),所以返回false。
此外,對(duì)于undefined和null,instanceOf運(yùn)算符總是返回false
上面代碼中,字符串不是String對(duì)象的實(shí)例(因?yàn)樽址皇菍?duì)象),所以返回false。
此外,對(duì)于undefined和null,instanceOf運(yùn)算符總是返回false
5.構(gòu)造函數(shù)的繼承
5.1在子類的構(gòu)造函數(shù)中,調(diào)用父類的構(gòu)造函數(shù)
Sub是子類的構(gòu)造函數(shù),this是子類的實(shí)例。在實(shí)例上調(diào)用父類的構(gòu)造函數(shù)Super,就會(huì)讓子類實(shí)例具有父類實(shí)例的屬性
5.2是讓子類的原型指向父類的原型,這樣子類就可以繼承父類原型
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.prototype.method = "...";
上面代碼中,Sub.prototype是子類的原型,要將它賦值為Object.create(Super.prototype),而不是直接等于Super.prototype。否則后面兩行對(duì)Sub.prototype的操作,會(huì)連父類的原型Super.prototype一起修改掉。
5.3Sub.prototype等于一個(gè)父類實(shí)例Sub.prototype = new Super();
上面代碼中,子類是整體繼承父類。有時(shí)只需要單個(gè)方法的繼承,這時(shí)可以采用下面的寫法。
ClassB.prototype.print = function() {
ClassA.prototype.print.call(this);
// some code
}
上面代碼中,子類B的print方法先調(diào)用父類A的print方法,再部署自己的代碼。這就等于繼承了父類A的print方法
6.多重繼承
function M1() {
this.hello = "hello";
}
function M2() {
this.world = "world";
}
function S() {
M1.call(this);
M2.call(this);
}
// 繼承 M1
S.prototype = Object.create(M1.prototype);
// 繼承鏈上加入 M2
Object.assign(S.prototype, M2.prototype);
// 指定構(gòu)造函數(shù)
S.prototype.constructor = S;
var s = new S();
s.hello // "hello"
s.world // "world"
上面代碼中,子類S同時(shí)繼承了父類M1和M2。這種模式又稱為 Mixin(混入
7.模塊
JavaScript 不是一種模塊化編程語言,ES6 才開始支持“類”和“模塊”。下面介紹傳統(tǒng)的做法,如何利用對(duì)象實(shí)現(xiàn)模塊的效果。
7.1基本的實(shí)現(xiàn)方法(對(duì)象)
模塊是實(shí)現(xiàn)特定功能的一組屬性和方法的封裝
簡(jiǎn)單的做法是把模塊寫成一個(gè)對(duì)象,所有的模塊成員都放到這個(gè)對(duì)象里面。
var module1 = new Object({
_count : 0,
m1 : function (){
//...
},
m2 : function (){
//...
}
});
上面的函數(shù)m1和m2,都封裝在module1對(duì)象里。使用的時(shí)候,就是調(diào)用這個(gè)對(duì)象的屬性。
module1.m1();
但是,這樣的寫法會(huì)暴露所有模塊成員,內(nèi)部狀態(tài)可以被外部改寫。比如,外部代碼可以直接改變內(nèi)部計(jì)數(shù)器的值。
module1._count = 5
7.2封裝私有變量:構(gòu)造函數(shù)的寫法
function StringBuilder() {
var buffer = [];
this.add = function (str) {
buffer.push(str);
};
this.toString = function () {
return buffer.join("");
};
}
上面代碼中,buffer是模塊的私有變量。一旦生成實(shí)例對(duì)象,外部是無法直接訪問buffer的。但是,這種方法將私有變量封裝在構(gòu)造函數(shù)中,導(dǎo)致構(gòu)造函數(shù)與實(shí)例對(duì)象是一體的,總是存在于內(nèi)存之中。無法在使用完成后清除。這意味著,構(gòu)造函數(shù)有雙重作用,既用來塑造實(shí)例對(duì)象,又用來保存實(shí)例對(duì)象的數(shù)據(jù),違背了構(gòu)造函數(shù)與實(shí)例對(duì)象在數(shù)據(jù)上相分離的原則(即實(shí)例對(duì)象的數(shù)據(jù),不應(yīng)該保存在實(shí)例對(duì)象以外)。同時(shí),非常耗費(fèi)內(nèi)存。
function StringBuilder() {
this._buffer = [];
}
StringBuilder.prototype = {
constructor: StringBuilder,
add: function (str) {
this._buffer.push(str);
},
toString: function () {
return this._buffer.join("");
}
};
這種方法將私有變量放入實(shí)例對(duì)象中,好處是看上去更自然,但是它的私有變量可以從外部讀寫,不是很安全
7.3封裝私有變量:立即執(zhí)行函數(shù)的寫法
將相關(guān)的屬性和方法封裝在一個(gè)函數(shù)作用域里面,可以達(dá)到不暴露私有成員的目的
將相關(guān)的屬性和方法封裝在一個(gè)函數(shù)作用域里面,可以達(dá)到不暴露私有成員的目的。
var module1 = (function () {
var _count = 0;
var m1 = function () {
//...
};
var m2 = function () {
//...
};
return {
m1 : m1,
m2 : m2
};
})();
使用上面的寫法,外部代碼無法讀取內(nèi)部的_count變量。
console.info(module1._count); //undefined
上面的module1就是 JavaScript 模塊的基本寫法。下面,再對(duì)這種寫法進(jìn)行加工。
7.4模塊的放大模式
如果一個(gè)模塊很大,必須分成幾個(gè)部分,或者一個(gè)模塊需要繼承另一個(gè)模塊,這時(shí)就有必要采用“放大模式”(augmentation)。
var module1 = (function (mod){
mod.m3 = function () {
//...
};
return mod;
})(module1);
上面的代碼為module1模塊添加了一個(gè)新方法m3(),然后返回新的module1模塊
在瀏覽器環(huán)境中,模塊的各個(gè)部分通常都是從網(wǎng)上獲取的,有時(shí)無法知道哪個(gè)部分會(huì)先加載。如果采用上面的寫法,第一個(gè)執(zhí)行的部分有可能加載一個(gè)不存在空對(duì)象,這時(shí)就要采用"寬放大模式"(Loose augmentation)。
var module1 = (function (mod) {
//...
return mod;
})(window.module1 || {});
與"放大模式"相比,“寬放大模式”就是“立即執(zhí)行函數(shù)”的參數(shù)可以是空對(duì)象
7.5輸入全局變量
獨(dú)立性是模塊的重要特點(diǎn),模塊內(nèi)部最好不與程序的其他部分直接交互。
為了在模塊內(nèi)部調(diào)用全局變量,必須顯式地將其他變量輸入模塊。
var module1 = (function ($, YAHOO) {
//...
})(jQuery, YAHOO);
上面的module1模塊需要使用 jQuery 庫(kù)和 YUI 庫(kù),就把這兩個(gè)庫(kù)(其實(shí)是兩個(gè)模塊)當(dāng)作參數(shù)輸入module1。
這樣做除了保證模塊的獨(dú)立性,還使得模塊之間的依賴關(guān)系變得明顯。
(function($, window, document) {
function go(num) {
}
function handleEvents() {
}
function initialize() {
}
function dieCarouselDie() {
}
//attach to the global scope
window.finalCarousel = {
init : initialize, destroy : dieCarouselDie
}
})( jQuery, window, document );
上面代碼中,finalCarousel對(duì)象輸出到全局,對(duì)外暴露init和destroy接口,內(nèi)部方法go、handleEvents、initialize、dieCarouselDie都是外部無法調(diào)用的
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105715.html
摘要:因?yàn)檫@造成了繼承鏈的紊亂,因?yàn)榈膶?shí)例是由構(gòu)造函數(shù)創(chuàng)建的,現(xiàn)在其屬性卻指向了為了避免這一現(xiàn)象,就必須在替換對(duì)象之后,為新的對(duì)象加上屬性,使其指向原來的構(gòu)造函數(shù)。這個(gè)函數(shù)接收兩個(gè)參數(shù)子類型構(gòu)造函數(shù)和超類型構(gòu)造函數(shù)。 最近一直在研究js面向?qū)ο螅玩溊^承是一個(gè)難點(diǎn),下面是我對(duì)繼承的理解以下文章借鑒自CSDN季詩(shī)筱的博客 原型鏈繼承的基本概念: ES中描述了原型鏈的概念,并將原型鏈作為實(shí)現(xiàn)...
摘要:綜上所述有原型鏈繼承,構(gòu)造函數(shù)繼承經(jīng)典繼承,組合繼承,寄生繼承,寄生組合繼承五種方法,寄生組合式繼承,集寄生式繼承和組合繼承的優(yōu)點(diǎn)于一身是實(shí)現(xiàn)基于類型繼承的最有效方法。 一、前言 繼承是面向?qū)ο螅∣OP)語言中的一個(gè)最為人津津樂道的概念。許多面對(duì)對(duì)象(OOP)語言都支持兩種繼承方式::接口繼承 和 實(shí)現(xiàn)繼承 。 接口繼承只繼承方法簽名,而實(shí)現(xiàn)繼承則繼承實(shí)際的方法。由于js中方法沒有簽名...
摘要:簡(jiǎn)單回顧一下構(gòu)造函數(shù)原型和實(shí)例對(duì)象之間的關(guān)系每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象。找到生成構(gòu)造函數(shù)的原型對(duì)象的構(gòu)造函數(shù),搜索其原型對(duì)象,找到了。 JS面向?qū)ο蟮某绦蛟O(shè)計(jì)之繼承的實(shí)現(xiàn) - 原型鏈 前言:最近在細(xì)讀Javascript高級(jí)程序設(shè)計(jì),對(duì)于我而言,中文版,書中很多地方翻譯的差強(qiáng)人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯(cuò)誤,會(huì)非常感謝您的指出。文中絕大部分內(nèi)容引用自《JavaS...
摘要:想要解決這樣的問題的話,可以借助構(gòu)造函數(shù)也可以叫做偽造對(duì)象或經(jīng)典繼承。通過借助構(gòu)造函數(shù)實(shí)現(xiàn)對(duì)實(shí)例對(duì)象的屬性和繼承。 原型鏈 原型鏈?zhǔn)鞘裁?構(gòu)造函數(shù)或構(gòu)造器具有prototype屬性 對(duì)象具有__proto__屬性 這就是之前學(xué)習(xí)的原型如果構(gòu)造函數(shù)或?qū)ο驛 A的原型指向構(gòu)造函數(shù)或?qū)ο驜 B的原型在指向構(gòu)造函數(shù)或?qū)ο驝 以此類推 最終的構(gòu)造函數(shù)或?qū)ο蟮脑椭赶騉bject的原型 由此形成一...
摘要:繼承原型式繼承所謂原型是繼承,就是定義一個(gè)函數(shù),該函數(shù)中創(chuàng)建一個(gè)零食性的構(gòu)造函數(shù),將作為參數(shù)傳入的對(duì)象作為這個(gè)構(gòu)造函數(shù)的原型,最后返回這個(gè)構(gòu)造函數(shù)的實(shí)例對(duì)象。借助構(gòu)造函數(shù)無論是原型鏈還是原型是繼承,都具有相同的問題。 繼承 原型鏈 原型鏈?zhǔn)钦f明 構(gòu)造函數(shù)或構(gòu)造器具有prototype屬性,對(duì)象具有proto屬性,這就是之前學(xué)習(xí)的原。如果構(gòu)造函數(shù)或?qū)ο驛 。A的原型指向構(gòu)造函數(shù)或?qū)ο驜,...
摘要:既然構(gòu)造函數(shù)有屬于自己的原型對(duì)象,那么我們應(yīng)該能讓另一個(gè)構(gòu)造函數(shù)來繼承他的原型對(duì)象咯我們?cè)跇?gòu)造函數(shù)內(nèi)部執(zhí)行了函數(shù)并改變了函數(shù)內(nèi)部的指向其實(shí)這個(gè)指向的是實(shí)例化之后的對(duì)象。 我們?cè)谟懀╩ian)論(shi)JavaScript這門語言時(shí),總是繞不過的一個(gè)話題就是繼承與原型鏈。那么繼承與原型鏈到底是什么呢? 我很喜歡的一個(gè)聊天模式是:我不能說XX是什么,我只能說XX像什么。也就是說我不直接跟...
閱讀 2677·2021-11-23 09:51
閱讀 961·2021-09-24 10:37
閱讀 3664·2021-09-02 15:15
閱讀 1987·2019-08-30 13:03
閱讀 1916·2019-08-29 15:41
閱讀 2659·2019-08-29 14:12
閱讀 1456·2019-08-29 11:19
閱讀 3330·2019-08-26 13:39