>웹 프론트엔드 >JS 튜토리얼 >JavaScript 객체 마스터하기: 실시간 사용자 관리 시스템의 메소드, 속성, 프로토타입 및 __proto__

JavaScript 객체 마스터하기: 실시간 사용자 관리 시스템의 메소드, 속성, 프로토타입 및 __proto__

DDD
DDD원래의
2024-09-26 16:29:30401검색

Mastering JavaScript Objects: Methods, Properties, Prototypes, and __proto__ in Real-Time User Management System

다음은 JavaScript의 다양한 Object 메소드를 실제로 사용하는 방법을 보여주는 실시간 프로젝트 시나리오입니다. 이 시나리오에서는 사용자가 자신의 프로필을 관리할 수 있는 온라인 애플리케이션용 사용자 관리 시스템을 구축하고 있습니다. 제공된 각 방법을 사용하는 방법과 이유를 살펴보겠습니다.


실시간 프로젝트 시나리오: 사용자 관리 시스템

프로젝트 개요:

이 프로젝트에서 사용자는 자신의 프로필을 등록하고 업데이트할 수 있습니다. 우리는 사용자 데이터를 개체에 저장하며 역할별로 사용자 그룹화, 특정 속성 고정, 특정 유효성 검사 보장, 사용자 입력 변환 처리 등 다양한 기능이 필요합니다.


1. Object.sign(): 사용자 데이터 병합

시나리오: 사용자가 프로필을 업데이트하면 업데이트된 데이터를 기존 사용자 개체에 병합해야 합니다.

사용하는 이유: Object.sign()을 사용하면 서로 다른 객체의 속성을 결합할 수 있습니다. 기존 사용자 개체를 업데이트된 값과 쉽게 병합할 수 있습니다.

:

const existingUser = { name: "John", age: 30, role: "admin" };
const updatedData = { age: 31, city: "New York" };

// Merging updated data into the existing user object
const mergedUser = Object.assign({}, existingUser, updatedData);
console.log(mergedUser); 
// Output: { name: "John", age: 31, role: "admin", city: "New York" }

2. Object.create(): 사용자 역할 프로토타이핑

시나리오: 관리자 또는 게스트 등 사용자 역할과 관련된 특정 메서드를 상속하는 사용자 개체를 생성해야 한다고 가정합니다.

사용 이유: Object.create()를 사용하면 프로토타입에서 상속되는 객체를 생성할 수 있으므로 유사한 객체 간에 기능을 재사용할 수 있습니다.

:

const rolePrototype = {
  getRole: function() {
    return this.role;
  }
};

// Creating a user object with role prototype
const adminUser = Object.create(rolePrototype);
adminUser.role = "admin";
console.log(adminUser.getRole()); // Output: "admin"

3. Object.defineProperty(): 읽기 전용 속성 설정

시나리오: 사용자 프로필에서 userID는 생성된 후에 변경할 수 없어야 합니다. 사용자ID가 변경되지 않도록 해야 합니다.

사용 이유: Object.defineProperty()를 사용하면 속성에 쓸 수 있는지, 수정할 수 있는지 등 속성의 특성을 제어할 수 있습니다.

:

const user = { name: "Alice" };
Object.defineProperty(user, 'userID', {
  value: '12345',
  writable: false,  // Making userID read-only
  enumerable: true
});

console.log(user.userID); // Output: 12345
user.userID = '67890'; // This will not change userID
console.log(user.userID); // Output: 12345

물론이죠! 다음은 Object.defineProperty 메소드를 사용하여 enumerable: true와 enumerable: false의 차이를 보여주는 예입니다.

열거 가능한 예: true

이 예에서는 열거 가능 항목이 true로 설정된 속성을 정의합니다. 이 속성은 열거 연산에 표시됩니다.

const user1 = { name: "Alice" };

// Define an enumerable property
Object.defineProperty(user1, 'userID', {
  value: '12345',
  writable: false,  // Making userID read-only
  enumerable: true  // This property is enumerable
});

// Check enumerable properties
console.log("For...in loop:");
for (let key in user1) {
  console.log(key); // Logs: name, userID
}

console.log("Object.keys():", Object.keys(user1)); // Logs: ['name', 'userID']
console.log("JSON.stringify():", JSON.stringify(user1)); // Logs: {"name":"Alice","userID":"12345"}

열거 가능한 예: false

이 예에서는 열거 가능 항목을 false로 설정하여 속성을 정의합니다. 이 속성은 열거 작업에 표시되지 않습니다.

const user2 = { name: "Bob" };

