Extensible object which doesn't allow existing properties to be changed? - javascript

JavaScript objects can be sealed, which prevents new properties from being added and existing properties from being removed or reconfigured, but the properties remain writable. They can also be frozen, which is sealed plus all properties become non-writable.
Conspicuously absent is the ability to make the existing properties read-only, non-removable, non-configurable while leaving the object extensible. This would be useful to prevent accidental trampling on an object's properties while allowing other code to augment it's properties.
What's the best or idiomatic way to create such an object using vanilla JavaScript?

For instance you could use a getter to set your properties like:
var object = {get property(){return "value"} };
making them read only, meaning you can't change its value by assignment anymore as in:
object.property = "other";
since it has no setter. So object.property will continue to return "value", but you can still delete the 'property' key using the deleteoperator.

Simply loop through the object's properties and make each one non-writable and non-configurable:
var obj = { "foo": 1, "bar": 3 };
Object.defineProperty(obj, "baz", { set: a=>a });
Object.getOwnPropertyNames(obj).forEach(function(name) {
var desc = Object.getOwnPropertyDescriptor(obj, name);
desc.configurable = false;
// make the property non-writable if it is not an accessor property
if(!desc.set && !desc.get) { desc.writable = false; }
Object.defineProperty(obj, name, desc);
});
Note that this doesn't make accessor properties defined by set and get methods "non-writable" since the notion of writability doesn't apply to an accessor property -- storing into an accessor property runs the setter.

Related

Why can't we mutate prototype of Built-in Function Constructors?

String.prototype = {};
String.prototype; //returns original value, i.e hasn't changed
function my_func(){};
my_func.prototype = {};
my_func.prototype; // returns {}, i.e has changed.
Why hasn't String.prototype changed?
You can't change it because the prototype property of String is non-writable and non-configurable per the spec which states:
String.prototype
The initial value of String.prototype is the intrinsic object %StringPrototype%.
This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false )
Your own object's prototype is writable which you can see with Object.getOwnPropertyDescriptor():
// String
console.log("String",
Object.getOwnPropertyDescriptor(String, 'prototype'))
// Custom Object
function my_func(){};
console.log("Your own object",
Object.getOwnPropertyDescriptor(my_func, 'prototype'))
Writable means:
writable
true if and only if the value associated with the property may be changed (data descriptors only).
You also can't make it writable because it is non-configurable:
configurable
true if and only if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object.
From: MDN
This doesn't mean you can't change the prototype object, you just can't delete or replace it with something else. For example you can add to it (not that I'm recommending this):
String.prototype.init = function (){
return this.slice(0, 1).toUpperCase()
}
h = "hello"
console.log(h.init())

How to access the Symbol(Symbol.toStringTag) property?

I'm looking to read the data from the Symbol(Symbol.toStringTag) field of an object - in this case, it should return "Array Iterator".
This does not work
a.__proto__["Symbol(Symbol.toStringTag)"]
Symbol(Symbol.toStringTag) indicates that the property is a symbol. Symbols are unique values and you can only reference such a property if you already have a reference to the symbol.
Luckily Symbol.toStringTag is a well-known symbol, so you can just reference it:
a[Symbol.toStringTag]
Here is an example where you cannot access the property directly:
var obj = (function() {
// prop is a unique value
var prop = Symbol();
return {[prop]: 42};
}());
There is no way to access obj[<the symbol>] directly since we don't have access to prop. You can still iterate over the properties though.

Javascript: Why are getters used over methods?

