Consider this:
function f2(x) {
return x+1;
};
X = function(){
this.f1=function (x) {
return 2*f2(x);
}
return this;
};
then x = new X(); x.f1(1) works fine.
But when i want to do this:
X = function(){
this.f2 = function(x) {
return x+1;
};
this.f1=function (x) {
return 2*f2(x);
}
return this;
};
The same statement will complain that it can't find f2.
In, for example c#, you can say
class X {
int f2(int x){return x+1;}
int f1(int x){return 2*f2(x);}
}
and this will work
X x=new X();
x.f1(1)
Why?
You need to reference the f2 with the this keyword explicitly.
X = function(){
this.f2 = function(x) {
return x+1;
};
this.f1=function (x) {
return 2*this.f2(x);
}
return this;
};
because you forgot this.f2. Javascript don't see class variables without this
To reference f2 in your second code block, you'll need to use this.f2. this references the context in which the function is being executed. Since you call f1 in the following way:
x.f1();
... the context is set to the instance, x.
JavaScript does not make instance variables available to the scope in the same way as scope variables, i.e. those directly available:
X = function(){
var f2 = 123;
this.f2 = function(x) {
return x+1;
};
this.f1=function (x) {
console.log(f2); // => 123
console.log(this.f2); // => function(){}
return 2 * this.f2(x);
};
return this;
};
Javascript doesn't have the implicit this that you get in C#. You need to add the this in:
X = function(){
this.f2 = function(x) {
return x+1;
};
this.f1=function (x) {
return 2*this.f2(x);
};
return this;
};
X = function(){
this.f2 = function(x) {
return x+1;
};
this.f1=function (x) {
return 2*this.f2(x); // <-- Need this here since it is not implicit
}
return this;
};
Related
I am trying to simulate a simple Holder "class" in JavaScript with a "private" property that holds something and "public" getter and setter "methods" to access the value.
The approach exhibited by HolderA below is mentioned e.g. here. The other approach I more or less arrived at by mutation but I guess it must be recognizable as an idiom as well. I like it because it contains no this or prototype stuff and seems very elementary and functional. Is there a difference between the two?
The test code (I run it under nodejs) seems to suggest that the two approaches are identical except that in the first case the objects I get have typeof object whereas in the second function.
var test = function(o) {
var initialValueCorrect = (!(typeof o.getX()==='undefined'))&&(o.getX()===0);
var VALUE_TO_SET = 10;
o.setX(VALUE_TO_SET);
var getSetWorks = o.getX()===VALUE_TO_SET;
var xIsPrivate = (typeof o.x === 'undefined');
var xHasCorrectValue;
if (!xIsPrivate)
xHasCorrectValue = o.x === VALUE_TO_SET;
return {initialValueCorrect: initialValueCorrect,
getSetWorks : getSetWorks,
xIsPrivate: xIsPrivate,
xHasCorrectValue: xHasCorrectValue};
};
var HolderA = (function() {
function foo(x) {
this.getX = function() {
return x;
};
this.setX = function(_x) {
x = _x;
};
};
return foo;
})();
var createHolderB = (function() {
var x;
function foo(_x) {
x = _x;
return foo;
}
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
})();
var objects = [{object: new HolderA(0), name: "approach with constructor-invocation and 'this'"},
{object: createHolderB(0), name: "approach with normal function invocation and closed variable"}];
for (var i = 0; i<objects.length ; i++) {
var testResult = test(objects[i].object);
console.log('['+objects[i].name+']: the object is a: '+(typeof objects[i].object)
+'\n\n\t\t\t'+JSON.stringify(testResult)+'\n\n\n\n\n');
}
update
As Bergi has pointed out function createHolderB in my code above is plain wrong and only creates a singleton object. So, is not really a "constructor" function. To that end I've now created createHolderC which can be used to really create multiple objects with a hidden private property like this:
var objectC1 = createHolderC()(0);
Now, is there any material difference between HolderA and the createHolderC function or is the difference purely stylistic?
var createHolderC = function () {
return (function() {
var x;
function foo(_x) {
x = _x;
return foo;
};
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
})();
};
createHolderB does not create new holders like HolderA does. It's essentially a singleton pattern. You might also want to call it a module. Notice that createHolderB() === createHolderB.
createHolderC is still different from HolderA in that it returns function objects, not instances. You may see the differences better when you strip out the unnecessary IEFEs:
function HolderA(x) {
this.getX = function() {
return x;
};
this.setX = function(_x) {
x = _x;
};
// implicit `return this;` when called via `new`
}
function createHolderC() {
var x;
function foo(_x) {
x = _x;
return foo;
};
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
}
A typical factory would rather look like this:
function createHolderD(x) {
var foo = {};
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
}
(or even with return {getX: …, setX: …};), the only difference to HolderA is the prototypical inheritance then.
Basically both exhibits the private access behavior to the x variable. But the difference between those two are
Constructor function
var HolderA = (function() {
function foo(x) {
this.getX = function() {
return x;
};
this.setX = function(_x) {
x = _x;
};
};
return foo;
})();
This is a self executing function, where it returns Constructor foo.
That is the reason you are using new while creating this type of holder {object: new HolderA(0)
A function
var createHolderB = (function() {
var x;
function foo(_x) {
x = _x;
return foo;
}
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
})();
Even this is also a self executing function, but while execution it creates x variable and returns function foo, and foo access x by closure.
You are creating this type of holder just by normal function call object: createHolderB(0).
Both cases are really bad in practice.
If you're making closures (anonymous functions) that hold your "private variable" scope, it means you're creating those functions for every instance of the class. That costs memory and performance. I used to create classes like this, but then once I was making large number of instances and found out that it hits performance hard.
Just as in Python, make private variables private by some convention, like underscore in _name. Then you can break it for debug reasons (because variables in closure scope are not accessible).
This is how I would do it:
function MyClass(arg) {
this._privateVar = arg;
}
MyClass.prototype.getPrivateVar = function() {
return this._privateVar;
}
You can also make getters and setters:
Object.defineProperty(MyClass.prototype, "privateVar", {
get: function() {
return this._privateVar;
}
}
But don't try to push javascript into things it's not designed to, or you'll pay with performance, code readability and debugging complexity. Applies to other languages too.
// I don't understand why this is not working
y = 'window';
var x = {
y : 'x',
func : function(){
return function(){
return this.y
}
}
};
x.func()();
// when I execute x.func()() I understand why it should return y
// x.func() would return a function in the global context and then execute it ' should return 'window' '
y = 'window'
var x = {
y : 'x',
func : function(){
return function(){
return this.y
}()
}
};
x.func();
why would this code also return 'window' it is executed inside of the x object
The call x.func()() calls the function x.func using x as a context, then calls the value it returned using no context. Where the function was defined doesn’t matter; only how it was called does.
To avoid this, you can bind the function to a particular context before returning that:
var x = {
y: 'x',
func: function() {
return function() {
return this.y;
}.bind(this);
}
};
ES6 arrow functions also use a lexical this, equivalent to bind:
var x = {
y: 'x',
func: function() {
return () => this.y;
}
};
With this code:
function printStuff(thing1, thing2) {
console.log(thing1 + ', ' + thing2);
};
function callWith() {
theFunc = arguments[0];
theArgs = [].slice.call(arguments, 1);
return function() {
theFunc.apply(this, theArgs);
};
};
x = callWith(printStuff, "apples", "cheese");
y = callWith(printStuff, "monkeys", "bananas");
x();
y();
...why is it that x and y seem to hold the same function? How do I get the desired behavior (i.e., store two different functions which always run printStuff with the parameters they were given when created)?
You need to create a different NEW instant of the printStuff method like this --
function printStuff(thing1, thing2) {
console.log(thing1 + ', ' + thing2);
};
function callWith() {
theFunc = arguments[0];
theArgs = [].slice.call(arguments, 1);
return new function() { // RETURN A NEW INSTANCE SO THAT IT HAS ITS OWN ARGUMENTS
theFunc.apply(this, theArgs);
};
};
x = callWith(printStuff, "apples", "cheese");
y = callWith(printStuff, "monkeys", "bananas");
x();
y();
When defining an object literal its possible to use a self-invoking function so the function has access to private variables,
obj={
value:(function(){
var private;
return function(){
return true;
}
}())
};
But is it possible to do the same thing with a getter/setter in an object literal?
obj={
get value(){
return value;
},
set value(v) {
value=v;
}
};
[edit 2022]
A pretty old answer.
More actual: you can create a factory function. In the snippet the factory creates an object with (get and set) access (through the closure) to a private variable.
const obj1 = objFactory(`Hello`);
const obj2 = objFactory(`World`);
console.log(`${obj1.privateThing} ${obj2.privateThing}`);
obj1.privateThing = `Goodbye`;
console.log(`${obj1.privateThing} ${obj2.privateThing}`);
function objFactory(somethingPrivate) {
return {
get privateThing() { return somethingPrivate; },
set privateThing(value) { somethingPrivate = value; }
};
}
The old answer:
Not really. You can also create an Immediately Invoked Function Expression (IIFE) for obj though:
obj = function(){
var privatething = 'hithere';
return {
get value() {
return privatething;
},
set value(v) {
privatething = v;
}
};
}();
obj.value; //=> 'hithere';
obj.value = 'good morning to you too';
obj.value; //=> 'good morning to you too'
As per ES6 and later style, default values can also be set in the signature.
const _ = function(_x = 'leafy greens', _y = 'chicken', _z = 'noodle soup') {
// console.log(_x)
return {
_x,
_y,
_z,
get x() { return this._x },
set x(value) { this._x = value }
}
}()
console.log(_.x)
_.x = 'beef & pork'
console.log(_.x)
This works.
var obj = {a:1};
console.log(obj.a); //1
Object.defineProperty(obj,"a",{
get: function(){
return this.b;
},
set: function(x){
this.b = x;
},
configurable:true,
});
obj.a = 10;
console.log(obj); //10
for(var key in obj)
{
console.log(key)
}
If you don't want the property b to be enumerated while using for...in loop or Object.keys(), just add the line
Object.defineProperty(obj,"b",{enumerable:false})
Note : The above code can also be used as a listener, to detect changes in the property's value. (By implementing the needed function in the set method, after setting the value.)
var obj = {a:1};
console.log(obj.a); //1
Object.defineProperty(obj,"a",{
get: function(){
return this.b;
},
set: function(x){
this.b = x;
console.log("obj.a value has been changed to "+x);
},
configurable:true,
});
obj.a = 10;
obj.c = 20;
obj.a = 30;
Why am I getting undefined on the x.test()? It's an anonymous function.
var Calculator = function () {
// private stuff
var x = 55;
return {
// public members
y: x,
test: function () {
console.log(x);
}
};
};
var x = new Calculator();
console.log(x.y);
console.log(x.test());
You're logging the return value of x.test which is implicitly undefined:
console.log(x.y); // Logs 55
var x = x.test(); // Logs 55 (because of the console.log call in x.test)
console.log(x); // Logs undefined (because that's what x.test returned)
Did you mean to return the "private" x from the test method?
// ...
return {
y: x,
test: function () {
return x;
}
}
Because you are printing (console.log) the return of a function that doesn't return anything.
Just return the x value in your test function:
var Calculator = function () {
// private stuff
var x = 55;
return {
// public members
y: x,
test: function () {
return x;
}
};
};