国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JS面向對象的程序設計_創建對象_工廠模式,構造函數模式,原型模式-0

RaoMeng / 2814人閱讀

摘要:構造函數模式中的構造函數可以創建特定類型的對象。默認情況下,所有的函數原型對象都會自動獲得一個構造函數屬性,該屬性指向屬性所在函數。要明確的一點,這個連接存在于實例對象與構造函數的原型對象之間,而不是存在于實例對象與構造函數之間。

前言:最近在細讀Javascript高級程序設計,對于我而言,中文版,書中很多地方翻譯的差強人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯誤,會非常感謝您的指出。文中絕大部分內容引用自《JavaScript高級程序設計第三版》。

雖然可以通過Object構造函數或對象字面量來創建單個對象。
但是這些方式有明顯的缺點: 使用同一個接口創建很多對象,會產生大量重復的代碼,從性能角度來講,也會占用大量的內存。 為了解決這個問題,人們開始解決使用工廠模式。那什么是工廠模式呢?

工廠模式

工廠模式是計算機領域一種廣為人知的設計模式,這種設計模式抽象了創建具體對象的過程??紤]到在ECMAScript中無法創建類,開發者們就發明了一種函數,用函數來封裝以特定接口創建對象的細節。

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

函數createPerson能夠根據接受的參數來構建一個包含所有必要信息的Person對象。 可以無數次地調用這個函數,而每次都會返回帶有四個屬性和一個方法的對象。

就像工廠里生產產品的模具一樣,使用模具,就會生產出一個產品。

工廠模式,雖然解決了創建多個類似對象的問題,但卻沒有解決對象識別的問題, 別人沒看到你的源碼之前,怎么知道創建對象的類型呢?(即怎么知道一個對象的類型)。 隨著javaScript的發展,又一個新設計模式出現。

構造函數模式

ECMAScript中的構造函數可以創建特定類型的對象。像Object和Array這樣的原生構造函數, 在運行時會自動出現在執行環境中。

我們也可以創建自定義的構造函數,從而定義 - 自定義對象類型的屬性和方法。

例如: 我們可使用構造函數模式將前面的例子重寫如下。

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

var person1 = new Person("Shaw", "Secret, "Male", "Designer");
var person2 = new Person("Roc", 18, "Female", "Engineer");

在這個例子中, Person函數取代了createPerson函數。 Person函數里面的代碼除了和createPerson函數中相同的部分外,還存在以下不同之處:

沒有顯式地創建對象

直接將屬性和方法賦予==this對象==

沒有return語句

此外,還應該注意到函數名Person使用的是大寫字母P。 這又是一種約定俗成,構造函數始終都應該以一個大寫字母開頭, 而非構造函數則應該以一個小寫字母開頭。

這種做法借鑒了其他OO語言, 主要是為了區別于ECCMAScript中的其他函數。

==構造函數本身也是函數,只是可以用來創建對象而已。==

要創建Person的新實例對象,必須使用new操作符。以這種方式調用構造函數實際上會經歷一下4個步驟:

創建一個新對象,new操作符的作用。

將構造函數的作用域賦予給新對象this。

執行構造函數中的代碼(為這個對象添加屬性,this對象就指向了這個新對象)。

返回新對象。

//實例過程偽代碼
var person = new Person(name, age, job);

person = function Person(name, age, job) {
    this = new Object()// this是對象??!
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        return this.name;
    }
    return this;
}()

// this = {
//   name: name;
//   age: age;
//   job: job;
//   sayName: function() {
//      return this.name  
//}
//}

person = this = {
    name: name;
    age: age;
    job: job;
    sayName: function() {
    return this.name  }
}

在前面例子的最后,person1和person2分別保存著Person的一個不同的實例對象。 這兩個實例對象都有著一個constructor(構造函數屬性)屬性,該屬性指向Person。

console.log(person1.constructor == Person); // true
console.log(person2.constructor == Person); // true

==對象的constructor屬性最初是用來標識對象類型的。==

提到檢測對象類型,還是instanceof操作符更可靠一些。 我們在這個例子中創建的所有對象既是Object的實例對象,也是Person的實例對象,這一點可以通過instanceof操作符得到驗證。

console.log(person1 instanceof Person); // true
console.log(person2 instanceof Person); // true
console.log(person1 instanceof Object); // true
console.log(person2 instanceof Object); // true

創建自定義的構造函數意味著將來可以將它的實例標識為一種特定的類型; 而這正是構造函數模式勝過工廠模式的地方。在這個例子中, person1和person2之所以同時是Object的實例,是因為所有對象均繼承自Object。