// Define a non-enumerable property
Object.defineProperty(user2, 'userID', {
  value: '67890',
  writable: false,  // Making userID read-only
  enumerable: false // This property is not enumerable
});

// Check enumerable properties
console.log("For...in loop:");
for (let key in user2) {
  console.log(key); // Logs: name (userID is not logged)
}

console.log("Object.keys():", Object.keys(user2)); // Logs: ['name'] (userID is not included)
console.log("JSON.stringify():", JSON.stringify(user2)); // Logs: {"name":"Bob"} (userID is not included)

출력 요약

  • user1(열거 가능: true):

    • for...in 루프에 이름과 사용자 ID를 모두 기록합니다.
    • Object.keys()는 ['name', 'userID']를 반환합니다.
    • JSON.stringify()에는 두 속성이 모두 포함되어 있습니다.
  • user2의 경우(열거 가능: false):

    • for...in 루프에는 이름만 기록합니다.
    • Object.keys()는 ['이름']을 반환합니다.
    • JSON.stringify()에는 name 속성만 포함됩니다.

4. Object.defineProperties(): 여러 속성 설정

시나리오: 새 사용자를 등록할 때 userID와 역할을 모두 정의하려고 합니다. 여기서 userID는 변경할 수 없지만 역할은 쓸 수 있습니다.

사용 이유: Object.defineProperties()는 서로 다른 특성을 가진 여러 속성을 동시에 정의할 때 유용합니다.

:

const newUser = {};
Object.defineProperties(newUser, {
  userID: {
    value: '98765',
    writable: false,
    enumerable: true
  },
  role: {
    value: 'guest',
    writable: true,
    enumerable: true
  }
});

console.log(newUser); 
// Output: { userID: "98765", role: "guest" }

5. Object.entries(): 사용자 데이터 반복

시나리오: 관리자 패널에서 쉽게 볼 수 있도록 사용자 프로필의 모든 키-값 쌍을 표시해야 합니다.

사용 이유: Object.entries()는 객체를 키-값 쌍의 배열로 변환하므로 데이터를 더 쉽게 반복할 수 있습니다.

:

const userProfile = { name: "Alice", age: 28, city: "Paris" };
const entries = Object.entries(userProfile);

entries.forEach(([key, value]) => {
  console.log(`${key}: ${value}`);
});
// Output: 
// name: Alice
// age: 28
// city: Paris

6. Object.freeze(): 사용자 프로필 동결

시나리오: 사용자가 프로필을 제출한 후 우발적인 수정을 방지하기 위해 전체 프로필을 변경할 수 없게 만들고 싶습니다.

사용 이유: Object.freeze()는 객체의 속성을 추가, 제거 또는 변경하는 것을 방지합니다.

:

const userProfile = { name: "John", age: 31, city: "New York" };
Object.freeze(userProfile);

userProfile.city = "San Francisco"; // This change won't be applied
console.log(userProfile.city); // Output: "New York"

7. Object.fromEntries(): 양식 데이터를 객체로 변환

시나리오: 양식 제출에서 사용자 프로필 데이터를 수신한 후 이를 키-값 쌍의 배열로 가져와 다시 객체로 변환해야 합니다.

Why Use It: Object.fromEntries() converts an array of key-value pairs into an object.

Example:

const formData = [['name', 'Alice'], ['age', '28'], ['city', 'Paris']];
const userProfile = Object.fromEntries(formData);

console.log(userProfile); 
// Output: { name: "Alice", age: "28", city: "Paris" }

8. groupBy() (Custom Implementation): Group Users by Role

Scenario: We need to categorize users into different groups based on their roles (e.g., admin, guest).

Why Use It: JavaScript doesn’t have a built-in groupBy() function, but we can create one to group users by a certain property like their role.

Example:

const users = [
  { name: "Alice", role: "admin" },
  { name: "Bob", role: "guest" },
  { name: "Charlie", role: "admin" },
];

const groupBy = (array, key) => {
  return array.reduce((acc, obj) => {
    const keyValue = obj[key];
    if (!acc[keyValue]) acc[keyValue] = [];
    acc[keyValue].push(obj);
    return acc;
  }, {});
};

const groupedUsers = groupBy(users, 'role');
console.log(groupedUsers);
// Output: 
// { 
//   admin: [{ name: "Alice", role: "admin" }, { name: "Charlie", role: "admin" }],
//   guest: [{ name: "Bob", role: "guest" }]
// }

9. Object.hasOwn(): Checking User-Specific Properties

Scenario: Before allowing a user to access a feature, we want to check if they have a specific property like role.

