I searched if JavaScript offers a mean to define symbolic constants, but didn't find anything. Did I miss something ?
Is it a common practices to use const var instead ?
var const MAXIMUM_VALUE = 100;
Thanx.
const is not supported by IE, so if you want to support IE that is out of the question.
As far as I know, and the best way of doing this to keep it simple is to just have a naming convention for your constants like the ever-popular ALL UPPERCASE. There are some examples out there to force constants but they are not worth it for the most part. Alternatively, you could use a function:
function myConst() { return 'myValue'; }
This can of course still be overridden but I've seen it used.
Also see:
Are there constants in Javascript?
Is it possible to simulate constants in Javascript using closures?
Javascript: final / immutable global variables?
Yes. But you remove the var. const replaces var.
const MAXIMUM_VALUE = 100;
Object.defineProperty(window, 'CONSTANT_NAME', {value: CONSTANT_VALUE});
// usage
console.log(CONSTANT_NAME);
Object.defineProperty() creates a property with the following default attributes:
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.
writable true if and only if the value associated with the property may be changed with an assignment operator. Defaults to false.
if the "constant" is an object you might additionally want to make it immutable by freezing it. obj =Object.freeze(obj). have in mind that child-property-objects are not automatically frozen.
Related
Is it generally safe to use get and set as the names of properties for an object literal, like this:
var myObject={
get: 'value1',
set: 'value2'
}
Get and set do not appear to be reserved words in javascript, but I'm worried this could cause clashes with some built-in behaviors. In particular get and set seem to have some pretty unique behavior in the Object.defineProperties method:
Object.defineProperties(someObject,{
'Property1':{
get:function(){
return property1;
},
set:function(val){
property1=val;
},
enumerable:true;
}
});
In this built-in behavior, the get and set aren't treated as normal properties but actually execute their respective functions when someObject.Property1 is accessed or assigned to.
Because of how they're used in Object.defineProperties I've always sort of treated get and set as special reserved words. So my question is, is that a good rule of thumb or should I just learn to stop worrying and just use get and set whenever it seems semantically appropriate?
Yes, it's safe. get and set are always treated as normal properties. The fact that they are used to define getters and setters in property descriptors is completely irrelevant.
An example of an unsafe property would be valueOf, because it actually changes how an object behaves:
console.log(+{}); // NaN
console.log(+{ valueOf: () => 5 }); // 5
The fact that get and set are valid properties in the object you pass to Object.defineProperties is proof that it's safe to use get and set as property names.
The only time get and set have a special meaning in JavaScript is when you're creating a getter or setter, but the syntax is different, so there isn't a clash with property names.
the setter getter mechanism in javascript described , I don't see any conflict or reason to consider it unsafe except maybe others do it too, so careful about overwriting by other libraries so beside that it's good to go.
Object.prototype.getB = function() {
// how to get the current value a
return a.b;
};
const a = {b: 'c'};
a.getB();
As you can see, I want to make a function to all Object value. And I need to get the object value in this function then do something.
Monkey Patching
What you want to do is called monkey patching — you mutate a built-in prototype.
There are many wrong ways to do it, and it should be avoided entirely, because it has negative Web compatibility impacts.
I will, however, demonstrate how this can be done in a way that matches existing prototype features most closely.
In your case, the function body should return this.b.
In functions called as methods, you can get the object itself with the this keyword.
See How does the "this" keyword work? (section 4: “Entering function code”, subsection “Function properties”) for more details.
You correctly added the method to Object.prototype.
See Inheritance and the prototype chain for more details.
The tools
There is a number of tools involved when reasoning about monkey-patching:
Checking own property existence
Using property descriptors
Using the correct function kind
Getters and setters
Let’s assume you’re trying to implement theMethod on TheClass.
1. Checking own property existence
Depending on your use case you may want to check if the method you want to introduce already exists.
You can do that with Object.hasOwn; this is quite a new method, but in older environments it can simply be replaced by Object.prototype.hasOwnProperty.call.
Alternatively, use hasOwnProperty normally, but be aware that if you or someone else monkey-patched the hasOwnProperty method itself, this may lead to incorrect results.
Note that in does not check for own properties, exclusively, but for inherited properties as well, which isn’t (necessarily) what you want when you’re about to create an own property on an object.
Also, note that if(TheClass.prototype.theMethod) is not a property existence check; it’s a truthiness check.
Code samples
if(Object.hasOwn(TheClass.prototype, "theMethod")){
// Define the method.
}
if(Object.prototype.hasOwnProperty.call(TheClass.prototype, "theMethod")){
// Define the method.
}
if(TheClass.prototype.hasOwnProperty("theMethod")){
// Define the method.
}
2. Using property descriptors
You can choose the property descriptor however you like, but existing methods are writable, configurable, and non-enumerable (the last of which is the default when using defineProperty).
defineProperties can be used to define multiple properties in one go.
When simply assigning a property using =, the property becomes writable, configurable, and enumerable.
Code samples
// Define the method:
Object.defineProperty(TheClass.prototype, "theMethod", {
writable: true,
configurable: true,
value: function(){}
});
// Define the method:
Object.defineProperties(TheClass.prototype, {
theMethod: {
writable: true,
configurable: true,
value: function(){}
}
});
3. Using the correct function kind
JavaScript has four major kinds of functions which have different use cases.
“Is invokable” means that it can be called without new and “Is constructable” means that it can be called with new.
Function kind
Example
Is invokable
Is constructable
Has this binding
Arrow function
() => {}
Yes
No
No
Method
({ method(){} }).method
Yes
No
Yes
Class
(class{})
No
Yes
Yes
function function
(function(){})
Yes
Yes
Yes
What we’re looking for is a method that can be called (without new) and has its own this binding.
We could use functions, but, looking at existing methods, not only is new "HELLO".charAt(); strange, it also doesn’t work!
So the method should also not be constructable.
Therefore proper Methods are what we’re looking for.
Note that this obviously depends on your use case.
For example, if you want a constructable function, by all means, use a class instead.
Code sample
We go back to the previous code sample and instead of function(){} use a method definition.
// Define the method:
Object.defineProperty(TheClass.prototype, "theMethod", {
writable: true,
configurable: true,
value: {
theMethod(){
// Do the thing.
}
}.theMethod
});
Why bother with 2. and 3.?
The goal of the enumerability and constructability considerations is to create something that has the same “look and feel” as existing, built-in methods.
The difference between those and a naive implementation can be demonstrated using this snippet:
class TheClass{}
TheClass.prototype.theMethod = function(){};
Let’s compare this to a different, built-in method, like String.prototype.charAt:
Code snippet
Naive example
Built-in example
thePrototype
Is TheClass.prototype
Is String.prototype
theMethod
Is TheClass.prototype.theMethod
Is String.prototype.charAt
for(const p in thePrototype){ console.log(p); }
"theMethod" will be logged at some point.
"charAt" will never be logged.
new theMethod
Creates an instance of theMethod.
TypeError: theMethod is not a constructor.
Using the tools from subsections 2 and 3 make it possible to create methods that behave more like built-in methods.
4. Getters and setters
An alternative is to use a getter. Consider this:
const arr = [
"a",
"b",
"c",
];
console.log(arr.indexOfB); // 1
How would an indexOfB getter on the Array prototype look like?
We can’t use the above approach and replace value by get, or else we’ll get:
TypeError: property descriptors must not specify a value or be writable when a getter or setter has been specified
The property writable needs to be removed entirely from the descriptor.
Now value can be replaced by get:
Object.defineProperty(Array.prototype, "indexOfB", {
configurable: true,
get: {
indexOfB(){
return this.indexOf("b");
}
}.indexOfB
});
A setter can also be specified by adding a set property to the descriptor:
Object.defineProperty(Array.prototype, "indexOfB", {
configurable: true,
get: {
indexOfB(){
return this.indexOf("b");
}
}.indexOfB,
set: {
indexOfB(newValue){
// `newValue` is the assigned value.
// Use `this` for the current Array instance.
// No `return` necessary.
}
}.indexOfB
});
Web compatibility impact
There are a few reasons why anyone would want to extend built-in prototypes:
You got a brilliant idea yourself for a new feature to be added to all instances of whatever class you’re extending, or
You want to backport an existing, specified feature to older browsers.
If you extend the target object, there are two options to consider:
If the property doesn’t exist, supply it, otherwise, leave the existing property in place, or
Always replace the property with your own implementation, no matter if it exists or not.
All approaches mostly have disadvantages:
If you or someone else invented their own feature, but a standard method comes along which has the same name, then these features are almost guaranteed to be incompatible.
If you or someone else try to implement a standard feature in order to backport it to browsers that don’t support it, but don’t read the specification and “guess” how it works, then the polyfilled feature is almost guaranteed to be incompatible with the standard implementation.
Even if the spec is followed closely, who will make sure to keep up with spec changes?
Who will account for possible errors in the implementation?
If you or someone else choose to check if the feature exists before overriding it, then there’s a chance that as soon as someone with a browser which supports the feature visits your page, suddenly everything breaks because the implementations turn out to be incompatible.
If you or someone else choose to override the feature regardless, then at least the implementations are consistent, but then migration to the standard feature may be difficult.
If you write a library that is used a lot in other software, then the migration cost becomes so large that the standard itself has to change; this is why Array.prototype.contains had to be renamed to Array.prototype.includes[Reddit] [ESDiscuss] [Bugzilla] [MooTools], and Array.prototype.flatten could not be used and had to be named Array.prototype.flat instead[Pull request 1] [Pull request 2] [Bugzilla].
In other words, this is the reason we can’t have nice things.
Also, your library may not be interoperable with other libraries.
Alternatives
The simplest alternative is to define your own plain function:
const theMethod = (object) => object.theProperty; // Or whatever.
const theProperty = theMethod(theObject);
You could also consider a Proxy.
This way you can dynamically query the property and respond to it.
Let’s say you have an object with properties a through z and you want to implement methods getA through getZ:
const theProxiedObject = new Proxy(theObject, {
get(target, property, receiver){
const letter = property.match(/^get(?<letter>[A-Z])$/)?.groups?.letter.toLowerCase();
if(letter){
return () => target[letter];
}
return Reflect.get(target, property, receiver);
}
});
console.assert(theProxiedObject.getB() === theProxiedObject.b);
You could also extend your object’s prototype using another class, and use this instead:
class GetterOfB extends Object{
b;
constructor(init){
super();
Object.assign(this, init);
}
getB(){
return this.b;
}
}
const theObject = new GetterOfB({
b: "c"
});
const theB = theObject.getB();
All you have to keep in mind is to not modify things you didn’t define yourself.
I quote the recent Google JavaScript Style Guide:
Do not use JavaScript getter and setter properties. They are potentially surprising and difficult to reason about, and have limited support in the compiler. Provide ordinary methods instead.
Exception: when working with data binding frameworks (such as Angular and Polymer), getters and setters may be used sparingly. Note, however, that compiler support is limited. When they are used, they must be defined either with get foo() and set foo(value) in the class or object literal, or if that is not possible, with Object.defineProperties. Do not use Object.defineProperty, which interferes with property renaming. Getters must not change observable state.
Illegal:
class Foo {
get next() { return this.nextId++; }
}
It is an opinion of Google but I would like to truly understand the reasons.
First, I think the "compilers" that have a limited support are Babel / Traceur / TypeScript? Or is there a lack of support in some ES5 engines?
Additionally, I would like to understand what are the "surprising and difficult" reasons for which the author thought. I see these limitations:
Object.assign() doesn't copy the getters, but execute them and copy the values;
With the get name() syntax, it's not possible to add a getter to an existing object. With an existing object, the Object.defineProperty must be used.
It's not possible to define a getter with a lambda, this limitation can lead us to use the old school let that = this in some case.
I don't understand the statement: "Object.defineProperty interferes with property renaming". (What is a "property renaming"?)
I would presume what they meant is:
"[potentially] surprising":
Properties with getters and setters do not necessarily act like normal properties. Therefore, they violate the principle of least surprise. Suppose you had a property that returned 0 if the value assigned to it was a negative number:
myObj.prop = -7;
expect(myObj.prop).to.equal(-7); // fails
"[potentially] difficult to reason about":
For similar reasons. Properties with getters and setters don't necessarily act like like normal properties which means you can't necessarily reason about them the same way you would reason about normal properties.
Suppose we had the above property again. You could not do the following:
myObj.prop = -7;
console.log(myObj.prop * 9);
// use substitution to figure out the result of the above statement:
console.log(-7 * 9); // substitute -7 for myObj.prop
console.log(-63); // wrong - the above actually logs 0
Regarding property renaming: according to this question, the optimizer in the Closure compiler attempts to rename (minify) property names, but it will do so in an incomplete way by renaming the references to them, but not updating the .defineProperty() call. Using .defineProperties() allows the optimizer to rename the properties correctly.
I've seen two different ways of implementing getters/setters in a module pattern. One uses "defineProperty", while the other doesn't. What are the advantages/disadvantages of one versus the other?
var MyModule = (function() {
var _val;
var api = {
get value1() {
return _val
},
set value1(value) {
_val = value
}
};
Object.defineProperty(api, 'value2', {
get: function() {
return _val;
},
set: function(value) {
_val = value
}
});
return api;
}());
https://plnkr.co/edit/TbJSD4noZTew8II83eTH?p=preview
Using getter syntax you create a property which, prior to ES2015, you had to know the name of at the time that you were writing the code.
Object.defineProperty allows you to perform the same as the above but, even before ES2015, does not require you to know the name of the property in advanced. It also allows you to decide if the property is configurable, enumerable, or writable which is not possible using the get/set syntax.
To answer your exact question: neither is better. They're for different situations. The get/set syntax is simpler to read and write but isn't as powerful as Object.defineProperty.
As mentioned in documentation.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Normal property addition through assignment creates properties which show up during property enumeration (for...in loop or Object.keys method), whose values may be changed, and which may be deleted. This method allows these extra details to be changed from their defaults. By default, values added using Object.defineProperty() are immutable.
One "advantage" of Object.defineProperty vs get/set notation is that defineProperty can be used at any time, even on objects that have already been created (on which you cannot use the shorthand 'get' notation). From the mdn:
To append a getter to an existing object later at any time, use Object.defineProperty().
First, I had asked is it possible: How to create Javascript constants as properties of objects using const keyword?
Now, I gotta ask: why? The answer to me seems to be 'just because', but it would be so useful to do something like this:
var App = {}; // want to be able to extend
const App.goldenRatio= 1.6180339887 // throws Exception
Why can constants set on an activation object work but not when set on they are set on any other?
What sort of damage can be done if this were possible?
What is the purpose of const, if not to prevent a public API from being altered?
If you want an unchangeable value in a modern browser, use defineProperty to create an unwritable (aka read-only) property:
var App = {};
Object.defineProperty(App,'goldenRatio',{value:1.6180339887});
console.log( App.goldenRatio ); // 1.6180339887
App.goldenRatio = 42;
console.log( App.goldenRatio ); // 1.6180339887
delete App.goldenRatio; // false
console.log( App.goldenRatio ); // 1.6180339887
If you don't pass writable:true in the options to defineProperty it defaults to false, and thus silently ignores any changes you attempt to the property. Further, if you don't pass configurable:true then it defaults to false and you may not delete the property.
Apart from the fact that const is and not supported cross-browser (it's an ES6 feature), const App.goldenRatio= 1.6180339887 doesn't work for the same reason var App.goldenRatio= 1.6180339887 doesn't: you're setting an object property, so it's a syntax error to prepend it with a var or const keyword.
Going to answer the question you meant to ask and say never use const. The interpreter doesn't do anything with it. All it does is mislead the developer into thinking s/he can assume that the value never changes, which is about as likely as if the const keyword weren't present.
var MY_CONSTANT = "some-value";
You can use conventions like ALL_CAPS to show that certain values should not be modified