您的位置:首页 > Web前端 > JavaScript

JavaScript创建对象–如何在JS中定义对象

2020-08-19 23:05 489 查看

Objects are the main unit of encapsulation in Object-Oriented Programming. In this article, I will describe several ways to build objects in JavaScript. They are:

对象是面向对象编程中封装的主要单元。 在本文中,我将介绍几种使用JavaScript构建对象的方法。 他们是:

  • Object literal

    对象文字
  • Object.create()

    Object.create()
  • Classes

    班级
  • Factory functions

    工厂功能

对象文字 (Object Literal)

First, we need to make a distinction between data structures and object-oriented objects. Data structures have public data and no behavior. That means they have no methods.

首先,我们需要区分数据结构和面向对象的对象。 数据结构具有公共数据,没有任何行为。 那意味着他们没有方法。

We can easily create such objects using the object literal syntax. It looks like this:

我们可以使用对象文字语法轻松创建此类对象。 看起来像这样:

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

console.log(product);

Objects in JavaScript are dynamic collections of key-value pairs. The key is always a string and has to be unique in the collection. The value can a primitive, an object, or even a function.

JavaScript中的对象是键值对的动态集合。 密钥始终是字符串,并且在集合中必须唯一。 该值可以是基元,对象甚至函数。

We can access a property using the dot or the square notation.

我们可以使用点或正方形符号访问属性。

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

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

Here is an example where the value is another object.

这是一个值是另一个对象的示例。

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

The value of the

carbs
property is a new object. Here is how we can access the
carbs
property.

carbs
属性的值是一个新对象。 这是我们如何访问
carbs
属性的方法。

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

简写属性名称 (Shorthand Property Names)

Consider the case where we have the values of our properties stored in variables.

考虑将属性值存储在变量中的情况。

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

JavaScript supports what is called the shorthand property names. It allows us to create an object using just the name of the variable. It will create a property with the same name. The next object literal is equivalent to the previous one.

JavaScript支持所谓的速记属性名称。 它允许我们仅使用变量名来创建对象。 它将创建一个具有相同名称的属性。 下一个对象文字与上一个相同。

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

对象创建 (Object.create)

Next, let's look at how to implement objects with behavior, object-oriented objects.

接下来,让我们看看如何使用行为,面向对象的对象来实现对象。

JavaScript has what is called the prototype system that allows sharing behavior between objects. The main idea is to create an object called the prototype with a common behavior and then use it when creating new objects.

JavaScript具有所谓的原型系统,该系统允许对象之间共享行为。 主要思想是创建一个具有常见行为的对象称为原型,然后在创建新对象时使用它。

The prototype system allows us to create objects that inherit behavior from other objects.

原型系统允许我们创建从其他对象继承行为的对象。

Let’s create a prototype object that allows us to add products and get the total price from a shopping cart.

让我们创建一个原型对象,使我们能够添加产品并从购物车中获得总价。

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

Notice that this time the value of the property

addProduct
is a function. We can also write the previous object using a shorter form called the shorthand method syntax.

请注意,这次属性

addProduct
的值是一个函数。 我们还可以使用称为“简写方法”语法的较短形式来编写先前的对象。

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

The

cartPrototype
is the prototype object that keeps the common behavior represented by two methods,
addProduct
and
getTotalPrice
. It can be used to build other objects inheriting this behavior.

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

The

cart
object has
cartPrototype
as its prototype. It inherits the behavior from it.
cart
has a hidden property that points to the prototype object.

cart
对象具有
cartPrototype
作为其原型。 它从中继承行为。
cart
具有指向原型对象的隐藏属性。

When we use a method on an object, that method is first searched on the object itself rather than on its prototype.

当我们在对象上使用方法时,首先在对象本身而不是其原型上搜索该方法。

这个 (this)

Note that we are using a special keyword called

this
to access and modify the data on the object.

请注意,我们正在使用一个名为

this
的特殊关键字来访问和修改对象上的数据。

Remember that functions are independent units of behavior in JavaScript. They are not necessarily part of an object. When they are, we need to have a reference that allows the function to access other members on the same object.

this
is the function context. It gives access to other properties.

请记住,函数是JavaScript中行为的独立单元。 它们不一定是对象的一部分。 当它们存在时,我们需要有一个引用,该引用允许该函数访问同一对象上的其他成员。

this
是函数上下文。 它提供对其他属性的访问。

数据 (Data)

You may wonder why we haven’t defined and initialized the

products
property on the prototype object itself.

您可能想知道为什么我们没有在原型对象本身上定义和初始化

products
属性。

We shouldn't do that. Prototypes should be used to share behavior, not data. Sharing data will lead to having the same products on several cart objects. Consider the code below:

我们不应该那样做。 原型应用于共享行为,而不是数据。 共享数据将导致在多个购物车对象上拥有相同的产品。 考虑下面的代码:

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.

无论是

cart1
cart2
对象继承自共同行为
cartPrototype
也有着相同的数据。 我们不想要那个。 原型应用于共享行为,而不是数据。

类 (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:

这是使用类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

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.

注意,该类具有一个构造函数方法,该方法针对每个新对象初始化该数据。 实例之间不共享构造函数中的数据。 为了创建一个新实例,我们使用

new
关键字。

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
.

我认为对于大多数开发人员而言,类语法更加清晰和熟悉。 但是,它做类似的事情,它使用所有方法创建一个原型,并使用它来定义新对象。 可以使用

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.

唯一的事情是,默认情况下,新对象上的

products
属性是公共的。

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:

私有属性使用

#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)

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.

闭包是一个函数即使在外部函数执行后也可以从另一个函数访问变量和参数的能力。 看一下使用工厂函数构建的

cart
对象。

function Cart() {
const products = [];

functionaddProduct(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.

addProduct
getTotalPrice
是两个内部函数,用于从其父级访问变量
products
。 他们在执行父
Cart
后可以访问
products
变量事件。
addProduct
getTotalPrice
是两个共享同一私有变量的闭包。

Cart
is a factory function.

Cart
是出厂功能。

The new object

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

使用工厂功能创建的新对象

cart
具有
products
变量private。 不能从外部访问它。

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.

工厂函数不需要

new
关键字,但是您可以根据需要使用它。 无论是否使用它,它都会返回相同的对象。

回顾 (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.

JavaScript提供了两种创新的方式来创建面向对象的对象。 第一种是使用原型对象共享常见行为。 对象从其他对象继承。 类提供了很好的糖语法来创建此类对象。

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.

有关闭包和函数编程技术的更多信息,请参阅我的书系列《 使用JavaScript和React进行函数式编程》

The Functional Programming in JavaScript book is coming out.

JavaScript中 功能编程》 一书即将出版。

翻译自: https://www.freecodecamp.org/news/javascript-create-object-how-to-define-objects-in-js/

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: