ECMA-262把對象定義爲:無序屬性的集合,其屬性能夠包含基本值、對象或者函數函數
建立自定義對象的最簡單方式就是建立一個Object的實例,再爲它添加屬性和方法。this
var person = new Object(); person.name = "Nicholas"; person.age = 29; person.job = "Software Engineer"; person.sayName = function(){ alert(this.name); };
另外一種方法,對象字面量成爲建立對象的首選模式。code
var person = { name: "Nicholas"; age: 29; job: "Software Engineer"; sayName: function(){ alert(this.name); } }
ECMA-262第五版在定義只有內部才用的特性(attribute)時,描述了屬性(property)的各類特性。特性用兩對方括號包含。對象
ECMAScript中有兩種屬性:數據屬性和訪問器屬性繼承
1.數據屬性接口
要修改屬性默認的特性,必須使用ECMAScript5的Object.defineProperty()方法。這個方法接收三個參數:屬性所在的對象、屬性的名字和一個描述符對象。ip
var person = {}; Object.defineProperty(person, "name", { writable: false, value: "Nicholas" }); console.log(person.name);//Nicholas person.name = "Greg"; console.log(person.name);//Nicholas
2.訪問器屬性作用域
var book = { _year: 2004, edition: 1 } Object.defineProperty(book, "year",{ get: function(){ return this._year; }, set: function(newValue){ if(newValue > 2004){ this._year = newValue; this.edition += newValue - 2004; } } }); book.year = 2005; console.log(book.edition);//2
_year前面的下下劃線是一種經常使用的記號,用於表示只能經過對象方法訪問的屬性。
這是使用訪問器屬性的常見方法,即設置一個屬性的值會致使其餘屬性發生變化。get
只指定getter意味着屬性是不能寫域名
object構造函數或者對象字面量均可以用來建立單個對象,但這些方式有個明顯的缺點:使用同一個接口建立不少對象,會產生大量的重複代碼
用函數來封裝以特定接口建立對象的細節
function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); } retrun o; } var person1 = createPerson("Nicholas", 29, "Software Engineer"); var person2 = createPerson("Greg", 27, "Doctor");
工廠模式雖然解決了建立多個類似對象的問題,但卻沒有解決對象識別的問題(怎樣知道一個對象的類型)
function Person(name,age,job){ this.name = name; this.job = job; this.age = age; this.sayName = function(){ alert(this.name); }; } var person1 = new Person("Trick",29,"SoftWare Engineer"); var person2 = new Person("John",23,"Doctor");
Person與createPerson函數存在如下不一樣之處:
要建立Person的新實例,必須使用new操做符。
person1和person2分別保存着Person的一個不一樣的實例。這兩個對象都有一個constructor(構造函數),該屬性指向Person
alert(person1.constructor == Person);//true alert(person2.constructor == Person);//true
constructor最初是用來標識對象類型的,檢測對象類型
instanceof操做符要更可靠一些,在如下例子中建立的全部對象既是Object的實例,同時也是Person的實例
alert(person1 instanceof Object);//true alert(person1 instanceof Person);//true alert(person2 instanceof Object);//true alert(person2 instanceof Person);//true
將構造函數看成函數
構造函數與其餘函數的惟一區別,就在於調用它們的方式不一樣。任何函數,只要經過new操做符,那它就能夠做爲構造函數;而任何函數,若是不經過new操做符來調用,那它跟普通函數也不會有什麼兩樣
//看成構造函數使用 var person = new Person("Nicholas", 29, "Software Engineer"); person.sayName(); //做爲普通函數調用 Person("Greg", 27, "Doctor");//添加到window window.sayName();//"Greg" //在另一個對象的做用域中調用 var o = new Object(); Person.call(o, "Kristen", 25, "Nurse"); o.sayName();
每一個方法都要在每一個實例上建立一遍,前面的例子中,person1和person2都有一個名爲sayName()的方法,但那兩個方法不是Function的實例。在ECMAScript中的函數是對象,所以每定義一個函數,也就是實例化了一個對象
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = new Function("alert(this.name)"); //與聲明函數在邏輯上是等價的 }
經過把函數定義轉移到構造函數外部來解決這個問題。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = sayName; } function sayName(){ alert(this.name); } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor");
這麼作問題又來了,全局做用域中定義的函數實際上只能被某個對象調用,這讓全局做用域名存實亡;而且若是有多個對象,對象又各自有多個方法,那就要定義不少個全局函數,就沒有封裝性可言了待續....