quiz/js: property flags and descriptors (#468)
This commit is contained in:
parent
d1aac68a31
commit
4da5a4a5e5
|
|
@ -0,0 +1,284 @@
|
|||
---
|
||||
title: What are property flags and descriptors?
|
||||
---
|
||||
|
||||
**TL;DR**
|
||||
|
||||
In JavaScript, property flags and descriptors manage the behavior and attributes of object properties.
|
||||
|
||||
### Property Flags
|
||||
|
||||
Property flags are used to specify the behavior of a property. Here are the available flags:
|
||||
|
||||
- `writable`: Specifies whether the property can be written to. Default is `true`.
|
||||
- `enumerable`: Specifies whether the property is enumerable. Default is `true`.
|
||||
- `configurable`: Specifies whether the property can be deleted or its attributes changed. Default is `true`.
|
||||
- `value`: Specifies the initial value of the property.
|
||||
- `get`: Specifies a getter function for the property.
|
||||
- `set`: Specifies a setter function for the property.
|
||||
|
||||
### Property Descriptors
|
||||
|
||||
These provide detailed information about an object's property, including its value and flags. They are retrieved using `Object.getOwnPropertyDescriptor()` and set using `Object.defineProperty()`.
|
||||
|
||||
### Use Cases
|
||||
|
||||
- **Data Validation**: Validate data before setting, like ensuring age is a positive number.
|
||||
- **Computed Properties**: Derive properties from others, like creating a fullName property from firstName and lastName.
|
||||
- **Encapsulation**: Control data access with getter and setter methods, ensuring private properties.
|
||||
- **Inheritance**: Control property inheritance behavior in class hierarchies, setting non-writable or non-configurable properties.
|
||||
|
||||
---
|
||||
|
||||
In JavaScript, property flags and descriptors are used to manage the behavior and attributes of object properties. These flags and descriptors are essential for understanding how properties are accessed, modified, and inherited.
|
||||
|
||||
## Property Flags
|
||||
|
||||
Property flags are used to specify the behavior of a property. They are set using the `Object.defineProperty()` method, which allows you to define a property on an object with specific attributes. The available property flags are:
|
||||
|
||||
- `writable`: Specifies whether the property can be written to. Default is `true`.
|
||||
- `enumerable`: Specifies whether the property is enumerable. Default is `true`.
|
||||
- `configurable`: Specifies whether the property can be deleted or its attributes changed. Default is `true`.
|
||||
- `value`: Specifies the initial value of the property.
|
||||
- `get`: Specifies a getter function for the property.
|
||||
- `set`: Specifies a setter function for the property.
|
||||
|
||||
## Property Descriptors
|
||||
|
||||
Property descriptors provide detailed information about an object's property, encapsulating its value and flags. They are retrieved using `Object.getOwnPropertyDescriptor()` and set using `Object.defineProperty()`
|
||||
|
||||
```js
|
||||
let user = { name: 'John Doe' };
|
||||
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
|
||||
|
||||
console.log(descriptor); // Output: {value: "John Doe", writable: true, enumerable: true, configurable: true}
|
||||
```
|
||||
|
||||
## Manipulating property flags
|
||||
|
||||
### `writable` Flag
|
||||
|
||||
The `writable` flag specifies whether a property can be written to. When `writable` is `false`, trying to assign value to the property fails silently in non-strict mode, and it throws a `TypeError` in strict mode.
|
||||
|
||||
```js
|
||||
const obj = {};
|
||||
|
||||
Object.defineProperty(obj, 'name', {
|
||||
writable: false,
|
||||
value: 'John Doe',
|
||||
});
|
||||
|
||||
console.log(obj.name); // Output: John Doe
|
||||
obj.name = 'Jane Doe'; // TypeError: Cannot assign to read only property 'name' of object '#<Object>'
|
||||
```
|
||||
|
||||
### `enumerable` Flag
|
||||
|
||||
The `enumerable` flag specifies whether a property is enumerable. The `enumerable flag` is set to `true`, which means the property is visible in a `for...in` loop.
|
||||
|
||||
```js
|
||||
const obj = {};
|
||||
|
||||
Object.defineProperty(obj, 'name', {
|
||||
enumerable: false,
|
||||
value: 'John Doe',
|
||||
});
|
||||
|
||||
for (const prop in obj) {
|
||||
console.log(prop); // Output: nothing
|
||||
}
|
||||
|
||||
const obj1 = {};
|
||||
|
||||
Object.defineProperty(obj1, 'name', {
|
||||
enumerable: true,
|
||||
value: 'John Doe',
|
||||
});
|
||||
|
||||
for (const prop in obj1) {
|
||||
console.log(prop); // Output: name
|
||||
}
|
||||
```
|
||||
|
||||
## `configurable` Flag
|
||||
|
||||
The `configurable` flag specifies whether a property can be deleted or its attributes changed. When `configurable` is `false`, trying to delete or change the property fails silently in non-strict mode, and it throws a `TypeError` in strict mode.
|
||||
|
||||
```js
|
||||
const obj = {};
|
||||
|
||||
Object.defineProperty(obj, 'name', {
|
||||
configurable: false,
|
||||
value: 'John Doe',
|
||||
});
|
||||
|
||||
delete obj.name; // Output: TypeError: Cannot delete property 'name' of #<Object>
|
||||
```
|
||||
|
||||
## `value` Flag
|
||||
|
||||
The `value` flag specifies the initial value of a property.
|
||||
|
||||
```js
|
||||
const obj = {};
|
||||
|
||||
Object.defineProperty(obj, 'name', {
|
||||
value: 'John Doe',
|
||||
});
|
||||
|
||||
console.log(obj.name); // Output: John Doe
|
||||
```
|
||||
|
||||
## `get` Flag
|
||||
|
||||
The `get` flag specifies a getter function for a property.
|
||||
|
||||
```js
|
||||
const obj = {};
|
||||
|
||||
Object.defineProperty(obj, 'name', {
|
||||
get() {
|
||||
return 'John Doe';
|
||||
},
|
||||
});
|
||||
|
||||
console.log(obj.name); // Output: John Doe
|
||||
```
|
||||
|
||||
## `set` Flag
|
||||
|
||||
The `set` flag specifies a setter function for a property.
|
||||
|
||||
```js
|
||||
const obj = {};
|
||||
|
||||
Object.defineProperty(obj, 'name', {
|
||||
set(value) {
|
||||
if (value.length < 3) {
|
||||
throw new Error('Name must have more than 3 characters');
|
||||
}
|
||||
console.log(`Setting name to ${value}`);
|
||||
},
|
||||
});
|
||||
|
||||
obj.name = 'Jane Doe'; // Output: Setting name to Jane Doe
|
||||
obj.name = 'Ja'; // Error: Name must have more than 3 characters
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Data Validation
|
||||
|
||||
You can use property descriptors to validate data before it is set on an object. For example, you can create a `person` object with an `age` property that has a setter function that checks if the `age` is a valid number:
|
||||
|
||||
```js
|
||||
const person = {};
|
||||
|
||||
Object.defineProperty(person, 'age', {
|
||||
set(value) {
|
||||
if (typeof value !== 'number' || value < 0) {
|
||||
throw new Error('Age must be a positive number');
|
||||
}
|
||||
this._age = value;
|
||||
},
|
||||
get() {
|
||||
return this._age;
|
||||
},
|
||||
});
|
||||
|
||||
person.age = 30;
|
||||
console.log(person.age); // Output: 30
|
||||
|
||||
person.age = -10; // Output: Error: Age must be a positive number
|
||||
```
|
||||
|
||||
### Computed Properties
|
||||
|
||||
You can use property descriptors to create computed properties that are derived from other properties on the object. For example, you can create a `fullName` property that is derived from `firstName` and `lastName` properties:
|
||||
|
||||
```js
|
||||
const person = {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
};
|
||||
|
||||
Object.defineProperty(person, 'fullName', {
|
||||
get() {
|
||||
return `${this.firstName} ${this.lastName}`;
|
||||
},
|
||||
set(value) {
|
||||
[this.firstName, this.lastName] = value.split(' ');
|
||||
},
|
||||
});
|
||||
|
||||
console.log(person.fullName); // Output: John Doe
|
||||
person.fullName = 'Jane Smith';
|
||||
console.log(person.firstName); // Output: Jane
|
||||
console.log(person.lastName); // Output: Smith
|
||||
```
|
||||
|
||||
### Encapsulation
|
||||
|
||||
You can use property descriptors to encapsulate data and control access to it. For example, you can create a `BankAccount` class with a `balance` property that is only accessible through getter and setter methods:
|
||||
|
||||
```js
|
||||
class BankAccount {
|
||||
#balance = 0;
|
||||
|
||||
get balance() {
|
||||
return this.#balance;
|
||||
}
|
||||
|
||||
deposit(amount) {
|
||||
this.#balance += amount;
|
||||
}
|
||||
|
||||
withdraw(amount) {
|
||||
if (amount <= this.#balance) {
|
||||
this.#balance -= amount;
|
||||
} else {
|
||||
console.log('Insufficient funds');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const account = new BankAccount();
|
||||
account.deposit(1000);
|
||||
console.log(account.balance); // Output: 1000
|
||||
account.withdraw(500);
|
||||
console.log(account.balance); // Output: 500
|
||||
account.#balance = 10000; // Error: Private field '#balance' must be declared in an enclosing class
|
||||
```
|
||||
|
||||
### Inheritance
|
||||
|
||||
You can use property descriptors to control how properties are inherited in a class hierarchy. For example, you can create a `Vehicle` class with a make property that is non-writable and non-configurable, and a `Car` class that inherits from `Vehicle`:
|
||||
|
||||
```js
|
||||
class Vehicle {
|
||||
constructor(make) {
|
||||
Object.defineProperty(this, 'make', {
|
||||
value: make,
|
||||
writable: false,
|
||||
configurable: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Car extends Vehicle {
|
||||
constructor(make, model) {
|
||||
super(make);
|
||||
this.model = model;
|
||||
}
|
||||
}
|
||||
|
||||
const car = new Car('Toyota', 'Camry');
|
||||
console.log(car.make); // Output: Toyota
|
||||
car.make = 'Honda'; // No effect
|
||||
```
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Object.defineProperty() | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)
|
||||
- [Property flags and descriptors | Javascript.info](https://javascript.info/property-descriptors)
|
||||
- [JavaScript: Property Flags and Descriptors | W3docs.com](https://www.w3docs.com/learn-javascript/property-flags-and-descriptors.html)
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"slug": "what-are-property-flags-and-descriptors",
|
||||
"languages": [],
|
||||
"companies": [],
|
||||
"premium": false,
|
||||
"duration": 5,
|
||||
"published": true,
|
||||
"topics": ["javascript"],
|
||||
"importance": "medium",
|
||||
"difficulty": "medium"
|
||||
}
|
||||
Loading…
Reference in New Issue