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'
Related
i have passed the value 'hp' to the key function get() and can access it in the next line when it log it to console and then i proceed to pass the same variable to another function set() which is defined inside of the key function get() but the value is undefined when i try to access it in log statement of the inner function.
note- If i dont pass the variable company inside set() i can access it.
I have just started learning JS so any links would be appreciated.
var laptop = {
company: "none",
get: function(company) {
console.log("outer", company);
var set = function(company) {
console.log("inner", company);
};
set();
}
};
laptop.get("hp");
I think what you're looking for is something like the following:
var laptop = {
company: "none",
get: function(company) {
// use `this` to access the object's properties
console.log("outer", this.company);
var set = function(arg) {
console.log("inner", arg);
// alternatively, just use `company`
// because of `closures`, which give you
// access to an outer function's scrope
// from an inner function
console.log("inner #2", company);
};
set(company);
}
};
laptop.get("hp");
However, I'd strongly advise against using the same name for your variables/arguments/parameters inside the same scope, the way you have it in your example, to avoid confusion.
Learn more about closures in JavaScript:
JavaScript.info - Variable scope
I am really struggling to understand how does this works in constructor function for object creation in Javascript environment.
below is the code snippet:
function Employee() {
basicSalary = 12000;
overtime = 10;
rate = 200;
this.getWage = function () {
return basicSalary+(overtime*rate);
}
}
let emp = new Employee();
console.log(emp.basicSalary);//undefined
console.log(emp.getWage());//14000
in above code I am getting undefined for basicSalary variable. So in order to get basicSalary value I prefix it with this inside function like:
this.basicSalary = 12000;
But, in doing so I am getting error in accessing getWage() method. It says:
Uncaught ReferenceError: basicSalary is not defined
at Employee.getWage (index.js:6)
at index.js:11
However, if I prefix this to the basicSalary in returning statement inside getWage() function, I am able to access both basicSalary variable and getWage() method.
Also If I replace all my code to factory function i.e. Object literal type like here:
function employee(){
return {
basicSalary : 12000,
overtime : 10,
rate : 200,
getWage:function(){
return this.basicSalary+(this.overtime*this.rate); // accessing values through `this`
}
};
}
const e2 = employee();
console.log(e2.basicSalary);
console.log(e2.getWage());
Now I am easily able to get both basicSalary and getWage() method. Only thing that I needed to do was put this in return statement inside getWage() method.
in both these approaches this works differently. I am confused. Please share some knowledge on how to use this in Javascript.
In the first case, you are either (implicitly) declaring new local variables (scoped to the execution of the function), or referencing global variables that exist in a higher scope.
They are not bound to the object returned with a new.
In the second case you are returning an object literal, so they are bound to it, obviously.
Amit’s code is good. You can also do this:
function Employee() {
this.basicSalary = 12000;
this.overtime = 10;
this.rate = 200;
this.getWage = () => this.basicSalary + (this.overtime*this.rate)
}
If you have arrow functions though, you also have ES6 classes. That may be a better match.
Once you read the article I commented you can try this.
function Employee() {
this.basicSalary = 12000;
this.overtime = 10;
this.rate = 200;
this.getWage = function () {
return this.basicSalary + (this.overtime*this.rate);
}
}
let emp = new Employee();
console.log(emp.basicSalary);//12000
console.log(emp.getWage());//14000
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.
Watching a Douglas Crockford lecture on advanced JavaScript and he brings up the idea of parasitic inheritance which is essentially having constructors call other constructors to modify the object in question. Here is his code:
function gizmo(id, secret) {
secret = secret || {};
secret.id = id;
return {
toString: function () {
return "gizmo " + secret.id;
}
};
}
function hoozit(id) {
var secret = {},
that = gizmo(id, secret);
that.test = function (testid) {
return testid === secret.id;
};
return that;
}
var myHoozit = hoozit(20);
console.log(myHoozit.test(20)); //returns true
I understand the code and there is nothing too difficult to grasp here. The confusion takes place in the hoozit function. If you do not set secret = {} you will not get the a true being returned.
This is baffling because in the gizmo function, you see secret = secret || {} which should take care of this for us... but it doesn't.
Why is that short circuit not working correctly (secret = secret || {}) in the gizmo function when not being passed a second parameter in the hoozit function (breaks in both Chrome and Firefox)??
Why is that short circuit not working correctly (secret = secret || {}) in the gizmo function when not being passed a second parameter in the hoozit function (breaks in both Chrome and Firefox)??
Simple because you cannot access secret inside that.test because it does not exist in that scope:
function hoozit(id) {
var that = gizmo(id);
that.test = function (testid) {
// secret is not defined in this or in any higher scope
// hence you get a refernece error
return testid === secret.id;
};
return that;
}
The only secret object that exists is local to the gizmo function.
If you define it and just don't pass it to gizmo, then secret = secret || {} will evaluate to secret = {}, i.e. a new object is created inside the gizmo function. That value is only accessible within the gizmo function and is not related at all to the secret variable in the hoozit function. The secret object inside gizmo is a different than the one in hoozit.
function hoozit(id) {
var secret = {}, // secret object is created here
that = gizmo(id);
that.test = function (testid) {
// you never set `secret.id`, hence the comparison results in `false`
return testid === secret.id;
};
return that;
}
There is nothing wrong with secret = secret || {}, it is working as expected.
Let's look at a simpler example.
function Foo(baz){
baz = baz || {};
baz.boo = 1;
}
function Bar(baz){
baz = baz || {};
Foo(baz);
return baz;
}
If we call Bar(), we are passing an object to Foo. It then aguments the object by setting a boo property. Sweet!
Now, let's say our Bar function looks like this:
function Bar(baz){
baz = baz || {};
Foo();
return baz;
}
The only difference is that we are not passing an object to Foo. Because of this, Foo is creating an object inside its scope. We set a property on that new object, and then the function ends. The object isn't the parent scope of Bar, so Bar never knows this object is created, and has no way of accessing it. In the next few milliseconds, the object is deleted from RAM because there are no references to it.
That's not exactly the case with your question. The toString function references it. Because the outer scope is complete, it is now a local variable to that toString function. If we didn't pass a secret object, then it never leaves that scope. It must in some way be exported.
The more logical tactic would be to just create it as a property of our original object. Our secret could easily be accessed by a user that knows what JavaScript is, so we should save some headaches and use a sensible inheritance method.
If you don't know SomeFunction.call takes any number of arguments. The first is whatever you want to be this in the function, and the remainder are just the regular arguments.
function gizmo() {
this.toString = function () {
return "gizmo " + this.id;
};
};
function hoozit(id) {
this.id = id;
gizmo.call(this); // allow gizmo to manipulate this object
this.test = function (is) {
return this.id === is;
};
};
h = new hoozit(1);
console.log(h.test(1)); // true
console.log(h.toString()); // "gizmo 1"
You need secret = {}.
It's erroring at return testid === secret.id; because secret needs to exist.
The main magic you're probably looking for is, where is secret.id being populated, since all the operations are happening in gizmo(). The answer is this line: that = gizmo(id, secret);
secret is passed to gizmo and in JavaScript objects are passed by reference. This means that if you have a local object and pass that object as an argument to another function, any operations to that object will be reflected locally.
If you didn't want that to occur, you'd need some sort of copy/clone (the term clone has been used incorrectly by libraries to suggest a deep copy) of the argument. But in the example, you do want changes to secret in gizmo to update the secret in hoozit, so everything is working as it should.
Here's another way of writing it:
function gizmo(secret) { // only receive secret, which already has an "id"
secret = secret || {'id':null}; // if secret not passed create it with a default "id"
return {
toString: function () {
return "gizmo " + secret.id; // the reason why we needed a default "id"
}
};
}
function hoozit(id) {
var secret = {'id':id}, // create a object and set key "id" to hoozit's argument
that = gizmo(secret); // only pass secret
that.test = function (testid) {
return testid === secret.id;
};
return that;
}
var myHoozit = hoozit(20);
console.log( myHoozit.test(20) ); //returns true
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.