Other than coding style is there any advantages/disadvantages to either:
Circle.prototype = { radius : 10};
Object.defineProperty(Circle.prototype, 'circumference', {
get: function() { return 2*Math.PI*this.radius; }
});
vs
Circle.prototype = {
radius : 10,
get circumference() { return 2*Math.PI*this.radius; }
}
In property definition, JavaScript handles it via the internal method DefineOwnProperty, where assignment is handled by the internal method Put. In short, the second one checks whether or not a property is read-only and if yes, leads to rejection.
This can have consequences when using read-only properties, which prevent assignment, but not definition.
If you want to create a new property it is better use definition. If you want to change the value of a property, you can use assignment.
Take a look here for more, very interesting article.
EDIT: In fact, defineProperty is used for reasons such as defining read only properties and other reasons about the behavior of the property defined.
Take a look here for more too.
Related
I am trying to understand what is going on under all the syntactic sugar when we use "classes" in ES6, as well as what is happening when we are using the "new" and the "this" keyword. I know JavaScript is a prototype based programming language, and I have come up with the following code below to do this without using "new" or "this".
//Constructor function passes 3 custom properties to a predetermined object and then returns that object.
function ConstructorTestFunction(value1, value2, value3, object){
object.property1 = value1;
object.property2 = value2;
object.property3 = value3;
return object;
}
/*Since I am not using "this" I have wrapped the "ConstructorTestFunction.prototype" object into a function
called "ConstructorTestFunctionPrototype". This will allow me to pass the parent object's properties into
methods within the objects ".prototype" property */
function ConstructorTestFunctionPrototype(object){
ConstructorTestFunction.prototype = {
addValues(){
return object.property1 + object.property2 + object.property3;
},
minusValues(){
return object.property1 - object.property2 - object.property3;
},
multiplyValues(){
return object.property1 * object.property2 * object.property3;
},
divideValues(){
return object.property1 / object.property2 / object.property3;
}
};
return ConstructorTestFunction.prototype
};
// Since I am not using "new", I set _object1 to a blank javascript object with [[prototype]] = Object.prototype
const _object1 = {};
/*Change _object1's [[prototype]] to "ConstructorTestFunction.prototype", with the object's parent object
passed into it's hidden [[prototype]] property*/
_object1.__proto__ = ConstructorTestFunctionPrototype(_object1);
/*Creates object1, which is an instance of ConstructorTestFunction. It passes in "_object1" which is an object
with "[[prototype]] = ConstructorTestFunctionPrototype(_object1)", so it inherits all the methods from
"ConstructorTestFunction.prototype" whilst referencing the properties of the parent object. It also passes in
values for all assigned properties included in the class.
*/
const object1 = ConstructorTestFunction(40, 50, 245, _object1);
// Since I am not using "new", I set _object2 to a blank javascript object with [[prototype]] = Object.prototype
const _object2 = {};
/*Change _object2's [[prototype]] to "ConstructorTestFunction.prototype", with the object's parent object
passed into it's hidden [[prototype]] property*/
_object2.__proto__ = ConstructorTestFunctionPrototype(_object2);
/*Creates object2, which is an instance of ConstructorTestFunction. It passes in "_object2" which is an object
with "[[prototype]] = ConstructorTestFunctionPrototype(_object2)", so it inherits all the methods from
"ConstructorTestFunction.prototype" whilst referencing the properties of the parent object. It also passes in
values for all assigned properties included in the class.
*/
const object2 = ConstructorTestFunction(1, 2, 3, _object2);
Other than this not being the standard way of doing things in Javascript. Is this method of me reformulating the code to not use "this" or "new" going to have major performance issues? If so, then how would I go about replacing "this" and "new" without having major performance issues?
Basically, if I truly understand [[prototype]] and what "new" and "this" is doing, then I should be able to create some code that uses [[prototype]] to create "classes" within JavaScript that does not use "new" and "this". Otherwise, I do not truly know what these keywords are doing if I can't replace their functionality within custom code.
Thanks for your help in advance.
Each call to ConstructorTestFunctionPrototype generates a new set of methods (addValues, etc.). This removes the performance/memory advantages of using prototypes in the first place.
You can test this by noticing that if you run your code, object1.addValues !== object2.addValues. Whereas traditional code would have them be ===, using polymorphism over this to operate on the correct object.
The benefit of using the usual class/prototype system in JavaScript is precisely to share the memory and optimization advantages of methods among all instances of the class. In exchange you need to find the method's target using this, instead of binding it to a specific single object as you have done here.
As #Pointy notes in his comment on the OP, there are things the language can do that you cannot do at runtime. In this case the magic built in to the language is the translation of x.y(z) causing y to be called with x as the this value.
You can "emulate" this using a non-special argument, but then you lose out on the nice syntax. For example, you can write y(x, z) and then instead of using this instead the definition of y, use the first argument. But if you want access to the thing before the ., you need to use this.
class Foo {
getName = () => this.name;
setName = (name) => this.name = name;
}
and
class Foo {
get name () {
return this.name;
}
set name (name) {
this.name = name;
}
}
I can think of several examples where ES6 getters are at a disadvantage, e.g.
You cannot write a getter that will return a value based on a parameter value:
/**
* Returns an instance of Card associated with an element.
*
* #param {HTMLElement} element
* #return {Card|undefined}
*/
getCard = (element) => {
return _.find(this.index, {
element: element
});
};
That's okay, however, if you use this and ES6, you are introducing code style inconsistency.
You cannot distinguish between a direct property access and method access.
class Foo {
get name () {
return this.name;
}
get expensive () {
// An operation that takes a lot of computational power.
}
get randomValue () {
// Returns random value.
}
}
let foo = Foo();
foo.name;
foo.expensive;
foo.randomValue;
The disadvantage is that it is not intuitive that a property that you are accessing might require heavy computational power (and therefore should be memoized) or that it changes every time you access it.
Finally, getters/setters do not work with arrow functions. Invalid example:
class Foo {
get name = () => {
return this.name;
}
set name = (name) => {
this.name = name;
}
}
Which exposes them to the context issues.
What is the advantage of using ES6 getters and setters over the conventional get{PropertyName} and set{PropertyName} abstraction?
You cannot distinguish between a direct property access and method access.
This is the main argument in their favour.
One of the weirdest great pains of writing classic Java-style OO code is that any object with an exposed property on it has to have getters and setters written, and a huge amount of boilerplate comes rolling out of this, especially for large data-structure type objects (e.g. DTOs).
The reasoning for all of these is that you can't just make properties public, because otherwise you can't ever add logic to them without breaking the API (e.g. logic to only allow settings certain values, or to refactor and store a property in a slightly different way while still exposing the same external API, or similar). See https://softwareengineering.stackexchange.com/questions/176876/why-shouldnt-i-be-using-public-variables-in-my-java-class for some typical arguments around this.
I think you could comfortably say that this has been taken to its logical extreme in recent years, but that doesn't mean it's wrong; by exposing a public field for direct access you are indeed exposing the internal implementation of how you're storing that data, and that means you can't change it as easily or safely any more.
ES6 getters/setters fix this. The fact that something is readable as a direct property on an object no longer tells you anything about the implementation of that property. It could have been a field originally, but recently turned into a ES6 property accessor instead, without the API changing. The implementation of the property is hidden from the rest of your codebase, and is therefore easier to change.
The disadvantage is that it is not intuitive that a property that you are accessing might require heavy computational power (and therefore should be memoized) or that it changes every time you access it.
You're right, this is a risk. That's also a gotcha with any getX() though; there's a strong convention suggesting that nice simple methods like 'getName()' shouldn't ever be doing expensive things behind the scenes, even if they are methods, and if you break that you'll almost certainly end up catching people out (including yourself, 6 months from now)
Moving to properties doesn't change this, but you're right that ES6 means you're no longer guaranteed to be safe for simple property accesses. The answer is really just that you have to make sure you (and everybody else) sticks with convention and Principle of Least Astonishment: as with existing simple looking getters and setters, ES6 property accessors should do simple cheap things, and shouldn't have strange side effects elsewhere.
There aren't any specific things you can't do with getMethod() and setMethod(), but it allows for different code styles. For instance you could, with a get foo and set foo, write:
obj.foo++;
which would call the getter then the setter. You could, of course, have the set function then validate that the value is within a specific range, for instance. The traditional code would look like:
obj.setFoo(obj.getFoo() + 1);
You cannot distinguish between a direct property access and method access.
That's kinda the point. I'd argue that if something is really expensive, you just shouldn't use getters for it.
I read a few books about JavaScript, and it is a very powerful language for building web applications using OOP, but now I have a problem that I don't know if it can be solved:
var myObj = {};
myObj.properties = {};
myObj.properties.myProperty = 'foo';
Let me explain, now I have myObj .. setted the properties object inside... now, if I ask
for myObj.properties.myProperty I will get foo. But what if I ask for myObj.properties.notSettedValue ? (that is undefined)
I read about Object.defineProperty on MDN that can set a setter and a getter on a property.
I tried to set the "get" value on myObj.properties
Object.defineProperty(myObj, 'properties', {
get : function(){
console.log("called custom get property.", this, arguments);
}
});
... but nothing to do, the get function declared for myObj.properties work only for itself. The custom function is called only when I request myObj.properties. Not for its children.
There's a solution to pass the requested myObj.properties.notSettedValue to a get function of myObj.properties?
(without using a function or a method like myObj.properties.getItem('notSettedValue'))
Basically what you're asking is whether it's possible to set up a function that gets called if code tries to retrieve a property from an object that doesn't exist on that object.
No, JavaScript doesn't have that ability. ES6's proxies will make something quite similar possible, but as of ES5, there is no "catch all" getter feature.
Your only ES5-compatible mechanism for doing that kind of catch-all is as you've said, using a function rather than a property (your getItem).
Object in JavaScript are dynamic. You can add new properties on the fly, like you did in you example e.g. myObj.properties = {}
What happens under the hood is that the interpreter will try to find a property called properties on the myObj instance; If there is no such property then it will navigate through the prototype chain. In this example it will visit the prototype of the Object constructor, as myObj is an object literal. If it cannot find that property in the prototype chain then it will create it and add it as a new member of the object.
The same applies when you want to read a property. But in this case it will return undefined if it can't find the requested property; So unless you explicitly define the property, it will always return undefined. This applies both for when you access a simple property and a ES5 property.
Is such declaration of getter and setter for a property correct and most efficient?
var AxisRange = (function(){
function AxisRange(){
this._userMaxValue = 0.0;
Object.defineProperty(AxisRange.prototype, "UserMaxValue", {
get : function(){
return this._userMaxValue;
},
set : function(value){
if(value != this._userMaxValue){
this._userMaxValue = value;
this.validateUserMaxValue();
this.validateUserStep();
this.synchronizeActualRange();
}
}
});
}
AxisRange.prototype.validateUserMaxValue = function(){
alert("validateUserMaxValue");
};
return AxisRange;
})();
Also, I'm using JetBrains WebStorm to write my JS code, and it warns me that
AxisRange.prototype used in Object.defineProperty is not assignable to parameter type Object.
in line if(value != this._userMaxValue) is says "Possible invalid usage of this."
Before I go any further with code typing I need to make sure what I'm using is correct.
You're defining a prototype property inside the constructor. That means it will be re-defined every time you make a new AxisRange object. Move that out of the constructor function.
You also might want to get rid of that closure, because you're not using it to namespace any local variables and it will complicate attempts to optimize your JavaScript. (Google Closure Compiler will especially complain if you ever use it.)
I want to create an object just using the Object.create function (without any prototype, first argument as null).
I read somewhere that properties determine the state of an object in JavaScript and JavaScript has three different kinds of properties:
named data properties
named accessor properties
internal properties
So when I define a function in an object, should I always define it as a named accessor property:
var obj = Object.create(null, {
a:{
get:function(){
alert('jQuery nyan!');
}
}
});
Or should I just define the function as a named data property when it is neither a setter nor a getter? [e.g some jQuery functions that makes changes to the DOM obj]
var obj = Object.create(null, {
a:{
value:function(){
alert('jQuery nyan!');
}
}
});
Which approach should I take? In terms of performance (speed) and memory management do they have differences? They both appear to work without any exception.
obj.a;
//output: 'jQuery nyan!'
To make it easier to refer to them, let's define them as follows
var objA = Object.create(null, {a: {get: function(){alert('jQuery nyan!');}}});
var objB = Object.create(null, {a: {value:function(){alert('jQuery nyan!');}}});
Now, there is almost no difference between the invocation of objA.a vs objB.a(), except the use of ().
There are some differences, however.
The main difference is that you can't pass parameters to a getter, it is invoked as-is. This means objA.a(1,2,3) will not invoke the function with arugments 1, 2, 3. It will in fact throw an error after invoking, assuming the getter does not return a Function (you're effectively trying to do undefined(1,2,3)).
A second difference requires us to remember Object.create's second parameter takes an object of descriptors, which includes the flag writable (which defaults to false). The difference here is you can not set writable:true on objA.a because "A property cannot both have accessors and be writable or have a value". This means that if you want the method of the getter changed, you must re-define the property, whereas for value you could enable the use of = to change the method associated with the property.
Additionally, with no setter objA.a = <expr> will not perform any action at all.
Normally, you'd only use getters and setters in the following instances, with value as the standard behaviour otherwise;
Lightweight calculating an output
Validating input (to protect an object)
Hiding a variable from direct access
Keeping a standard API where variable or property names may change
If you don't care about compatibility, using getter and setters could be a good approach to replace setNAME()s and getNAME()s. And there is no significant performance gain/loss comparing to function version.
Note that, cause it looks like accessing an variable, instead of calling a function, so the getter/setter function should be very light weight to meet this expectation.
And don't ever use one function for both getter and setter like jQuery does, it's simply very slow. As there is no function signature in javascript, simulate it with if/else will cause lots of performance loss.