好家伙,本篇為《JS高級程式設計》第八章“對象、類與面向對象編程”學習筆記 1.工廠模式 工廠模式是另外一種關註對象創建概念的創建模式。 它的領域中同其它模式的不同之處在於它並沒有明確要求我們使用一個構造器。 取而代之,一個工廠能提供一個創建對象的公共介面,我們可以在其中指定我們希望被創建的工廠對象 ...
好家伙,本篇為《JS高級程式設計》第八章“對象、類與面向對象編程”學習筆記
1.工廠模式
工廠模式是另外一種關註對象創建概念的創建模式。
它的領域中同其它模式的不同之處在於它並沒有明確要求我們使用一個構造器。
取而代之,一個工廠能提供一個創建對象的公共介面,我們可以在其中指定我們希望被創建的工廠對象的類型。
function createPerson(name,age,job){
let person =new Object();
person.name= name;
person.age =age;
person.job =job;
person.getName = function(){
console.log(this.name);
}
return person;
}
let person_1 = createPerson("panghu","20","student")
person_1.getName();
console.log(person_1);
(看上去沒什麼問題,但怎麼總覺得怪怪的)
let person =new Object();
/...
...
...
../
return person;
2.構造函數模式
前面幾章提到過,ECMAScript中的構造函數是用於創建特定類型對象的。
像Object和Array這樣的原生構造函數,運行時可以直接在執行環境中使用。
當然也可以自定義構造函數,以函數的形式為自己的對象類型定義屬性和方法。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.getName = function () {
console.log(this.name);
};
}
let person_1 = new Person("panghu", "20", "student");
person_1.getName();
console.log(person_1);
在這個例子中,Person()構造函數代替了 createPerson()工廠函數。
實際上,Person()內部的代碼跟createPerson()基本是一樣的,只是有如下區別。
□沒有顯式地創建對象。
□屬性和方法直接賦值給了this。
□沒有return。
另外,要註意函數名Person的首字母大寫了。
按照慣例,構造函數名稱的首字母都是要大寫的,非構造函數則以小寫字母開頭。
這是從面向對象編程語言那裡借鑒的( 是的,非常好的借鑒 ),有助於在ECMAScript中區分構造函數和普通函數。
畢竟ECMAScript的構造函數就是能創建對象的函數。
要創建Person的實例,應使用new操作符。以這種方式調用構造函數會執行如下操作。
(1)在記憶體中創建一個新對象。
(2)這個新對象內部的[[Prototype]]特性被賦值為構造函數的prototype屬性。
(3)構造函數內部的this被賦值為這個新對象(即this指向新對象)。
(4)執行構造函數內部的代碼(給新對象添加屬性)。
(5)如果構造函數返回非空對象,則返回該對象;否則,返回剛創建的新對象。
2.1.構造函數也是函數
構造函數與普通函數唯一的區別就是調用方式不同。除此之外,構造函數也是函數。
並沒有把某個函數定義為構造函數的特殊語法。
任何函數只要使用new操作符調用就是構造函數,而不使用new操作符調用的函數就是普通函數。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.getName = function () {
console.log(this.name);
};
}
let person_1 = new Person("panghu", "20", "student");
person_1.getName();
//作為函數調用,添加到window對象
Person("xiaofu","20","student")
window.getName();
此處如果將Person當做普通函數來調用,那麼this指向的就是全局作用域,數據會被添加到window對象
2.2. 構造函數的問題
構造函數雖然有用,但也不是沒有問題。
構造函數的主要問題在於,其定義的方法會在每個實例上都創建一遍。
因此對前面的例子而言,person1和person2都有名為sayName()的方法,但這兩個方法不是同一個Function 實例。
我們知道,ECMAScript中的函數是對象,因此每次定義函數時,都會初始化一個對象。
如果把方法分離出來,在全局作用域中進行定義,在當前對象需要多個方法,那麼就要在全局作用域中定義多個函數.
這個問題可以通過原型模式解決
3.原型模式
(他來了,被營銷號譽為Js三大難點的"原型"他來了)
理解複雜概念之前我們先從簡單的地方入手(比如新華字典)
然後我們知道,原型指的是原來的模型
每個函數都會創建一個prototype屬性,這個屬性是一個對象,包含應該由特定引用類型的實例共用的屬性和方法。
實際上,這個對象就是通過調用構造函數創建的對象的原型。
使用原型對象的好處是,在它上面定義的屬性和方法可以被對象實例共用。
原來在構造函數中直接賦給對象實例的值,可以直接賦值給它們的原型,
3.1.實例共用原型模式的屬性和方法
function Person(){};
Person.prototype.name = "panghu";
Person.prototype.age = "20";
Person.prototype.job = "student";
Person.prototype.getName =function(){
console.log(this.name);
}
let person_1 = new Person();
person_1.getName();
let person_2 = new Person();
person_2.getName();
然後,我們來理解一下這段代碼,
首先,我們要把Person的原型當成一個對象來看待,
於是我們現在有了三方勢力,Person構造函數,Person原型對象,person_1和person_2兩個實例對象
看看這幅圖,
Person構造函數Person.prototype指向原型對象,而Person原型對象的constructor指向Person構造函數,
兩個實例都只有唯一屬性[[Prototype]]指向Person.prototype
3.2.原型層級
在通過對象訪問屬性時,會按照這個屬性的名稱開始搜索,搜索開始於對象實例本身
如果在對象實例上找到了,則返回對應的值,如果沒找到,則搜索會沿著指針進入原型對象,然後在原型對象上找到屬性後,再返回對應的值
function Person(){};
Person.prototype.name = "panghu";
Person.prototype.age = "20";
Person.prototype.job = "student";
Person.prototype.getName =function(){
console.log(this.name);
}
let person_1 = new Person();
person_1.getName();
person_1.name ="xiaofu";
person_1.getName();
delete person_1.name
person_1.getName();
只要給對象實例添加一個屬性,這個屬性就會遮蔽原型對象上的同名屬性,雖然不會修改,但會屏蔽對它的訪問
3.3.原型的動態性
因為從原型上搜索值的過程是動態的,所以即使實例在修改原型之前已經存在,任何時候對原型對象所做的修改也會在實例上反映出來
function Person(){};
Person.prototype.saysomething =function(){
console.log("yes,we can");
}
let person_1 = new Person();
person_1.saysomething();
但重寫原型又是另一碼事了
function Person() {};
let person_1 = new Person();
Person.prototype = {
constructor: Person,
name: "panghu",
age: "20",
job: "student",
saySomething() {
console.log("yes,we can");
}
}
person_1.saySomething();
雖然隨時能給原型添加屬性和方法,並能夠立即反映在所有對象實例上、但這跟重寫整個原型是兩回事。
實例的[[Prototype]]指針是在調用構造函數時自動賦值的,這個指針即使把原型修改為不同的對象也不會變。
重寫整個原型會切斷最初原型與構造函數的聯繫,但實例引用的仍然是最初的原型。記住,實例只有指向原型的指針,沒有指向構造函數的指針。
Person的新實例是在重寫原型對象之前創建的。在調用person_1.saySomething()的時候,會導致錯誤。
這是因為person_1指向的原型還是最初的原型,而這個原型上並沒有saySomething屬性。
That's all