以這種方式定義的構造函數, 是定義在Global對象(在瀏覽器中是window對象)中的。

將構造函數當做普通函數

構造函數與其他函數的唯一區別,就在于調用它們的方式不同。

==構造函數就是函數,不存在定義構造函數的特殊語法。==

==任何函數, 只要通過new操作符來調用,那它就可以作為構造函數。==

==而任何函數,只要不通過new操作符來調用,那它就是普通的函數。==

//當做構造函數使用
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("xxx", 18, "Soft Engineer");
person1.sayName // "xxx"

//作為普通函數調用
Person("Roc", 27, "Doctor");
window.sayName(); // "Greg"

// 在另外一個對象的作用域中調用
var o = new Object();
Person.call(o,"Kris", 25, "Nurse");
o.sayName(); //"Kris";

這個例子的前兩行代碼展示了構造函數的典型用法,即使用new操作符來創建一個新對象。接下來的兩行代碼展示了不使用new操作符調用Person()會出現什么結果:屬性和方法都被添加給window對象了。當在全局作用中調用一個函數時,this對象總是指向Global對象(瀏覽器中就是window對象)。因此,在調用完函數之后,可以通過window對象來調用sayName()方法,并且返回了“Greg”。最后,也可以使用call()(或者apply())在某個特殊對象的作用域調用Person()函數。 這里是在對象o的作用中調用的,因此調用后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)");
    // 與聲明函數在邏輯上是等價的。
}

從這個角度上來看構造函數,更容易明白每個Person實例化對象的sayName()方法,都包含一個不同的Function實例的本質。換句話說,以這種話方式創建函數,會導致不同的作用域鏈和標識符解析,但創建Function新實例的機制仍然是相同的。

==不同實例上的同名函數是不相等的,一下代碼可以證明這一點:==

alert(person1.sayName == person2.sayName); // false

然而,創建兩個完成同樣任務的Function實例的確沒有必要; 況且有this對象在,根本不用在執行代碼前就把函數綁定到特定對象上面。因此,大可像下面那樣,通過把函數定義轉移到構造函數外部解決這個問題。

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
}
function sayName(){
    console.log(this.name);
}
var person1 = new Person("Nancy", 29, "Soft Engineer");
var person2 = new Person("Nanth",27, "Doctor");

在這個例子中, 我們把sayName函數的定義轉移到了構造函數外部。

而在構造函數內部,我們將sayName屬性設置為全局的sayName函數。 這樣一來,sayName屬性都包含一個指向函數的指針, 因此person1和person2對象就共享了在全局作用中定義的同一個sayName()函數。 這樣做確實解決了兩個函數做同一件事的問題,可是新問題來了: 在全局定義的函數,實際上只能被某個對象調用,這樣全局作用域有點名不副實。而更讓人無法接受的是: 如果對象需要定義很多方法,那么就要定義很多個全局函數,玉石我們這個自定義的引用類型就絲毫沒有封裝性可言了。

==好在,這些問題可以通過使用原型模式解決==

那么原型模式是什么呢?

原型模式

==簡而言之,每個函數都有一個prototype(原型)屬性,這個屬性指向一個對象,prototype屬性是一個對象!==

而這個對象的用途是可以包含由特定類型的所有實例對象所共享的屬性和方法。

==按照字面意思去理解,prototype就是通過調用構造函數而創建的那個實例對象的原型對象。==

==使用原型對象的優點,可以讓所有的實例對象共享prototype上所包含的屬性和方法。

==換句話說,不必在構造函數上定義所有實例對象的屬性和方法,而是可以把這些信息直接添加到函數的原型對象中。==

==記住,ECMAScript中,函數也是對象。==

function Person(){
}

Person.prototype.name = "Shaw";
Person.prototype.age = 18;
Person.prototype.job = "Soft Engineer";
Person.prototype.sayName = function(){
    console.log(this.name);
}

var person1 = new Person();
person1.sayName(); // "Shaw"
var person2 = new Person();
person2.sayName(); // "Shaw"

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

理解原型對象

無論什么時候,只要創建一個新函數,就會根據一組特定的規則為該函數創建一個prototype屬性,這個屬性的值是一個對象,所以也可以稱作函數的原型對象。默認情況下,所有的函數原型對象都會自動獲得一個constructor(構造函數)屬性, 該屬性指向prototype屬性所在函數。 就拿前面的例子來說,Person.property.constructor指向Person。 而通過這個構造屬性,我們還可以繼續為原型對象添加其他屬性和方法。

