今天,咱來聊聊JavaScript中的原型跟原型鏈設計模式
這一塊的知識,主要是設計模式方面的。
首先,咱們知道JavaScript是面向對象的。既然是面向對象,那它天然也有相應的類跟對象等概念。
在JavaScript中,function這個東西仍是比較特殊的,它既能用來聲明方法,還能用來聲明一個相似C#/.NET中的類,而後new一下獲得一個對象。
舉例函數
//js中的function使用方式一: function testFunc() { cosnole.log(123456); } testFunc();//123456; //js中的function使用方式二:在這裏用到了構造函數模式 function Person(name, age) { this.name = name; this.age = age; this.sayName = function () { alert(this.name); } } var person1 = new Person('qwe', 26);//function還能夠new一個出來,獲得一個對象。 person1.sayName(); console.log(person1.constructor === Person);//true var person2 = new Person('fgh', 26); person2.sayName(); console.log(person1.sayName === person2.sayName);//false,這裏是個重點,person1跟person2的sayName方法是不同的,是各自的方法 //方式二還能夠這樣用 Person('qwe', 26); window.sayName();
思考:
既然person1.sayName===person2.sayName返回false,兩個對象person1和person2各自的sayName方法雖然自己不一樣,實現的效果是同樣的,那可不可讓每一個對象調用的是同一個公共的方法sayName呢?答案是能夠的。
這裏有個優化方案優化
function Person(name, age) { this.name = name; this.age = age; this.sayName = sayName; } function sayName() { alert(this.name); } var person1 = new Person('qwe', 26); var person2 = new Person('jkl', 26); console.log(person1.sayName === person2.sayName);//true 這裏是調用的公共的方法,不是各自本身的方法
以上的方案的確實現了想要的結果,但咱們知道JavaScript是面向對象的,面向對象的三要素有個封裝。但以上的方案並無體現出封裝的思想。更好的方式是經過原型模式來解決。ui
function Person() { } Person.prototype.name = "uip";//這裏的Person是屬性,不是構造函數,這個Person還有個屬性prototype,原型模式即是經過這個來實現的 Person.prototype.age = 26; Person.prototype.sayName = function () { alert(this.name); } //另一種寫法 Person.prototype = { name: 'uio', age = 26, sayName = function () { alert(this.name); } } var person1 = new Person(); person1.sayName();
以上即是原型模式,也並不是沒有缺點,雖然不用在構造函數裏面傳遞參數來初始化,但獲得的對象屬性都是同樣的。這即是最大的問題。
怎麼辦呢?
構造函數模式能夠經過構造函數來傳遞參數進行初始化,原型模式能夠共享某些屬性跟方法。那可不能夠合二爲一,將二者結合起來,豈不更好?
兩者結合,代碼以下:this
function Person(name, age) { this.name = name; this.age = age; } Person.prototype = { sayName: function () { alert(this.name); } } var person1 = new Person('sdaf', 26); person1.sayName(); var person2 = new Person('ssdf', 26); person2.sayName(); console.log(person1.sayName === person2.sayName);//true,看到這裏,相信便會體會到這個模式精妙之處了吧。sayName是共有的方法,你們共享。
如下三句話特別重要,須要深入理解。
一、每一個函數都有個prototype屬性,prototype是函數獨有的屬性。
二、每一個對象都有個__proto__屬性。__proto__是對象獨有的屬性。
二、每一個函數的portotype的是Object的實例
在JavaScript中,繼承即是經過原型鏈實現的。prototype
function Person(name, age) { this.name = name; this.age = age; } Person.prototype = { sayName: function () { alert(this.name); } } var person1 = new Person('sdaf', 26); console.log(person1.__proto__ === Person.prototype);//true console.log(Person.prototype.__proto__ == Object.prototype);//true //在這裏說明一下,Person的基類是Object,Person的prototype是Object的實例,因此是個對象,它有__proto__這個屬性,而這個屬性等價於Object的prototype屬性。 //這樣一環扣一環,構成了一道鏈,即是所謂的原型鏈 console.log(Ojbect.prototype.__proto__ === null);//true
既然明白以上的知識,怎麼優雅地運用到實際工做中呢?
在搞Vue項目時,咱們幾乎不可避免地會經過EventBus進行組件通訊。
每次都須要var bus=new Vue();
在這裏即可以經過原型模式來優化。設計
Vue.prototype.$bus = new Vue(); // EventBus用於無關係組件間的通訊。
在其餘組件,即可以直接經過this.$bus發佈訂閱事件了。code