一塊兒來搞對象吧

什麼是對象?

無序屬性的集合, 其屬性能夠包含基本值、 對象或者函數javascript

對象的屬性類型

ECMA-262定義了一些爲實現JavaScript引擎用的屬性,所以不能直接訪問,爲了表示其是特殊的內部值,把他們放在兩對兒方括號中。例如[[[Enumerable]]。對象的屬性分爲數據屬性和訪問器屬性java

數據屬性

通常數據屬性的值都爲基礎數據類型。設計模式

  • [[Configurable]] 表示可否經過delete刪除屬性從而重新定義…(默認爲true PS:一旦修改,不能反悔)
  • [[Enumerable]] 表示可否經過for-in循環返回屬性…(自定義屬性默認爲true,默認屬性的默認值爲false,例如:constructor )
  • [[Writable]] 表示可否修改屬性值…(默認爲true))
  • [[Value]] 包含屬性的數據值…(就是上例中的‘Jiangwen’)
訪問器屬性
  • [[Configurable]] 表示可否經過delete刪除屬性從而重新定義…默認爲true)
  • [[Enumerable]] 表示可否經過for-in循環返回屬性…(默認爲true)
  • [[get]]: 在讀取屬性時,調用的函數,默認undefined。
  • [[set]]: 在寫入屬性時,調用的函數,默認undefeated。

修改默認屬性

ECMAScript給我提供了一個方法,Object.defineProperty()方法。這個方法接受三個參數:屬性所在對象、屬性的名字和一個描述對象。函數

var person = {};
Object.defineProperty(person,'name',{
    Writable:false,
    value:'jiangwen'
})
console.log(person.name) //'jiangwen'
person.name = 'xiaoming';
console.log(person.name) // 'jiangwen'
複製代碼

修改默認屬性爲不容許修改,所以從新進行賦值修改無效ui

定義多個對象屬性

因爲爲對象定義多個屬性的可能性很大,ECMAScript 5 又定義了一個 Object.definePro- perties()方法。利用這個方法能夠經過描述符一次定義多個屬性。這個方法接收兩個對象參數:第一個對象是要添加和修改其屬性的對象,第二個對象的屬性與第一個對象中要添加或修改的屬性一一對應。this

var people = {};
Object.defineProperties(book,{ //定義多個對象屬性,用到Object.defineProperties();
    _year:{
        value:18
    },
    name:{
        value:'jiangwen'
    }
})
複製代碼

獲取描述屬性

ECMAScript也給咱們提供了一個方法:Object.getOwnPropertyDescriptor()這個方法接收兩個參數:屬性所在的對象和要讀取其描述符的屬性名稱;返回值是一個對象spa

var book = {};  
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;             
      }        
    }     
  } 
}); 
var descriptor = Object.getOwnPropertyDescriptor(book,"_year");
console.log(descriptor.value) // 18
console.log(descriptor.enumerable);   //false
複製代碼

建立對象

1.基本方法

var person = new Object(); 
person.name = "JiangWen"; 
person.age = 29; 
person.job = "Software Engineer";  
person.sayName = function(){
  	alert(this.name);  
};
複製代碼

2.對象字面量方法

var person = {     
  name: "JiangWen",      
  age: 25,     
  job: "Software Engineer",      
  sayName: function(){         
    alert(this.name);     
  } 
}; 
複製代碼

雖然上面兩種建立對象的方式,均可以用來建立單個對象,可是有個明顯的缺點,使用同一接口建立不少對象,會產生大量的重複代碼,所以就產生了不一樣模式知足不一樣狀況下的需求prototype

3.工廠模式

工廠模式是軟件工程領域一種廣爲人知的設計模式,用函數來封裝以特定接口建立對象的細節設計

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("Jiangwen", 29, "Software Engineer"); 
var person2 = createPerson("Owen", 27, "Doctor");
複製代碼

4.構造函數模式

function Person(name, age, job){     
  this.name = name;     
  this.age = age;     
  this.job = job;     
  this.sayName = function(){         
		alert(this.name);     
  };     
}  
var person1 = new Person("Jiangwen", 29, "Software Engineer"); 
var person2 = new Person("Owen", 27, "Doctor");
複製代碼

調用構造函數實際上會經歷如下 4 個步驟:3d

  1. 建立一個新對象
  2. 將構造函數的做用域賦值給新對象(所以 this 就指向了這個新對象)
  3. 執行構造函數中的代碼(爲這個新對象添加屬性方法)
  4. 返回新對象 (注:默認返回新建立的對象; 不過若是在構造函數中顯式返回一個對象數據類型,那麼未來new的對象就是該顯式return的對象)

構造函數模式和工廠模式有如下幾個不一樣之處

  • 沒有顯式地建立對象
  • 直接將屬性方法賦值給this
  • 沒有return語句

5.原型模式

