JavaScript Create Object –JSでオブジェクトを定義する方法

オブジェクトは、オブジェクト指向プログラミングにおけるカプセル化の主要な単位です。この記事では、JavaScriptでオブジェクトを作成するいくつかの方法について説明します。彼らです:

  • オブジェクトリテラル
  • Object.create()
  • クラス
  • 工場機能

オブジェクトリテラル

まず、データ構造とオブジェクト指向オブジェクトを区別する必要があります。データ構造には公開データがあり、動作はありません。つまり、メソッドがないということです。

オブジェクトリテラル構文を使用して、このようなオブジェクトを簡単に作成できます。次のようになります。

const product = { name: 'apple', category: 'fruits', price: 1.99 } console.log(product);

JavaScriptのオブジェクトは、キーと値のペアの動的なコレクションです。キーは常に文字列であり、コレクション内で一意である必要があります。値は、プリミティブ、オブジェクト、または関数にすることができます。

ドットまたは正方形の表記を使用してプロパティにアクセスできます。

console.log(product.name); //"apple" console.log(product["name"]); //"apple"

値が別のオブジェクトである例を次に示します。

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 }

Object.create

次に、動作を伴うオブジェクト、オブジェクト指向オブジェクトを実装する方法を見てみましょう。

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); } }

今回は、プロパティの値が addProduct関数であることに注意してください。短縮メソッド構文と呼ばれる短い形式を使用して、前のオブジェクトを作成することもできます。

const cartPrototype = { addProduct(product){/*code*/}, getTotalPrice(){/*code*/} }

cartPrototype二つの方法によって表される一般的な振る舞いを続けるプロトタイプオブジェクトである、addProductgetTotalPrice。この動作を継承する他のオブジェクトを構築するために使用できます。

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

cartオブジェクトがありcartPrototype、そのプロトタイプとして。それはそれから振る舞いを継承します。cartプロトタイプオブジェクトを指す隠しプロパティがあります。

オブジェクトでメソッドを使用する場合、そのメソッドは最初に、プロトタイプではなくオブジェクト自体で検索されます。

この

thisオブジェクトのデータにアクセスして変更するために呼び出される特別なキーワードを使用していることに注意してください。

関数はJavaScriptの独立した動作単位であることを忘れないでください。それらは必ずしもオブジェクトの一部ではありません。その場合、関数が同じオブジェクト上の他のメンバーにアクセスできるようにする参照が必要です。this関数コンテキストです。他のプロパティへのアクセスを提供します。

データ

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

Both the cart1 and cart2 objects inheriting the common behavior from the cartPrototype also share the same data. We don’t want that. Prototypes should be used to share behavior, not data.

Class

The prototype system is not a common way of building objects. Developers are more familiar with building objects out of classes.

The class syntax allows a more familiar way of creating objects sharing a common behavior. It still creates the same prototype behind the scene but the syntax is clearer and we also avoid the previous data-related issue. The class offers a specific place to define the data distinct for each object.

Here is the same object created using the class sugar syntax:

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

Notice that the class has a constructor method that initialized that data distinct for each new object. The data in the constructor is not shared between instances. In order to create a new instance, we use the new keyword.

I think the class syntax is more clear and familiar to most developers. Nevertheless, it does a similar thing, it creates a prototype with all the methods and uses it to define new objects. The prototype can be accessed with Cart.prototype.

It turns out that the prototype system is flexible enough to allow the class syntax. So the class system can be simulated using the prototype system.

Private Properties

The only thing is that the products property on the new object is public by default.

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

We can make it private using the hash # prefix.

Private properties are declared with #name syntax. # is a part of the property name itself and should be used for declaring and accessing the property. Here is an example of declaring products as a private property:

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

Another option is to create objects as collections of closures.

Closure is the ability of a function to access variables and parameters from the other function even after the outer function has executed. Take a look at the cart object built with what is called a factory function.

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 and getTotalPrice are two inner functions accessing the variable products from their parent. They have access to the products variable event after the parent Cart has executed. addProduct and getTotalPrice are two closures sharing the same private variable.

Cart is a factory function.

The new object cart created with the factory function has the products variable private. It cannot be accessed from the outside.

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

Factory functions don’t need the new keyword but you can use it if you want. It will return the same object no matter if you use it or not.

Recap

Usually, we work with two types of objects, data structures that have public data and no behavior and object-oriented objects that have private data and public behavior.

Data structures can be easily built using the object literal syntax.

JavaScript offers two innovative ways of creating object-oriented objects. The first is using a prototype object to share the common behavior. Objects inherit from other objects. Classes offer a nice sugar syntax to create such objects.

The other option is to define objects are collections of closures.

For more on closures and function programming techniques check out my book series Functional Programming with JavaScript and React.

The Functional Programming in JavaScript book is coming out.