【上一篇】:JavaScript對象內部屬性及其特性總結segmentfault
先在內部 顯示地建立一個臨時對象,根據接收的參數來構建(賦值屬性和方法)該對象,並返回該對象。
缺點:沒有解決對象識別的問題(即沒法確認一個對象的類型)。
function PersonFactory (name, age){ var o = new Object(); o.name = name; o.age = age; o.sayName = function(){ console.log(this.name) } return o; } var p1 = PersonFactory(); var p2 = PersonFactory(); p1 instanceof Object; // true p2 instanceof Object; // true p1 instanceof PersonFactory; // false 沒法識別具體對象類型 p2 instanceof PersonFactory; // false
- 函數名首字母通常大寫(如:Person,借鑑OO語言命名習慣)區別於其餘函數;
- 構造函數自己也是函數,只不過能夠用來參加對象而已;
- 每一個新建的實例對象都有一個
constructor
屬性,該屬性指向構造函數自身Person
;構造函數也能夠被當作普通函數來調用;瀏覽器
- 在全局做用域中調用一個函數時,
this
對象老是指向Global
對象(瀏覽器中即window
對象)- 【優勢】自定義構造函數能夠將它的實例標識爲一種特定的類型;
- 【缺點】構造函數的主要問題是,每一個方法都要在每一個實例上從新建立一遍;
- 建立(隱式地)一個新對象;
- 將構造函數的新對象賦值給新對象(此時
this
指向這個新對象);- 執行構造函數中的代碼(爲這個新對象添加屬性);
- 返回新對象(該構造函數的實例);
- 沒有顯示地建立對象;
- 直接將屬性和方法賦值給
this
對象;- 沒有
return
語句;- 優勢:能夠確認具體對象類型;
function Person (name, age){ this.name = name; this.age = age; this.sayName = function(){ console.log(this.name); } } // 當作普通函數 var p1 = new Person('Tom', 20); var p2 = new Person('Greg', 30); p1 instanceof Object; // true p2 instanceof Object; // true p1 instanceof PersonFactory; // true 能夠確認具體對象類型 p2 instanceof PersonFactory; // true // 做爲普通函數調用 Person('Green', 30); // 屬性和方法都被添加到window對象了 window.sayName(); // 'Green' // 在另外一個對象的做用域中調用 var obj = new Object(); Person.call(obj , 'Jim', 23); obj .sayName(); // 'Jim'
- 構造函數變爲空函數;
- 將全部屬性和方法都添加到了構造函數的
prototype
屬性中;
- 不一樣:新對象的屬性和方法由全部實例共享(p1和p2訪問的都是同一組屬性和同一個函數);
function Person(){} Person.prototype.name = 'Tom'; Person.prototype.age = 24; Person.prototype.sayName = function(){ console.log(this.name); } var p1 = new Person(), p2 = new Person(); p1.sayName();// 'Tom' p2.sayName();// 'Tom' console.log(p1.sayName === p2.sayName);// true
- 每建立一個函數,就會同時建立它的
prototype
對象,該prototype
對象也會自動得到constructor
屬性。- 用對象字面量形式建立的對象,直接賦值給函數的原型對象(
Person.prototype
),本質上徹底重寫了其prototype
對象,- 所以
constructor
屬性也就變成了新對象的constructor
屬性(指向Object
構造函數),再也不指向Person
函數。- 此時
instanceof
操做符能夠返回正確結果,但經過constructor
已經沒法肯定對象類型了。
function Person(){} Person.prototype = { // construtor : Person, // 須要從新指設constructor屬性 name : 'Tom', age : 24, sayName : function(){ console.log(this.name); } } var p = new Person(); console.log(p instanceof Object); // ture console.log(p instanceof Person); // ture console.log(p.construtor == Object); // ture console.log(p.construtor == Person); // false
constructor
以上述方式重設constructor
屬性會致使它的[[Enumerable]]
特性被設置爲true
;
默認狀況下,原生的constructor
屬性是不可枚舉的;
所以可利用Object.defineProperty()
重設;
Object.defineProperty(Person.prototype, 'constructor', { enumerable : false, value : Person });
定義安全
- 組合使用構造函數模式和原型模式;
- 構造函數模式用於定義實例屬性,原型模式用於定義方法和共享的屬性;
優勢:函數
- 每一個實例都會有本身的一份實例屬性的副本,同時又共享着對方方法的引用,最大限度節省了內存;
- 這種混成模式支持向構造函數傳遞參數;
- 目前使用最普遍、認同度最高的一種自定義類型的方法,能夠說是用來定義引用類型的一種默認模式;
// 實例屬性均在構造函數中定義 function Person(name, age){ this.name = name; this.age = age; this.friends = ['Nicholas', 'Jim']; } // 由全部實例共享屬性constructor和方法則是在原型中定義的 Person.prototype = { construtor: Person, sayName: function(){ console.log(this.name); } } var p1 = new Person('Tom', 25); var p2 = new Person('Greg', 26); p1.friends.push('Tony'); console.log(p1.friends); // ["Nicholas", "Jim", "Tony"] console.log(p2.friends); // ["Nicholas", "Jim"] console.log(p1.friends === p2.friends); // false console.log(p1.sayName === p2.sayName); // true
定義:this
- 動態原型模式把全部信息都封裝在了構造函數中;
- 經過在構造函數中初始化原型(僅在必要的狀況下,好比第一次新建實例時);
- 經過檢查某個應該存在(原型中)的方法是否有效,來決定是否須要初始化原型;
- 優勢:保持了同時使用構造函數和原型的特色;
- 下段代碼中只會在初次調用構造函數時纔會執行,此後原型已經完成初始化,無需再作修改;
- 這裏對原型所作對修改,可以當即在全部實例中獲得反映;
if
語句檢查的能夠是初始化以後應該存在的任何屬性或方法,檢查其中一個便可;
function Person(name, age){ // 屬性 this.name = name; this.age = age; // 方法 if(typeof this.sayName != 'function'){ Person.prototype.sayName = function(){ console.log(this.name); } } }
- 【定義】: 基本思想是建立一個函數,該函數的做用僅僅是封裝建立對象的代碼,而後再返回新建立的對象。
【特色】:spa
- 返回的對象與構造函數或者與構造函數的原型屬性之間沒有關係;
- 寄生構造函數返回的對象與在寄生構造函數外部建立的對象沒有什麼不一樣;
- 不能依賴
instanceof
操做符來肯定對象類型;
- 除了使用
new
操做符並把使用的包裝函數叫作構造函數以外,該模式和工廠模式實際上是如出一轍的;
- 從表面上看,很像典型的構造函數;
- 構造函數在不返回值的狀況下,默認會返回新對象實例;
而寄生構造函數經過在構造函數的末尾加一個
return
語句,重寫了調用構造函數時返回的值;prototype
- 此時返回的對象的
__proto__
屬性指向Object;
function Person(name, age){ // 建立臨時新對象 var o = new Object(); // 初始化該對象 o.name = name; o.age = age; o.sayName = function(){ console.log(this.name); } // 返回該臨時新對象 return o; } var p1 = new Person('Tom', 25); var p2 = new Person('Greg',30); console.log(p1 instanceof Object); // true console.log(p1 instanceof Person); // false console.log(p1.sayName == p2.sayName); // false console.log(p1.constructor == Object); //true
所謂 穩妥對象,就是指沒有公共屬性,並且其方法也不引用this
的對象。
穩妥對象最適合在一些安全的環境中(這些環境會禁用this
和new
),或者防止數據被其餘應用程序改動時調用。
與寄生構造函數相似,但又不一樣:code
- 一是新建立的對象實例方法不引用
this
;- 二是不使用
new
操做符調用構造函數;
function Person (name, age){ var o = new Object(); // 可定義私有變量和函數 // ... // 新建立的對象實例方法未引用this; o.sayName = function(){ console.log(name) } return o; } var p = Person('Tom', 23); p.sayName(); // 'Tom' console.log(p instanceof Person); // false
- 除了
sayName()
方法外,沒有別的方法能夠訪問其數據成員;- 即便有其餘代碼給這個對象添加方法或數據成員,也不可能有別的辦法訪問傳入到構造函數中的原始數據;
- 與寄生構造函數模式相似,使用穩妥構造函數模式建立的對象與構造函數之間也沒有關係(故
instanceof
操做符對這種對象也無心義);