無序屬性的集合, 其屬性能夠包含基本值、 對象或者函數javascript
ECMA-262定義了一些爲實現JavaScript引擎用的屬性,所以不能直接訪問,爲了表示其是特殊的內部值,把他們放在兩對兒方括號中。例如[[[Enumerable]]。對象的屬性分爲數據屬性和訪問器屬性java
通常數據屬性的值都爲基礎數據類型。設計模式
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
複製代碼
var person = new Object();
person.name = "JiangWen";
person.age = 29;
person.job = "Software Engineer";
person.sayName = function(){
alert(this.name);
};
複製代碼
var person = {
name: "JiangWen",
age: 25,
job: "Software Engineer",
sayName: function(){
alert(this.name);
}
};
複製代碼
雖然上面兩種建立對象的方式,均可以用來建立單個對象,可是有個明顯的缺點,使用同一接口建立不少對象,會產生大量的重複代碼,所以就產生了不一樣模式知足不一樣狀況下的需求prototype
工廠模式是軟件工程領域一種廣爲人知的設計模式,用函數來封裝以特定接口建立對象的細節設計
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");
複製代碼
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
構造函數模式和工廠模式有如下幾個不一樣之處
咱們建立的每一個函數都有一個 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()函數,以下圖所示:
一些關於原型的方法:
// 肯定實例對象和構造函數原型之間是否存在關係
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"
複製代碼
該模式是建立自定義類型的最多見方式: 構造函數模式用於定義實例屬性,而原型模式用於定義方法和共享的屬性
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
複製代碼
能夠經過檢查某個應該存在的方法是否有效,來決定是否須要初始化原型,從而避免改寫原型上原有的同名屬性或方法
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()方法不存在於原型的狀況下,纔會將它添加到原型中
該模式的基本思想是建立一個函數,該函數的做用僅僅是封裝建立對象的代碼,而後再返回新建立的對象;
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 語句,能夠重寫調用構造函數時返回的值。
所謂穩妥對象,指的是沒有公共屬性,並且其方法也不引用 this 的對象。穩妥構造函數遵循與寄生構造函數相似的模式,但有兩點不一樣:一是新建立對象的實例方法不引用 this;二是不使用 new 操做符調用構造函數。
function Person(name, age, job){
//建立要返回的對象
var o = new Object();
//能夠在這裏定義私有變量和函數
//添加方法
o.sayName = function(){
alert(name);
};
//返回對象
return o;
}
複製代碼
注意, 在以這種模式建立的對象中, 除了使用 sayName()方法以外, 沒有其餘辦法訪問 name 的值
在Javascript中實現繼承主要是依靠原型鏈來實現的;其基本思想是經過原型實現一個引用類型繼承另外一個引用類型的屬性和方法。
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()
複製代碼
子類型構造函數的內部調用超類型構造函數
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"
複製代碼
將原型鏈和借用構造函數的技術組合到一塊,從而發揮兩者之長的一種繼承模式。其背後的思路是使用原型鏈實現對原型屬性和方法的繼承,而經過借用構造函數來實現對實例屬性的繼承。這樣,既經過在原型上定義方法實現了函數複用,又可以保證每一個實例都有它本身的屬性。
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 中最經常使用的繼承模式。
將須要被繼承的對象掛載到函數內部新建立的構造函數原型上並返回該構造函數的實例
/* 模擬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"
複製代碼
寄生式(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()函數不是必需的;任何可以返回新對象的函數都適用於此模式。
所謂寄生組合式繼承,即經過借用構造函數來繼承屬性,經過原型鏈的混成形式來繼承方法 ; 本質上,就是使用寄生式繼承來繼承超類型的原型,而後再將結果指定給子類型的原型。
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); //建立對象
prototype.constructor = subType; //加強對象
subType.prototype = prototype; //指定對象
}
複製代碼