天天看點

4種用JavaScript建立對象的方法

4種用JavaScript建立對象的方法

英文 | https://medium.com/programming-essentials/4-ways-to-create-objects-in-javascript-ccb88772a61b

翻譯 | 小愛

本文将介紹4種使用JavaScript建構對象的方法。具體方法如下:

  • Object literals
  • Object.create()
  • Classes
  • Factory Functions

1、Object literals

建立對象的最簡單方法之一是建立對象文字文法。

示例如下:

const product = {
  name: 'apple',
  category: 'fruits',
  price: 1.99
}


console.log(product);      

JavaScript中的對象是鍵值對的動态集合。密鑰始終是字元串,并且在集合中必須是唯一。該值可以是基元、對象甚至函數。

這是一個值是另一個對象的示例:

const product = {
  name: 'apple',
  category: 'fruits',
  price: 1.99,
  nutrients : {
   carbs: 0.95,
   fats: 0.3,
   protein: 0.2
 }
}      

該carbs屬性的值是一個新對象。接下來檢查我們如何通路該carbs屬性。

console.log(product.nutrients.carbs);
//0.95      

簡寫屬性名稱​

考慮将屬性值存儲在變量中的情況。

const name = 'apple';
const category = 'fruits';
const price = 1.99;
const product = {
  name: name,
  category: category,
  price: price
}      

JavaScript支援速記屬性名稱。它允許我們僅使用變量名來建立對象。它将建立一個具有相同名稱的屬性。下一個對象文字與上一個相同。

const name = 'apple';
const category = 'fruits';
const price = 1.99;
const product = {
  name,
  category,
  price
}      

2、對象建立

JavaScript具有所謂的原型系統,該系統允許在對象之間共享行為。主要思想是建立一個具有常見行為的原型對象,然後在建立新對象時使用它。

原型系統允許建立從其他對象繼承行為的對象。

原型通常用于存儲方法,而不是資料。讓我們建立一個原型對象,使我們可以添加産品并從購物車中獲得總價。

const cartPrototype = {
  addProduct: function(product){
    if(!this.products){
     this.products = [product]
    } else {
     this.products.push(product);
    }
  },
  getTotalPrice: function(){
    return this.products.reduce((total, p) => total + p.price, 0);
  }
}      

請注意,這次屬性的值 ddProduct是一個函數。我們還可以使用一種稱為“簡寫方法”文法的較短形式來編寫先前的對象。

const cartPrototype = {
  addProduct(product){ },
  getTotalPrice(){ }
}      

該cartPrototype是原型對象,保持在兩種方法addProduct和getTotalPrice的共同行為。它可用于建構繼承此行為的其他對象。

const cart = Object.create(cartPrototype);
cart.addProduct({name: 'orange', price: 1.25});
cart.addProduct({name: 'lemon', price: 1.75});
console.log(cart.getTotalPrice());
//3      

cartPrototype對象是原型cart的對象。cart有一個稱為 __proto__該原型對象的隐藏屬性。

cart.__proto__ === cartPrototype;
//true      

當我們在對象上使用該方法時,首先在對象本身而不是原型上搜尋該方法。

this​

請注意,我們使用特殊的關鍵字來通路和修改名為this對象上的資料。

請記住,函數是JavaScript中行為的獨立單元。它們不一定是對象的一部分。

當函數是對象的一部分時,它們将成為方法,并且它們需要一種方法來通路同一對象上的其他成員。this是函數上下文,并提供對同一對象的其他屬性的通路。

Data

你可能想知道為什麼我們沒有在products原型對象本身上定義和初始化屬性。

因為我們不應該那樣做。原型應該用于共享行為,而不是資料。共享資料将導緻在多個購物車對象上擁有相同的産品。請看下面的代碼。

const cartPrototype = {
  products:[],
  addProduct: function(product){
      this.products.push(product);
  },
  getTotalPrice: function(){}
}
const cart1 = Object.create(cartPrototype);
cart1.addProduct({name: 'orange', price: 1.25});
cart1.addProduct({name: 'lemon', price: 1.75});
console.log(cart1.getTotalPrice());
//3
const cart2 = Object.create(cartPrototype);
console.log(cart2.getTotalPrice());
//3      