So I'm wondering what the difference is between methods and getters?
Reading the Mozilla documentation:
Sometimes it is desirable to allow access to a property that returns a
dynamically computed value, or you may want to reflect the status of
an internal variable without requiring the use of explicit method
calls
But don't they achieve the same thing? Why is one better than the other?
Why is saying:
var obj = {
log: ['a', 'b', 'c'],
get latest() {
if (this.log.length == 0) {
return undefined;
}
return this.log[this.log.length - 1];
}
}
console.log(obj.latest);
Sometimes preferable to just having a latest() method and calling obj.latest(). If the code is run in both cases, then what's the point? Aren't they both dynamic?
The documentation also reads:
Getters give you a way to define a property of an object, but they do
not calculate the property's value until it is accessed.
What exactly is the difference between 'accessed' and 'called'? A method isn't run until it's called, just as a property isn't accessed until its included? So where does the significance lie?
Note, the following is an opinion.
A getter is for retrieving the value of a specific property, and it allows your object to have dynamic properties based on the value of other properties but otherwise behave just as you would expect properties to behave. The getter allows you to define a method that is called when you access that property, even though you access it with normal object property accessors, myObject.dynamicProp and myObject['dynamicProp'].
Getters are provided for exactly that purpose. If that's the intent of the method you are writing, then use the getter syntax.
Methods exist to do other things that are verbal, action oriented, and not to simply return some property of the object you're writing.
You can always write a method to be a getter, but why?
So it depends on your intent. Getters specify intent, and if your intent is to provide a dynamic property that behaves just like an object property then use a getter over a method.
Hope that helps!
Updated:
While there is technical nuance to be understood here, and though I've leaned toward using the getters above, I'd say, after further reflection, that the approach you wish to take is up to you based on:
(1) Do you want to use normal accessors to access this property? Are you ok with dynamic properties that hide some logic? (They don't really 'hide' the logic per se, but they look just like normal props to the caller). Then use the get().
(2) Do you want to make it obvious and more readable that your property is dynamic? Are you doing things besides just computing a dynamic property based on the current values of existing properties of this object? Are you ok with having to call the method explicitly? Then use a method, e.g. getLatest().
According to the documentation for Object.defineProperty() on MSDN
Property descriptors present in objects come in two main flavors: data
descriptors and accessor descriptors. A data descriptor is a property
that has a value, which may or may not be writable. An accessor
descriptor is a property described by a getter-setter pair of
functions. A descriptor must be one of these two flavors; it cannot be
both.
Both data and accessor descriptors are objects. They share the
following optional keys:
configurable true if and only if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object. Defaults to false.
enumerable true if and only if this property shows up during enumeration of the properties on the corresponding object. Defaults to false.
A data descriptor also has the following optional keys:
value The value associated with the property. Can be any valid JavaScript value (number, object, function, etc). Defaults to undefined.
writable true if and only if the value associated with the property may be changed with an assignment operator. Defaults to false.
An accessor descriptor also has the following optional keys:
get A function which serves as a getter for the property, or undefined if there is no getter. When the property is accessed, this function is called without arguments and with this set to the object through which the property is accessed (this may not be the object on which the property is defined due to inheritance). The return value will be used as the value of the property. Defaults to undefined.
set A function which serves as a setter for the property, or undefined if there is no setter. When the property is assigned to, this function is called with one argument (the value being assigned to the property) and with this set to the object through which the property is assigned. Defaults to undefined.
If a descriptor has neither of value, writable, get and set
keys, it is treated as a data descriptor. If a descriptor has both
value or writable and get or set keys, an exception is thrown.
This indicates that get and set intercept access calls and assignment calls respectively.
A very good example (and description) here points out a clear example of when this is useful.
Take a case where you have the first and last name on a person, with the common need for the full name.
person.setLastName('Smith');
person.setFirstName('Jimmy');
person.getFullName(); // Jimmy Smith
Using the get and set keys, gives you the option of declaring the object like so:
var person = {
firstName: 'Jimmy',
lastName: 'Smith',
get fullName() {
return this.firstName + ' ' + this.lastName;
},
set fullName (name) {
var words = name.toString().split(' ');
this.firstName = words[0] || '';
this.lastName = words[1] || '';
}
}
and assigning it, and accessing it like so:
person.fullName = 'Jack Franklin';
console.log(person.firstName); // Jack
console.log(person.lastName) // Franklin
console.log(person.fullName) // Jack Franklin
This allows the developer to interact with fullname, without accidentally leaving firstname or lastname unassigned or improperly assigned.
Lastly,
The use strict directive will enforce read and write attempts to attributes defined through get or set accordingly. See W3Schools.
"use strict";
var obj = {get x() {return 0} };
obj.x = 3.14; // This will cause an error

Experimenting JavaScript Properties Attribute defaults

While reading a book of JavaScript I read that
all the attributes of Data Properties defaults to true when "defined directly on an object".
then after some description it again says that
"When you are using Object.defineProperty()", the values for configurable, enumerable, and
writable default to false unless otherwise specified.
I guess, in first statement "defined directly on an object" means using dot operator or by object literal notation like this:
var obj = new Object();
obj.name = "Mahesh";
But is there any way to experiment to check what these attributes have been set to, after the property has been added to the object by either method?
You can use getOwnPropertyDescriptor:
> var desc = Object.getOwnPropertyDescriptor(obj, 'name');
{"value":"Mahesh","writable":true,"enumerable":true,"configurable":true}
desc will contain the the flags configurable and enumerable. If the property is a data descriptor (no get or set), desc will also contain value and the flag writable. If the property is an accessor descriptor, desc will also contain the get and set methods.

Can I tell what property a generic getter/setter applies to in its body?

The context (this) is naturally the object that the property is being requested on, but there are no arguments passed to the getter function. I'd like to be able to get the name of the property being requested without using closures but it's looking like that's the only way to do it.
Object.defineProperty( someObj, "prop1", { get: genericGetter } );
Object.defineProperty( someObj, "prop2", { get: genericGetter } );
function genericGetter() {
// i want to figure out whether this is called on prop1 or prop2
}
Can I tell what property a generic getter/setter applies to in its
body?
That's not how getters work. A property of an object can either have a value or a get function. If the property has a value, then reading the property:
var x = obj.prop;
returns that value. However, if a property has a get function instead, then reading that property triggers that function. So, you use getters if the value of a certain property has to be computed dynamically, or if you want to perform certain operations whenever the property is read.
For instance, .innerHTML requires a getter, because its value is not stored statically, but computed on access:
var html = div.innerHTML;
Here, the browser has to serialize the DOM structure that is contained within the div element.
So, if you want a .get() function that retrieves various properties (Backbone.js has such a function), then you're not looking for getters.
The simplest implementation of what you want would be:
someObj.getProp = function ( name ) {
// perform required tasks
return this[ name ];
};

Categories