咱們建立的每一個函數都有一個 prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含能夠由特定類型的全部實例共享的屬性和方法。

function Person(){ }  
Person.prototype.name = "Jiangwen"; 
Person.prototype.age = 25; 
Person.prototype.job = "Software Engineer"; 
Person.prototype.sayName = function(){     
  alert(this.name); 
};  
var person1 = new Person(); 
person1.sayName();   //"Jiangwen" 
var person2 = new Person();
person2.sayName();   //"Jiangwen" 
alert(person1.sayName == person2.sayName);  //true
複製代碼

與構造函數模式不一樣的是,新對象的這些屬性和方法是由全部實例共享的。也就是說,person1 和 person2 訪問的都是同一組屬性和同一個 sayName()函數,以下圖所示:

img

一些關於原型的方法:

// 肯定實例對象和構造函數原型之間是否存在關係
Person.prototype.isPrototypeOf(person1) // true
person1 instanceof Person)       //true 

// 獲取實例的原型對象
Object.getPrototypeOf(person1) == Person.prototype  // true
Object.getPrototypeOf(person1).name; //"Jiangwen"

// 檢測一個屬性是存在於實例中,仍是存在於原型中
Person.prototype.name = "Owen"
var person1 = new Person()
person1.hasOwnProperty("name")  // false 來自原型 
person1.name = 'jiangwen'
person1.hasOwnProperty("name")  // true 來自實例

// 檢測一個屬性是存在於實例或者原型中,即該屬性存在便可
"name" in person1  //true

// 取得當前對象上全部可枚舉的實例屬性
Object.keys(Person.prototype)  // "name,age,job,sayName"

// 取得當前對象上全部實例屬性
Object.getOwnPropertyNames(Person.prototype); //"constructor,name,age,job,sayName"
複製代碼

6.組合構造函數模式和原型模式

該模式是建立自定義類型的最多見方式: 構造函數模式用於定義實例屬性,而原型模式用於定義方法和共享的屬性

function Person(name, age, job){     
  this.name = name;     
  this.age = age;     
  this.job = job;     
  this.friends = ["Shelby", "Court"]; 
}  
Person.prototype = {     
  constructor : Person,     // 對象字面量的形式至關於重寫了原型對象,因此須要從新指定
  sayName : function(){         
    alert(this.name);     
  } 
}

var person1 = new Person("Nicholas", 29, "Software Engineer"); 
var person2 = new Person("Greg", 27, "Doctor");  
person1.friends.push("Van"); 
alert(person1.friends);    //"Shelby,Count,Van" 
alert(person2.friends);    //"Shelby,Count" 
alert(person1.friends === person2.friends);    //false 
alert(person1.sayName === person2.sayName);    //true
複製代碼

7.動態原型模式

能夠經過檢查某個應該存在的方法是否有效,來決定是否須要初始化原型,從而避免改寫原型上原有的同名屬性或方法

function Person(name, age, job){
  //屬性 
  this.name = name;     
  this.age = age;     
  this.job = job;

  //方法 
  if (typeof this.sayName != "function"){              
    Person.prototype.sayName = function(){             
      alert(this.name);         
    };              
  }
}
複製代碼

該模式下只在 sayName()方法不存在於原型的狀況下,纔會將它添加到原型中

8.寄生構造函數模式(不建議使用)

該模式的基本思想是建立一個函數,該函數的做用僅僅是封裝建立對象的代碼,而後再返回新建立的對象;

