0

JS libraries like Underscore.js and LoDash.js provide functions, usually via a global object _:

_.map(...);
_.filter(...);

Such basic functions are global functions in Python and part of the standard library:

map(...)
filter(...)

If I promoted the library functions to global functions...

window.map = _.map;
window.filter = _.filter;

map(...);
filter(...);

... what downsides would there be (other than global namespace pollution)?

Do I have to expect any performance issues?

Will it lead to errors in certain browsers? (e.g. if a built-in JS function is overwritten by a library function, which in turn relies on the native function)

Will chaining still work?

CodeManX
  • 11,159
  • 5
  • 49
  • 70
  • Why global? If it's *you* who are explicitly importing them, then import the functions into local variables. – Bergi Nov 02 '14 at 18:24
  • I find it much more convenient without having to type `_.` for basic functions, especially with my Python background (plus I'm a code aesthet). One could also argue the other way around, why to have global functions at all. A function like `alert()` should then be available via `window.alert()` only and so on. A great answer why you would want certain things to be global / short is given in this talk: https://www.youtube.com/watch?v=o9pEzgHorH0 – CodeManX Nov 02 '14 at 18:48
  • Yes, omit the `_.` and assign the `map` etc functions to *variables*. But there's no reason to make those variables global. Make them local to your module - to the section of code that you control. You wouldn't import a module and assign its methods to global variables in python either. – Bergi Nov 02 '14 at 20:21
  • Got your point. It's not uncommon in Python to import module functions into the global scope however: `from x import *`. You wouldn't do this with every module of course. To resolve name collisions, you can use `from x import y as z` – CodeManX Nov 02 '14 at 21:51
  • 1
    Notice that this imports functions into the *module* scope, not the global one (maybe Python has a little different terminology, as far as I can judge from [this question](http://stackoverflow.com/q/423379/1048572) the only application-wide globals in Python are modules, all other "global" variables are module-specific - in JS, "global" means application-wide). Therefore, globals are frowned upon in JS, and you'd [locally import everything explicitly](http://stackoverflow.com/q/21562973/1048572) – Bergi Nov 02 '14 at 23:04
  • You're right, functions will be global inside the module (or actually, just the current file from my own experience), they won't affect any other modules. So Python and JS work very differently here, and it makes sense to avoid the global namespace in JS. – CodeManX Nov 03 '14 at 09:30

4 Answers4

2

There are multiple downsides, but performance is not likely to be one of them.

What if two libraries have a map() function and the behaviors are slightly different? If they both put their map() in the global space, you won't be able to access one of them. And you might not be able to easily know that the map() you actually want has been overwritten.

By the way, map() and filter() are part of recent-ish JS implementations, but even there, they are part of the prototype of the objects that use them. So you have Array.prototype.map() and Array.prototype.filter().

Trott
  • 66,479
  • 23
  • 173
  • 212
  • If you try to use underscore and lodash alongside, they will overwrite `_` as well, unless you do e.g. `lodash = _.noConflict();`. I would only promote functions of one library to global functions of course, so I don't think this is an issue. Using prototype functions on objects is fine, but it's object-oriented in contrast to the functional programming-style of JS libarries. I wouldn't want to use `Array.prototype.map(...);` to call it, it would defeat the purpose of making functions global (=less to type, cleaner visual code). – CodeManX Nov 02 '14 at 18:34
2

Here are some reasons to avoid making lots of things like map and filter be global variables:

  1. Naming conflicts with other code/libraries attempting to do something similar, but incompatible. If all code is using numerous global functions, then the simple definition of a single global function could completely break your app because it would be redefining such a function used for something else somewhere else in the code. This, all by itself, is a reason to use as few globals as possible. In a team project or a project using significant third party libraries, this is impossible to manage and will inevitably lead to lost productivity or, even worse, bad bugs that might not immediately be apparent.

  2. Code modules are less self describing when a symbol is just global. Hmmm, where does the symbol map() come from. Is this built in? Is it defined locally? Is it defined by some library? It's a lot easier to know where gutils.map() comes from (from the gutils module). Large numbers of global anything is just less maintainable than breaking things into well defined modules.

  3. There are numerous advantages to defining functions as methods on the objects on which they operate (such as ES5 .map() and .filter() being methods on the Array object rather than as generic globals). Now, it is considered bad practice to add non-standard methods to existing built-in objects (also for naming collision reasons), but adding polyfills that implement standard behavior is encouraged.

See these references for other info:

How and Why to Avoid Globals in Javascript

I've Heard Global Variables Are Bad, What Alternative Solution Should I Use?

Essential Javascript Namespacing Patterns

Javascript Namespaces and Modules

How do I declare a namespace in JavaScript?


As to the topic of performance, it should not be your first concern and will likely not even be a relevant concern in most code. It is much more important to start with a robust, maintainable coding strategy and then optimize only small pieces of code where performance is critical than to sacrifice robustness at the beginning in the name of performance.

In any tight loop where performance is criticial, you can always assign any function to a local variable to slightly improve performance and this will be faster than either a module reference or a global reference (because locals are resolved before globals).

So, if you had gUtils.map() in a module and you want to maximize performance inside a particular function, you can do this:

function x() {
    var map = gUtils.map;


    for (var i = 0; i < someBigNumber; i++) {
        map(...);
    }
}
Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
1

You don't need to worry about performance; even if you add an extra layer to a symbol lookup, most modern JS engines will alias it. Nor should you worry about augmenting native objects causing exceptions - that's allowed. No, the big issue is the namespace pollution, which you seem to be a bit dismissive of.

What happens when your code runs on a newer browser that implements these functions natively? Let's say you'd written this code in 2009, and added .map() to Array.prototype. Your code would now have some big issues if someone added a new library that expected one function signature (the native one's) and got a different one (yours).

If you must do this, at least check whether the global symbol already exists, e.g.

window.map = window.map || myLib.map;

Also, make sure that any code relying on your map implementation can either signal its dependencies to a JavaScript dependency management tool like RequireJS, or that you have a robust DM system already in place, and that your native overrides are attached before any other code executes (e.g., don't put your code in an async script tag, don't defer execution until DOMContentLoaded, etc.)

Jimmy Breck-McKye
  • 2,923
  • 1
  • 22
  • 32
  • There are a lot of global functions in Python, thus you can't use their names for your own variables, and I never had any problems with that (so yes, I don't really mind the namespace pollution). If the signature of a lib function changed to follow a new standard, I would have to adjust my code I guess, or use an older version of the library. This does not seem to be a particular issue of having library functions in the global namespace (and having them overwrite built-in functions). I wonder if chaining will work however... – CodeManX Nov 02 '14 at 18:42
1

Consider a library like this,

_ = (function(){

   lib = {};

   lib.function1 = function(){
      //code
   };
   lib.function2 = function(){
     this.function1();
   };

   //more code

   return lib;

})();

if you use,

window.function2 = _.function2;
function2();

The library uses "this" operator in function2. Your way changes the scope of the function2 here. The function2() call will give an error saying "function1 is not defined".

Sampath Liyanage
  • 4,776
  • 2
  • 28
  • 40
  • Good point, so I would have to change the entire library to use globals, wouldn't I? – CodeManX Nov 02 '14 at 18:52
  • You can use "call()" method or "apply()" method. For example, here you can use either function2.call( _ ) or function2.apply( _ ) instead of using function2() in the last line. – Sampath Liyanage Nov 02 '14 at 19:06
  • Or you can use "bind()" method with "window.function2 = _.function2.bind( _ )". Then no problem occurs in "function2()" call. – Sampath Liyanage Nov 03 '14 at 09:40