Consider this trivial code I tried in Chrome's console:
function Container() {
var secret = 3;
this.getSecret = function() {
return secret;
}
}
Now, I cannot retrieve 3 by executing:
var c1 = new Container();
c1.secret //yields undefined
However, this works as expected
c1.getSecret() //returns 3
Now, this is the quirky thing I tried:
c1.secret = 10;
c1.getSecret(); //I was expecting it to return 10
However, it returns 3. When I see the object in the console, it looks like this:
Container {getSecret: function, secret: 10}
Can someone explain why c1.secret = 10 didn't change the value of the secret private variable in the object? Are there two fields with the name "secret"?
I'm confused what the final object really looks like in memory.
private is a really confusing word.
The secret var you declared using var secret = 3; is not a 'private' variable. This is a variable that is only visible in the Container constructor scope. And because you declare the method getSecret in the same scope, it has access to it.
if you had done:
function Container() {
var secret = 3;
}
Container.prototype.getSecret = function() {
return secret;
}
calling getSecret would say that secret is undefined.
And, with your current code, adding:
Container.prototype.getSecret2 = function() {
return this.secret;
}
would return 10. Because your object has now a property called secret when you do
c1.secret = 10;
But remember:
var secret = 3; does not attach the variable to the current object. It just create a variable that live in the current scope. Every function declared in that scope will have access to it.
Related
Regarding private variable encapsulation - achieved by defining the private variable as var instead of it being a property of this ( not private this.private ). This is the most direct way of making a member private.
But, the above rule makes sense if the instance is being returned and used. In case we return a new object with get/set methods being exposed. Now, does storing the private variable in the this param still achieve private variable encapsulation
Is there any way that I can get access to the instance param when it's not being returned?
function X(init) {
this.private = init;
var that = this
function get() {
return that.private;
}
function set (tmp) {
that.private = tmp;
}
return {
get: get,
set: set
}
}
var tmp = new X(1);
console.log(tmp.get()) // 1
console.log(tmp instanceof X) // false
tmp.private = 20 // doesnt work as tmp isnt an instance object
console.log(x.get()) //1
x.set(20)
console.log(x.get()) //20
Do I have access to private as it's a property of this when the this isnt being returned?
No, we can not have access to private property on this as you are explicitly returning new object.
Consider this example when using a constructor:
function X()
{
// Scope of X()
var i = 1; // this variable is accessible only in this scope
this.get = function() { return i; };
}
a = new X(); // call the constructor
console.log(a.i); // this will be undefined
a.i = 2; // even if you try to set `i` on `a`
console.log(a.get()); // the actual `i` of `a` will still be 1
console.log(a.i); // this will be two.
Now for the example which uses a function which returns an object.
function Y()
{
// Scope of Y()
var i = 1; // this variable is accessible only in this scope
function get() { return i; };
return { // now you return an object which exposes a function that returns the value of `i` of this SCOPE.
get: get
}
}
b = Y();
console.log(b.i); // this will be undefined
console.log(b.get()); // this will be 1
b.i = 3; // this will work
console.log(b.get()); // this will still be 1
console.log(b.i); // this will be 3
I'm sorry I don't have enough time to continue the explanation, I'll return to this later.
You can hide(make private) member variables by hiding it in the scope.
Also, between these two approaches, only the first approach where you will be able to make use of the this keyword. You can't use this keyword in the second approach as its value will be the Window
You can achieve true privacy using a WeakMap.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap
WeakMaps allow you to create an affiliated object on which you can append any values.
You can't access them if you can't access the scope in which they were defined.
Using ES5 in a browser
"use strict";
{
const privacy = new WeakMap();
function X(init)
{
privacy.set(this, {});
const props = privacy.get(this);
props.init = init;
}
X.prototype.setInit = function(tmp)
{
privacy.get(this).init = tmp;
};
X.prototype.getInit = function()
{
return privacy.get(this).init;
};
window.X = X;
}
Using ES6 class notation in NodeJS
const privacy = new WeakMap();
class X
{
constructor(init)
{
privacy.set(this, {});
const props = privacy.get(this);
props.init = init;
}
setInit(tmp)
{
privacy.get(this).init = tmp;
}
getInit()
{
return privacy.get(this).init;
}
}
module.exports = X;
I have a simple issue, but i can't understand.
<script type="text/javascript">
var man = function(){
var age = 1;
this.shoes = 2;
};
man.prototype = {
create: function(){
//age = 5;
console.log(age);
console.log(this.shoes);
}
}
var man1 = new man;
man1.create();
</script>
-Ok,I create a man with 2 vars, age and shoes. I use "this" only in shoes. After I prototype a method in man.
-If I execute this code, first console.log say me:
Uncaught ReferenceError: age is not defined (logic)
And the second console is "2" (correct).
-If I write: console.log(this.age) the message error is: undefined.
-But if I put a value for age (age=5)(without using "var" for create var) in new method, it works.
Why I can only use age var if I put a value before read this but?
The age variable is within the "constructors" closure, which makes it only available there. You can use the age variable inside that function (closure), but not outside.
If you want to make it accessable from outside, add it to this or make a getter function like:
var man = function(){
var age = 1;
this.shoes = 2;
this.getAge = function() { return age; };
};
But if you don't put declare the variable inside the function, but still set it, then it will be defined on the global object, in the browser window. This will not make age available on the man object, but you will get access to it anywhere. Here's an to clarify a bit:
function tellAgeDelayedGlobal() {
age = 1;
setTimeout(function() { console.log(age) }, 1000);
}
function tellAgeDelayedDeclared() {
var age = 1;
setTimeout(function() { console.log(age) }, 1000);
}
tellAgeDelayedGlobal();
tellAgeDelayedDeclared();
age = 2;
this will print to console:
2
1
When you use var inside a function it is available within it(local scope).
var man = function(){
var age = 1;
this.shoes = 2;
};
Here age will be available only in the man method / constructor because this creates a local scope or rather is private to the constructor.
man.prototype = {
create: function(){
age = 5;
console.log(age);
console.log(this.shoes);
}
}
Here age will create a global scope. That is, this is equivalent to window.age = 5 and hence making it available in the entire app. Because of which you will be able to retrieve the value in any function since it is global now.
This must be avoided since it will not be automatically garbage collected(GC). If you really need it because other parts of your code has a dependency w.r.t. it, you can namespace it like: MyApp.age = 5. But w.r.t your current situation, I do not find a need in here.
I have this prompt on code wars
"There's no such thing as private properties on a javascript object! But, maybe there are?
Implement a function createSecretHolder(secret) which accepts any value as secret and returns an object with ONLY two methods"
I'm pretty sure it wants me to use closures to achieve this and I have read about how to do this here:
Private variables and closures
https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Contributor_s_Guide/Private_Properties
This is my code:
function createSecretHolder(secret) {
return {
var _secret = secret;
this.getSecret = function(){
return _secret;
}
this.setSecret = function(secret){
_secret = secret;
}
}
}
However, I get this error:
[eval]:6
var _secret = secret;
^
SyntaxError: Unexpected token =
at Object. ([eval]-wrapper:6:22)
at
at evalScript (node.js:536:25)
at startup (node.js:80:7)
at node.js:906:3
I tried to make an object literal with a private value to hold the value of secret and mostly followed the examples from the sources I listed above. How do I create a closure with ONLY two methods to get and set data and where do I store the value of secret without adding another property?
You are trying to return an object literal, in which you cannot have an assignment statement. To have the closure property, you need to store the variable in the function scope, like this
function createSecretHolder(secret) {
var _secret = secret;
return {
getSecret: function() {
return _secret;
},
setSecret: function(secret) {
_secret = secret;
}
}
}
Now, _secret is in the scope of the getSecret and setSecret functions, because of the closure property. So they can access it.
The problem is return keyword actually returns object with a structure which you didn't follow.
try this:
function createSecretHolder() {
var _secret = secret;
return {
getSecret : function(){
return _secret;
}
,
setSecret : function(secret){
_secret = secret;
}
}
}
Using _ to prepend a property is a common naming convention JavaScript developers follow to show the intention that the property should not be altered, and it is definitely related to this prompt, but it seems like it is not hinting that it's needed and it may not be required for this challenge specifically.
The SyntaxError you're seeing is related to the _secret initialization being within the object literals rather than outside. Object literal syntax is different from variable declaration & assignment and should be mixed. You can move the var _secret = secret; to outside of and before the return statement (but still inside of the function createSecretHolder). (Super Hornet is right, but his code is missing the secret parameter, so his code would get a Reference error saying that secret is not defined.) While your attempt is slightly more declarative, you can actually do without the const _secret = secret. The first thing that the function does is to pair its argument with its parameter, in this case, declaring secret and assigning the input to it. So it is already "in closures".
Here's my take and it works almost the same, except I like to include the console.log() into my functions that I'm expecting to see a result.
function createSecretHolder(secret) {
// input: secret
// output: object (with only 2 methods)
return {
getSecret() { console.log(secret) },
setSecret(input) { secret = input }
}
}
const obj1 = createSecretHolder(5);
obj1.getSecret() // => 5
obj1.setSecret(2)
obj1.getSecret() // => 2
Same as:
function createSecretHolder2(secret) {
return {
getSecret() { return secret },
setSecret(input) { secret = input }
}
}
const obj2 = createSecretHolder2(91);
console.log(obj2.getSecret()) // => 91
console.log(obj2.setSecret(82)) // => undefined
obj2.setSecret('new Secret') // logs nothing, returns undefined
obj2.getSecret() // logs nothing, returns 'new Secret'
example:
function Foo() {
this.bla = 1;
var blabla = 10;
blablabla = 100;
this.getBlabla = function () {
return blabla; // exposes blabla outside
}
}
foo = new Foo();
original question:
I know that bla will be assigned to every instance of Foo.
What will happen with blabla?
new question:
what I understand now:
this.bla = 1; // will become an attribute of every instance of FOO.
var blabla = 10; // will become a local variable of Foo(**not** an attribute of every instance of FOO), which could be accessed by any instance of FOO only if there's a method like "this.getBlabla".
blablabla = 100; // will define a **new** (or change if exist) global(window) variable.
[Question:] Did i understand correctly?
Any internal-methods you give to this -- ie: this.method = function () {}; while inside of your Foo constructor function, are all going to have a reference to the blahblah which is unique to each instance of a Foo object.
function Wallet () {
var balance = 0;
this.checkBalance = function () { return balance; };
this.depositAmount = function (amount) { balance += amount; };
}
var wallet = new Wallet();
wallet.checkBalance(); // 0
wallet.depositAmount(3);
wallet.checkBalance(); // 3
But it's completely protected from access outside of wallet, unless you return it to somebody, from a privileged function.
wallet.balance; // undefined;
(added bit of interest -- if balance is a string, a number, or a boolean, even if you return it, that won't give people editing rights, or even permanent viewing access -- scalar variables are passed by value, so you're just passing the value of balance at the time -- if, however, balance was an object, a function or an array, they'd have permanent access to modify the crap out of your internal workings)
Note: methods HAVE to be assigned inside of the constructor for this to work.
Prototypes can't access internal variables.
Adding methods later won't give them access to internal variables.
This means that each instance will take up a little more memory, because each has its own copy of the methods, and has its own copy of the vars.
But if what you're doing requires private data, this would be a good way to get it.
In your example blabla is a local variable, so it will go away when the constructor function ends.
If you declare a function inside the constructor, which uses the variable, then the variable will be part of the closure for that function, and survives as long as the function (i.e. normally as long as the object):
function Foo() {
this.bla = 1;
var blabla = 10;
this.getBlabla = function() {
alert(blabla); // still here
}
}
It will become a local (think of 'private') variable within Foo(). Meaning that you can't access it outside of Foo().
function Foo() {
this.bla = 1; // this becomes an extension of Foo()
var blabla = 10; // this becomes a "Local" (sort of like a 'private') variable
}
You could expose it (by returning it) with a Foo method.
function Foo() {
var blabla = 10; // local
this.getBlabla = function () {
return blabla; // exposes blabla outside
}
}
Now outside of Foo():
var FooBar = new Foo();
var what_is_blabla = FooBar.getBlabla(); //what_is_blabla will be 10
jsFiddle demonstration
Variables declared with var inside a function used as a constructor will, like all other variables declared with var inside any function, be visible only during the execution of that function (unless the value is closed over using closures).
In other words, blabla is effectively invisible outside the function:
var foo = new Foo();
console.log(foo.bla); // 1
console.log(foo.blabla); // throws NameError
By defining functions which close over these variables, they become the closest thing JavaScript has to "private" variables:
function Foo() {
this.bla = 1;
var private = 1;
this.increment = function() {
++private;
}
this.getPrivateValue = function() {
return private;
}
}
foo = new Foo();
console.log(foo.bla); // 1
foo.bla = 6; // legal
console.log(foo.bla); // 6
console.log(foo.getPrivateValue()); // 1
// console.log(foo.private); // would throw an error
foo.increment(); // legal
console.log(foo.getPrivateValue()); // 2
// foo.getPrivateValue() = 5; // syntax error. Still can't reassign to private no matter what you try!
That variable is local to the constructor and won't be accessible outside of that scope (be it through this or otherwise), unless it is captured by a closure.
If you don't use the var keyword, "blabla" becomes a global variable. At other points in the code if you also use blabla without var, it will also be global, and you can accidentally change other instances of blabla and introduce unintended bugs in your code. "var" puts the variable in the current scope, so in the case above, it's only accessible to Foo.
blabla can almost be considered a private member of Foo.
See this article from Douglas Crockford.
I'm trying to assign a callback dynamically to an Object of mine, I can't seem to figure out a way to do this while granting this function access to private variables. I've listed the relavant code below with comments where I ran into walls.
Object Factory
function makeObj ( o ) {
function F() {}
F.prototype = o;
return new F();
}
Module
var MODULE = (function(){
var myMod = {},
privateVar = "I'm private";
return myMod;
})();
Various Attempts
myMod.someDynamicFunc = function someDynamicFunc(){
//privateVar === undefined;
alert( privateVar );
}
myMod.someDynamicFunc();
myMod.prototype.someDynamicFunc = function someDynamicFunc(){
//ERROR: Cannot set property 'someDynamicFunc' of undefined
alert(privateVar);
}
myMod.someDynamicFunc();
In this attempt I tried making a setter in the module object... to no avail.
var MODULE = (function(){
var myMod = {},
privateVar = "I'm private";
myMod.setDynamicFunction = function ( func ){
if(func !== undefined && typeof func === "function"){
//Uncaught TypeError:
// Cannot read property 'dynamicFunction' of undefined
myMod.prototype.dynamicFunction = func;
//also tried myMod.dynamicFunction = func;
}
}
return myMod;
})();
var myModule = makeObject( MODULE );
myModule.setDynamicFunction(function(){
alert(privateVar);
});
myModule.dynamicFunction();
Am I just using JavaScript wrong? I'd really like to be able to assign callbacks after the object is initiated. Is this possible?
You can't access the private variable via a callback function set dynamically (since it can't be a closure if it's attached later), but you can set up a system by which you would be able to access the variable:
var MODULE = (function(){
var myMod = {},
privateVar = "I'm private";
myMod.callback = function(fn) {fn(privateVar);};
return myMod;
})();
var someDynamicFunc = function(param) {alert(param);};
myMod.callback(someDynamicFunc);
Of course, this makes it not really private, since anyone could do this. I don't see how it would be possible at all for you to have a "private" variable that you access via dynamically attached functions, without allowing anyone else's dynamically attached functions to have the same privilege (thus making it not really private).
I guess you did not really understand exactly how closures work.
Closures mean that scopes always have access to the outer scope they were defined in.
function Counter(start) {
var count = start;
return {
increment: function() { // has access to the outer scope
count++;
},
get: function() {
return count;
}
}
}
var foo = new Counter(4);
foo.increment();
foo.get(); // 5
The above example returns two closures, both the function increment as well as get keep a reference to the count variable defined in the constructor.
One cannot access count from the outside, the only way to interact with it is via the two "closured" functions.
Remember, closures work by keeping a reference to their outer scopes, so the following does not work:
var foo = new Counter(4);
foo.hack = function() { // is not getting defined in the same scope that the original count was
count = 1337;
};
This will not change the variable count that's inside of Counter since foo.hack was not defined in that scope, instead, it will create or override the global variable count.