摘要:規定,每個函數都有一個屬性,指向一個對象大毛二毛構造函數的屬性,就是實例對象和的原型對象。這就是所有對象都有和方法的原因,因為這是從繼承的構造函數的屬性指向一個數組,就意味著實例對象可以調用數組方法。
目錄 [隱藏]
原型對象概述
構造函數的缺點
prototype 屬性的作用
原型鏈
constructor 屬性
instanceof 運算符
構造函數的繼承
多重繼承
模塊
基本的實現方法
封裝私有變量:構造函數的寫法
封裝私有變量:立即執行函數的寫法
模塊的放大模式
輸入全局變量
參考鏈接
1.原型對象概述
每新建一個實例,就會新建一個meow方法。這既沒有必要,又浪費系統資源,因為所有meow方法都是同樣的行為,完全應該共享。
這個問題的解決方法,就是 JavaScript 的原型對象(prototype)
2.prototype 屬性的作用
原型對象的作用,就是定義所有實例對象共享的屬性和方法。這也是它被稱為原型對象的原因,而實例對象可以視作從原型對象衍生出來的子對象。
JavaScript 繼承機制的設計思想就是,原型對象的所有屬性和方法,都能被實例對象共享。
JavaScript 規定,每個函數都有一個prototype屬性,指向一個對象
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
構造函數Animal的prototype屬性,就是實例對象cat1和cat2的原型對象。
原型對象上添加一個color屬性,結果,實例對象都共享了該屬性
Animal.prototype.color = "yellow";
cat1.color // "yellow"
cat2.color // "yellow"
上面代碼中,原型對象的color屬性的值變為yellow,兩個實例對象的color屬性立刻跟著變了。這是因為實例對象其實沒有color屬性,都是讀取原型對象的color屬性。也就是說,
當實例對象本身沒有某個屬性或方法的時候,它會到原型對象去尋找該屬性或方法。如果實例對象自身就有某個屬性或方法,它就不會再去原型對象尋找這個屬性或方法。這就是原型對象的特殊之處。
3.原型鏈
JavaScript 規定,所有對象都有自己的原型對象(prototype)。一方面,任何一個對象,都可以充當其他對象的原型;另一方面,由于原型對象也是對象,所以它也有自己的原型。因此,就會形成一個“原型鏈”(prototype chain):對象到原型,再到原型的原型
如果一層層地上溯,所有對象的原型最終都可以上溯到Object.prototype,即Object構造函數的prototype屬性。也就是說,所有對象都繼承了Object.prototype的屬性。這就是所有對象都有valueOf和toString方法的原因,因為這是從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
構造函數的prototype屬性指向一個數組,就意味著實例對象可以調用數組方法。
mine是構造函數MyArray的實例對象,由于MyArray.prototype指向一個數組實例,使得mine可以調用數組方法(這些方法定義在數組實例的prototype對象上面)
3.constructor 屬性
prototype對象有一個constructor屬性,默認指向prototype對象所在的構造函數
function P() {}
P.prototype.constructor === P // true
由于constructor屬性定義在prototype對象上面,意味著可以被所有實例對象繼承
function P() {}
var p = new P();
p.constructor === P // true
p.constructor === P.prototype.constructor // true
p.hasOwnProperty("constructor") // false
上面代碼中,p是構造函數P的實例對象,但是p自身沒有constructor屬性,該屬性其實是讀取原型鏈上面的P.prototype.constructor屬性
3.1作用
3.1.1可以得知某個實例對象,到底是哪一個構造函數產生的。
3.1.2另一方面,有了constructor屬性,就可以從一個實例對象新建另一個實例
function Constr() {}
var x = new Constr();
var y = new x.constructor();
y instanceof Constr // true
3.1.3在實例方法中,調用自身的構造函數成為可能
Constr.prototype.createCopy = function () {
return new this.constructor();
};
3.2constructor屬性表示原型對象與構造函數之間的關聯關系,如果修改了原型對象,一般會同時修改constructor屬性,防止引用的時候出錯。
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屬性重新指向原來的構造函數,要么只在原型對象上添加方法,這樣可以保證instanceof運算符不會失真。
如果不能確定constructor屬性是什么函數,還有一個辦法:通過name屬性,從實例得到構造函數的名稱。
function Foo() {}
var f = new Foo();
f.constructor.name // "Foo"
4.instanceof 運算符
instanceof運算符返回一個布爾值,表示對象是否為某個構造函數的實例
instanceof運算符的左邊是實例對象,右邊是構造函數。它會檢查右邊構建函數的原型對象(prototype),是否在左邊對象的原型鏈上。因此,下面兩種寫法是等價的。
v instanceof Vehicle
// 等同于
Vehicle.prototype.isPrototypeOf(v)
由于instanceof檢查整個原型鏈,因此同一個實例對象,可能會對多個構造函數都返回true。
var d = new Date();
d instanceof Date // true
d instanceof Object // true
Null都為false
注意,instanceof運算符只能用于對象,不適用原始類型的值。
var s = "hello";
s instanceof String // false
上面代碼中,字符串不是String對象的實例(因為字符串不是對象),所以返回false。
此外,對于undefined和null,instanceOf運算符總是返回false
上面代碼中,字符串不是String對象的實例(因為字符串不是對象),所以返回false。
此外,對于undefined和null,instanceOf運算符總是返回false
5.構造函數的繼承
5.1在子類的構造函數中,調用父類的構造函數
Sub是子類的構造函數,this是子類的實例。在實例上調用父類的構造函數Super,就會讓子類實例具有父類實例的屬性
5.2是讓子類的原型指向父類的原型,這樣子類就可以繼承父類原型
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.prototype.method = "...";
上面代碼中,Sub.prototype是子類的原型,要將它賦值為Object.create(Super.prototype),而不是直接等于Super.prototype。否則后面兩行對Sub.prototype的操作,會連父類的原型Super.prototype一起修改掉。
5.3Sub.prototype等于一個父類實例Sub.prototype = new Super();
上面代碼中,子類是整體繼承父類。有時只需要單個方法的繼承,這時可以采用下面的寫法。
ClassB.prototype.print = function() {
ClassA.prototype.print.call(this);
// some code
}
上面代碼中,子類B的print方法先調用父類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);
// 指定構造函數
S.prototype.constructor = S;
var s = new S();
s.hello // "hello"
s.world // "world"
上面代碼中,子類S同時繼承了父類M1和M2。這種模式又稱為 Mixin(混入
7.模塊
JavaScript 不是一種模塊化編程語言,ES6 才開始支持“類”和“模塊”。下面介紹傳統的做法,如何利用對象實現模塊的效果。
7.1基本的實現方法(對象)
模塊是實現特定功能的一組屬性和方法的封裝
簡單的做法是把模塊寫成一個對象,所有的模塊成員都放到這個對象里面。
var module1 = new Object({
_count : 0,
m1 : function (){
//...
},
m2 : function (){
//...
}
});
上面的函數m1和m2,都封裝在module1對象里。使用的時候,就是調用這個對象的屬性。
module1.m1();
但是,這樣的寫法會暴露所有模塊成員,內部狀態可以被外部改寫。比如,外部代碼可以直接改變內部計數器的值。
module1._count = 5
7.2封裝私有變量:構造函數的寫法
function StringBuilder() {
var buffer = [];
this.add = function (str) {
buffer.push(str);
};
this.toString = function () {
return buffer.join("");
};
}
上面代碼中,buffer是模塊的私有變量。一旦生成實例對象,外部是無法直接訪問buffer的。但是,這種方法將私有變量封裝在構造函數中,導致構造函數與實例對象是一體的,總是存在于內存之中。無法在使用完成后清除。這意味著,構造函數有雙重作用,既用來塑造實例對象,又用來保存實例對象的數據,違背了構造函數與實例對象在數據上相分離的原則(即實例對象的數據,不應該保存在實例對象以外)。同時,非常耗費內存。
function StringBuilder() {
this._buffer = [];
}
StringBuilder.prototype = {
constructor: StringBuilder,
add: function (str) {
this._buffer.push(str);
},
toString: function () {
return this._buffer.join("");
}
};
這種方法將私有變量放入實例對象中,好處是看上去更自然,但是它的私有變量可以從外部讀寫,不是很安全
7.3封裝私有變量:立即執行函數的寫法
將相關的屬性和方法封裝在一個函數作用域里面,可以達到不暴露私有成員的目的
將相關的屬性和方法封裝在一個函數作用域里面,可以達到不暴露私有成員的目的。
var module1 = (function () {
var _count = 0;
var m1 = function () {
//...
};
var m2 = function () {
//...
};
return {
m1 : m1,
m2 : m2
};
})();
使用上面的寫法,外部代碼無法讀取內部的_count變量。
console.info(module1._count); //undefined
上面的module1就是 JavaScript 模塊的基本寫法。下面,再對這種寫法進行加工。
7.4模塊的放大模式
如果一個模塊很大,必須分成幾個部分,或者一個模塊需要繼承另一個模塊,這時就有必要采用“放大模式”(augmentation)。
var module1 = (function (mod){
mod.m3 = function () {
//...
};
return mod;
})(module1);
上面的代碼為module1模塊添加了一個新方法m3(),然后返回新的module1模塊
在瀏覽器環境中,模塊的各個部分通常都是從網上獲取的,有時無法知道哪個部分會先加載。如果采用上面的寫法,第一個執行的部分有可能加載一個不存在空對象,這時就要采用"寬放大模式"(Loose augmentation)。
var module1 = (function (mod) {
//...
return mod;
})(window.module1 || {});
與"放大模式"相比,“寬放大模式”就是“立即執行函數”的參數可以是空對象
7.5輸入全局變量
獨立性是模塊的重要特點,模塊內部最好不與程序的其他部分直接交互。
為了在模塊內部調用全局變量,必須顯式地將其他變量輸入模塊。
var module1 = (function ($, YAHOO) {
//...
})(jQuery, YAHOO);
上面的module1模塊需要使用 jQuery 庫和 YUI 庫,就把這兩個庫(其實是兩個模塊)當作參數輸入module1。
這樣做除了保證模塊的獨立性,還使得模塊之間的依賴關系變得明顯。
(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對象輸出到全局,對外暴露init和destroy接口,內部方法go、handleEvents、initialize、dieCarouselDie都是外部無法調用的
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105715.html
摘要:因為這造成了繼承鏈的紊亂,因為的實例是由構造函數創建的,現在其屬性卻指向了為了避免這一現象,就必須在替換對象之后,為新的對象加上屬性,使其指向原來的構造函數。這個函數接收兩個參數子類型構造函數和超類型構造函數。 最近一直在研究js面向對象,原型鏈繼承是一個難點,下面是我對繼承的理解以下文章借鑒自CSDN季詩筱的博客 原型鏈繼承的基本概念: ES中描述了原型鏈的概念,并將原型鏈作為實現...
摘要:綜上所述有原型鏈繼承,構造函數繼承經典繼承,組合繼承,寄生繼承,寄生組合繼承五種方法,寄生組合式繼承,集寄生式繼承和組合繼承的優點于一身是實現基于類型繼承的最有效方法。 一、前言 繼承是面向對象(OOP)語言中的一個最為人津津樂道的概念。許多面對對象(OOP)語言都支持兩種繼承方式::接口繼承 和 實現繼承 。 接口繼承只繼承方法簽名,而實現繼承則繼承實際的方法。由于js中方法沒有簽名...
摘要:簡單回顧一下構造函數原型和實例對象之間的關系每個構造函數都有一個原型對象。找到生成構造函數的原型對象的構造函數,搜索其原型對象,找到了。 JS面向對象的程序設計之繼承的實現 - 原型鏈 前言:最近在細讀Javascript高級程序設計,對于我而言,中文版,書中很多地方翻譯的差強人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯誤,會非常感謝您的指出。文中絕大部分內容引用自《JavaS...
摘要:想要解決這樣的問題的話,可以借助構造函數也可以叫做偽造對象或經典繼承。通過借助構造函數實現對實例對象的屬性和繼承。 原型鏈 原型鏈是什么 構造函數或構造器具有prototype屬性 對象具有__proto__屬性 這就是之前學習的原型如果構造函數或對象A A的原型指向構造函數或對象B B的原型在指向構造函數或對象C 以此類推 最終的構造函數或對象的原型指向Object的原型 由此形成一...
閱讀 2629·2021-11-23 09:51
閱讀 861·2021-09-24 10:37
閱讀 3612·2021-09-02 15:15
閱讀 1962·2019-08-30 13:03
閱讀 1881·2019-08-29 15:41
閱讀 2624·2019-08-29 14:12
閱讀 1424·2019-08-29 11:19
閱讀 3301·2019-08-26 13:39