Working in ReactJS, I run into an issue where imported functions that are 'unused' are failing to be recognized by the program and I believe are not being added to the window.
In my case, I'm trying to be able to import functions from other files and then call them by string name.
Ex)
import {myFunction} from '../otherFile';
functionNameString = 'myFunction'
window[functionNameString]() //call function by it's string name
//ERROR: window[functionNameString] is not a function
Without changing my above syntax, I've found two ways I can resolve this:
Add the actual function to the same file as the window[functionNameString]() call
Explicitly assign the function to the window at the top of my file like window.myFunction = myFunction
I'm trying to avoid the first case to keep this file shorter, but also don't understand why I need to do the explicit assignment of the function to the window as shown in the second case (and why defining the function in the same file doesn't need this)
Overall, my question is how can I avoid this explicit assignment of and have these imported functions callable from import (or in a shorter syntax)? Assigning like so is fine for a function or two, but I'm looking at importing 15 funcs from this other file which makes things messy working in this fashion. Thanks!
Modules have their own scope, they aren't at global scope, and imports are created as bindings within the module scope, not globals. That's at least half the point of modules: to not make everything global anymore.
Overall, my question is how can I avoid this explicit assignment of and have these imported functions callable?
They are callable. myFunction() will call your function in your example code. There's no reason whatsoever for window to be involved.
If you need to refer to them by a name in a string for some reason, you can put them in an object:
const functions = {
myFunction,
// ...
};
Then functions[functionNameString]() will work.
Related
I've work with some framework & even wrote some libraries for my own purpose. I now working at implementation of an AngularJs router... And looked again at DI of angular:
[...String, Function]
function.$inject
For long I've been using the first syntax. Now while testing my router, I wanted to see the behaviour if it differs for both syntaxes and how to handle this, but...
First Hand
module.controller(function SampleController()
{
});
// Since it's and `invokelater...` function which is called right away,
SampleController.$inject = [/** my component dependencies **/]
See my face when I faced:
ReferenceError: SampleController is not defined
The Other Hand
I consider it unclean to write:
function SampleController()
{
}
SampleController.$inject = [];
moddule.$inject = [];
So Finally
I know it won't work. Why? - That's my question.
Why?
We have been taught that module, class, method/functions, some for loop, if...else create new scope.
Never have I read something like:
function's parameters are evaluated in their own scope
Please Tell Me
Thanks!
Named function expressions only create a matching variable in their own scope (which is useful for calling the function recursively).
They create nothing in the scope of the function that holds them.
When you pass it as a function argument, then the variable you pass it into is scoped to the function call. i.e. function function_you_pass_the_function_to(someFunction) { } scopes the someFunction variable to itself.
I was experimenting to define the built-in method alert() to see what kind of error the console will throw, but instead the built-in alert() got overridden.
This is the code -
function alert(some_string) {
console.log(some_string+some_string);
}
function say() {
alert("Hello");
}
say();
The output is : HelloHello
Coming from a Java background, it would throw a compile error as :
error: method alert() is already defined
So what happened here? Did I actually override the method?
Overriding is a classical OO term which means that a child class has method with the same name as one of the classes it inherits from which is used instead of that method on the child class.
In this case, you are straight up taking the alert variable (which by default has a value of a function provided by the browser) and assigning it a new value (the function you just declared).
(That assumes you are working in the global scope. If you were in a local scope you would just be masking the variable so you would only be making alert inaccessible to other code in the same scope … and since you were writing that code too, it wouldn't be a problem because you would know if you needed the global alert and could avoid reusing its name.)
There might be hundreds of built-in functions and I might accidentally define a function with a same name as one of them, how can I check if such accidents don't happen?
There are two basic techniques for this.
Avoid creating globals
When you create a new variable, do it in as narrow a scope as possible. A common pattern is to use an IIFE to create a new scope for all the variables related to a given piece of code.
(function () {
"use strict";
function alert() {
// Locally scoped alert that doesn't get in the way
// of any variable called `alert` from the browser
// or another library
}
})();
Use namespaces
This is just a term for having a single global as the entry point to a bunch of related code. It is usually given an ALL_CAPS name that is unlikely to conflict with other code.
This allows the functions to be accessed from anywhere, like globals, without creating lots of global variables.
var MYLIBRARY;
(function () {
"use strict";
MYLIBRARY = {
alert: alert
};
function alert() { /* etc */ }
})();
I've seen the next way of writing self-invoking functions:
(function (app) {
app.foo = {
bar: function(){}
};
}(App));
Where App is a global object.
I wonder, why do we need to pass App as a param into a function? Why don't just use this:
(function () {
App.foo = {
bar: function(){}
};
}());
I see only one advantage of using the first way. If we for some reason rename the App object, then we can easily rename the param in brackets and our code will work as it works. But in case of the second way we will probably need to rename App in all places where we use it.
Are there other differences?
It means that the contents of the function – with regards to the app identifier – are agnostic to the global (or parent) scope.
One of the scenarios in which this is done is, for example, with jQuery, when you don't want to assume that the jQuery object is called $ (e.g. if no-conflict mode is on), but you do want to call it by that name. Passing it through an anonymous function like this (i.e. (function($) {})(jQuery)) allows you to generate a locally-scoped alias that doesn't interfere with the external meaning of the name $ in the parent/global scope.
The other answers explain the benefit of having a locally scoped copy. There are a few other benefits too:
As a micro-optimization it reduces scope lookups (it's less expensive to find a local var than a global one).
It can help in the minification process as all your params are reduce to single letter named vars.
It gives you a pseudo dependency management technique...in your example, you know your code is dependent on App.
For example, many libraries use the "$" character as a shortcut for their main function (e.g. JQuery). This is convenient, but can cause a clash when using multiple libraries. So, you could pass JQuery in like this:
(function($) {
// Code
var nav = $('#nav');
// More code
})(JQuery);
That way, you can still use the convenient shortcut, but you can also avoid clashes (as long as you also configure the libraries not to use the "$" shortcut).
You can also use it to redefine global variables locally to ensure they contain whay you expect:
(function($, undefined) { ... })(jQuery);
Where undefined is now the expected undefined. Also see: What is the purpose of passing-in undefined?
Most other uses are well covered in the other answers.
I'm new to JavaScript, and have a simple (I presume) question regarding best practices for accessing variables in functions:
When should I declare a global variable, as opposed to simple passing a value into a function?
Declaring a global variable should only be used as an option of last resort.
Global variables are bad in general and especially so in javascript. There is simply no way to prevent another piece of javascript from clobbering your global. The clobbering will happen silently and lead to runtime errors.
Take the following as an example.
// Your code
myParam = { prop: 42 };
function operateOnMyParam() {
console.log(myParam.prop);
}
Here i've declared 2 global variables
myParam
operateOnMyParam
This might work fine while testing your javascript in isolation. However what happens if after testing a user combines your javascript library with my javascript library that happens to have the following definitions
// My code
function myParam() {
console.log("...");
}
This also defines a global value named myParam which clashes with your myParam. Which one wins depends on the order in which the scripts were imported. But either way one of us is in trouble because one of our global objects is dead.
There are many, many reasons.. but an easy one is.. The argument of a function only exists in the function, while it's running. A global variable exists all the time, which means:
it takes up memory until you manually 'destroy' it
Every global variable name needs to be unique
If, within your function.. you call another function.. which ends up calling the first function, all of a sudden you may get unexpected results.
In short: because the function argument only lives for a really short time and does not exist outside the function, it's much easier to understand what's going on, and reduced the risk of bugs greatly.
When dealing with framework-less JavaScript I'll store my simple variables and functions in an object literal as to not clutter up the global namespace.
var myObject = {
variableA : "Foo",
variableB : "Bar",
functionA : function(){
//do something
//access local variables
this.variableA
}
}
//call functions and variables
myObject.variableA;
myObject.functionA();
I'm using JSLint to verify most of my external Javascript files, but the largest amount of errors I'm getting is from functions being used before they're defined.
Is this really an issue I should worry about?
It seems Firefox, IE7 and Chrome don't care. Functions like the popular init() (which I use often) normally stick at the top as that makes sense to me (I like to pretend it's analogous to main()) will, according to JSLint, need to be pushed to the bottom of the file.
As this is the top rated google hit and other people might not be seeing it at first in the jslint tool, there is a option called "Tolerate misordered definitions" that allows you to hide this type of error.
/*jslint latedef:false*/
If you declare functions using the function keyword, you can use them before they're declared. However, if you declare a function via another method (such as using a function expression or the Function constructor), you have to declare the function before you use it. See this page on the Mozilla Developer Network for more information.
Assuming you declare all your functions with the function keyword, I think it becomes a programming-style question. Personally, I prefer to structure my functions in a way that seems logical and makes the code as readable as possible. For example, like you, I'd put an init function at the top, because it's where everything starts from.
If you're using jshint you can set latedef to nofunc, which will ignore late function definitions only.
Documentation - http://www.jshint.com/docs/options/#latedef
Example usage:
/* jshint latedef:nofunc */
noop();
function noop() {}
Hope this helps.
From jslint's website (http://www.jslint.com/lint.html), you can read about a /*global*/ directive that allows you to set variables that are assumed to be declared elsewhere.
Here is an example (put this at the top of the file):
/*global var1,var2,var3,var4,var5*/
The :true :false is not actually needed from my experience, but it looks like it's recommended from what I read on the site.
Make sure the initial global statement is on the same line as /*, or else it breaks.
To disable this warning in jshint for all files, place this in your .jshintrc file:
{
"latedef": false
}
In your .jshintrc file, set:
"latedef": "nofunc",
it is very unfortunate the latedef option was removed. This is essential when trying to create a 'class' with an interface at the top, ie,
function SomeClass() {
var self = this;
self.func = func;
function func {
...
}
}
This style is very common but does not pass jsLint because func is 'used' before being defined. Having to use global for each 'member' function is a total pain.
You can always declare the offending function at the top
eg:
var init;
.... but then you'll have to remove the "var" when you get to the true definition further down:
init = function() {
};