I have the following code that I am trying to use to register a callback on an array of buttons. But I cannot seem to understand how I can bind the strings that I would need in the callback. Any suggestions would be much appreciated!
for (var i = 0; i < this.car_types.length; ++i) {
this.select_car_buttons.push($("#button_select_car_" +
this.car_types[i].car_type));
this.select_car_buttons[this.select_car_buttons.length - 1]
.click(function() {
console.log(this.car_types[i].car_type);
}.bind(this));
}
Somehow the this object is the button itself and not the object under whose scope the function is called.
EDIT : It seems like the this object was indeed being passed in properly. The issue is that the variable i is not going out of scope and is being captured by reference not by value. How should I go about solving this problem?
Also there seem to lots of such issues with JavaScript as a language (at least things that can be classified as an issue considering the semantics employed by the traditional C family languages such as C and C++ to be correct), is there some article I can read that warns me against these types of issues?
ANOTHER EDIT : On trying making a closure with the value of i captured by value I tried the following code
this.select_car_buttons[this.select_car_buttons.length - 1]
.click((function(scoped_i) {
return function() {
console.log(this.car_types[scoped_i].car_type);
}.bind(this);
}(i)));
But I get the following error in Safari
TypeError: undefined is not an object (evaluating 'scoped_i')
EDIT : The same code works in Firefox and Chrome but not in Safari!
This is a scope issue. For modern browsers (that support ES6) you could just change var to let in your for loop and it would get fixed.
for (let i = 0; i < this.car_types.length; ++i)
Quoting the MDN docs
The let statement declares a block scope local variable, optionally initializing it to a value.
For more global support (non ES6 support) use an immediately invoked function to create extra scope for the variable (which you will pass as a parameter)
this.select_car_buttons[this.select_car_buttons.length - 1]
.click((function(scoped_i) { // IIF starts here, the new variable is called scoped_i for verbosity
return function() { // your original function code goes here
console.log(this.car_types[scoped_i].car_type); // use the newly scoped variable
}.bind(this);
}.bind(this)(i))); // end and execute the IIF while passing the i variable to it
Yes, this structure do make a lot of closures and make code very hard to read. Since you use jQuery, there are a much better way to solve this problem which saves the data in html:
html:
<button class="select-car" data-car-type="CarA">Select CarA</button>
<button class="select-car" data-car-type="CarB">Select CarB</button>
<!-- And a lot of buttons -->
js:
var selectCarOnClick = function() {
console.info($(this).data('car-type'));
};
$('button.select-car').click(selectCarOnClick);
Live exmaple: http://codepen.io/SCLeo/pen/VaQYjW
If you have a lot of other information to store and you want to use a object to store them instead of DOM, you can save car-name or car-id instead of car-type.
Here is the document about $.data: https://api.jquery.com/jquery.data/
Related
In Javascript, local variables do not live on any object that I'm aware of. That is,
function foo() {
const x = 2;
self.x; // undefined
this.x; // undefined
window.x; // undefined
x; // 2, obviously
eval('x'); // 2
}
The last option eval('x') shows that it is possible to refer to these variables by name. I'm looking to extend this and access a variable using a name pattern:
function foo() {
// Code not under my direct control
function foobar_abc() {}
function other_functions() {}
// Code under my control
const matchingFunction = // find the function with name matching 'foobar_*'
}
If this lived on an object, I would use something like myObject[Object.keys(myObject).find((key) => key.startsWith('foobar_'))]. If it were in the global scope, myObject would be window and everything works.
The fact that eval is able to access the variable by name implies that the value is available somewhere. Is there any way to find this variable? Or must I resort to techniques which re-write the (potentially very complex) code which is not under my direct control?
I'm targeting modern browsers, and in this use case I don't mind using eval or similarly hacky solutions. Arbitrary code is already being executed, because the execution of user-provided code is the purpose.
Another option is to use code parsing to deduce the function names using a javascript AST (abstract syntax tree) library. The "esprima" package will probably be good place to look:
https://www.npmjs.com/package/esprima
So you can do
import esprima from 'esprima'
const userCodeStructure = esprima.parseScript( userProvidedJavascriptString );
const allTopLevelFunctionDeclarations = userCodeStructure.body.filter( declaration => declaration.type === "FunctionDeclaration" );
const allTopLevelFunctionNames = allTopLevelFunctionDeclarations.map( d => d.id );
I haven't tried this myself by the documentation suggests it should work (or something like it):
http://esprima.readthedocs.io/en/latest/
One possible approach that might help you here is to evaluate at global scope rather than in a function, and that might put the functions on the window object instead of in local scope.
Easiest way to do this is probably to write a new tag into the document and inject the user-provided code.
Relying on variable names is the wrong approach.
eval is evil. It may not be available under CSP. Considering that the code is supposed to run in browser, the biggest problem is that variables don't have expected names in minified code. They are a, b, c...
In order to maintain their names, they should be object properties - and so they will be available on the object.
Or must I resort to techniques which re-write the (potentially very complex) code
This is refactoring and that's what should be done to avoid bad code that smells and creates major problems.
I'm still learning so I might be wording this wronf (which might be why I haven't had any luck in finding an answer by searching thus far) or I might be doing this wrong all together.
Basically I'm trying to create a function that, when called, will create an object (which is made up of other objects). If I create this Characters object outside of the function, I can access it as I'd expect to but when done inside the function, I can't seem to access it. I'm testing this in the console built into Chrome.
Here's my code.
function q1(ans) { //Are you male or female?
"use strict";
//store all above objects into new object
if (ans === "male") {
var Characters = {
spike: spike,
jet: jet,
vicious: vicious
};
}
if (ans === "female") {
//store all above objects into new object
var Characters = {
faye: faye,
julia: julia,
ed: ed
};
}
return Characters;
}
(The objects spike, jet, vicious (etc) are defined elsewhere and can be accessed without any issue.)
If I call my function q1("male"); the console seems to log that the Characters object is created but if I then try to access it by just typing Characters I get an error (ReferenceError: Characters is not defined). If I were to create this Characters object outside of a function and then try to access it as mentioned above, this works fine. This leads me to believe I'm not returning it properly (or I'm doing something else wrong entirely). What am I missing here?
Thanks in advance for any assistance.
this is due to scope. this means that character is only live inside the function so you can't access it outside of the function. what you have to do is set a new variable equal to the return value of the function when u call it. That way you can reference the returned variable outside the function and use it later on.
var male = q1("male")
Since you have already defined characters outside the scope of q1 you will need to reassign the Characters variable by returning Characters from the method.
So reassign the Characters and it should fix the problem.
One more very bad way is to not use var Characters, instead use Characters = ..., but bear in mind using global variable is not at all recommended and is a very bad practice.
I have some function defined inside a function, but set to global:
function someFn(someVar) {
var arr = makeArray(someVar);
// global function:
foo = function(bar) {
return arr.indexOf(bar)==-1;
}
}
When I debug with chrome console:
> foo
function (bar){return arr.indexOf(bar)==-1;}
can I get, without changing the source code, the content of arr?
Let's assume I can't just get and execute the commands by which arr was generated, because I don't know the last value of someVar.
The variable has to be somewhere in the memory, because when I call the function, it can be accessed, but what name does it have? I already tried:
> foo.arr
undefined
X-Y problem response; I don't know how you could access arr from the console, but I can give you a few tips to debugging via a breakpoint, even if the code is minified.
Try using Google Chrome, and in Debugger, click the {} icon below the code window. That will pretty-print the script, though the varnames will still be minified. (_a1 = new_43` etc.)
Next is the art of reading minified code; Code minifiers can't change anything that is accessed via a string; the most signature of which are object properties (anything after a .). It also helps that without whitespace, it becomes easier to find a particular sequence of symbols. You could try CTRL-Fing for terms like .indexOf, ==-1, etc. If this is an actual corporate-released library, chances are you can go beyond your little example and refer to things like myCompanyLibrary.dockInstance.locator=.
Once you find that line, you can put a breakpoint there. Good luck!
var a = {};
a.__defineGetter__('test',function() {return 5;});
var i ="test";
Is there any other way I can execute the getter besides a[i] (while using var i to do it)
EDIT:
I was asking ways to use var i to do it. I'll explain the real problem a bit better.
I am using getters on my namespace object to load modules only when needed.
MyNameSpace.__defineGetter__('db',function(){MyNameSpace.loadModule('db');});
In this case I am trying to load all modules:
for (var i in MyNameSpace){
MyNameSpace[i];
}
I use Google closure compiler on my code, and it reduces that loop above to:
for(var i in MyNameSpace);
No modules get loaded. I am trying to "trick" gcc into letting me load the modules.
You can do either a.test or a['test'] - both will access the test property of a and hence call the getter.
Edit: Ah, I see exactly what you want now. What you're doing is a clever use of getters, but unfortunately getters and setters aren't part of the current JavaScript standard (they are in ECMAScript 5 which isn't widely supported yet). Google Closure Tools seems to assume that reading a variable can't have any side-effect, which is true in the current versions of JavaScript, so I see no way to get around that. You'll have to edit the output to insert that stuff back.
Also, this isn't related to your question, but I do hope you're doing an additional hasOwnProperty check within the for-in construct.
I guess closure compiler optimizes out the code because it doesn't actually do anything but access the properties. this should work:
module = {}; // global
for (var i in MyNameSpace){
module = MyNameSpace[i];
}
Looking at your module example, seems like you just want a little refactoring there.
var moduleNames = { 'db', 'input', 'etc' };
for ( var name in moduleNames ) {
MyNameSpace.__defineGetter__(name,function(){MyNameSpace.loadModule(name);});
}
function loadAll() {
for ( var name in moduleNames ) {
MyNameSpace.loadModule(name);
}
}
If the functions themselves are less trivial than that, then you similarly want to collect the functions into a handy dictionary ahead of time, then loop over those to create the getter, and loop again to create the load all function.
I prefer to declare one Javascript file for my all website. I am trying to decrease the usage of global variables. My examples at the below, in both case each object has a myName field.
I would like to know when they are initialized?
And In terms of memory and efficiency which one is more effective?
For variable a, is declaring a.myName the same as global "var myName = Rebecca" ?
var a = {
myName : 'Rebecca' ,
sayHello : function() {
console.log(this.myName);
}
};
var b = {
myName : function() {
return 'Rebecca';
},
sayHello : function() {
console.log(this.myName());
}
};
Thanks
I believe these will be initialized identically (i.e. when the code is reached). What's different is what's happening when they are initialized and where the load is placed when their data is actually required.
To me, it would depend a lot on what you were expecting to have in myName. If it were just a string, I'd avoid the function and go with choice a. On the other hand, if there were a great deal of logic involved and that logic might not need to be invoked (for example, if it only gets executed when a user clicks on a button or the application reaches a certain state), I'd go with choice b. As I understand it, the function does consume memory and won't get garbage collected (which is a minus), but it also won't consume CPU resources until it's actually needed (which can be a huge plus).
I'm not sure I understand the question, but I'd say it's not the same. If the only member of a is myName then the two are equivalent (both are occupying the global namespace. But if you have multiple properties, the savings become obvious. From your examples, I think it's clear you understand this, so again I may not understand the question.
They will be initialized when the statements are first encountered. In a, 'Rebecca' is initialized as the value for the myName key. In b, it's just data internal to the myName (anonymous) function. a will be slightly more efficient because it avoids a function call. I also find it more readable in this simple example.
I find the choice to put everything in a single file questionable. In some cases, you want a modular design. And since you're worried about efficiency (albeit perhaps prematurely), note that having one big file can actually hurt performance if pages include code they don't need.
1) They are initialized when the script is processed in the browser, unless you declare the objects in an event handler. In that case the object is created when the event script is executed.
2) In terms of efficiency, a will probably be more efficient. Note though that in the first case you use a.myName and in the second b.myName() to get the value of the property.
3) No. If you assign a value to a property of an object, you always have to get that value through the object. In this case either a.myName or a['myName'].
a doesn't make any sense, because you're logging a function reference. B is the way to go, since you're actually invoking the method, using ().