function Person(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 friend = new Person("Nicholas", 29, "Software Engineer"); 
friend.sayName();  //"Nicholas"
複製代碼

構造函數在不返回值的狀況下,默認會返回新對象實例。而經過在構造函數的末尾添加一個 return 語句,能夠重寫調用構造函數時返回的值。

9.穩妥構造函數模式

所謂穩妥對象,指的是沒有公共屬性,並且其方法也不引用 this 的對象。穩妥構造函數遵循與寄生構造函數相似的模式,但有兩點不一樣:一是新建立對象的實例方法不引用 this;二是不使用 new 操做符調用構造函數。

function Person(name, age, job){          
  //建立要返回的對象 
  var o = new Object();
  
  //能夠在這裏定義私有變量和函數 
  
  //添加方法 
  o.sayName = function(){         
    alert(name);     
  };              
  //返回對象 
  return o; 
}
複製代碼

注意, 在以這種模式建立的對象中, 除了使用 sayName()方法以外, 沒有其餘辦法訪問 name 的值

對象的繼承

在Javascript中實現繼承主要是依靠原型鏈來實現的;其基本思想是經過原型實現一個引用類型繼承另外一個引用類型的屬性和方法。

1.原型鏈

function SuperType(){
  this.super = 'SuperType'
}
SuperType.prototype.sayType = function(){
  alert(this.super)
}
function SubType(){
  this.sub = 'SubType'
}
SubType.prototype = new SuperType()
let a1 = new SubType()
複製代碼

2.借用構造函數

子類型構造函數的內部調用超類型構造函數

function SuperType(){     
	this.colors = ["red", "blue", "green"]; 
}  
function SubType(){       
//繼承了 SuperType 
SuperType.call(this); 
}  
var instance1 = new SubType(); 
instance1.colors.push("black"); 
alert(instance1.colors);    //"red,blue,green,black" 
var instance2 = new SubType(); 
alert(instance2.colors);    //"red,blue,green"
複製代碼

3.組合繼承

將原型鏈和借用構造函數的技術組合到一塊,從而發揮兩者之長的一種繼承模式。其背後的思路是使用原型鏈實現對原型屬性和方法的繼承,而經過借用構造函數來實現對實例屬性的繼承。這樣,既經過在原型上定義方法實現了函數複用,又可以保證每一個實例都有它本身的屬性。

function SuperType(name){     
  this.name = name;     
  this.colors = ["red", "blue", "green"]; 
}  
SuperType.prototype.sayName = function(){     
  alert(this.name);
};  
function SubType(name, age){        
  //繼承屬性 
  SuperType.call(this, name);          
  this.age = age; 
}  
//繼承方法 
SubType.prototype = new SuperType(); 
SubType.prototype.constructor = SubType; 
SubType.prototype.sayAge = function(){     
  alert(this.age); 
}; 

// 建立實例對象
var instance1 = new SubType("Nicholas", 29); 
instance1.colors.push("black"); 
alert(instance1.colors);      //"red,blue,green,black" 
instance1.sayName();          //"Nicholas"; 
instance1.sayAge();      			//29

var instance2 = new SubType("Greg", 27); 
alert(instance2.colors);      //"red,blue,green" 
instance2.sayName();          //"Greg"; 
instance2.sayAge();           //27
複製代碼

因此組合繼承避免了原型鏈和借用構造函數的缺陷,融合了它們的優勢,成爲 JavaScript 中最經常使用的繼承模式。

4.原型式繼承

將須要被繼承的對象掛載到函數內部新建立的構造函數原型上並返回該構造函數的實例

/* 模擬Object.create()方法 * o: 須要被繼承的對象 * o2: 自定義參數對象,默認爲空 */
function object(o,o2={}){
   // 建立一個構造函數F爲接收自定義參數
  var F = function(){
    for (const key in o2) {
      for (const key2 in o2[key]) {
        this[key] = o2[key][key2]//跳坑指南: 不能使用this.key,由於點賦值會將變量key轉爲字符串「key」 
      }
    }
  };
  F.prototype = o   // 重寫構造函數F原型對象爲傳入的對象o
  return new F()  //返回構造函數的實例
}

var person = {
  name: 'no-Name',
  friend:[1,2,3],
  sayName:function(){
    alert(this.name)
  }
}
let a1 = object(person)

複製代碼

ECMAScript 5 經過新增 Object.create()方法規範化了原型式繼承。這個方法接收兩個參數:一個用做新對象原型的對象和(可選的)一個爲新對象定義額外屬性的對象

// 在傳入一個參數的狀況下,Object.create()與 object()方法的行爲相同
 var person = {     
   name: "Nicholas",     
   friends: ["Shelby", "Court", "Van"] 
 };  
var anotherPerson = Object.create(person); 
anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob");      
var yetAnotherPerson = Object.create(person); 
yetAnotherPerson.name = "Linda"; 
yetAnotherPerson.friends.push("Barbie");  
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

複製代碼

5.寄生式繼承

寄生式(parasitic)繼承是與原型式繼承緊密相關的一種思路 ,其與工廠模式相似,即建立一個僅用於封裝繼承過程的函數,該函數在內部以某種方式來加強對象,最後再像真地是它作了全部工做同樣返回對象

function createAnother(original){
  var clone = object(original)  //建立對象
  clone.sayHi = function(){			
    alert('hi!')
  }
  return clone
}
var person = {     
  name: "Jiangwen",     
  friends: ["A", "B", "C"] 
};  
var anotherPerson = createAnother(person); 
anotherPerson.sayHi(); //"hi"

複製代碼

在主要考慮對象而不是自定義類型和構造函數的狀況下,寄生式繼承也是一種有用的模式。前面示範繼承模式時使用的 object()函數不是必需的;任何可以返回新對象的函數都適用於此模式。

6.寄生組式繼承

所謂寄生組合式繼承,即經過借用構造函數來繼承屬性,經過原型鏈的混成形式來繼承方法 ; 本質上,就是使用寄生式繼承來繼承超類型的原型,而後再將結果指定給子類型的原型。

function inheritPrototype(subType, superType){     
  var prototype = object(superType.prototype);     //建立對象 
	prototype.constructor = subType;                 //加強對象 
  subType.prototype = prototype;               //指定對象 
}

複製代碼
相關文章
相關標籤/搜索