I'm new to JS, so I'm trying to find a good pattern for having private fields with ECMAScript 6.
I'm using ES6's classes running on Node.js (latest version). I came up with the following snippet but I don't understand why a variable declared with let (which in my probably-incorrect-understanding has only block scope in ES6) will survive after the block has been executed:
class PrivateTest {
constructor(aNumber) {
let _aNumber = aNumber;
//Privileged setter/getter with access to private _number:
this.aNumber = function(value) {
if (value !== undefined && (typeof value === typeof _aNumber)) {
_aNumber = value;
}
else {
return _aNumber;
}
}
}
}
const privateTest = new PrivateTest(99);
console.log(privateTest.aNumber());
privateTest.aNumber(86);
console.log(privateTest.aNumber());
console.log(privateTest._aNumber); //Undefined.
// Just to try inheritance:
class PrivateTest2 extends PrivateTest {
}
const privateTest2 = new PrivateTest2(13);
console.log(privateTest2.aNumber());
The output is this:
99
86
undefined
13
From the code above, it seems that this private field can even be inherited.
So the questions are:
Am I doing right?
What's the life cycle of _number supposed to be?
Your _aNumber (declared with let _aNumber = aNumber) doesn't exist outside of the class scope. You would get undefined if you tried to do console.log(_aNumber).
But JavaScript has something called closures that "freeze" variables inside of functions. This means that when you call the aNumber method of your class, the let variables still exist within that function.
Also, because JavaScript functions are first-class, assigning this.aNumber to a value is exactly equivalent to assigning this.aNumber to a function that returns a value, and then calling that function.
E.g.,
let a = 'foo';
let b = function() {
return 'foo';
};
b();
console.log(a); // foo
console.log(b); // foo
It's tough to say if you are "doing it right", as I'm not exactly sure what you are going for. But learning more about closures and first-class functions might help you have a better grasp on the lifecycle of variables and the nature of variable assignments.
Related
I am curious why methods cannot call other methods or themselves in javascript. For example, this produces a Reference error saying add is not defined.
class sum {
add(x, amt) {
if(amt == 0) return x
return add(x+1, amt-1)
}
}
summer = new sum()
console.log(summer.add(5,5))
You must use this.add() instead.
Now I understand that the methods get translated to functions on the prototype but I don't see how that explains this limitation I'm pointing out?
Couldn't one reason that when add is defined it could have references to itself or other methods with a closure capture.
Why is it this way?
I was in the middle of illustrating this shortcoming when #briosheje made the comment:
function add() {
console.log('called the wrong add!');
}
class sum {
add(x, amt) {
if(amt == 0) return x
return add(x+1, amt-1)
}
}
summer = new sum()
console.log(summer.add(5,5))
I will try to make it "slightly" more theorical, without going too deep in the documentations and so on.
The main answer to your question can be found by transpiling your code to plain javascript. To accomplish that, you can either use babel online or the typescript online playground.
Either case, the transpiled code will look like this:
"use strict";
var sum = /** #class */ (function () {
function sum() {
}
sum.prototype.add = function (x, amt) {
if (amt == 0)
return x;
return add(x + 1, amt - 1);
};
return sum;
}());
var summer = new sum();
console.log(summer.add(5, 5));
As you can see, the add method belongs to the sum prototype, which is a function. Therefore, you can guess that accessing add whithin the add scope just can't implicitly lead to invoking the sum.prototype.add function.
Differently, if you look at the correct code:
class sum {
add(x, amt) {
if(amt == 0) return x
return this.add(x+1, amt-1)
}
}
var summer = new sum()
console.log(summer.add(5,5))
You will see that the transpiled code will invoke the this method:
"use strict";
var sum = /** #class */ (function () {
function sum() {
}
sum.prototype.add = function (x, amt) {
if (amt == 0)
return x;
return this.add(x + 1, amt - 1);
};
return sum;
}());
var summer = new sum();
console.log(summer.add(5, 5));
This is not really matter of being ambiguous, it's rather that, in javascript, such kind of invocation is allowed, because the add method is implicitly available from the global scope. Being able to access the global scope in your function scope (because remember that, whatever happens, a class in javascript is always transpiled to a function) allows to inherit the standard behavior of a function, which is having access to its parent, having its own scope and granting access to the global scope.
A little curiosity: if you actually could access this.add using add, you wouldn't be able to use undefined, since it's a global variable, hence you wouldn't be able to access it and use it, because it would implicitly be this.undefined.
So, once again, it's not about ambiguity, it's about how javascript functions works.
Fundamentally OOP in Javascript is very different to java and other languages. There's not actually a strong concept of "classes". The classic OOP concepts like classes and instances in Javascript don't really come from "classes", but from the rules surrounding property lookups and the this and new keywords. Everything else is just functions, objects and properties—some of which are "special", like prototype.
E.g. you can assemble a "class" like this:
function Foo() {
this.bar = 42;
}
function baz() {
return this.bar;
}
Foo.prototype.baz = baz;
let foo = new Foo;
console.log(foo.baz());
There's no real cohesion here, baz doesn't intrinsically belong to any class, yet assembled like this they act like one. And the class syntax is just a thin veneer over this mechanism. So, if you write add inside a function, it follows variable scoping rules and will look up a variable in some surrounding scope, it's not going to find a method on a prototype.
This question already has answers here:
What is the purpose of the var keyword and when should I use it (or omit it)?
(19 answers)
Closed 3 years ago.
I'm trying to create a class like way of accessing objects with public and private functions/variables, but I'm a little confused as to why this simple test code doesn't work.
// Class type object with just one value.
function Play(arg) { // Constructor.
var test = arg;
// Private, as not declared with "this." Obviously more complex in the future.
private_settest = function( val ) {
test = val;
}
// Public.
this.settest = function( val ) {
private_settest(val);
}
this.gettest = function() {
return test;
}
}
var a = new Play(1);
var b = new Play(2);
a.settest(100);
console.log(a.gettest());
console.log(b.gettest());
I'd expect the output to be 1 2 but the actual output is 1 100.
I believe this to be a closure issue, anyone care to explain what I'm missing?
When stepping through this.settest():
The closure value of test is 1 (this is correct).
When stepping into private_settest():
The closure value of test is 2. This is wrong, it should be 1.
On stepping out of private_settest() the closure value is back to 1 again. I guess this is the closure being popped.
Help would be appreciated!
Thanks.
private_settest = function(val) {
test = val;
}
This implicitly defines a global variable private_settest, and when you construct new Play(), the global is overwritten each time, referencing the scope of the last initialized instance of Play, in this case b. That is why the "private" function accesses the second instance's scope, as you have observed. This should be changed to
function private_settest(val) {
test = val;
}
in order to correctly declare a function within each instance's scope.
// Class type object with just one value.
function Play(arg) { // Constructor.
var test = arg;
// Private, as not declared with "this." Obviously more complex in the future.
function private_settest(val) {
test = val;
}
// Public.
this.settest = function(val) {
private_settest(val);
}
this.gettest = function() {
return test;
}
}
var a = new Play(1);
var b = new Play(2);
a.settest(100);
console.log(a.gettest());
console.log(b.gettest());
This produces the expected output 100 2 (not 1 2).
The problem is that private_settest is a global variable. You did not use var (let, const or function) to declare that function variable with local scope. So every time you call the constructor the previous version of that function is overwritten.
When you then call a.setttest, you actually (via the method) call that single global function that modifies the private variable test which really is the private variable of the b instance (the last one that was created).
If you would have used "use strict", you would have received an error message about this.
In want to define a function-constructor inside a namespace. The way in which I defined the constructor till now was a simple constructor function without NS, combined with prototypal inheritance.
The code looked kind of like:
function mySuperObject() {
var self = this;
self.p1 = "";
self.p2 = "jkejie";
// and others
}
mySuperObject.prototype.func1 = function(){...}
// and others
Introducing namespace:
After reading many articles I decided to start up with a very simple way to define a namespace, maybe the most simplest.
Basically it is just about defining a variable which points to an object-literal and the content is the object (the "mySuperObject" in the code-snippet above). The constructor function is following: mySuperObjectInNS.
The code of the object:
var MYNAMESPACE = {
//some variable outside the object
file : "ConstructorInNamespace_obj.js: ",
//Defining contructor function inside a namespace
mySuperObjectInNS : function(propOne, propTwo){
var self = this;
self.objectName = "mySuperObject";
self.propertyOne = propOne;
self.propertyTwo = propTwo;
self.doSomething = function(){
console.log(file + " doSomething called - function of object");
};
///many more functions and attributes
}
}
MYNAMESPACE.mySuperObjectInNS.prototype.specialFunction = function(){
console.log(file + " specialFunction called - prototypical inheritance defined in file of object, outside of namespace");
};
///many more functions and attributes
In another file it is possible to intantiate the object, as follows:
...
var objOne = new MYNAMESPACE.mySuperObjectInNS("param11", "40");
//following line works just fine
objOne.doSomething();
....
Questions:
It seems to me that this all is about defining an Object-Literal and
I will come into trouble the latest I am trying to define "private"
properties of that object. Is this correct?
Is that mySuperObjectInNS still a constructor function? (For me it
seems it is something else,even if I can intantiate objects from it.
Is is a very bad very bad way of namespacing or is kind of ok?
It seems to me that this all is about defining an Object-Literal and I will come into trouble the latest I am trying to define "private" properties of that object. Is this correct?
"Private properties" have nothing to do with using an object for namespacing. In fact, originally when answering this question, I read that as "private functions" because that would be relevant.
There are lots of ways to do private and semi-private properties in JavaScript, but they relate to how you create the constructor function and the methods it gives the object, not how you expose the constructor function. "Namespace" objects are about how you expose the constructor.
One common pattern for creating "private" properties is to define methods that need to access them within the constructor, and make the "properties" local variables within the constructor (so they aren't really properties at all), like this:
function SuperObject() {
var privateInformation;
this.method = function() {
// This can access `privateInformation`, which is completely
// hidden from outside the constructor
};
}
It doesn't matter at all if you do that within a "namespacing" pattern or on its own.
Private functions, on the other hand, affect the pattern. I'll show both below.
A fairly common variant that provides for private functions is to use a function to create the object, which also gives you the opportunity to create private functions:
var TheNamespace = function() {
function privateFunction() {
}
function SuperObject() {
var privateInformation;
this.method = function() {
// This can access `privateInformation`, which is completely
// hidden from outside the constructor
};
}
SuperObject.prototype.otherMethod = function() {
// Can't access `privateInformation`, but has the advantage
// that the function is shared between instances
};
return {
SuperObject: SuperObject
};
}();
// usage
var s = new TheNamespace.SuperObject();
Is that mySuperObjectInNS still a constructor function? (For me it seems it is something else,even if I can intantiate objects from it.
Yes. A constructor function is any function that expect you to use new with it.
Is is a very bad very bad way of namespacing or is kind of ok?
Using objects as pseudo-namespaces is common practice. You might also consider various asynchronous module definition (AMD) technologies, which largely make "namespace" objects largely unnecessary.
Re your comment:
You defined a self-invoking function....which returns an an object?
It's not a self-invoking function, it's an inline-invoked function, but yes, it's a function that returns an object.
(if so I think parantheses are missing)
No, we don't need any parens that aren't there because the only reason for the outer parens other places you've seen this are to tell the parser that the word function starts an expression rather than declaration; we don't need that in the above because we're already on the right-hand side of an assignment, so there's no ambiguity when function is encountered.
Due to you proposed this way, is it a better way to define the ns?
"Better" is a subjective term. It gives you a scope in which you can define private functions, which you'd asked about.
Whereas I often also saw the option: var = {} | someNSName; What is this all about?
If you have several files that will add things to the "namespace" (as is common), then you frequently see this in each of them:
var TheNamespace = TheNamespace || {};
What that does is declare the variable if it hasn't been declared before, and assign an empty object to it if it doesn't already have one. In the first file that gets loaded, this happens:
The var is processed and creates a new variable, TheNamespace, with the value undefined.
The TheNameSpace = TheNameSpace || {} assignment is processed: Since undefined is falsey, the curiously-powerful || operator results in the new {}, which gets assigned to TheNamespace.
When the next file loads, this happens:
The var is a no-op, because the variable already exists.
The TheNameSpace = TheNameSpace || {} assignment is processed: Since TheNamespace has a non-null object reference, it's truthy, and the curiously-powerful || operator results in a reference to the object TheNamespace refers to.
That is, it has no effect at all.
This is used so you can load the files in any order, or load just one file in isolation.
Here's an example:
thingy.js:
var TheNamespace = TheNamespace || {};
TheNamespace.Nifty = function() {
function privateFunction() {
}
function Nifty() {
var privateInformation;
this.method = function() {
// Can access `privateInformation` here
};
}
Nifty.prototype.otherMethod = function() {
// ...
};
return Nifty;
}();
thingy.js:
var TheNamespace = TheNamespace || {};
TheNamespace.Thingy = function() {
function privateFunction() {
}
function Thingy() {
var privateInformation;
this.method = function() {
// Can access `privateInformation` here
};
}
Thingy.prototype.otherMethod = function() {
// ...
};
return Thingy;
}();
There are lots of variations on that basic pattern, particularly if one file may add multiple things to TheNamespace. Here's one that supports doing so fairly concisely:
var TheNamespace = function(exports) {
function privateFunction() {
}
function Nifty() {
var privateInformation;
this.method = function() {
// Can access `privateInformation` here
};
}
Nifty.prototype.otherMethod = function() {
// ...
};
exports.Nifty = Nifty;
function Thingy() {
var privateInformation;
this.method = function() {
// Can access `privateInformation` here
};
}
Thingy.prototype.otherMethod = function() {
// ...
};
exports.Thingy = Thingy;
}(TheNamespace || {});
Let's consider simple singleton implementation:
var singleton = function (Constructor) {
var singleton;
return function () {
if (!singleton) {
singleton = new Constructor();
}
return singleton;
};
};
We could move declaration of singleton variable to arguments:
var singleton = function (Constructor, singleton) {
return function () {
if (!singleton) {
singleton = new Constructor();
}
return singleton;
};
};
So I simply curious about side effects.
One more example:
var counter = (function (i) {
return function () {
i = (i || 0) + 1;
return i;
};
}());
We could move declaration of singleton variable to arguments
First, let's make it possible to talk about this without tying outselves up in knots by using the same symbol (singleton) for two completely different things within the same few lines of code.
Here's your seecond example renamed:
var singleton = function (Constructor, instance) {
return function () {
if (!instance) {
instance = new Constructor();
}
return instance;
};
};
If you did that, then calling the singleton function with two arguments would specify instance, making passing Constructor in pointless — Constructor would never be called by singleton (again, if you passed in two args [and the second arg was truthy]). So it would be a bit odd to do that.
But you asked about side-effects. There are no external effects involved if you assign to a formal argument within the function. It doesn't, for instance, have any effect outside the function:
function foo(arg) {
arg = 67;
}
var a = 42;
foo(a);
console.log(a); // Still 42
However, assigning to an argument rather than to a local variable does, in non-strict mode, have a small cost, because there's overhead involved: It relates to the magic arguments pseudo-array. In non-strict mode, there's a link between the formal arguments of a function and the arguments pseudo-array:
function foo(a, b) {
console.log(a);
console.log(b);
b = 42;
console.log(arguments[1]);
}
If we call that like this:
foo(1, 2);
we see
1
2
42
Note the magic: Assigning to the formal argument b updated the pseudo-array arguments. (It works the other way, too.) That link has a small but real runtime cost.
(In strict mode, there is no link, for this very reason.)
As far as closure goes, there is no difference in the two implementations. However, in first example, singleton is not settable by the caller.
I will choose between the two implementation, just based on whether singleton could have been created outside these example functions or not. If it can be, use model 2 otherwise use model 1
How can this object be rewritten so you don't need to declare it with "new"?
var Lang = new function(){
this.get = function(str, trans){
if(TRANSLATE[str]){
var str = TRANSLATE[str][LANG];
if(count_obj(trans) > 0){
for(var key in trans){
str = str.replace('%'+key+'%', trans[key]);
}
}
}
return str;
};
};
To something like this:
var Lang = {
get : function(){}
};
You wrote the solution to your own question in the question...this is a perfectly valid way to create an object in javascript:
var Lang = {
get: function(str, trans){
if(TRANSLATE[str]){
var str = TRANSLATE[str][LANG];
if(count_obj(trans) > 0){
for(var key in trans){
str = str.replace('%'+key+'%', trans[key]);
}
}
}
return str;
};
};
If you want private variables, the usual way to do that is to create a function with local variables that return the object with those variables encapsulated in a closure like so:
var Lang = (function() {
var p1; // private variable
var p2; // another private variable
return {
getP1: function () {
return p1;
},
setP1: function(value) {
p1 = value;
}
};
})();
Note that the function that creates the object you want is executed right away and returns the object with two private variables captured in the closure. Those variables will not be accessible from outside the object.
Patterns for Enforcing new
As mentioned already, constructors are still just functions but invoked with new.
What
happens if you forget new when you invoke a constructor? This is not going to cause
syntax or runtime errors but might lead to logical errors and unexpected behavior.
That’s because when you forget new, this inside the constructor will point to the global
object. (In browsers this will point to window.)
When your constructor has something like this.member and you invoke the constructor
without new, you’re actually creating a new property of the global object called
member and accessible through window.member or simply member. This behavior is highly
undesirable, because you know you should always strive for keeping the global namespace
clean.
// constructor
function Waffle() {
this.tastes = "yummy";
}
// a new object
var good_morning = new Waffle();
console.log(typeof good_morning); // "object"
console.log(good_morning.tastes); // "yummy"
// antipattern:
// forgotten `new`
var good_morning = Waffle();
console.log(typeof good_morning); // "undefined"
console.log(window.tastes); // "yummy"
Self-Invoking Constructor
To address the drawback of the previous pattern and have prototype properties available
to the instance objects, consider the following approach. In the constructor you
check whether this is an instance of your constructor, and if not, the constructor invokes
itself again, this time properly with new:
function Waffle() {
if (!(this instanceof Waffle)) {
return new Waffle();
}
this.tastes = "yummy";
}
Waffle.prototype.wantAnother = true;
// testing invocations
var first = new Waffle(),
second = Waffle();
console.log(first.tastes); // "yummy"
console.log(second.tastes); // "yummy"
console.log(first.wantAnother); // true
console.log(second.wantAnother); // true
Another general-purpose way to check the instance is to compare with
arguments.callee instead of hard-coding the constructor name.
if (!(this instanceof arguments.callee)) {
return new arguments.callee();
}
This pattern uses the fact that inside every function, an object called arguments is created
containing all parameters passed to the function when it was invoked. And arguments
has a property called callee, which points back to the function that was called. Be
aware that arguments.callee is not allowed in ES5’s strict mode, so it’s best if you limit
its future use and also remove any instances should you find them in existing code.
“JavaScript Patterns, by Stoyan Stefanov (O’Reilly). Copyright 2010 Yahoo!, Inc., 9780596806750.”
just leave the "new" out, rest is the same. :)
var Lang = function(){
...
}
Edit: example copy-paste from firebug, "dsa" is just an object, "dsaFunc" is a function, but you can do the same with them:
>>> var dsa = {};
undefined
>>> dsa.get = function(a){ return a+1;}
function()
>>> dsa.get(2)
3
>>> var dsaFunc = function(){};
undefined
>>> dsaFunc.get = function(a){ return a+1;}
function()
>>> dsaFunc.get(2)
3