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
Related
I'm trying to get my head around prototype inheritance in Javascript. I think I got the basic concept, but when I was playing around with this I ran into the following which still has me puzzled.
There is a very similar question and answer here but it doesn't fully answer why this is happening, at least not for me.
I create a new object like this:
var User = {
username: "",
name: {
first: "",
last: ""
}
}
Next I create two "instances" of that object:
var user1 = Object.create(User);
var user2 = Object.create(User);
Now I set the name property like so:
user1.name = { first: "John", last: "Jackson"}
user2.name = { first: "James", last: "Jameson"}
Now I do
alert(user1.name.first) \\ -> John
alert(user2.name.first) \\ -> James
All as expected. So far so good.
However, if I set the name.first property like this:
user1.name.first = "John";
user2.name.first = "James";
and I get
alert(user1.name.first) \\ -> James
alert(user2.name.first) \\ -> James
Clearly now the property is being set on the prototype object User (or rather the contained name object) instead of overriding it in the current object user1. Why does that occur?
Further if I do
user1.name.middle = "Mortimer"
I can now do
alert(User.name.middle) // -> Mortimer
which is not what I would expect. Generally, whenever a property is set on a derived object, that object either already has that property as an ownProperty in which case the value is simply assigned, or the property is newly created as an ownProperty on the derived object, overriding the prototype property. Just like happens when I assign to user1.name.
So why does assigning to an object contained in the prototype object cause such (at least to me) unexpected and counter-intuitive behavior?
The way I understand it, when the assignment is made the first check is to see if user1 has an ownProperty called name, which it doesn't. If this were a read operation the prototype property would now be looked up and User checked to see if it has ownProperty name. But since this is a set operation why walk the prototype chain when usually a missing ownProperty is simply created?
But since this is a set operation why walk the prototype chain when usually a missing ownProperty is simply created?
When you say user1.name.first = "John", the user1.name part has to be resolved before the .first property can be retrieved or set. And in your example the user1.name part only exists on the prototype object, so it is that object whose .first property you are setting.
Similarly, when you say user1.name.middle = "Mortimer", again the user1.name part resolves to the nested object from the prototype, so then you create a .middle property on that object, which is why User.name.middle also returns "Mortimer".
If you said user1.name.first and user1.name could not be resolved (on the current object or in its prototype chain) then you'd have a TypeError: Cannot set property 'first' of undefined. (You can try that concept with your existing code by saying user1.address.street = "something" - you'd get the TypeError, because user1.address doesn't exist on user1 or its prototype chain.)
Since you've already read over similar questions and answers and still seem to be having trouble understanding the behavior, I'll try to make my explanation as clear as possible. Here is where I think you are going wrong (emphasis mine):
Generally, whenever a property is set on a derived object, that object either already has that property as an ownProperty in which case the value is simply assigned, or the property is newly created as an ownProperty on the derived object, overriding the prototype property. Just like happens when I assign to user1.name.
The problem here is that you assume that user.name.first counts as "a property . . . set on a derived object" (an instance of User). However, this is simply not the case. In JavaScript, inheritance of properties is only shallow (a single layer deep). user.name is just an object value shared by reference through the prototype, so modifications to it from one place are reflected everywhere.
Think of user1.name and user2.name like firstReference and secondReference in the following example snippet, and hopefully the behavior will seem a bit clearer to you.
var User = {
username: "",
name: {
first: "",
last: ""
}
}
var firstReference = User.name
var secondReference = User.name
firstReference.name.first = 'First!'
console.log(secondReference.name) //=> 'First!' (logical and expected result)
The Object.create method creates an object using the first parameter as a prototype, and the second, optional, parameter is an additional object of own properties.
I think the problem here is that name is, by definition, in the prototype, and so not an own property.
If you want separate properties, then you should use the second parameter. The prototype is where you store methods and shared properties.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
has more details.
The key is that you want both prototype and own properties.
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.
This is more of a general practices question.
The language I am working with is Javascript. I have a function that is getting an object with many variables (okay, just 10 variables). What is the best way to make sure this function is getting all the required variables and that they are all set?
I know, I know, why not just use a if statement. That's a big chunk of if statements! As I grow as a programmer, I know that may not be the best method for this. I'm looking for a shortcut actually. How would you check a large sum of variables for existence and non-blank values?
This is a pretty neat way of handling validation, I usually use this when checking for required fields in form inputs.
var theObj = { /* object loaded from server */ }
function checkTheObj(testObj)
{
var requiredKeys = ['key1', 'key2', 'key3'];
for(var keyPos = 0; keyPos < requiredKeys.length; keyPos++)
{
if(typeof(testObj[requiredKeys[keyPos]]) == 'undefined')
{
return false;
}
}
return true;
}
if(checkTheObj(theObj))
{
//do stuff
}
You can of course tweak this to return or throw an exception telling the first missing field (or use an internal array to return a list of all missing fields).
function objectHas(obj, properties) {
var len = properties.length
for (var i=0; i<len; i++) {
if (i in properties) {
if((!obj.hasOwnProperty(properties[i])) || (!obj.propertyIsEnumerable(properties[i]))) {
return false;
}
}
}
return true;
}
Usage:
if(objectHas(user, ["email", "password", "phone"])) {
console.log("awesome");
}
It's simple, but does the job.
Edit: On an ideal world you could extend the Object prototype for an even neater syntax such as if(object.has(["a", "b", "c"])), but apparently extending the Object prototype is the incarnation of evil so a function will have to do :)
First of all, you need to improve your understanding of these languages and learn the correct terminology.
There is no (single) language named "Javascript" at all. You are implicitly using several languages here (depending on the runtime environment), all of which are ECMAScript implementations, and one of which is Netscape/Mozilla JavaScript (in Mozilla-based software like Firefox).
An object does not have variables, it has properties (not: keys). Global code, function code, and eval code can have variables; that is a different (but similar) concept.
The function is not getting an object, it is being passed a reference to an object as argument.
As a programmer, you should already know that you can do repetitive tasks in a loop; the associated statements in ECMAScript implementations are for, for-in, while and do. So you do not have to write several if statements.
You can access the properties of an object in two ways, where property is the property name:
Dot notation: obj.property
Bracket notation: obj["property"]
The second one is equivalent to the first if the property name is an identifier, i.e. if it follows certain naming rules. If the property name is not an identifier or if it is variable, you have to use the second one. This also shows that all property names are string values. So you can store the name of a property as value of a variable or another property, and then access the variable or property in the property accessor. In the following, a property name (property) is stored in and used from a variable:
var propertyName = "property";
obj[propertyName]
Combining that with a loop allows you to iterate over certain properties of an object. Unfortunately, the solutions presented so far are flawed in two respects: A for-in statement iterates only over the enumerable properties of an object, and it does so in arbitrary order. In addition, it also iterates over the enumerable inherited properties (which is why one solution requires the hasOwnProperty() call).
A simple, sure and efficient way to iterate only over certain properties of an object in a defined order looks as follows:
var propertyNames = ['name1', 'name2', 'name3'];
for (var i = 0, len = propertyNames.length; i < len; ++i)
{
/* … */ myObject[propertyNames[i]] /* … */
}
This works because propertyNames refers to an Array instance, which encapsulates an array data structure. The elements of an array are the properties of the Array instance that have integer indexes from 0 to 65535 (232−1). Because indexes are not identifiers (they start with a decimal digit), you have to use the bracket property accessor syntax (some people misunderstand this and refer to all ECMAScript objects as "arrays", even call them "associative arrays" and […] the "Array operator"). Therefore, propertyNames[i] evaluates to the values of the elements of the array in each iteration as i is increased by 1 each time. As a result, myObject[propertyNames[i]] accesses the property with that name in each loop.
Now, to find out whether the property is set, you need to define what that means. Accessing a property that does not exist results in the undefined value (not in an error). However an existing property may also have the undefined value as its value.
If "not set" means that the object does not have the property (but may inherit it), then you should use hasOwnProperty() as used in Mahn's solution.
If "not set" means that the object does not have the property and does not inherit it, then you should use the in operator, provided that the object is not a host object (because the in operator is not specified for them):
if (propertyNames[i] in obj)
If "not set" means that the object either has or inherits the property, but the property has the undefined value, or the object neither has nor inherits the property, then you should use the typeof operator as used in Bob Davies' and aetaur's solutions (but the latter approach using Array.prototype.every() is less compatible as-is; that method was not specified before ECMAScript Edition 5, and is not available in IE/JScript < 9).
There is a third option with ECMAScript Edition 5.x, the Object.keys() method which (despite its name) returns a reference to an Array instance that holds the names of all not-inherited properties of the argument:
var propertyNames = Object.keys(obj);
/* continue as described above */
It is a good idea to emulate Object.keys() if it is not built-in, as this algorithm is frequently useful.
This expression returns true, if all variables from variableNameList (list of required variable names) set in object o:
variableNameList.every(function(varName){ return typeof o[varName] !== 'undefined'; });
You can use underscore _.all function instead of native every, and underscore _.isUndefined instead of typeof ....
should a getter of a private property return a reference to the property or the value of the property (or a clone if the property is an object).
If the property has a primitive type (string, number, boolean) or a reference to an immutable object (String, Number), the answer is that you return the value/reference directly, since there's nothing the caller can do with it that will change your object's state.
If the property has a reference type to a mutable object (an array, an Object, a Date, lots and lots of other things), it depends on whether you want the caller to be able to change the properties on your object's copy of it. If you do, return the reference. If not, clone it.
For example, let's assume an object with a private array and some operations you can perform on that array:
var obj = (function() {
var o = {};
var theArray = [];
o.addItem = function(item) {
theArray.push(item);
};
o.getItemAt = function(index) {
return theArray[index];
};
return o;
})();
(Note that this is not meant to be a glorious example of object design. :-) )
Now you want to add a getArray operation. You can do that in two ways:
You can return a reference to the array:
o.getArray = function() {
return theArray;
};
...which means the caller can do all sorts of things to it (like remove items from it, an operation the object didn't previously support) that will change your object's state (because you've given them direct access to the formerly-private array).
Or you can return a clone of the array:
o.getArray = function() {
return theArray.slice(0);
};
...which means the caller can't do anything to the private array, they can only operate on the copy of it you gave them.
This depends whether the private property is immutable or not. If it's immutable, it can't be changed and there is no harm in returning it. If however it is mutable, then returning it will break the OOP principle of information hiding.
If it's a primitive immutable int/number, then returning the private property is fine since it achieves two things:
You hide the private property and provide an interface, and any changes to the implementation of the private property will not break the interface as easily
The returned object (in this case a value) can't be changed by the caller because it's a primitive type and is passed by value
If however, you return a mutable Array from a method and your language passes objects by reference, then you must accept the fact that the caller may decide to change this internal object - which could very easily lead to your internal code breaking.
For these reasons, if you plan to return a copy to a mutable object, return a copy and not the underlying object itself.
Generally wherever the property is to be used should not be able to change the property value. This satisfies the important principle of the OOP i.e Encapsulation.
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 ];
};