Working with multiple closures has me all turned around. It seems that you define a variable in one closure then return it publicly so it can be accessed. Returning it requires giving it a public name. Then you pull it into another closure by defining it as a new variable with yet a third name?
My head is spinning a bit.
Here's a code snippet. It obviously doesn't work (obvious to you maybe, I had to struggle to get this far) Where am I breaking down? AM I making this too difficult on myself? Is there a simpler way or merely a 'best practice' way?
If this is the correct course... I'll commit to it. I'm not looking for an easy answer, but I am looking to truly grasp what I'm doing rather than regurgitate structures I've seen somewhere already. Thanks in advance.
//CREATES A PRIVATE CLOSURE TO GRAB DOM CLASS & ASSOCIATED VALUE
var createUI = (function(){
//Stores a class from DOM into VARIABLE
var buttonClass = ".add__btn";
//RETURNS...
return {
// THE VALUE FOUND IN THE DOM ELEMENT OF THE STORED CLASS ABOVE
classPublicValue : function(){
return {
value: document.querySelector(buttonClass).value
}
},
// RETURNS THE CLASS ITSELF FOR FUTURE SHORTHAND USAGE. A GOOD PRACTICE I'M TOLD??
theClass : function() {
return buttonClass;
}
}
})();
//CREATES A SECOND PRIVATE CLOSURE TO PERFORM ACTION ON CLICK THAT USING STRUCTURES FROM FIRST CLOSURE.
var clickToHappen = (function() {
//PULLS IN THE CLASS ".add__Btn" as a string.
var myClass = createUI.theClass();
//POINTS TO THE VALUE OF THE ELEMENT CONTAINING THE PULLED IN CLASS.
var myValue = createUI.classPublicValue();
//RETURNS...
return{
clickMe : function(){
//A CLICK LISTENER ON DOM ELEMENT WITH CLASS CHOSEN IN FIRST PRIVATE CLOSURE.
document.querySelector(myClass).addEventListener('click', function() {
//STORES CURRENT VALUE INTO VARIABLE
var value = createUI.classPublicValue();
//PRINTS THAT VALUE TO CONSOLE
console.log(value);
});
}
}
})();
Related
I have a Angular service and in it I have variables like this:
export class MyService {
someVariableA = 1;
someParams = {
someVariableB,
otherVariable: this.someVariableA
};
}
and in a component I set the 'someVariableA' to 3
this.myService.someVariableA = 3;
and I want 'otherVariable' to get that value 3 as well, but it doesn't. It remains 1 when I go to get the value.
let v = this.myService.someParams.otherVariable;
Is it possible to set 'otherVariable' this way or any other way via 'someVariableA'?
As #Zulwarnain answered, 1 is a number or a primitive data type. Primitive data types in javascript are passed by value, not by reference which you seem to be expecting here.
An easy fix for this is to assign a function to otherVariable instead. Now just invoke the function someParams.otherVariable() and it will return the value of someVariableA. No need to make this complicated.
export class SingletonService {
public someVariableA = 1;
public someParams = {
otherVariable: () => this.someVariableA
};
}
This is basic javascript with multiple sources covering the subject.
https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0
I concur with this answer that you will have a better time if you use a reference type like an object/array instead of a primitive value type like a number. By adding one layer of indirection (e.g., someVar = 123 becomes someVar = {value: 123}) you could very easily get similar functionality to what you're seeking.
If, however, your use case requires an object's property to directly act like a reference to a primitive value type stored somewhere else, you can get this behavior by implementing the property as a getter and setter pair. It's more complicated, but it acts the way you want.
Here's an example:
class MyService {
someVariableA = 1;
someParams: {
someVariableB: number;
otherVariable: number;
};
constructor() {
this.someVariableA = 1;
const that = this;
this.someParams = {
someVariableB: 2,
get otherVariable() {
return that.someVariableA
},
set otherVariable(val: number) {
that.someVariableA = val;
}
}
}
}
Note that in order for the otherVariable getter and setter to be able to access the right context, I had to move the code into the constructor and copy this into a new variable I called that. The this context of a getter/setter refers to the object it's a member of, and not some this from an outer scope.
Let's make sure it works:
const ms = new MyService();
ms.someVariableA = 100;
console.log(ms.someParams.otherVariable); // 100
ms.someParams.otherVariable = -5;
console.log(ms.someVariableA); // -5
Looks good; changes to ms.someVariableA are immediately reflected in ms.someParams.otherVariable, and vice versa. All right, hope that helps; good luck!
Playground link to code
You are assigning the value type this will not work like you want. you need to assign reference type
obj ={someVariableA : 1};
someParams = {
otherVariable: this.obj
};
in the above code, if you change the value of obj.someVariableA it will also change the value of someParams.otherVariable
I am expexting that you have knowledge about reference type and value types variables
click here for demo
I don't think you want to do that. I believe you are getting a new instance of the service each time you call it, so the variables get reset.
you might want to set that variable in localStorage instead, and then have the service retrieve it from localStorage. That way it will always be getting whatever it was last set to.
or just pass that variable into your service call, instead of trying to use a local service variable.
I've some functions, stored in a collection/array and would like to get the key (function-name) without retyping it. Is there any short way to access it?
var functions_collection = {
"function_x": function() {
var name = "function_x";
// name = this.key; <- how to get the key/function-name "function_x"?
// some more code like:
$(".function_x .button").val();
alert(name);
}
}
Edit: I'd like to avoid retyping the "function_x" inside the function itself and prefer to call it like this.key.
Sorry for the weird topic and thanks in advance!
Solution: A lot of good answers, but I was just looking for this snipped:
Object.keys(this)
I'm not sure it's what you want but you can do this :
var functions_collection = {};
(function(name){
functions_collection[name] = function(){
// use name, which is the function
alert(name);
};
})("function_x");
I'm not really sure it's better. But depending on your (unspecified) goal, there's probably a better solution.
To get the name of the objects keys, you can use Object.getOwnPropertyNames(this) or in newer browsers just Object.keys(this), and that will get you an array of all and any keys the this object has :
var functions_collection = {
function_x: function() {
var name = Object.keys(this);
console.log(name);
}
}
FIDDLE
In my opinion you´d need to change you above code since you are having anonymous functions which have no name - a change like this should work:
var functions_collection = {
'function_x' : function function_x () {
var myName = arguments.callee.name;
alert(myName);
}
}
see http://jsfiddle.net/9cN5q/1/
There are several ways you could go here. Some are good ideas, some are not.
First, some bad ideas
Bad idea: arguments.callee.name
This translates most directly to what you ask. arguments.callee is
a reference to the function you're currently in. However, it's
considered bad
practice,
and you should avoid using it unless you have a really good reason.
Bad idea: Currying
After constructing the function, bind its own name into it as a parameter:
var functions_collection = {
"function_x": function(name) {
alert(name);
},
//more functions
};
for (var name in functions_collection) {
if (typeof functions_collection[name] === "function") {
functions_collection[name] =
functions_collection[name].bind(functions_collection, name);
}
}
Currying is useful for lots of things in JavaScript, and it's a great idea in many situations. Not here, though, and I'll explain why below.
Bad idea: Use a local parameter and iterate through the containing object
var functions_collection = {
"function_x": function(name) {
alert(name);
},
//more functions
};
for (var name in functions_collection) {
if (typeof functions_collection[name] === "function") {
functions_collection[name](name);
}
}
Of course, the obvious problem with this one is that you might not want to call every function in the collection at once. The more fundamental problem is that it continues the trend of dangerously tight coupling. This is a bad thing, potentially a Very Bad Thing that will cost you all kinds of headaches down the line.
Now the "right" way
Change your whole approach. Forget trying to recycle class names from your HTML; just keep it simple.
Good idea: Use a local variable
Who cares what you name your functions? If you know which HTML classes you want them to touch, just code them that way.
var functions_collection = {
"function_x": function() {
var name = "function_x"; //or "button" or any other class name
alert(name);
},
//more functions
};
functions_collection.function_x();
Good idea: Pass a parameter
You're already calling the function, right? So there's probably already code somewhere with access to the name you want.
var functions_collection = {
"function_x": function(name) {
alert(name);
},
//more functions
};
functions_collection.function_x("function_x"); //or any other class name
Now you can use function_x on any class in your HTML, even if it doesn't match the function name:
functions_collection.function_x("function_y");
functions_collection.function_x("class_z");
functions_collection.function_x("button");
I've saved the simplest for last because I think you're making a mistake by trying to be "clever", if that makes sense. There are significant risks in your approach, and the payoff isn't going to be worth it.
Why the bad ideas are bad and the good ideas are good
Other than the arguments.callee.name option, the reason 2 and 3 are bad in this case is tight coupling. You're coupling function_x to the structure of functions_collection; you're coupling behavior to a variable name; and worst of all, you're coupling JS variables to the class names of HTML elements. This will make your code extremely fragile, and when you want to change something (and you will), get ready for a world of hurt.
For example, what happens if you reorganize your HTML? The page probably breaks, since the structure of your JS has to match the classes in your HTML/CSS. You'll have to rename or rewrite functions_collection and all others like it, or else you'll have to carefully plan new HTML around the JS you already have.
What happens if you want to use a JS minifier? Depends, but if you allow it to change member names in object literals, it completely breaks everything and you have to start over with one of the "good" ideas.
Now, what do you get in exchange for this inflexibility? You save an extra line at the beginning of each function. Not worth it, IMHO. Just bite the bullet and keep it simple.
Supposing that the variable name has the same name as its containing function:
var keys = [];
for (var p in functions_collection) {
if (typeof(functions_collection[p]) == 'function') {
keys.push(p);
}
}
And there you have it, an array with all the function names.
Which is the best way between:
var myClass = function() {
this.myContainer = function(){ return $(".container");}
this.mySubContainer = function(){ return this.myContainer().find(".sub"); }
}
AND
var myClass = function() {
this.myContainer = $(".container");
this.mySubContainer = this.myContainer.find(".sub");
}
Is there any concrete differences?
The memory problem arose when I have seen that my web page, that has enough javascript ( about 150KB of mine + libs ) takes more then 300-400MB of RAM. I'm trying to find out the problem and I don't know if this could be one of them.
function myClass{
this.myContainer = function(){ return $(".container");}
this.mySubContainer = function(){ return this.myContainer().find(".sub"); }
}
Here you will need to call it something like myClassInstance.myContainer() and that means jquery will search for .container element(s) any time you are using that function. Plus, it will create 2 additional closures any time you will create new instance of your class. And that will take some additional memory.
function myClass{
this.myContainer = $(".container");
this.mySubContainer = this.myContainer.find(".sub");
}
Here you will have myContainer pointing to an object which already contains all links to DOM nodes. jQuery will not search DOM any time you use myClassInstance.myContainer
So, second approach is better if you do not want to slow down your app. But first approach could be usefull if your DOM is frequently modified. But I do not beleave you have it modified so frequently that you may need to use second approach.
If there is a single variable you are trying to assign , then the second approach looks cleaner..
Yes they are different.
MODEL 1:
In the first model, myContainer is a function variable.It does not contain the jQuery object.You cannot call any of jQuery's methods on the objet. To actually get the jQuery object you will have to say
var obj = this.myContainer() or this.myContainer.call()
MODEL 2:
The second model stores the actual jQuery object.
try alerting this.myContainer in both models, u will seee the difference.
Yes this is different. after you fix your syntax error :
function myClass(){... // the parentheses
1st
When you create a new object var obj = new myClass(), you are creating two functions, and the jquery object is not returned until you call it
var container = obj.myContainer();
2nd
As soon as the object is initialized the dom is accessed and you have your objects cached for later use;
var container = obj.myContainer;
I'm making a google chrome extension and trying to get reference of a local variable within a closure scope.
// The script model of the target website
// I can't change any code of these
function Player(playerName){
this.name = playerName;
this.score = 0;
}
function Match(playerRed,playerBlue){
var player_red = new Player(playerRed);
var player_blue = new Player(playerBlue);
}
var tennis = new Match("Mike","John")
so what I'm trying to do in my content script is to inject a function into prototype of Match
just to get the variable player_red and player_blue:
function Match(playerRed,playerBlue){
var player_red = new Player(playerRed);
var player_blue = new Player(playerBlue);
//hoping to add this into Match.prototype
this.showMatchInfo = function(){
alert(player_red.name + " vs " + player_blue.name);
}
}
but this will not work because player_red and player_blue isn't defined under this.
I found this question through search. The solution is to "wrap the constructor in a new constructor and then set the prototypes equal". Unfortunately this doesn't work for me as I have no access to the original script of the website and probably because:
even by create new myMatch, the new myMatch doesn't not inherit the player_red and player_blue variable from their original Match instance.
Are there any possible workarounds? Thanks.
Notes on "partial solution":
Please note that the code snippets posted below only show "some alternatives which may or may not provide enough to get by". This is because they don't capture the values (Player objects) within the constructor, but only wrap the values going inside.
A "full solution" might also wrap the Player constructor and use a property or other mechanism to "remember" the objects created for different input values; alternatively, it could remember object creation order. This could then be used to wrap Match and then extract the created Players from the shared store after the Match constructor had run -- those details, however, are left as an exercise. The Player wrapping code can utilize the code presented below (assuming Player is a global/accessible property).
The exact request is not possible given the above context.
Variables (real variables, not properties) can only be accessed from the scope they are declared in or a nested scope as they are resolved through scope chains. This also includes usage of eval. While this may seem like a limitation, it also ensures that scope chains (and their variables) can't be externally mucked with unless exposed.
However, consider this fun approach, which utilizes the fact that an explicit object can be returned from a Constructor:
var oldMatch = Match
// note this form, else above would be pre-clobbered
Match = function Match (playerRed, playerBlue) {
var m = new oldMatch(playerRed, playerBlue)
// either "inject" method here, or save in object for later
m.myPlayerRed = playerRed
m.myPlayerBlue = playerBlue
return m
}
Of course, this will break things like new Match(...) instanceof Match.
Happy coding.
Update:
Here is a modification of the above to work with the "wrap the constructor in a new constructor and then set the prototypes equal" method as discussed in the link in the post. The trick is "stealing" the global properties name. I have also altered the code to keep oldMatch "private" to avoid pollution.
// note this form, else Match property would be pre-clobbered
Match = (function (oldMatch) {
function Match (playerRed, playerBlue) {
oldMatch.call(this, playerRed, playerBlue);
// either "inject" method here, or save in object for later
this.myPlayerRed = playerRed
this.myPlayerBlue = playerBlue
}
Match.prototype = oldMatch.prototype
return Match
})(Match)
Unlike the first code snippet, this should work with new Match(...) instanceof Match, but it may still break depending upon particular assumptions made within the Match object methods.
Example of how to invert ("extract") data from Player constructor:
// original -- remember this method will only work
// if Player is used as a property (and not itself a closure'd variable)
function Player (name) {
this.name = name
}
Player = (function (oldPlayer) {
function Player (name) {
oldPlayer.call(this, name)
var fn = arguments.callee
fn.recent = fn.recent || []
fn.recent.push([name, this])
}
Player.prototype = oldPlayer.prototype
return Player
})(Player)
var p1 = new Player("fred");
var p2 = new Player("barney");
alert("instanceof check? " + p1 instanceof Player)
alert("name check? " + ("barney" == p2.name))
alert(Player.recent.join(","))
Player.recent = [] // reset
I'm looking for patterns which have been found acceptable when working with instances of js objects on the same page. (If there is a thread already covering this, a link will be appreciated.)
The issue is one of reference. After an object/feature is instantiated, it has to be referenced at some point later.
I've seen jQuery people store a reference to the object on the target DOM element using data(). However, I'm interested in a framework agnostic option if possible.
This could be accomplished if there was a clean, viable way to generate an unique id for a DOM element. Alas, I have not found one yet.
So my question is: What is the best way to store reference to an object, via a DOM element, so that you can reference it at a future arbitrary time?
Hopefully this makes sense, and I'm not just rambling. :)
Thanks.
There is nothing stopping you from maintaining your own cache:
var cache = [];
function locate(el) {
// search for the element within our cache.
for (var i=0;i<cache.length;i++) {
if (cache[i].elem === el) {
return cache[i].data;
};
};
// if we get this far, it isn't in the cache: add it and return it.
return cache[cache.push({
elem: el,
data: {}
}) - 1].data;
};
// used to add data to an element and store it in our cache.
function storeData(el, data) {
var store = locate(el);
for (var x in data) {
store[x] = data[x];
};
};
// used to retrieve all data stored about the target element.
function getData(el) {
return locate(el);
};
and then use as follows:
storeData(document.getElementById("foo"), {
something: 4,
else: "bar"
});
var data = getData(document.getElementById("foo"));
alert(data.something); // "4";
Objects in JavaScript (unlike classical OOP languages) can be augmented. There's nothing wrong with that; that's the way JavaScript was designed to be used:
Write:
document.getElementById( 'foo' ).customAttribute = 5;
Read:
alert( document.getElementById( 'foo' ).customAttribute );
If you don't want to alter the original object, the only way to point at it is using a dictionary as pointed out in one of the previous answers; however, you don't need to do a linear search to find the object: it can be done in logarithmic time providing you use an ID per element (potentially not its HTML ID but a custom one)