==創建自定義的構造函數之后,其原型對象默認只會取得constructor屬性;至于其他方法,則都是從Object繼承而來。==

==這也側面反映出, 函數在ECMAScript中是個對象。==

當調用構造函數創建一個新實例對象之后,該實例對象的內部將包含一個指針(__proto__內部屬性),指向構造函數的原型對象。

ECMAScript-262第五版中管這個指針叫[[Prototype]]。雖然在腳本中沒有標準的方式訪問[[Prototype]], 但Firefox、Safari和Chrome在每個對象上都支持一個屬性__proto__;而在其他實現中,這個屬性對腳本是完全不可見的。

==要明確的一點,這個連接存在于實例對象與構造函數的原型對象之間, 而不是存在于實例對象與構造函數之間。==

以使用Person構造函數和Person.prototype創建實例的代碼為例:

//偽代碼
Person.prototype = {
    constructor: function Person(arguments){native code},
    name: "Shaw",
    age: "18",
    job: "Soft Engineer",
    sayName: function() {
        console.log(this.name);
        //這里的this指向prototype
    }
}

Person.prototype.constructor => function Person(..){native code}
//=> 指向的意思。
person1.__proto__ => Person.prototype
person2.__proto__ => Person.prototype

以上偽代碼展示構造函數以及它的原型對象,與它的兩個實例對象之間的關系。

在此, Person.prototype 構造函數的原型屬性指向原型對象, 而Person.prototype.constructor又指回了Person。

原型對象中除了包含constructor屬性,還包括后來添加的其他屬性。

Person的每個實例對象-person1和person2都有一個內部屬性,該屬性僅僅指向了Person.prototype;

換句話說:調用誰創造了實例對象, 實例對象的__proto__就指向創造者的原型。 很像造人~~

至此,說的都是與構造函數的prototype打交道,和構造函數本身沒有任何關系。

此外,要格外注意的是, 雖然這兩個實例都不包含屬性和方法,但我們可以調用person1.sayName()。這是通過查找對象屬性的過程來實現的。

我們可以通過isPrototypeOf()方法來確定對象之間是否存在這種關系。

從本質上講,如果實例對象的__proto__屬性指向調用isPrototype()方法的原型對象(Person.prototype), 那么這個方法就返回true。

//示例代碼

function Person(){}
var person1 = new Person();
var person2 = new Person();
console.log(Person.prototype.isPrototypeOf(person1)); // true, 實例化對象的原型鏈是否指向構造函數的原型對象
console.log(Person.prototype.isPrototypeOf(person2)); // true。

我們用原型對象的isPrototypeOf()方法測試了person1和person2。因為實例化對象內部都有一個指向Person.prototype的指針,因此都返回了true。

吐槽- 這個方法有點難記~

==還好,ECMAScript 5 新增一個方法,叫Object.getPrototypeOf(), 這個方法比較直觀好記理解。
在所有的支持的實現中,這個返回[[Prototype]]的值。==

function Person(){};
var person1 = new Person();
var person2 = new Person();

var PersonConstructorProto = Object.getPrototypeOf(person1); //獲取實例對象指向的構造函數的原型對象。
console.log(PersonConstructorProto);
console.log(PersonConstructorProto == Person.prototype);// true

這里的第一行代碼返回的是Object.getPrototypeOf(實例對象)方法得到實例對象的原型對象。

==使用Object.getPrototypeOf()可以方便地獲取實例對象的原型對象,而這在利用原型實現繼承的情況下是非常重要的。==

支持這個方法的瀏覽器有IE9+、Firefox 3.5+、Safari 5+、Opera 12+和Chrome。

插一條: 查兼容 可以使用這個網站 caniuse.com。

每當代碼讀取某個對象的屬性的時候,都會執行一次搜索, 目標是具有給定名字的屬性。 搜索首先從實例對象本身開始。

如果在實例對象中找到了具有給定名字的屬性,則返回實例對象屬性的值。

如果沒有找到,則沿著實例對象內部的指針,搜索實例對象指向的構造函數的原型對象, 如果在構造函數的原型對象中找到了這個屬性,則返回該屬性的值。

//實例對象搜索屬性的過程
//也就是說, 我們調用person1.sayName()的時候,會先后執行兩次搜索。
//首先,解析器會問:“實例對象person1有sayName屬性嗎?”; 答:“沒有”。
//然后,它繼續搜索,在問:“person1的原型上有sayName屬性嗎?”; 答:“有”。
//于是,它就讀取那個保存在原型對象中的函數。
//同理,當我們調用person2.sayName()時,將會出現相同的搜索過程,得到相同的結果。
//而這正是多個實例對象共享原型所保存的屬性和方法的基本原理。

