- Object properties in JavaScript
- Kinds of properties
- Named data properties (“properties”)
- Named accessor properties
- Internal properties
- Property attributes
- Default values
- Property descriptors
- Functions that use property descriptors
- Enumerability
- Operations affected by enumerability
- Operations that ignore enumerability
- Best practices
- Conclusion
- References
- Manage blob properties and metadata with JavaScript
- About properties and metadata
- Set blob http headers
- Set metadata
- Get blob properties
- Resources
- REST API operations
- Code samples
- Client library resources
Object properties in JavaScript
Update 2013-08-25: Blog post “Protecting objects in JavaScript” (Object.preventExtensions(), Object.seal(), Object.freeze()).
Properties determine the state of an object in JavaScript. This blog post examines in detail how they work.
Kinds of properties
JavaScript has three different kinds of properties: named data properties, named accessor properties and internal properties.
Named data properties (“properties”)
“Normal” properties of objects map string names to values. For example, the following object obj has a data property whose name is the string "prop" and whose value is the number 123.
console.log(obj.prop); // 123 console.log(obj["prop"]); // 123
obj.prop = "abc"; obj["prop"] = "abc";
Named accessor properties
Alternatively, getting and setting a property value can be handled via functions. Those functions are called accessor functions. A function that handles getting is called a getter. A function that handles setting is called a setter.
> obj.prop 'Getter' > obj.prop = 123; Setter: 123
Internal properties
- The internal property [[Prototype]] points to the prototype of an object. It can be read via Object.getPrototypeOf(). Its value can only be set by creating a new object that has a given prototype, e.g. via Object.create() or __proto__[1].
- The internal property [[Extensible]] determines whether or not one can add properties to an object. It can be read via Object.isExtensible(). It can be set false via Object.preventExtensions(). Once false, it cannot be become true again.
Property attributes
All of the state of a property, both its data and its meta-data, is stored in attributes. They are fields that a property has, much like an object has properties. Attribute keys are often written in double brackets.
The following attributes are specific to named data properties:
- [[Value]] hold the property’s value, its data.
- [[Writable]] holds a boolean indicating whether the value of a property can be changed.
- [[Get]] holds the getter, a function that is called when a property is read. That function computes the result of the read access.
- [[Set]] holds the setter, a function that is called when a property is set to a value. The function receives that value as a parameter.
- [[Enumerable]] holds a boolean. Making a property non-enumerable hides it from some operations (see below).
- [[Configurable]] holds a boolean. If false, you cannot delete a property, change any of its attributes (except [[Value]]) or convert between data property and accessor property. In other words, [[Configurable]] controls the writability of a property’s meta-data.
Default values
Attribute key | Default value |
[[Value]] | undefined |
[[Get]] | undefined |
[[Set]] | undefined |
[[Writable]] | false |
[[Enumerable]] | false |
[[Configurable]] | false |
These defaults are especially important for property descriptors (see below).
Property descriptors
A property descriptor encodes the attributes of a property as an object. Each of the properties of that object corresponds to an attribute. For example, the following is the descriptor of a read-only property whose value is 123:
< get: function () < return 123 >, enumerable: true, configurable: false >
Functions that use property descriptors
- Object.defineProperty(obj, propName, propDesc)
Create or change a property on obj whose name is propName and whose attributes are specified via propDesc. Return the modified object. Example:
var obj = Object.defineProperty(<>, "foo", < value: 123, enumerable: true // writable and configurable via defaults >);
var obj = Object.defineProperties(<>, < foo: < value: 123, enumerable: true >, bar: < value: "abc", enumerable: true >>);
var obj = Object.create(Object.prototype, < foo: < value: 123, enumerable: true >, bar: < value: "abc", enumerable: true >>);
> Object.getOwnPropertyDescriptor(Object.prototype, "toString") < value: [Function: toString], writable: true, enumerable: false, configurable: true >> Object.getOwnPropertyDescriptor(<>, "toString") undefined
Enumerability
This section explains which operations are influenced by enumerability and which aren’t. Below, we are assuming that the following definitions have been made:
var proto = Object.defineProperties(<>, < foo: < value: 1, enumerable: true >, bar: < value: 2, enumerable: false >>); var obj = Object.create(proto, < baz: < value: 1, enumerable: true >, qux: < value: 2, enumerable: false >>);
> Object.getPrototypeOf(<>) === Object.prototype true
Operations affected by enumerability
The for-in loop iterates over the names of all enumerable properties, including inherited ones (note that none of the non-enumerable properties of Object.prototype show up):
> for (var x in obj) console.log(x); baz foo
Object.keys() returns the names of all own (non-inherited) enumerable properties:
If you want the names of all own properties, you need to use Object.getOwnPropertyNames() (see example below).
Operations that ignore enumerability
> "toString" in obj true > obj.toString [Function: toString]
> Object.getOwnPropertyNames(obj) [ 'baz', 'qux' ] > obj.hasOwnProperty("qux") true > obj.hasOwnProperty("toString") false > Object.getOwnPropertyDescriptor(obj, "qux") < value: 2, writable: false, enumerable: false, configurable: false >> Object.getOwnPropertyDescriptor(obj, "toString") undefined
obj.propName = value obj["propName"] = value delete obj.propName delete obj["propName"] Object.defineProperty(obj, propName, desc) Object.defineProperties(obj, descObj)
Best practices
The general rule is that properties created by the system are non-enumerable, while properties created by users are enumerable:
> Object.keys([]) [] > Object.getOwnPropertyNames([]) [ 'length' ] > Object.keys(['a']) [ '0' ]
> Object.keys(Object.prototype) [] > Object.getOwnPropertyNames(Object.prototype) [ hasOwnProperty', 'valueOf', 'constructor', 'toLocaleString', 'isPrototypeOf', 'propertyIsEnumerable', 'toString' ]
Thus, for your code, you should ignore enumerability. You normally shouldn’t add properties to built-in prototypes and objects, but if you do, you should make them non-enumerable to avoid breaking code.
As we have seen, non-enumerability mostly benefits for-in and ensures that legacy code using it won’t break. The non-enumerable properties create the illusion that for-in only iterates over the user-created own properties of an object. In your code, you should avoid for-in if you can [3].
If you use objects as maps from strings to values, you should only work with own properties and ignore enumerability. But there are more pitfalls for this use case [4].
Conclusion
In this post, we have examined the nature of properties, which is defined via so-called attributes. Note that actual JavaScript implementations do not necessarily organize properties via attributes, they are mainly an abstraction used by the ECMAScript specification. But one that is sometimes visible in the language itself, for example in property descriptors.
Further reading on 2ality:
- Read “JavaScript properties: inheritance and enumerability” for more information on how inheritance and enumerability affect property-related operations.
- Read “JavaScript inheritance by example” for an introduction to JavaScript inheritance.
References
Manage blob properties and metadata with JavaScript
In addition to the data they contain, blobs support system properties and user-defined metadata. This article shows how to manage system properties and user-defined metadata with the Azure Storage client library for JavaScript.
About properties and metadata
- System properties: System properties exist on each Blob storage resource. Some of them can be read or set, while others are read-only. Under the covers, some system properties correspond to certain standard HTTP headers. The Azure Storage client library for JavaScript maintains these properties for you.
- User-defined metadata: User-defined metadata consists of one or more name-value pairs that you specify for a Blob storage resource. You can use metadata to store additional values with the resource. Metadata values are for your own purposes only, and don’t affect how the resource behaves. Metadata name/value pairs are valid HTTP headers and should adhere to all restrictions governing HTTP headers. For more information about metadata naming requirements, see Metadata names.
Blob index tags also provide the ability to store arbitrary user-defined key/value attributes alongside an Azure Blob storage resource. While similar to metadata, only blob index tags are automatically indexed and made searchable by the native blob service. Metadata cannot be indexed and queried unless you utilize a separate service such as Azure Search.
Set blob http headers
The following code example sets blob HTTP system properties on a blob.
To set the HTTP properties for a blob, create a BlobClient then call BlobClient.setHTTPHeaders. Review the BlobHTTPHeaders properties to know which HTTP properties you want to set. Any HTTP properties not explicitly set are cleared.
/* properties= < blobContentType: 'text/plain', blobContentLanguage: 'en-us', blobContentEncoding: 'utf-8', // all other http properties are cleared >*/ async function setHTTPHeaders(blobClient, headers)
Set metadata
You can specify metadata as one or more name-value pairs on a blob or container resource. To set metadata, create a BlobClient then send a JSON object of name-value pairs with
The following code example sets metadata on a blob.
/* metadata= < reviewedBy: 'Bob', releasedBy: 'Jill', >*/ async function setBlobMetadata(blobClient, metadata)
To read the metadata, get the blob’s properties (shown below), specifically referencing the metadata property.
Get blob properties
The following code example gets a blob’s system properties, including HTTP headers and metadata, and displays those values.
async function getProperties(blobClient) < const properties = await blobClient.getProperties(); console.log(blobClient.name + ' properties: '); for (const property in properties) < switch (property) < // nested properties are stringified and returned as strings case 'metadata': case 'objectReplicationRules': console.log(` $: $`); break; default: console.log(` $: $`); break; > > >
The output for these console.log lines looks like:
my-blob.txt properties: lastModified: Thu Apr 21 2022 13:02:53 GMT-0700 (Pacific Daylight Time) createdOn: Thu Apr 21 2022 13:02:53 GMT-0700 (Pacific Daylight Time) metadata: objectReplicationPolicyId: undefined objectReplicationRules: <> blobType: BlockBlob copyCompletedOn: undefined copyStatusDescription: undefined copyId: undefined copyProgress: undefined copySource: undefined copyStatus: undefined isIncrementalCopy: undefined destinationSnapshot: undefined leaseDuration: undefined leaseState: available leaseStatus: unlocked contentLength: 19 contentType: text/plain etag: "0x8DA23D1EBA8E607" contentMD5: undefined contentEncoding: utf-8 contentDisposition: undefined contentLanguage: en-us cacheControl: undefined blobSequenceNumber: undefined clientRequestId: 58da0441-7224-4837-9b4a-547f9a0c7143 requestId: 26acb38a-001e-0046-27ba-55ef22000000 version: 2021-04-10 date: Thu Apr 21 2022 13:02:52 GMT-0700 (Pacific Daylight Time) acceptRanges: bytes blobCommittedBlockCount: undefined isServerEncrypted: true encryptionKeySha256: undefined encryptionScope: undefined accessTier: Hot accessTierInferred: true archiveStatus: undefined accessTierChangedOn: undefined versionId: undefined isCurrentVersion: undefined tagCount: undefined expiresOn: undefined isSealed: undefined rehydratePriority: undefined lastAccessed: undefined immutabilityPolicyExpiresOn: undefined immutabilityPolicyMode: undefined legalHold: undefined errorCode: undefined body: true _response: [object Object] objectReplicationDestinationPolicyId: undefined objectReplicationSourceProperties:
Resources
To learn more about how to manage system properties and user-defined metadata using the Azure Blob Storage client library for JavaScript, see the following resources.
REST API operations
The Azure SDK for JavaScript contains libraries that build on top of the Azure REST API, allowing you to interact with REST API operations through familiar JavaScript paradigms. The client library methods for managing system properties and user-defined metadata use the following REST API operations: