1. 程式人生 > >JavaScript中的原型、原型鏈、原型模式

JavaScript中的原型、原型鏈、原型模式

今天,咱來聊聊JavaScript中的原型跟原型鏈 # 原型跟原型模式 這一塊的知識,主要是設計模式方面的。 首先,我們知道JavaScript是面向物件的。既然是面向物件,那它自然也有相應的類跟物件等概念。 在JavaScript中,function這個東西還是比較特殊的,它既能用來宣告方法,還能用來宣告一個類似C#/.NET中的類,然後new一下得到一個物件。 舉例 ```js //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呢?答案是可以的。 這裡有個優化方案 ```js 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是面向物件的,面向物件的三要素有個封裝。但以上的方案並沒有體現出封裝的思想。更好的方式是通過原型模式來解決。 ```js 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(); ``` 以上便是原型模式,也並非沒有缺點,雖然不用在建構函式裡面傳遞引數來初始化,但得到的物件屬性都是一樣的。這便是最大的問題。 怎麼辦呢? 建構函式模式可以通過建構函式來傳遞引數進行初始化,原型模式可以共享某些屬性跟方法。那可不可以合二為一,將兩者結合起來,豈不更好? 二者結合,程式碼如下: ```js 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是共有的方法,大家共享。 ``` # 原型鏈的問題 以下三句話特別重要,需要深刻理解。 1、每個函式都有個prototype屬性,prototype是函式獨有的屬性。 2、每個物件都有個__proto__屬性。__proto__是物件獨有的屬性。 2、每個函式的portotype的是Object的例項 在JavaScript中,繼承便是通過原型鏈實現的。 ```js 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(); 在這裡便可以通過原型模式來優化。 ```js Vue.prototype.$bus = new Vue(); // EventBus用於無關係元件間的通訊。 ``` 在其他元件,便可以直接通過this.$bus釋出訂閱事