首頁 web前端 js教程 淺析在javascript中創建物件的各種模式_javascript技巧

淺析在javascript中創建物件的各種模式_javascript技巧

May 16, 2016 pm 03:01 PM
javascript 創建對象

最近在看《javascript高級程式設計》(第二版)

javascript中物件的建立

•工廠模式

•建構子模式

•原型模式

•結合建構子與原型模式

•原型動態模式

物件導向的語言大都有一個類別的概念,透過類別可以建立多個具有相同方法和屬性的物件。雖然從技術上講,javascript是一門物件導向的語言,但是javascript沒有類別的概念,一切都是物件。任意一個物件都是某種引用類型的實例,都是透過現有的參考類型建立;引用類型可以是原生的,也可以是自訂的。原生的參考類型有:Object、Array、Data、RegExp、Function。 !引用類型就是一種資料結構,將資料和功能組織在一起,通常被稱為類別。 在缺乏類別概念的javascript中,需要解決的問題就是如何有效率的創建物件。

1.1.0.建立物件的一般方法

var person = {}; //对象字面量表示,等同于var person = new Objcect();

person.name = 'evansdiy';
person.age = '22';
person.friends = ['ajiao','tiantian','pangzi'];
person.logName = function() {
  console.log(this.name);
}
登入後複製

基於Object引用類型,建立了一個對象,該對象包含四個屬性,其中一個為方法。如果需要很多類似person的實例,那就會有許多重複的程式碼。

1.1.1.工廠模式 [top]

透過一個可以包含了物件細節的函數來建立對象,然後傳回這個對象。

function person(name,age,friends) {

  var o = {
    name: name,
    age: age,
    friends: friends,
    logName: function() {
      console.log(this.name);
    }
  };

  return o;

}

var person1 = person('Evansdiy','22',['ajiao','tiantian','pangzi']);
登入後複製

每次調用person函數,都會透過該函數內部的對象o創建新的對象,然後返回,除此之外,這個為了創建新對象而存在的內部對象o沒有其他的用途。另外,無法判斷工廠模式所建立的物件的類型。

1.1.2.建構子模式 [top]

function Person(name,age,job) {

  this.name = name;
  this.age = age;
  this.job = job;
  this.logName = function() {
    console.log(this.name);
  }

}

//通过new操作符创建Person的实例
var person1 = new Person('boy-a','22','worker');

var person2 = new Person('girl-b','23','teacher');

person1.logName(); //boy-a

person2.logName(); //girl-a
登入後複製

對比工廠模式,可以發現,這裡並不需要創建中間對象,沒有return。另外,可以將建構函式的實例標識為一種特定的類型,這就解決了物件辨識的問題(透過檢查實例的constructor屬性,或利用instanceof操作符來檢查該實例是否透過某個建構函式建立)。

console.log(person1.constructor == Person);//constructor位於建構函數原型中,並指向建構函數,結果為true

console.log(person1 instanceof Person);//透過instanceof操作符,判斷person1是否為建構子Person的實例但建構子模式也有自己的問題,實際上,logName方法在每個實例上都會被重新創建一次,需要注意的是,透過實例化創建的方法且並不相等,以下程式碼將會得到false:

console.log(person1.logName == person2.logName);//false我們可以將方法移到建構器外部(變成全域函數)來解決這個問題:

function logName() {
  console.log(this.name);
}

function logAge() {
  console.log(this.age);
}
登入後複製

但是,在全域下創建的全域函數實際上只能被經由Person創建的實例調用,這就有點名不副實了;如果方法很多,還需要逐一定義,缺少封裝性。

1.1.3.原型模式 [top]

javascript中的每一個函數都包含一個指向prototype屬性的指針(大部分瀏覽器可以透過內部屬性__proto__存取),prototype屬性是一個對象,其中包含了由某種引用類型創建的所有實例共享的屬性和方法。 

function Person() {}

Person.name = 'evansdiy';

Person.prototype.friends = ['ajiao','jianjian','pangzi'];

Person.prototype.logName = function() {
  console.log(this.name);
}

var person1 = new Person();

person1.logName();//'evansdiy'
登入後複製

以上程式碼做了這幾件事:

