以前有篇博客曾提到過一點js的面向對象編程:js面向對象編程.javascript
這裏就結合js高程詳細剖析一下javascript的面向對象編程.html
前序:vue
1⃣️Object.defineProperty()java
var obj = { _name:'jack' }; Object.defineProperty(obj,'name',{ configurable:false,//表示可否經過delete刪除屬性從而從新定義屬性,默認值true value:'orange',//屬性的數據值,默認值undefined writable:false,//表示可否修改屬性的值,默認值爲true enumerable:false,//是否可枚舉,可否經過for-in循環返回屬性,默認值爲true get:function(){//getter return this._name; }, set:function(newval){//setter this._name = newval; } })
vue2.x版本的雙向數據綁定就是基於該API和訂閱/發佈模式實現的;編程
若是同時定義多個屬性,可經過以下API:函數
Object.defineProperties(book, { _year: { value: 2004 }, edition: { value: 1 }, year: { get: function(){ return this._year; }, set: function(newValue){ if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004; } }
} });
2⃣️讀取屬性的特性Object.getOwnPropertyDescriptor() this
var descriptor = Object.getOwnPropertyDescriptor(book, "_year"); alert(descriptor.value); //2004 alert(descriptor.configurable); //false
正文:spa
3⃣️建立對象prototype
3.1 工廠模式code
function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); }; return o;
} var person1 = createPerson("Nicholas", 29, "Software Engineer"); var person2 = createPerson("Greg", 27, "Doctor");
工廠模式建立了n個互不關聯的對象,可是卻沒有解決對象識別的問題(即怎樣知道一個對象的類型) .
3.2 構造函數模式
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }; } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor");
由於函數也是一個對象,因此能夠經過該方式建立一個Person對象;
按照慣例,構造函數始終都應該以一個 大寫字母開頭,而非構造函數則應該以一個小寫字母開頭 ;
要建立 Person 的新實例,必須使用 new 操做符。以這種方式調用構造函數實際上會經歷如下 4 個步驟:
(1) 建立一個新對象;
(2) 將構造函數的做用域賦給新對象(所以 this 就指向了這個新對象);
(3) 執行構造函數中的代碼(爲這個新對象添加屬性);
(4) 返回新對象。
alert(person1.constructor == Person); //true alert(person2.constructor == Person); //true
alert(person1 instanceof Object); //true alert(person1 instanceof Person); //true alert(person2 instanceof Object); //true alert(person2 instanceof Person); //true
構造函數模式雖然好用,但也並不是沒有缺點。使用構造函數的主要問題,就是每一個方法都要在每一個實例上從新建立一遍,不能共享。雖然咱們能夠將該方法寫到全局做用域,而後在構造函數內部調用,可是這種作法顯然耦合度太高.
3.3 原型模式
能夠順便參照以前的博客:js繼承的實現.
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); person1.sayName(); //"Nicholas" var person2 = new Person(); person2.sayName(); //"Nicholas" alert(person1.sayName == person2.sayName); //true
能夠看到,原型屬性上的屬性值和屬性方法是被全部實例共享的.
3.3.1實例屬性和原型屬性
person1.name = "Greg"; alert(person1.name); //"Greg"——來自實例 alert(person2.name); //"Nicholas"——來自原型
delete person1.name; alert(person1.name); //"Nicholas"——來自原型
咱們看到訪問實例屬性的時候,優先級是實例屬性-->原型屬性,刪除屬性的時候優先級也是如此;
實例上有該屬性,就直接拿實例的該屬性,沒有的話就去原型上找.
3.3.2 hasOwnProperty() 判斷屬性是否是來自於實例
var person1 = new Person(); var person2 = new Person(); alert(person1.hasOwnProperty("name")); //false person1.name = "Greg"; alert(person1.name); //"Greg"——來自實例 alert(person1.hasOwnProperty("name")); //true
實例的原型屬性不可枚舉:
var keys = Object.keys(Person.prototype); alert(keys); //"name,age,job,sayName"
var p1 = new Person(); p1.name = "Rob"; p1.age = 31; var p1keys = Object.keys(p1); alert(p1keys); //"name,age"