Why Use It: Object.hasOwn() ensures that the property exists directly on the object and not in the prototype chain.

Example:

const user = { name: "Alice", role: "admin" };
console.log(Object.hasOwn(user, 'role')); // true
console.log(Object.hasOwn(user, 'age'));  // false

10. Object.is(): Strict Equality for Profile Comparison

Scenario: We want to strictly compare two profile objects to check if they are exactly the same, including NaN.

Why Use It: Object.is() compares two values and correctly handles special cases, such as comparing NaN.

Example:

const user1 = { name: "John" };
const user2 = { name: "John" };
console.log(Object.is(user1, user2)); // false (different references)
console.log(Object.is(NaN, NaN));     // true

11. Object.keys() and Object.values(): Displaying User Data

Scenario: We want to get an array of keys (property names) or values (property values) from a user's profile.

Why Use It: Object.keys() is used to retrieve the keys, and Object.values() retrieves the values of an object.

Example:

const user = { name: "John", age: 30, city: "New York" };

console.log(Object.keys(user));   // ["name", "age", "city"]
console.log(Object.values(user)); // ["John", 30, "New York"]

12. Object.hasOwnProperty(): Custom Property Check

Scenario: When working with inherited objects, we need to ensure a property exists directly on an object and not on its prototype.

Why Use It: Object.hasOwnProperty() is useful for checking if a property belongs to the object itself, not its prototype chain.

Example:

const user = { name: "Alice" };
console.log(user.hasOwnProperty('name'));  // true
console.log(user.hasOwnProperty('toString'));  // false (inherited from prototype)

Here’s a detailed comparison of similar or related Object methods and additional topics, explaining how they differ in usage and when to use each one. This will help you choose the right method for specific use cases in your project.


1. Object.assign() vs Object.create()

Similarity:

Both Object.assign() and Object.create() are used to create objects.

Differences:

Method Purpose When to Use
Object.assign() Copies the properties of one or more objects into another object. When you need to merge or shallow copy objects (e.g., updating user data or combining settings).
Object.create() Creates a new object with a specified prototype. When you need to set up prototypal inheritance, such as creating users with role-specific behaviors.

Example:

// Object.assign: Merging objects
const target = {};
const source = { name: "John" };
Object.assign(target, source); // { name: "John" }

// Object.create: Inheriting from a prototype
const rolePrototype = { role: "admin" };
const adminUser = Object.create(rolePrototype);
console.log(adminUser.role); // "admin"

2. Object.defineProperty() vs Object.defineProperties()

Similarity:

Both methods define properties with configurable characteristics (e.g., writable, enumerable).

Differences:

Method Purpose When to Use
Object.defineProperty() Defines or modifies a single property on an object. Use when you need fine-grained control over a single property (e.g., making userID read-only).
Object.defineProperties() Defines or modifies multiple properties at once. Use when you need to set up multiple properties with different configurations in one go.

Example:

// Object.defineProperty: Setting a single property
const user = {};
Object.defineProperty(user, 'userID', { value: '123', writable: false });

// Object.defineProperties: Setting multiple properties
Object.defineProperties(user, {
  role: { value: 'admin', writable: true },
  age: { value: 30, enumerable: true }
});

3. Object.entries() vs Object.keys() vs Object.values()

Similarity:

All three methods return information about the properties of an object, but in different forms.

Differences:

Method Purpose When to Use
Object.entries() Returns an array of key-value pairs. Use when you need both keys and values (e.g., iterating over a profile’s data for display).
Object.keys() Returns an array of keys (property names). Use when you need only the property names (e.g., validating fields in a user form).
Object.values() Returns an array of values. Use when you only need the property values (e.g., aggregating numerical values like expenses in a budget app).

Example:

const userProfile = { name: "John", age: 30, city: "New York" };

// Object.entries: Key-value pairs
console.log(Object.entries(userProfile)); // [["name", "John"], ["age", 30], ["city", "New York"]]

// Object.keys: Only keys
console.log(Object.keys(userProfile));    // ["name", "age", "city"]

// Object.values: Only values
console.log(Object.values(userProfile));  // ["John", 30, "New York"]

4. Object.freeze() vs Object.seal()

Similarity:

Both methods control modifications to an object but differ in the level of restriction they impose.

Differences:

Method Purpose When to Use
Object.freeze() Makes an object completely immutable (cannot add, remove, or change properties). Use when you need a strict immutability guarantee, such as locking down a user’s profile after registration.
Object.seal() Prevents adding or removing properties but allows modification of existing ones. Use when you need to prevent property addition/removal but still allow changes to existing properties.