function Person(){}
Person.prototype.name = "Shaw";
Person.prototype.sayName = function(){
    alert(this.name);
}
var person1 = new Person();
var person2 = new Person();
person1.sayName(); // "Shaw"
person2.sayName(); // "Shaw"

==函數的原型對象,最初只包含constructor屬性, 而該屬性也是共享的, 因此可以通過對象實例訪問。==

== 雖然可以通過實例對象訪問保存在原型中的值,但不能通過實例對象重寫原型中的值。如果我們在實例對象中添加了一個屬性, 該屬性與實例對象的原型對象的一個屬性同名,那我們就在實例中創造該屬性,該屬性將會屏蔽原型中的那個屬性。==

function Person(){}
Person.prototype.name = "Shaw";
Person.prototype.age = 28;
Person.prototype.job = "Designer";
Person.prototype.sayName = function(){
    console.log(this.name);
}

var person1 = new Person();
var person2 = new Person();
person1.name = "Roc";
console.log(person1.name); //"Roc" 來自person1實例
console.log(person2.name); //"Shaw" 來自 Person.prototype

在這個例子中, person1的name屬性值就是實例對象屬性name的值。無論訪問person1.name還是person2.name都能返回響應的值,分別是實例對象自身的name屬性值:person1.name = "Roc" 和 person2.name = "Shaw", person2在自身實例對象上找不到name屬性,所以只能通過__proto__指針的指向,找到構造函數原型對象的name屬性值。

當為對象實例添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性;

==換句話說。添加這個屬性只會阻止我們訪問構造函數原型對象的那個屬性, 但不會修改那個屬性。==

不過,可以使用delete操作符則可以完全去除實例屬性,從而能夠重新訪問原型中的屬性。

function Person(){

}

Person.prototype.name = "Shaw";
Person.prototype.age = 28;
Person.prototype.job = "Designer";
Person.prototype.sayName = function() {
    console.log(this.name)
}

var person1 = new Person();
var person2 = new Person();
person1.name = "Roc";
console.log(person1.name); // "Roc" 來自實例對象person1
console.log(person2.name); // "Shaw" 來自構造函數的原型對象prototype.name
delete person1.name; // 刪除實例對象上的name屬性, 還記得之前的章節,說對象的屬性數據特性中的configurable, enumerable,writable默認都為true嗎? 所以這里可以通過delete操作符,刪除實例對象的屬性。
console.log(person1.name); // "Shaw" 只能去構造函數的原型對象prototype找name屬性,找到了~~

在這個修改后的例子,我們使用delete操作符刪除了實例對象的person1的name屬性,把它刪除后,就恢復了實例對象對構造函數原型對象中的name屬性連接。 再調用person1.name, 返回的就是原型中name屬性的值了。

使用hasOwnProperty()方法可以檢測一個屬性是存在于實例對象中,還是存在于構造函數的原型中。

這個方法是從Object原生引用類型中繼承而來的,只在給定屬性存在于對象實例中時,才會返回true。

function Person(){

}
Person.prototype.name = "Shaw";
Person.prototype.age = 28;
Person.prototype.job = "Designer";
Person.prototype.sayName = function(){
    console.log(this.name);
}
var person1 = new Person();
var person2 = new Person();

console.log(person1.hasOwnProperty("name")); // false

person1.name = "Roc";
console.log(person1.name); // "Roc"
console.log(person1.hasOwnProperty("name")); // true

console.log(person2.name); //"Shaw"
console.log(person2.hasOwnProperty("name")); // false

delete person1.name;
console.log(person1.hasOwnProperty("name")); // false
console.log(person1.name); // "Shaw"

通過使用Object.getOwnProperty()方法,就能很清楚地知道,訪問到的是實例對象屬性,還是實例對象的構造函數原型對象。

調用person1.hasOwnProperty("name")時, 只有當person1重寫name屬性后才會返回true,因為只有這個時候name才是一個實例對象屬性,而非構造函數的原型對象。

//偽代碼 實例對象訪問屬性或方法過程。

Person.prototype = {
    constructor: function Person(arguments){native code},
    name: "Shaw",
    age: 28,
    job: "Designer",
    sayName: function(){
        console.log(this.name);
    }
}

