js面向對象2--js中工廠模式的演化(重要,詳細)

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<script type="text/javascript">
/*
總括:
javascript創建對象的模式演化:1,函數創建----》2,構造函數創建
-----》3.原型創建
*/

//第一種模式:函數創建模式
//因爲js無法創建類,開發人員剛開始發明了一種函數,用函數來封裝以特定接口創建
//對象的細節,如:
function createPerson(name,age,job)
{
var o = new Object();
o.name = name;
o.age = age;
o.job = age;
o.sayName = function sayName(){
alert(this.name);
};
return o;
}

function onloadFunction()
{
var person1 = createPerson("陳超陽","27","java Soft Enginner")
var person2 = createPerson("heshengnan","23","java Soft Enginner")
console.log(person1);
console.log(person2);
console.log(person1.constructor); //打印出Object
console.log(person2.constructor); //打印出Object
}

/*
函數工廠模式雖然解決了創建多個相似對象的問題,但卻沒有解決對象識別的問題
(即怎樣知道一個對象的類型)
Person1,person2的構造函數都是Object,因此無法辨別對象的類型
隨着JavaScript的發展,出現了一種新的工程模式--構造函數模式
*/


//第二種模式:構造函數模式
/*
注意:
1,該種模式沒有顯示創建對象也就是沒有用new Object();
2,直接將屬性和方法賦值給this對象。
3,沒有return語句
4.函數名字的首字母是大寫的(以便區分構造函數和一般函數的區別)
5,要創建Person的新實例必須使用new關鍵字
*/
/*
使用new 構造函數創建對象的步驟:
1,js引擎會自動創建一個對象。
2,將構造函數的作用域賦給新對象(因此this就指向了新對象)
3,執行構造函數裏面的代碼(爲這個新對象添加屬性方法);
4,返回新對象
*/

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

function onloadFunction2()
{
var person3 = new Person("陳超陽","27","java Soft Enginner");
var person4 = new Person("heshengnan","23","java Soft Enginner")
console.log(person3.constructor); //打印出Person(name, age, job)
console.log(person4.constructor); //打印出Person(name, age, job)

//構造函數設計之初是用來標示對象類型的,但是提到檢測對象類型,還是用instanceof操作符
//要更可靠一些,我們在這個例子中創建的所有對象既是Object的實例,同時也是Person的實例
//這一點可以用instanceof操作符可以得到驗證
console.log(person3 instanceof Object); //true;
console.log(person3 instanceof Person); //true;
console.log(person4 instanceof Object); //true;
console.log(person4 instanceof Person); //true;
}
//注意以這種方法創建的構造函數是定義在global對象(在瀏覽器中是window中的)

/*
構造函數的缺點:
構造函數雖然好用,但並非沒有缺點。使用構造函數的主要問題,就是每個方法都要在每個實例上重新創建一邊
。在上面的例子中。person1和person2都有一個名字叫做sayName()的方法,但這兩個sayName()
方法不是同一個function實例。不要忘記了——EcmaScript中的對象,因此每定義一個函數也就實例化了
一個對象。從邏輯上講,此時的構造函數也可以如下面定義:
*/
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = new Function(){alert(this.name)};
}
/*
從這個角度來看構造函數,更容易明白每個Person實例都包含一個不同的function實例(以顯示name屬性)
的本質。說明白些,以這種方式創建函數,會導致不同的作用域鏈和標示符解析,但創建function新實例的
機制仍然相同。因此,不用實例上的同名函數是不相同的,一下代碼可以證明這一點:
console.log(person.sayName == person2.sayName);
然而,創建兩個完成同樣任務的function實例的確沒有必要,況且有this對象在,根本不用在執行代碼前就
把函數綁定到特定對象上面,大可像下面這樣,通過把函數定義在構造函數外來解決這個問題:
function Person (name,age,job)
{
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function syaName()
{
alert(this.name);
}
在這個例子中,我們把sayName()函數的定義轉移到了構造函數的外部。而在構造函數內部,我們
將sayname屬性值設置成等於全局函數sayName()。這樣一來,由於sayName包含的是一個指向
函數的指針,因此person1和person2對象就共享了在全局作用域中定義的同一個sayName函數
這樣做確實解決了兩個函數做同一件事情的問題,可是新問題又來了:在全局作用域中定義的函數
實際上只能被某個對象調用,這讓全局函數有點名不副實,而更讓人無法接受的是,如果對象需要定義
很多函數,那麼就需要定義很多個全局函數,於是我們這個自定義的引用類型就沒有封裝而言了,好在
這些問題可以使用原型模式解決。
*/