1.定義了一個建構函式Person,Person函式自動取得一個prototype屬性,該屬性預設只包含一個指向Person的constructor屬性;

2.透過Person.prototype加入三個屬性,其中一個作為方法;

3.建立一個Person的實例,隨後在實例上呼叫了logName()方法。

這裡要注意的是logName()方法的呼叫過程:

1.在person1實例尋找logName()方法,發現沒有這個方法,於是追溯到person1的原型

2.在person1的原型上尋找logame()方法,有這個方法,於是呼叫該方法基於這樣一個查找過程,我們可以透過在實例上定義原型中的同名屬性,來阻止該實例存取原型上的同名屬性,要注意的是,這樣做並不會刪除原型上的同名屬性,只是阻止實例存取。

var person2 = new Person();

person2.name = 'laocai';如果我們不再需要實例上的屬性時,可以透過delete操作符刪除。

delete person2.name;利用for-in迴圈枚舉出實例可以存取到的所有屬性(無論該屬性存在於實例或是原型中):

for(i in person1) {
  console.log(i);
}
登入後複製

同时,也可以利用hasOwnProperty()方法判断某个属性到底存在于实例上,还是存在于原型中,只有当属性存在于实例中,才会返回true:

console.log(person1.hasOwnProperty('name'));//true!hasOwnProperty来自Object的原型,是javascript中唯一一个在处理属性时不查找原型链的方法。[via javascript秘密花园] 另外,也可以通过同时使用in操作符和hasOwnProperty()方法来判断某个属性存在于实例中还是存在于原型中:

console.log(('friends' in person1) && !person1.hasOwnProperty('friends'));先判断person1是否可以访问到friends属性,如果可以,再判断这个属性是否存在于实例当中(注意前面的!),如果不存在于实例中,就说明这个属性存在于原型中。 前面提到,原型也是对象,所以我们可以用对象字面量表示法书写原型,之前为原型添加代码的写法可以修改为:

Person.prototype = {

  name: 'evansdiy',
  friends: ['ajiao','jianjian','pangzi'],
  logName: function() {
    console.log(this.name);
  }

}
登入後複製

由于对象字面量语法重写了整个prototype原型,原先创建构造函数时默认取得的constructor属性会指向Object构造函数:

//对象字面量重写原型之后

console.log(person1.constructor);//Object不过,instanceof操作符仍会返回希望的结果:

//对象字面量重写原型之后

console.log(person1 instanceof Person);//true当然,可以在原型中手动设置constructor的值来解决这个问题。

Person.prototype = {

  constructor: Person,
  ......

}
登入後複製

如果在创建对象实例之后修改原型对象,那么对原型的修改会立即在所有对象实例中反映出来:

function Person() {};

var person1 = new Person();

Person.prototype.name = 'evansdiy';

console.log(person1.name);//'evansdiy'
登入後複製

实例和原型之间的连接仅仅是一个指针,而不是一个原型的拷贝,在原型实际上是一次搜索过程,对原型对象的所做的任何修改都会在所有对象实例中反映出来,就算在创建实例之后修改原型,也是如此。 如果在创建对象实例之后重写原型对象,情况又会如何?

function Person() {};

var person1 = new Person1();//创建的实例引用的是最初的原型

//重写了原型
Person.prototype = {
  friends: ['ajiao','jianjian','pangzi']
}

var person2 = new Person();//这个实例引用新的原型

console.log(person2.friends);

console.log(person1.friends);
登入後複製

以上代码在执行到最后一行时会出现未定义错误,如果我们用for-in循环枚举person1中的可访问属性时,会发现,里头空无一物,但是person2却可以访问到原型上的friends属性。 !重写原型切断了现有原型与之前创建的所有对象实例的联系,之前创建的对象实例的原型还在,只不过是旧的。

//创建person1时,原型对象还未被重写,因此,原型对象中的constructor还是默认的Person()

console.log(person1.constructor);//Person()

//但是person2的constructor指向Object()

console.log(person2.constructor);//Object()需要注意的是,原型模式忽略了为构造函数传递参数的过程,所有的实例都取得相同的属性值。同时,原型模式还存在着一个很大的问题,就是原型对象中的引用类型值会被所有实例共享,对引用类型值的修改,也会反映到所有对象实例当中。