object person1 <= new from Person;
object person2 <= new from Person;

person1.__proto__ => Person.prototype;
person2.__proto__ => Person.prototype;

object person1.name = "Roc";

so person1.name, it is from object person1, the value it is "Roc"
the "name" from object person1, so getOwnProperty, it is true.

object person2.name, object person2 self not has "name" property, searching for  => Person.prototype.name => find the "name" property, the value is "Shaw", get it.

person2.name = " Shaw";

object person2 not has own Property "name", so person2.getOwnProperty("name"), it is false;

Alright, let us delete object person1.name. 
Okay, object person1 do not has property "name".
He only can find it from Person.prototype, is it has "name" property in Person.prototype.

Yes, it did have.
Okay~, the object person1.name => Person.prototype.name => "Shaw".

Because we delete the "name" property of object person1.
So object person1 not has Own Property.

person1.hasOwnProperty("name"), it is false;

ECMAScript 5的 Object.getOwnPropertyDescriptor()方法可用于實例對象的屬性,當然也可用于構造函數。
==一定要記住函數也是對象啊~只不過它是一個特殊的對象~ 要不然原型對象上怎么繼承那么多Object的方法呢?==

var people = {
    name: "Shaw"
}

function Person(){

}

Person.prototype.name = "Roc";
var PersonProp = Person.prototype;

var peoplePropDes = Object.getOwnPropertyDescriptor(people, "name");
console.log(peoplePropDes.value); // "Shaw"

var PersonPropDes = Object.getOwnPropertyDescriptor(PersonProp, "name");
console.log(PersonPropDes.value); //"Roc"

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98169.html

相關文章

  • JavaScript面向對象程序設計

    摘要:目錄導語理解對象和面向對象的程序設計創建對象的方式的繼承機制原型對象原型鏈與原型對象相關的方法小結導語前面的系列文章,基本把的核心知識點的基本語法標準庫等章節講解完本章開始進入核心知識點的高級部分面向對象的程序設計,這一部分的內容將會對對象 目錄 導語 1.理解對象和面向對象的程序設計 2.創建對象的方式 3.JavaScript的繼承機制 3.1 原型對象 3.2 原型鏈 3.3 與...

    gitmilk 評論0 收藏0
  • JS面向對象編程之封裝

    摘要:在基于原型的面向對象方式中,對象則是依靠構造函數和原型構造出來的。來看下面的例子優點與單純使用構造函數不一樣,原型對象中的方法不會在實例中重新創建一次,節約內存。 我們所熟知的面向對象語言如 C++、Java 都有類的的概念,類是實例的類型模板,比如Student表示學生這種類型,而不表示任何具體的某個學生,而實例就是根據這個類型創建的一個具體的對象,比如zhangsan、lisi,由...

    YFan 評論0 收藏0
  • js面向對象入門

    摘要:簡單來理解對象就是由屬性和方法來組成的面向對象的特點封裝對于一些功能相同或者相似的代碼,我們可以放到一個函數中去,多次用到此功能時,我們只需要調用即可,無需多次重寫。 什么是對象 我們先來看高程三中是如何對對象進行定義的 無序屬性的集合,其屬性可以包括基本值、對象或者函數,對象是一組沒有特定順序的的值。對象的沒個屬性或方法都有一個俄名字,每個名字都映射到一個值。 簡單來理解對象就是由屬...

    sihai 評論0 收藏0
  • JS面向對象程序設計創建對象_工廠模式,構造函數模式,原型模式-1

    前言:最近在細讀Javascript高級程序設計,對于我而言,中文版,書中很多地方翻譯的差強人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯誤,會非常感謝您的指出。文中絕大部分內容引用自《JavaScript高級程序設計第三版》。 2. 原型對象與in操作符 有兩種方式使用in操作符: 單獨使用和在for-in循環中使用。 單獨使用時,in操作符會通過對象能夠訪問給定屬性時返回true。 ==...

    incredible 評論0 收藏0
  • [javascript 學習筆記] 1. 面向對象

    摘要:當作構造函數來使用,作為普通函數來使用,當在全局作用域中調用一個函數時,對象總是指向對象。調用構造函數時會為實例添加一個指向最初原型的的指針,而把原型修改為另外一個對象就等于切斷了構造函數于最初原型之間的聯系。 ECMA-262 把對象定義為 無序屬性的集合,其屬性可以包含基本值、對象或者函數。 即對象是一組沒有特定順序的值。對象的每個屬性或方法都有一個名字,而每個名字都映...

    Berwin 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<