Let's say that I want to get a list of all the variables in the window that are user-defined. In other words, they're not properties or objects that the browser has created or defined in ECMAScript.
For example, let's say there's this script on a page:
<script>
window.__$DEBUG = true;
var Analytics = function() {};
</script>
I would like to be able to loop through window and get a list containing __$DEBUG and its value, and Analytics and its value:
var nonNatives = (function nonNative(scope) {
var result = {};
for (var child in scope) {
if (!isNative(child)) {
result[child] = scope[child];
}
}
return result;
})(window);
Can this be done?
I've previously done this by creating a single function (loaded before any other JS) which remembers the current keys of window (i.e. the built-in properties) and which when called again displays the differences.
If the purpose is just to detect accidentally global variables, the aforementioned function can be an anonymous IIFE (such that it doesn't itself pollute the global scope) which contains the current list in scope, which then periodically calls another enclosed function (with setTimeout) to compare the list, and update it for next time, e.g:
(function(scope) {
var keys = Object.keys(scope);
var map = {};
for (var i = 0, n = keys.length; i < n; ++i) {
map[keys[i]] = 1;
}
(function update() {
var current = Object.keys(scope);
// compare lists and print the differences
for (i = 0, n = current.length; i < n; ++i) {
var name = current[i];
if (!(name in map)) {
console.log(name + ' = ' + scope[name]);
map[name] = 1;
}
}
// loop
setTimeout(update, 1000);
})();
})(window);
Related
JSHint shows the error:
"Function declared within loop referencing an outer scope variable may lead to confusing semantics".
How can I improve the following code to get rid of the warning?
var getPrecedence = function getPrecedence(operator, operators) {
var keys = Object.keys(Object(operators));
for (var i = 0, len = keys.length; i < len; i++) {
var check = Object.keys(operators[keys[i]]).some(function (item) {
return item === operator;
});
if (check) return operators[keys[i]][operator];
}
};
You are supposed not to use the function expression inside the loop body, but instead declare it outside:
function getPrecedence(operator, operators) {
function isOperator(item) {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
return item === operator;
}
var keys = Object.keys(Object(operators));
for (var i = 0, len = keys.length; i < len; i++) {
var check = Object.keys(operators[keys[i]]).some(isOperator);
// ^^^^^^^^^^
if (check) return operators[keys[i]][operator];
}
}
Of course the whole thing could be simplified by just using includes instead of some, and find instead of the loop:
function getPrecedence(operator, operators) {
var keys = Object.keys(Object(operators));
var opkey = keys.find(key =>
Object.keys(operators[key]).includes(operator)
);
if (opkey) return operators[opkey][operator];
}
And finally, Object.keys(…).includes(…) can be simplified to operator in operators[key].
Add it before calling the function, this one will bypass that check
/* jshint -W083 */
I'm in Chapter 3 - Functions of Eloquent Javascript.
Everything about the following code makes sense to me -- except one thing.
Why don't the sub-functions (for lack of a better term) have return statements?
var landscape = function() {
var result = "";
var flat = function(size) {
for (var count = 0; count < size; count++)
result += "_";
};
var mountain = function(size) {
result += "/";
for (var count = 0; count < size; count++)
result += "'";
result += "\\";
};
flat(3);
mountain(4);
flat(6);
mountain(1);
flat(1);
return result;
};
console.log(landscape());
// → ___/''''\______/'\_
Maybe I'm missing something fundamental about the purpose of the return statement, even after reading different definitions from different sources.
I've tried adding return statements to the sub-functions. I've found that it'll either terminate the sub-function pre-maturely, or produce the same result as if it was never there.
Thanks for reading.
They are accessing the outer scoped variable result and change something there. So they change the actual result variable's value and doesn't need to return anything. If it reaches to the end of the function, function ends. You can do the same by creating local function scoped variables and make strings from them, return them and concatenate in the main landscape function.
See the alternative of your code with the return statements
var landscape = function() {
var result = "";
var flat = function(size) {
var localFlat = ''; // Function scoped variable
for (var count = 0; count < size; count++) {
localFlat += "_";
}
return localFlat; // Return function scoped variable
};
var mountain = function(size) {
var localMountain = "/"; // Function scoped variable
for (var count = 0; count < size; count++) {
localMountain += "'";
}
localMountain += "\\";
return localMountain; // Return function scoped variable
};
result = flat(3) + mountain(4) + flat(6) + mountain(1) + flat(1); // Concatenate the results of each function
return result;
};
console.log(landscape());
Because your sub-functions affect your global landscape variable - result and in the end, function return result variable..This example shows you that you can change global variables into sub-functions..
I have this closure :
function CFetchNextData(ofs, pag, fetchFunction) {
var offset = ofs;
var limit = pag;
return function(options, cb) {
//do stuff to create params
fetchFunction(params, cb);
offset += limit;
};
}
I then create a variable this way:
var fetchInfo = CFetchNextData(0, 10, specificFetchFunction);
fetchInfo(options, myCB);
So that everytime I call fetchInfo, pagination is automatically set to the next set of data. That works great, althought
I'd like to have multiple instance of : "fetchInfo", each one having its own scope.
var A = fetchInfo; // I'd like a clone with its own scope, not a copy
var B = fetchInfo; // I'd like a clone with its own scope, not a copy
I could do:
var A = new CFetchNextData(ofs, pag, fetchFunction);
var B = new CFetchNextData(ofs, pag, fetchFunction);
But obviously I would have to setup "ofs" and "pag" each time, whereas by cloning fetchInfo, I'd have a stable pagination, set only once and for good.
Do you know how to achieve that ?
Thanks in advance
There isn't a concept of cloning a function in JavaScript. You need to call CFetchNextData (or another function) multiple times if you want to create multiple closures.
You could have CFetchNextData return a factory function instead of returning the actual function. But I'm not sure that's really an improvement.
function CFetchNextDataFactory(ofs, pag, fetchFunction) {
return function() {
var offset = ofs;
var limit = pag;
return function(options, cb) {
//do stuff to create params
fetchFunction(params, cb);
offset += limit;
};
};
}
var fetchInfoFactory = CFetchNextData(0, 10, specificFetchFunction);
var A = fetchInfoFactory();
var B = fetchInfoFactory();
This may not answer all of your question but just to pitch in , you could try assigning your parameters to a default / fallback value which will allow you to avoid setting ofs and pag each declaration . Below is a prototype of what I came up with . Its using oop :
class CFetchNextData {
constructor(ofs, pag){
this.OFS = 1; //default value
this.PAG = 10; //default value
this.ofs = ofs;
this.pag = pag;
if(ofs == null || ofs == undefined){
this.ofs = this.OFS;
}
if(pag = null || pag == undefined){
this.pag = this.PAG;
}
}
fetchInfo(){
var data = this.ofs += this.pag;
return data;
}
}
var task1 = new CFetchNextData(); // Falls back to default values..
var task2 = new CFetchNextData(32,31); // Uses values from specified in args...
document.write(task1.fetchInfo() + "\n")
document.write(task2.fetchInfo())
Hope this helps...
Quick bit about my background:
-been learning for about 3 months;
-work in tech support for a small software company. 2 years exp.
-a lot of knowledge is secondhand and I am still learning the basics
I am trying to create an object every second. The object is created directly to the last position of an array that remembers a set quantity of objects created before the most recent one
function Fruit(name, position) {
this.name = name;
this.position = position;
}
var showXMostRecentFruits = 20;
var fruitCounter = 0;
function generateName() {
var name = 'Experimental Fruit' + fruitCounter;
return name;
}
var fruitsArray = [];
function shiftFruits() {
for (i = 0; i < showXMostRecentFruits; i++) {
fruitsArray[i] = fruitsArray[i + 1];
}
function updateFruitPositions() {
for (i = 0; i < showXMostRecentFruits; i++) {
fruitsArray[i].position = i;
}
}
var fruitTimer; //used for setting and clearing setTimeout
function createNewFruit() {
shiftFruits();
fruitsArray[showXMostRecentFruits - 1] = new Fruit(generateName());
updateFruitPositions();
fruitCounter += 1;
fruitTimer = setTimeout(function() {
createNewFruit();
}, 1000);
}
Say the function createNewFruit() is run once
createNewFruit();
Then I try to pull some meaning from the array
console.log(fruitsArray[19];
All I get is:
Fruit {}
undefined
This issue is when I want to run a loop (see updateFruitPositions()) that updates a propery of each object in the array, an error is returned that the objects are undefined. I get that they are undefined because they are not assigned to unique variables (at least not that I'm aware of). How can I identify the objects or how can I create unique containers for them so I access them in the array?
You need to test whether a given element is set to something before attempting to write to one of its properties.
Instead of this...
for (i = 0; i < showXMostRecentFruits; i++) {
fruitsArray[i].position = i;
}
Use this:
for (i = 0; i < showXMostRecentFruits; i++) {
if (fruitsArray[i])
fruitsArray[i].position = i;
}
You fill the array from the end, staring with element 20. Without the if (fruitsArray[i]), you're attempting to set undefined.position = i for the first 19 elements.
You could replace the showFruits function with something much more efficient:
function shiftFruits() {
if (fruitsArray.length > showXMostRecentFruits) {
fruitsArray.shift();
}
}
and updateFruitPositions only needs to update members that exist, the length is controlled by shiftFruits:
function updateFruitPositions() {
for (i = 0; i < fruitsArray.length; i++) {
fruitsArray[i].position = i;
}
}
or where forEach is supported:
function updateFruitPositions() {
fruitsArray.forEach(function(fruit, i){fruit.position = i});
}
so it only visits members that exist. And the createNewFruit has:
fruitsArray.push(new Fruit(generateName());
I have a question to a specific behavior of javascript:
I have an object which I want to fill with generated functions. Each function contains a variable which is changed during the loop of function generation.
My problem is that the variable does not get replaced when assigning the function to the object. Instead the reference to the variable stays in the function and when executing the function only the last value of the variable is remembered.
Here is a minimal example (also on jsfiddle: http://jsfiddle.net/2FN6K/):
var obj = {};
for (var i = 0; i < 10; i++){
var nr = i;
obj[i] = function(){
console.log("result: " + nr);
}
}
for (var x = 0; x < 10; x++){
obj[x]();
}
The second loop executes all generated functions and all print a 9 as result. But i want that they print the value which the variable had at the time of generation (0, 1, 2, ...).
Is there a way to do this? Thanks in advance.
One approach is to call a function that returns a function:
function makeFunc(i) {
return function() {
console.log("result: " + i);
}
}
for (...) {
obj[i] = makeFunc(i);
}
Another approach is the immediately invoked function expression:
for (i = 0; ...; ...) {
(function(i) {
obj[i] = function() {
console.log("result: " + i);
}
})(i);
}
where in the latter case the (function(i) ... )(i) results in a permanent binding of i passed as a parameter to the outer function within the scope of the inner function
The problem is that all the functions you create are sharing a reference to the same nr variable. When you call them they fetch the value using that reference and therefore all of them get the same result.
Solve it like this:
for (var i = 0; i < 10; i++){
(function(nr) {
obj[nr] = function(){
console.log("result: " + nr);
}
})(i);
}
Your surmise is correct, and yes, there's a solution:
obj[i] = function( nr_copy ) {
return function() {
console.log("result: " + nr_copy);
};
}( nr );
JavaScript variables are scoped at the function level, unlike some other block-structured languages. That is, the fact that you declare "nr" inside the for loop doesn't make it "local" to that block — the effect is precisely the same as if you'd declared it at the top of the function.
By introducing another function scope with that anonymous function, you make a new copy of the value of "nr", which is then privately accessible to the actual function that's returned. Each of those functions will have it's own copy of the value of "nr" as it stood when that slot of the "obj" array was initialized.
what you want is to create a closure for every function you create.
Yet, the var(s) have not a block scope, so your code is the same as :
var obj = {};
var i;
var nr;
for (i = 0; i < 10; i++){
nr = i;
obj[i] = function(){
console.log("result: " + nr);
}
}
which hopefully makes it more obvious all functions will refer to the very same 'nr' var.
What you want to do implies creating a new scope each time, which might be done using bind, but let's stick to your original intent and build a new closure each time with a lambda :
var obj = {};
for (var i = 0; i < 10; i++) {
obj[i] = (function(){
var this_nr = i;
return function(){
console.log("result: " + this_nr);
}
}() );
}