從cart1和cart2繼承相同行為的cartPrototype對象,它們都共享相同的資料。如果我們不想要那個的話,這樣就會導緻出錯。是以原型應該用于共享行為,而不是共享資料。

Classes

原型系統不是建構對象的常用方法。開發人員更熟悉在類之外建構對象。

類文法允許使用更熟悉的方式來建立共享共同行為的對象。它仍然在幕後建立相同的原型,但是文法更加清晰,我們還避免了先前與資料相關的問題。

該類提供了一個特殊的函數來定義每個對象(稱為構造函數)所不同的資料。

這是使用sugar類文法建立的同一對象。

class Cart{
  constructor(){
    this.products = [];
  }


  addProduct(product){
    this.products.push(product);
  }


  getTotalPrice(){
    return this.products.reduce((total, p) => total + p.price, 0);
  }
}
const cart = new Cart();
cart.addProduct({name: 'orange', price: 1.25});
cart.addProduct({name: 'lemon', price: 1.75});
console.log(cart.getTotalPrice());
//3
const cart2 = new Cart();
console.log(cart2.getTotalPrice());
//0      

請注意,該類具有一個構造函數方法,該方法為每個新對象初始化了不同的資料。執行個體之間不共享構造函數中的資料。為了建立一個新執行個體,我們使用new關鍵字。

我認為大多數開發人員都應該清楚和熟悉該類文法。但是,它做類似的事情,它使用所有方法建立一個原型,并使用它來定義新對象。可以使用通路原型Cart.prototype。

cart.__proto__ === Cart.prototype;
//true      

事實證明,原型系統足夠靈活,可以使用類文法。是以,可以使用原型系統對類系統進行仿真。

Private Properties

唯一的事情是products新對象的屬性預設情況下是公共的。

console.log(cart.products);
//[{name: "orange", price: 1.25}
// {name: "lemon", price: 1.75}]      

我們可以使用哈希#字首将其設為私有。

私有屬性使用#name文法聲明。#是屬性名稱本身的一部分,應用于聲明和通路屬性。這是一個聲明products為私有财産的示例。

class Cart{
  #products
constructor(){
    this.#products = [];
  }


  addProduct(product){
    this.#products.push(product);
  }


  getTotalPrice(){
    return this.#products.reduce((total, p) => total + p.price, 0);
  }
}
console.log(cart.#products);
//Uncaught SyntaxError: Private field '#products' must be declared in an enclosing class      

Factory Functions

另一種選擇是将對象建立為閉包的集合。

閉包是函數即使在外部函數執行後也可以從外部函數通路變量和參數的能力。看一下cart使用工廠功能建構的下一個對象。

function Cart() {
  const products = [];


  function addProduct(product){
    products.push(product);
  }


  function getTotalPrice(){
    return products.reduce((total, p) => total + p.price, 0);
  }


  return {
   addProduct,
   getTotalPrice
  }
}
const cart = Cart();
cart.addProduct({name: 'orange', price: 1.25});
cart.addProduct({name: 'lemon', price: 1.75});
console.log(cart.getTotalPrice());
//3      

addProduct和getTotalPrice是兩個内部函數products從其父級通路變量。products父級Cart執行後,他們可以通路變量事件。addProduct和getTotalPrice兩個關閉共享同一個私有變量。

Cart 是Factory 函數。

cart使用Factory函數建立的新對象具有products變量private。不能從外部通路它。

console.log(cart.products);
//undefined      

Factory 函數​不需要new關鍵字,但是你可以根據需要使用它。無論是否使用new運算符,它都将傳回相同的對象。

最後的總結​

使用對象文字文法可以輕松地建構對象。

JavaScript提供了兩種創新的方式來建立面向對象的對象。一種是使用原型對象共享常見行為。對象從其他對象繼承。類提供了一種很好的文法來建立此類對象。

繼續閱讀