Example:

const user = { name: "Alice", role: "guest" };

// Object.freeze: Completely immutable
Object.freeze(user);
user.name = "Bob"; // Won't change
delete user.role;  // Won't delete

// Object.seal: Modify existing, but can't add/remove
Object.seal(user);
user.name = "Bob";  // Allowed
delete user.role;   // Not allowed

5. Object.hasOwnProperty() vs Object.hasOwn()

Similarity:

Both methods check if an object has a particular property, but there are slight technical differences.

Differences:

Method Purpose When to Use
Object.hasOwnProperty() Checks if a property exists directly on the object (older). Use for older JavaScript codebases where backward compatibility is needed.
Object.hasOwn() Similar to hasOwnProperty() but designed as a more modern and robust method (ES2022). Use in modern JavaScript projects for checking if an object has a direct property (avoids issues with prototypes).

Example:

const user = { name: "John" };

// Object.hasOwnProperty
console.log(user.hasOwnProperty('name')); // true
console.log(user.hasOwnProperty('age'));  // false

// Object.hasOwn
console.log(Object.hasOwn(user, 'name')); // true
console.log(Object.hasOwn(user, 'age'));  // false

6. Object.is() vs ===

Similarity:

Both are used for comparing values, but they handle special cases differently.

Differences:

Method Purpose When to Use
Object.is() Compares two values, with special handling for NaN, -0, and +0. Use when you need strict equality, especially when comparing special values like NaN or zero.
=== Standard strict equality check. Use for typical strict equality checks, but be aware that NaN === NaN is false, and -0 === +0 is true.

Example:

console.log(NaN === NaN);      // false
console.log(Object.is(NaN, NaN)); // true

console.log(-0 === +0);        // true
console.log(Object.is(-0, +0));   // false

7. Object.fromEntries() vs Object.entries()

Similarity:

Both methods convert between objects and key-value pairs, but in opposite directions.

Differences:

Method Purpose When to Use
Object.entries() Converts an object into an array of key-value pairs. Use when you need to iterate or manipulate key-value pairs from an object.
Object.fromEntries() Converts an array of key-value pairs into an object. Use when you receive data as an array of pairs (e.g., form data) and need to transform it back into an object.

Example:

const userProfile = { name: "Alice", age: 28 };
const entries = Object.entries(userProfile);

// From key-value pairs to object
const newProfile = Object.fromEntries(entries);
console.log(newProfile); // { name: "Alice", age: 28 }

8. Object.keys() vs Object.getOwnPropertyNames()

Similarity:

Both methods retrieve the keys of an object.

Differences:

Method Purpose When to Use
Object.keys() Returns only the enumerable (visible) property keys of an object. Use when you only need the enumerable properties (common for iterating over user objects).
Object.getOwnPropertyNames() Returns both enumerable and non-enumerable property keys. Use when you need all properties, including non-enumerable ones (e.g., metadata, hidden properties).

Example:

const user = { name: "Alice" };
Object.defineProperty(user, 'role', { value: 'admin', enumerable: false });

console.log(Object.keys(user)); // ["name"]
console.log(Object.getOwnPropertyNames(user)); // ["name", "role"]

In this enhanced version, we will dive deeper into creating your own methods and properties in JavaScript objects, as well as using prototypes. This will help provide even more flexibility and power in a real-time project scenario.

  1. Creating Custom Methods and Properties in JavaScript Objects:

JavaScript objects allow you to define your own properties and methods. This can be useful when you want to extend the capabilities of an object to perform specific tasks related to your application.

Example Scenario: Adding a Role Check in the User Management System

Imagine you're building a user management system where each user has specific roles, like "admin" or "editor." You want to create a method to check the user's role.

   // Create a user object
   const user = {
     name: 'John Doe',
     role: 'admin',

     // Define a custom method to check the role
     checkRole() {
       if (this.role === 'admin') {
         return `${this.name} has admin access`;
       } else {
         return `${this.name} is a regular user`;
       }
     }
   };

   console.log(user.checkRole()); // John Doe has admin access

Here, we defined a custom method checkRole within the user object to determine if the user has admin privileges. This can be expanded to check for multiple roles and permissions.

  1. Using Object Prototypes to Extend Functionality

JavaScript prototypes allow you to add properties or methods to all instances of a particular object type. For example, you can add methods to an object prototype, so all objects created with a specific constructor can access that method.

Example Scenario: Extending the User Object with Prototypes

