I want to achieve behaviour like down in code:
function Foo(name) {
this.name = name;
};
var myFoo = new Foo('myName');
myFoo.name('newMyName'); // sets myFoo.name = 'newMyName'
myFoo.name(); // returns 'myName'
But it is obvious that in that case I'm overridding name property with name function. Is anyhow possible to achieve that functionality?
When talking about getters and setters in javascript you may be talking about one of two concepts:
1. Getters and setters as a concept like in any other OO language.
This is the case illustrated by the code in your question. In this case, an object's property is simply that, a property which may or be either an object or a function. javascript keeps track of both within the same namespace. Indeed, functions are just objects in javascript so there is no concept of a separate namespace for functions like you'd find in languages like C.
In this case "getters" and "setters" are just regular functions so the value needs to be stored separately. There are several strategies around this.
One is to use implicit getSomething() and setSomething() style functions commonly found in Java. This allows you to disambiguate the getters and setters from the property name since the getters and setters have the word "get" and "set" added to the name.
The second strategy is the one you've written in your question. In this case you need to store the property in another name so as not to share the same name with the getter/setter.
The third strategy is to store the value in a closure:
function Foo (name) {
var name = name;
this.name = function (str) {
if (str !== undefined) name = str;
return name;
}
}
Note that in the code above the value is stored in name but the getter/setter is this.name which is a completely different variable. This allows your example code to work as you expected:
var me = new Foo('Mark');
me.name(); // returns Mark
me.name('Andy'); // sets name to Andy
2. Getters and setters as a mechanism in javascript.
This is a feature of newer versions of javascript that follows the ECMAscript 5 specification. This feature allows properties to execute code when reading or writing to it similar to how the .innerHTML property of DOM object invokes the HTML parser when you assign something to it.
The syntax of getters and setters is similar to functions but introduces the get and set keywords in place of function.
A simple example of a property with getter and setter:
var me = {
first_name : "",
last_name : "",
get name() {
return this.first_name + " " + this.last_name;
},
set name(str) {
var n = str.split(/\s+/);
this.first_name = n.shift();
this.last_name = n.join(' ');
}
}
The code above allows you to treat the functions to get and set the first_name and last_name as if it is a variable instead of a function. To use the name getter and setter you'd simply do:
me.name = "James Bond";
alert(me.first_name); // should alert James
alert(me.last_name); // should alert Bond
me.last_name = "Dean";
alert(me.name); // should alert James Dean
Using the javascript get/set mechanism, you can't store the value in the object using the same name. For example:
var foo = {
set bar(x) {this.bar=x}
}
The code above will compile but trying to set bar: foo.bar = 1 will cause a stack overflow because of the infinite loop - the this.bar= inside the setter will call the setter again.
If you want to use JavaScript getter/setter with the same name as the properties, e.g. to intercept certain setters to implement side effects, you can create a Proxy for your object.
function editableProxy (myObj) {
return new Proxy(myObj, {
toJSON: () => myObj,
get: function (target, prop) {
return Reflect.get(myObj, prop);
},
set: function (target, prop, receiver) {
if (prop === 'billTo') {
myObj.billToId = receiver?.id;
}
return Reflect.set(myObj, prop, receiver);
},
});
};
All getters work normally. Setters work normally, but if you set a billTo it also sets a billToId.
let da = {id:123}
let wrapped = editableProxy(da)
let id = wrapped.id // 123
wrapped.id=234
wrapped.id===da.id // true
wrapped.billTo={id:567,name:'Big Bad Bill'} // triggers setter side effect
wrapped.billToId // 567
Related
When I printed the object I did't get the conventional style functions but only the arrow style functions. Are the conventional functions hidden part of the created object or they do not belong to the class object?
class Person {
pName = "Hasnain";
pAge = 22;
displayNormalFunc1()
{
return this;
}
displayNormalFunc2()
{
return this;
}
displayArrowFunc1 = ()=>
{
return this;
}
displayArrowFunc2 = ()=>
{
return this;
}
}
objp = new Person();
console.log(objp)
displayArrowFunc1, displayArrowFunc2 is like a variable it will be initialized every time Person is initialized
displayNormalFunc1, displayNormalFunc2 are methods that belong to prototype which is a design principle of prototypical programming languages they are created only once when declaring Person and directly written to your Person.prototype it is possible to access them through some javscript APIs but it is not in this question
More info:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#constructors
See example:
const p1 = new Person()
const p2 = new Person()
console.log(p1.displayNormalFunc1 === p2.displayNormalFunc1) // true
consele.log(p1.displayArrowFunc1 === p2.displayArrowFunc1) // false
and
class Foo {
pLog() {
console.log('p a')
}
log = () => console.log('a')
}
const bar = new Foo()
bar.pLog() // 'p a'
Foo.prototype.pLog = () => console.log('p b')
bar.pLog() // 'p b'
bar.log() // 'a'
Foo.prototype.log = () => console.log('b')
bar.log() // 'a'
This actually is nothing to do with whether the functions are arrow functions or not. I can rewrite your example with the displayArrowFunc properties defined in the same way but as "regular" function expressions, and the result is the same:
class Person {
pName = "Hasnain";
pAge = 22;
displayNormalFunc1()
{
return this;
}
displayNormalFunc2()
{
return this;
}
displayArrowFunc1 = function()
{
return this;
}
displayArrowFunc2 = function()
{
return this;
}
}
objp = new Person();
console.log(objp)
// see comments on answer
console.log(Person.prototype);
console.log(Person.__proto__);
console.log(Person.__proto__ === Function.prototype);
The result is actually down to a few different things about Javascript objects and "classes". I'll try not to get too deep into this but give a quick summary of the key facts, with links.
Javascript has a version of "inheritance" for objects - for objects directly, not "classes" - which is quite different from that found in languages like Java and C#. Basically, each object is linked to a "prototype object" which is used to look up properties (including function properties, or "methods") that don't exist on the original object.
When you console.log an object, you'll only be shown the properties that "directly exist" on that object - not those that exist on its prototype, or its prototype's prototype and so on. Even though accessing those properties that are in the prototype chain will still work.
That's the root of what you observe - it turns out the "arrow style functions" in your original example (as well as the non-arrow ones in my modified example) are direct properties of the class instance objp, but the other ones are not.
The "normal functions" are not because this is how Javascript "classes" work. As I've tried to imply by the use of quotation marks, JS doesn't really have "clases" - they're syntactic sugar for a regular Javascript function. JS has the feature that any function can be called using the new operator, which will then construct a new object, whatever the function body itself actually does. This used to be the way, before ES6 introduced the class keyword (in around 2014/5), that people used "classes" in JS.
And the way to add "methods" to such a "class" would be like this:
function SomeClass(a) {
this.a = a;
}
SomeClass.prototype.someMethod = function() {
// do something here
}
Notice how the method is actually a property of the object SomeClass.prototype - which then (due to how JS works internally) becomes the "prototype object" (in the sense mentioned above) of any instance you construct via const someInstance = new SomeClass(2);.
And that's exactly what your "class" code gets transformed into - it's just a syntactic sugar. This is why displayNormalFunc and so on aren't logged - they're not on the actual instance object, but on its prototype.
As for why displayArrowFunc1 and friends are logged, that's because you've defined these in a different way inside your class - a way that is a more recent JS feature than "classes" themselves. These, where you put someProperty = something inside the class body, are known as class fields. Notice this sentence in the docs I linked to:
Public instance fields exist on every created instance of a class.
So in short, that's why they are logged - because they're on the instance, not its prorotype. This applies not only to "regular" values like your pName and pAge, but also the functions/methods you defined this way - functions in Javascript are just values like any other. And this is why as I said it's nothing to do with whether you defined those function expressions as arrow functions or not - it's the syntax you use to add them to the class.
In short, someProperty = someValue inside the class body puts the property directly on each constructed instance, including when someValue is a function. Whereas "standard" method definitions are a special syntax and they end up added to the prototype of all such instances - therefore they don't appear when an instance is logged.
How can you add all of an objects key / values to a prototype.
For example if I had the object
var serverResponse = {
foo: 'bar'
id: 123
name: 'Rick Sanchez'
}
And I wanted to add each key to the prototype of a Person. I could manually do it for each key like this.
function Person(serverResponse) {
this.foo = serverResponse.foo;
this.id = serverResponse.id;
this.name = serverResponse.name;
}
But I'm sure there is a better way to do this....
I don't think you want to add it to the prototype of Person, because that makes it available on every instance of Person (and it seems like serverResponse contains variables specific to a certain instance, not all instances, of a Person - this is most likely just a problem with the terminology of your question title).
That being said, I do this all the time - I pass a generic JS object (like your serverResponse) to the constructor of one of my classes (like Person) and I save it as an instance variable. And then I create accessor methods on the prototype like so:
function Person(serverResponse) {
this.serverResponse = serverResponse;
}
Person.prototype.getFoo = function() {
return this.serverResponse.foo;
};
Person.prototype.getId = function() {
return this.serverResponse.id;
};
...
It sounds like you're not wanting to add to the prototype, but to copy all properties over. This can be done in modern browsers with Object.assign (which can be introduced with a polyfill for ancient browsers):
function Person(serverResponse) {
Object.assign(this, serverResponse);
}
...of course you have to trust that serverResponse doesn't have data you don't want in your object. If you do indeed want to assign to the prototype, you can use Object.assign on the prototype as well:
Object.assign(Person.prototype, serverResponse);
If functions are objects in javascript, then why can't they have name-value pair syntax for their properties? For example, why is the following not possible/allowed?
function xx() {
name: 'jhg'
}
alert(xx.name);
and vice versa:
var person = {
this.age = 32;
};
alert(person.age);
You could assign the properties, you want, but not Function.name, which is a read only property with the name of the function.
function xx() { }
xx.name = 'jhg'; // is read only, because it's the function's name
xx.foo = 'bar';
console.log(xx.name);
console.log(xx.foo);
Because function declarations aren't object initializers. They're just different syntaxes, for different purposes, with different use cases and design criteria. Arrays are also objects, but we use a different initializer notation for them, too. Same with regular expressions.
(Note that your first example is perfectly valid and will run; name is a label, not a property name, and labels an ExpressionStatement that isn't used for anything.)
(Also note that functions do, as of ES2015, have a built-in name property. That's just not how you initialize it. [That function's name will be "xx" in your example.])
They are objects, but they are not object literals.
A function can be used to create an instance of an object. For example:
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log('Hi, my name is ' + this.name);
};
}
var bob = new Person('Bob', 24);
The bob variable is an instance of the Person function.
You can access the properties from bob like this:
console.log( bob.name ) // "Bob"
You could also define it literally, like this:
var bob = {
name: 'Bob',
age: 24
};
The function syntax is used to create functions. They are templates that can be used over and over again. The object literal syntax is used when you only want one instance, or also for data, without any behavior attached to it.
The way I figured, when creating a new function (that represents a class), it is considered a good practice to define additional functions with the help of the prototype. If functions are declared through this within an existing function, they get created for each instance, which we don't necessarily want.
So, my question is - if I want to have a property that is completely private and can be access only through getters and setters, is it even possible to achieve this by using the prototype?
Here's an example:
function Item() {
var title = '';
this.setTitle = function(_title) {
title = _title;
};
this.getTitle = function() {
return title;
};
}
var car = new Item();
car.setTitle('car');
console.log(car.getTitle()); //car
console.log(car.title); // undefined
/*
Alternative
*/
function _Item() {
this.title = '';
}
_Item.prototype.setTitle = function(_title){
this.title = _title;
};
_Item.prototype.getTitle = function() {
return this.title;
};
var _car = new _Item();
_car.setTitle('car 2');
console.log(_car.getTitle()); // car
console.log(_car.title); // car
as can be seen from the example above, in the case of Item, I declared getters and setters within a function - not a good practice. But in this way, I managed to keep the title property private. However, in case of _Item, I'm using the prototype approach, which is preferred, but my title property is not private at all.
So, what's the best approach at creating private properties of "classes" in JavaScript?
No, if you want a private property, which by definition is a variable local to the constructor, then obviously methods which access it must also be defined within the constructor.
Many people worry about the efficiency implications of defining a method once on a prototype, versus defining it once on every instance. This might have been a valid concern ten years ago, or today in applications that are creating thousands or millions of objects. Otherwise, realistically it's not something you need to worry about.
I have created below two employee classes, one using a constructor function and the other with JSON notation. In the constructor function, the print function is created by prototype so only one copy will be kept and objects (emp1 and emp2) share this print function.
Question: In the JSON notation (EmployeeNew), will the print function be held in memory as one copy only? Or will each object keep its own copy? What is the fundamental difference between these two approaches? Which is best for which scenario?
var Employee = function (name) {
this.name = name;
};
Employee.prototype.print = function () {
console.log(this.name);
}
var emp1 = new Employee("jack"),
emp2 = new Employee("mark");
emp1.print();
emp2.print();
var EmployeeNew = {
init: function (name) { this.name = name; },
print: function () {
console.log(this.name);
}
};
var empNew1 = Object.create(EmployeeNew),
empNew2 = Object.create(EmployeeNew)
empNew1.init("jack")
empNew1.print();
empNew2.init("jack");
empNew2.print();
Your two code examples are generally equivalent (except for some minor details not relevant to the question).
This...
Object.create(EmployeeNew)
...creates a new object with the EmployeeNew object as its prototype. So the print and init functions are shared.
console.log(empNew1.init === empNew2.init); // true
console.log(empNew1.print === empNew2.print); // true
To further illustrate, here's an example that takes the following steps...
Create an EmployeeNew object to be used by Object.create
Create 2 unique objects using Object.create
Verify that the new objects can use the functions provided by EmployeeNew
Add a new function to EmployeeNew
See if the objects from step 2 can use that new function
Step 1: Create an EmployeeNew object
var EmployeeNew = {
init: function (name) { this.name = name; },
print: function () {
console.log(this.name);
}
};
Step 2: Create 2 unique objects using Object.create
var empNew1 = Object.create(EmployeeNew),
empNew2 = Object.create(EmployeeNew)
Step 3: Verify that the new objects can use the functions provided by EmployeeNew
empNew1.init("jack");
empNew1.print();
empNew2.init("jack");
empNew2.print();
Step 4: Add a new function to EmployeeNew
EmployeeNew.foo = function() {
console.log( 'Foo was invoked' );
};
Step 5: See if the objects from step 2 can use that new function
empNew1.foo(); // logs 'Foo was invoked'
empNew2.foo(); // logs 'Foo was invoked'
So you can see that empNew1 and empNew2 are able to observe changes to EmployeeNew. This is because when we passed EmployeeNew as the first argument to Object.create, we created a new object with EmployeeNew set as the prototype of that object.
In simpler terms, when we look up a property, for example on empNew1, if empNew1 doesn't have that property, it automatically looks to its prototype to see if the property exists on that object. If so, it uses it.
With respect to your comment...
"...suppose, if create this.name as property ( name : "") is the name property also will be treated as prototype..."
Yes, if we do this...
EmployeeNew.name = "unknown"
...then that property will be shared among all instances that have EmployeeNew as their prototype object.
BUT
Because the .name property on the prototype is an immutable primitive value (a string), if we try to write to that property, what happens is that the .name property is automatically added directly to the instance.
Continuing with the example above...
EmployeeNew.name = "unknown";
Now the previously created instances will reference that property...
empNew1.name; // "unknown"
empNew2.name; // "unknown"
...but now lets update the property on one instance...
empNew1.name = "bubba";
empNew1.name; // "bubba"
empNew2.name; // "unknown"
This is because empNew1 now has its own .name property that references "bubba". This shadows the .name property on the prototype of empNew1, so the search for that property never extends into the prototype object.
Since empNew2 wasn't assigned a .name, it still looks to its prototype for that property.
Both cases are equivalent, you just need to think what seems more intuitive for your coding style, if creating a new object using new and all it's initial parameters, or using an init method.
One advantage of the Class implementation is that, if you compose your object inside the Class definition (I know, it's not prototype-like) you can create inner private attributes, which using just prototype you can't (unless you flag them using a special notation like an underscore prefix). In the end is just a matter of style and what do you feel more comfortable with.