JavaScript 對象的建立

Object類型是JavaScript中使用最多的一種類型。建立Object實例的方式有多種,接下來一一列舉。

1. Object構造函數

var person =  new Object();
person.name = "Brittany";
person.age = 23;
person.job = "web front-end engineer";
person.sayName =  function() {
    console.log( this.name);
};
person.sayName();    // Brittany

2. 對象字面量模式

var person = {
    name: "Brittany",
    age: 23,
    job: "web front-end engineer",
    sayName:  function() {
        console.log( this.name);
    }
};
person.sayName();

雖然Object構造函數或對象字面量均可以用來建立單個對象,但這些方式有個明顯的缺點:使用同一個接口建立不少對象,會產生大量的重複代碼。爲解決這個問題,可使用工廠模式的一種變體。web

3. 工廠模式 

function createPerson(name, age, job) {
     var o =  new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName =  function() {
        console.log( this.name);
    };
     return o;
}

var person1 = createPerson("Brittany", 23, "Software Engineer");
var person2 = createPerson("Sam", 26, "Software Engineer");
console.log( typeof person1);    // Object

工廠模式雖然解決了建立多個類似對象的問題,但卻沒有解決對象識別的問題(即怎樣知道一個對象的類型)。如代碼中只能檢測出person1爲Object類型。隨着JavaScript的發展,又一個新模式出現了。數組

4. 構造函數模式

function Person(name, age, job) {
     this.name = name;
     this.age = age;
     this.job = job;
     this.sayName =  function() {
        console.log( this.name);
    }    
}
var person1 =  new Person("Brittany", 23, "Web front-end engineer");
var person2 =  new Person("Closure", 26, "Manager");
person1.sayName();
person2.sayName();
console.log(person1.sayName == person2.sayName);    // false

使用構造函數的主要問題:每一個方法都要在每一個實例上從新建立一遍。如代碼中所示,person1的sayName和person2的sayName不相等。能夠將函數定義轉移到構造函數外部來解決。函數

function Person(name, age, job) {
     this.name = name;
     this.age = age;
     this.job = job;
     this.sayName = sayName;
}
function sayName() {
    console.log( this.name);
}

sayName函數的定義轉移到了構造函數外部。而在構造函數內部,咱們將sayName屬性設置成等於全局的sayName函數。這樣一來,因爲sayName包含的是一個指向函數的指針,所以person1和person2對象就共享了在全局做用域中定義的同一個sayName()函數。這的確解決了兩個函數作同一件事的問題,但是新問題又來了:在全局做用域中定義的函數實際上只能被某個對象調用,這讓全局做用域有點名存實亡。而更讓讓人沒法接受的是:若是須要定義不少方法,那就要定義不少個全局函數,因而這個自定義的引用類型就無封裝性可言。這些問題可經過使用原型模式來解決。this

5. 原型模式

1)對象建立 

function Person() {}
Person.prototype.name = "Brittany";
Person.prototype.age = 23;
Person.prototype.job = "Web front-end engineer";
Person.prototype.getName =  function() {
    console.log( this.name);
};
var p1 =  new Person();
var p2 =  new Person();

console.log(p1.name);                     // Brittany
console.log(p2.name);                     // Brittany
console.log(p1.getName == p2.getName);    // true

實例中建立的屬性會覆蓋原型中的同名屬性,不能修改原型中的屬性。spa

p1.name = "Susan";
console.log(p1.name);                     // Susan

hasOwnProperty()檢測一個屬性是否存在於實例中。prototype

console.log(p2.hasOwnProperty("name"));   // false
p2.name = "koko";
console.log(p2.hasOwnProperty("name"));   // true
delete p2.name;
console.log(p2.hasOwnProperty("name"));   // false

isPropertyOf()設計

console.log(Person.prototype.isPrototypeOf(p1));    // true
console.log(Person.prototype.isPrototypeOf(p2));    // true

getPropertyOf()指針

console.log(Object.getPrototypeOf(p1) == Person.prototype);   // true
console.log(Object.getPrototypeOf(p1));                       // Person

2)原型與in操做符

in單獨使用時,經過對象訪問到特定屬性時返回true,不管該屬性存在於實例中仍是原型中。hasOwnProperty(),經過對象訪問到特定屬性時返回true,且該屬性存在於實例中。 code

var p3 =  new Person();
console.log("name"  in p3);                 // true
console.log(p3.hasOwnProperty("name"));    // false
p3.name = "insist";
console.log(p3.hasOwnProperty("name"));    // true

肯定屬性究竟是存在於對象中,仍是存在於原型中。以下函數hasPrototypePropery()返回true表示該屬性存在於原型中,而不是存在於實例中。對象

function hasPrototypeProperty(object, name) {
     return !hasOwnProperty("name") && (name  in object);
}

for..in循環,全部經過對象可以訪問的,可枚舉的(enumerated)屬性,既包括存在於實例中的屬性,也包括存在於原型中的屬性。

for( var prop  in p1) {
    console.log(prop);                     // name age job sayName
}

Object.keys(),ECMAScript5的方法,取得對象上全部可枚舉的屬性,接收一個對象做爲參數,返回值是一個包含全部可枚舉屬性的字符串數組。注意:Person.prototype也是對象。

var keys = Object.keys(Person.prototype);
console.log(keys);              // ["name age job sayName"]
var p1 =  new Person(); 
console.log(Object.keys(p1));   // []
p1.name = "get";
console.log(Object.keys(p1));   // ["name"]

Object.getOwnPropertyNames(),獲得全部實例屬性,不管它是否可枚舉。

var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys);      // ["constructor", "name", "age", "job", "getName"] 
var keys_p1 = Object.getOwnPropertyNames(p1);
console.log(keys_p1);   // []

3)更簡潔的原型語法

function Person() {}
Person.prototype = {
    name: "Brittany",
    age: 23,
    job: "Web front-end engineer",
    getName:  function() {
         return  this.name;
    }
};
var friend =  new Person();
console.log(friend  instanceof Person);       // true
console.log(friend  instanceof Object);       // true
console.log(friend.constructor == Person);   // false
console.log(friend.constructor == Object);   // false

在上面的代碼中,將Person.prototype設置爲等於一個對象字面量形式建立的新對象,最終結果相同。但有一個例外:constructor屬性再也不指向Person了。每建立一個函數,就會同時建立它的prototype對象,這個對象也會自動得到constructor屬性。而咱們在這裏使用的語法,本質上徹底重寫了默認的prototype對象,所以constructor屬性也就變成了新對象的constructor屬性(指向Object構造函數),再也不指向Person函數。此時儘管instanceof操做符還能返回正確的結果,但經過constructor已經沒法肯定對象的類型了。

經過以下方式,將constructor手動設置爲合適的值。

Person.prototype = {
    constructor: Person,
    name: "Brittany",
    age: 23,
    job: "Web front-end engineer",
    getName:  function() {
        console.log( this.name);
    }
};

4)原型的動態性

在原型中查找值的過程是一次搜索,所以咱們對原型對象所作的任何修改都可以當即從實例上反映出來——即便是先建立了實例後修改原型也照樣如此。

var friend =  new Person();
Person.prototype.sayHi =  function() {
    console.log("hi");
};
friend.sayHi();

儘管能夠隨時爲原型添加屬性和方法,而且修改可以當即在全部對象實例中反映出來,但若是是重寫整個原型對象,狀況就不同了。

function Person() {}
var friend =  new Person();
Person.prototype = {
    constructor: Person,
    name: "Brittany",
    age: 23,
    job: "Web front-end engineer",
    getName:  function() {
        console.log( this.name);
    }
};
friend.getName();                   // error

如果建立實例放在重寫原型對象以後,則不會報錯。

5)原生對象的原型

全部原生引用類型(Object、Array、String)都在其構造函數的原型上定義了方法,如:Array.prototype.sort()、String.prototype.subString(), 經過原生對象的原型能夠取得全部默認方法的引用,並能夠定義新方法。

console.log( typeof Array.prototype.sort);         // function
console.log( typeof String.prototype.substring);   // function

String.prototype.startsWith =  function(text) {
     return  this.indexOf(text) == 0;
};
var msg = "Hello World";
console.log(msg.startsWith("Hello"));            // true

6)原型對象的問題

缺點一:省略了爲構造函數傳遞初始化參數這一環節,結果全部實例在默認狀況下將取得相同的屬性值。

缺點二:原型中全部屬性被許多實例共享,這種共享對於函數很是適合。對於包含基本值屬性倒也說得過去,由於經過在實例上添加一個同名屬性,能夠隱藏原型中對應的屬性。但對於包含引用類型值得屬性來講,問題比較突出。

function Person() { }
Person.prototype = {
    constructor: Person,
    name: "Brittany",
    friends: ["pink", "judy", "sam"],
    age: 23,
    job: "Web front-end engineer",
    getName:  function() {
        console.log( this.name);
    }
};
var person1 =  new Person();
var person2 =  new Person();
person1.friends.push("leo");
console.log(person1.friends);           // ["pink", "judy", "sam", "leo"]
console.log(person2.friends);            // ["pink", "judy", "sam", "leo"]
console.log(Person.prototype.friends);   // ["pink", "judy", "sam", "leo"]
person1.age = 35;
console.log(person1.age);            // 35
console.log(person2.age);            // 23
console.log(Person.prototype.age);   // 23

person1的friends屬性修改影響了person2的friends,可是person1的age屬性修改並未影響person2的age屬性。

緣由在於:friends數組存在於Person.prototype中而非person1中,所以修改也會經過person2.friends(與person1.friends指向同一個數組)反映出來。而age屬性在person1中也存在一份,修改的age屬性只是修改person1中的,並不能修改Person.prototype中的age屬性。

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

構造函數模式用於定義實例屬性,而原型模式用於定義方法和共享的屬性。這樣,每一個實例都會有本身的一份實例屬性的副本,但又同時共享着對方法的引用。

function Person(name, age, job) {
     this.name = name;
     this.age = age;
     this.job = job;
     this.friends = ["aa", "bb", "cc"];
}
Person.prototype = {
    constructor: Person,
    sayName:  function() {
        console.log( this.name);
    }
};
var person1 =  new Person("Brittany", 23, "Web front-end Engineer");
person1.friends.push("dd");      // ["aa", "bb", "cc", "dd"]
console.log(person1.friends);
var person2 =  new Person("Sam", 26, "Web front-end Engineer");
console.log(person2.friends);    // ["aa", "bb", "cc"]
console.log(person1.friends == person2.friends);   // false
console.log(person1.sayName == person2.sayName);   // true


 

 

時間:2014-10-21

地點:合肥

引用:《JavaScript高級程序設計》 

相關文章
相關標籤/搜索