Let’s say you want all users in the system to have access to a method that can toggle their roles between "admin" and "user" without redefining it for every user object.

   // Define the constructor function for User
   function User(name, role) {
     this.name = name;
     this.role = role;
   }

   // Add a method to User prototype to toggle role
   User.prototype.toggleRole = function() {
     this.role = this.role === 'admin' ? 'user' : 'admin';
   };

   // Create user instances
   const user1 = new User('Alice', 'user');
   const user2 = new User('Bob', 'admin');

   console.log(user1.role); // user
   user1.toggleRole(); // Toggling role
   console.log(user1.role); // admin

In this example, toggleRole is added to the User.prototype, meaning any user instance created will inherit this method. This is useful for reusing code and maintaining DRY (Don't Repeat Yourself) principles in a real-time system.

By mastering custom methods, properties, and prototypes, you can create more advanced, maintainable, and efficient systems tailored to the needs of your application, such as the User Management System scenario described.


1. __proto__ (Dunder Proto or Double Underscore Proto)

In JavaScript, __proto__ and prototype are two related but distinct concepts that often confuse developers, especially when working with objects and inheritance. Here's a breakdown:

  • __proto__ is an internal property of an object that points to its prototype.
  • It refers to the actual prototype that was used to create the object instance.
  • This is part of JavaScript's prototypal inheritance system. When you try to access a property or method on an object, if it's not found directly on the object, JavaScript will look up the prototype chain via __proto__.

Key Points:

  • Every object has a __proto__ property (except Object.prototype, which is the root of the prototype chain).
  • __proto__ is a reference to the object's prototype, which is shared among all instances of that object.

Example:

const person = {
  name: 'Alice',
  sayHello() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

// Creating another object that inherits from 'person'
const student = {
  __proto__: person,
  grade: 'A'
};

console.log(student.name); // Inherited from 'person' => 'Alice'
student.sayHello();        // Inherited method => 'Hello, my name is Alice'

In this example, student doesn't directly have the name property or the sayHello method, but it can access them because its __proto__ points to person.

2. prototype

  • prototype is a property of constructor functions (functions used to create objects).
  • When you create an object using a constructor function (like using new), the newly created object’s __proto__ property is set to point to the constructor’s prototype object.
  • Only functions have the prototype property, and it's used to set up inheritance.

Key Points:

  • Functions (that act as constructors) have a prototype property.
  • Instances of that function will have their __proto__ set to the prototype of the constructor.
  • You can add properties and methods to the constructor’s prototype, and they will be shared across all instances.

Example:

function User(name) {
  this.name = name;
}

// Adding a method to the User prototype
User.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}`);
};

// Creating new user instances
const user1 = new User('John');
const user2 = new User('Jane');

// Both instances inherit the sayHello method from User.prototype
user1.sayHello(); // 'Hello, my name is John'
user2.sayHello(); // 'Hello, my name is Jane'

In this case:

  • The User function has a prototype property, and any instance created by new User() will have its __proto__ linked to User.prototype.
  • The sayHello method is defined on User.prototype, so both user1 and user2 can use it without having a separate copy of it.

Key Differences between __proto__ and prototype:

Aspect __proto__ prototype
What it refers to The prototype of an object instance The prototype of a constructor function
Where it’s used On all objects On functions (constructor functions)
Functionality Used to access the prototype chain of an object Used to define methods/properties shared by instances
Access Can be accessed directly (e.g., obj.__proto__) Used during object creation with new keyword

Example Illustrating Both:

// Constructor function
function Animal(name) {
  this.name = name;
}

// Adding a method to the prototype of the constructor
Animal.prototype.makeSound = function() {
  console.log(`${this.name} makes a sound`);
};

// Creating an instance
const dog = new Animal('Rex');

// dog.__proto__ points to Animal.prototype
console.log(dog.__proto__ === Animal.prototype); // true

// dog can access the method from Animal.prototype
dog.makeSound(); // Rex makes a sound
  • Animal is the constructor function.
  • Animal.prototype contains the method makeSound.
  • When dog is created, dog.__proto__ is set to Animal.prototype, allowing dog to inherit the makeSound method.

Conclusion

  • __proto__ is an internal property of every object that points to its prototype. It's used to look up properties in the prototype chain.
  • prototype is a property of functions (especially constructor functions) that allows you to add methods and properties that will be inherited by all instances created from the constructor.

By understanding the difference between __proto__ and prototype, you can take full advantage of JavaScript's prototypal inheritance system for efficient object creation and method sharing.

위 내용은 JavaScript 객체 마스터하기: 실시간 사용자 관리 시스템의 메소드, 속성, 프로토타입 및 __proto__의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.