function Person() {};

Person.prototype = {
  friends: ['ajiao','tiantian','pangzi']
}

var person1 = new Person();

var person2 = new Person();

person1.friends.push('laocai');

console.log(person2.friends);//['ajiao','tiantian','pangzi','laocai']
登入後複製

修改person1的引用类型值friends,意味着person2中的friends也会发生变化,实际上,原型中保存的friends实际上只是一个指向堆中friends值的指针(这个指针的长度是固定的,保存在栈中),实例通过原型访问引用类型值时,也是按指针访问,而不是访问各自实例上的副本(这样的副本并不存在)。

1.1.4.结合构造函数和原型模式创建对象 [top]

结合构造函数和原型模式的优点,弥补各自的不足,利用构造函数传递初始化参数,在其中定义实例属性,利用原型定义公用方法和公共属性,该模式应用最为广泛。

function Person(name,age) {

  this.name = name;
  this.age = age;
  this.friends = ['ajiao','jianjian','pangzi'];

}

Person.prototype = {

  constructor: Person,
  logName: function() {
    console.log(this.name);
  }

}

var person1 = new Person('evansdiy','22');

var person2 = new Person('amy','21');

person1.logName();//'evansdiy'

person1.friends.push('haixao');

console.log(person2.friends.length);//3
登入後複製

1.1.5.原型动态模式 [top]

原型动态模式将需要的所有信息都封装到构造函数中,通过if语句判断原型中的某个属性是否存在,若不存在(在第一次调用这个构造函数的时候),执行if语句内部的原型初始化代码。

function Person(name,age) {

  this.name = name;
  this.age = age;

  if(typeof this.logName != 'function') {
    Person.prototype.logName = function() {
      console.log(this.name);
    };
    Person.prototype.logAge = function() {
      console.log(this.age);
    };
  };

}

var person1 = new Person('evansdiy','22');//初次调用构造函数,此时修改了原型

var person2 = new Person('amy','21');//此时logName()方法已经存在,不会再修改原型
登入後複製

需要注意的是,该模式不能使用对象字面量语法书写原型对象(这样会重写原型对象)。若重写原型,那么通过构造函数创建的第一实例可以访问的原型对象不会包含if语句中的原型对象属性。

function Person(name,age) {

  this.name = name;
  this.age = age;

  if(typeof this.logName != 'function') {
    Person.prototype = {
      logName: function() {
        console.log(this.name);
      },
      logAge: function() {
        console.log(this.Age);
      }
    }
  };

}

var person1 = new Person('evansdiy','22');

var person2 = new Person('amy','21');

person2.logName();//'amy'

person1.logName();//logName()方法不存在
登入後複製

需要说明的是,各模式都有自己的应用场景,无所谓优劣。

以上这篇浅析在javascript中创建对象的各种模式就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

activex部件不能創建物件如何解決 activex部件不能創建物件如何解決 Jan 24, 2024 pm 02:48 PM

解決方法:1、檢查拼字和路徑;2、新增對元件的引用;3、檢查登錄;4、以管理員身份運作;5、更新或修復Office;6、檢查安全軟體;7、使用其他版本的組件;8、查看錯誤訊息;9、尋找其他解決方案。詳細介紹:1、檢查拼字和路徑:確保物件的名稱和路徑沒有拼字錯誤,且檔案確實存在於指定的路徑中;2、新增對組件的參考等等。

如何使用WebSocket和JavaScript實現線上語音辨識系統 如何使用WebSocket和JavaScript實現線上語音辨識系統 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術 WebSocket與JavaScript:實現即時監控系統的關鍵技術 Dec 17, 2023 pm 05:30 PM

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統 如何利用JavaScript和WebSocket實現即時線上點餐系統 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統 如何使用WebSocket和JavaScript實現線上預約系統 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript與WebSocket:打造高效率的即時天氣預報系統 JavaScript與WebSocket:打造高效率的即時天氣預報系統 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

javascript如何使用insertBefore javascript如何使用insertBefore Nov 24, 2023 am 11:56 AM

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

See all articles