/*
第三:原型模型
我們創建的每個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個
對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。如果按照字面意思來理解,那麼
prototype就是通過調用構造函數而創建的那個對象實例的原型對象。使用原型對象的好處是
可以讓所有對象共享它所包含的屬性和方法,換句話說,不必在構造函數中定義對象實例的信息,而是
可以將這些信息直接添加到這些對象上,如下面的這些例子:
*/
function Person(){}
Person.prototype.name = "chenchaoyang";
Person.prototype.age = "454";
Person.prototype.job = "soft enginner";
Person.prototype.sayName = function(){alert(this.name)};

var person1 = new Person(){};
person1.sayName();

var person2 = new Person(){};
person2.sayName();

console.log(person1.sayName = person2.sayName); //true

/*
在此,我們將sayname()方法和所有屬性直接添加到了Person的prototype屬性中,構造函數
變成了空函數,即使如此,也仍然可以通過調用構造函數來創建對象,而且對象還會具有相同的屬性
和方法,但與構造函數模式不同的是:新對象的浙西二屬性和方法是所實例共享的,換句話說,person1
和person2訪問的都是同一組屬性和同一個sayName函數,要理解原型模式的工作原理,必須先理解
EcmScript中原型對象的性質。

理解原型對象:
無論什麼時候,只要創建了一個函數,就會根據一組特定的規則爲這個函數創建一個prototype屬性,
這個屬性指向函數的原型對象。 在默認情況下,所有原型對象都會自動獲得一個constrcutor構造函數
,這個屬性包含一個紙箱prototype屬性所在函數的指針。就拿全面的例子來說,Person.prototype
.construtctor指向Person,而通過這個函數,我們可以繼續爲原型對象添加其他屬性和方法。
*/

/*
重要:訪問一個對象 的屬性的過程是這樣的:
首先檢查這個對象是否含有自身的屬性(不是這個對象含有的prototype對象的屬性),如果有就直接
訪問。 如果沒有就去這個對象的prototype對象中檢查是否含有這個屬性。
也就是 首先訪問自己是否含有這個屬性, 然後再檢查這個對象的prototype中是否含有這個屬性。
例如一下例子:
*/
function Person(){}
Person.prototype.name = "chenchaoyang";
Person.prototype.age = 27;
Person.prototype.job = "softWare enginner";
Person.prototype.syaName = function(){
console.log(this.name);
}
var person1 = new Person();
var person2 = new Person();
person1.name = "heshegnnan";

console.log(person1.name); //"heshegnnan"來自實例
console.log(person2.name); //"chenchaoyang"來自原型對象

/*
總結:當爲對象添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性,換句話說,添加這個屬性
只會阻止我們訪問原型中的那個屬性,但不會修改那個屬性。及時這個屬性設置爲null,也只會在實例中
設置這個屬性,而不會恢復指向原型的鏈接。不過,使用delete操作符則可以完全刪除實例屬性,從而
讓我們能夠重新訪問原型中的屬性:例如:
function Person(){};
Person.prototype.name = "chenchaoyang";
Person.prototype.age = 12;
Person.prototype.job = "softWare enginner";
Person.prototype.sayName = function sayName(){
alert(thos.name);
}

var person1 = new Person();
var person2 = new Person();

person1.name = "Greg";
alert(person1.name); //Greg 來自實例
alert(person2.name); //chenchoyang 來自原型對象

delete person1.name;
alert(person1.name); //chenchaoyang 來自原型對象


使用hanOwnProperty()方法可以檢測一個屬性是否存在於實例中,還是存在於原型中。
這個方法(不要忘了它是從Object繼承來的)只在屬性存在於對象實例中時,纔會返回
true,來看看一下的例子:
function Person(){}
Person.prototype.name = "chenchaoyang";
Person.prototype.age = 22;
Person.prototype.job = "software enginner";
Person.sayName = function sayName(){
alert(this.name);

var person1 = new Person();
person1.hasOwnProperty("name"); //false

person1.name = "heshegnnan";
person1.hasOwnProperty("name"); //true

使用hasOwnProperty方法,什麼時候訪問的實例屬性,什麼時候訪問的是原型屬性就一清二楚了。
}
*/

</script>
</head>
<body οnlοad="onloadFunction();onloadFunction2();">
</body>
</html>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章