What is the “x = x || {}” technique in JavaScript - and how does it affect this IIFE? [duplicate]

StackOverflow https://stackoverflow.com/questions/12774762

  •  05-07-2021
  •  | 
  •  

First, a pseudo code example:

;(function(foo){

    foo.init = function(baz) { ... }

    foo.other = function() { ... }

    return foo;

}(window.FOO = window.FOO || {}));

Called like so:

FOO.init();

My question:

  • What is the technical name/description of: window.FOO = window.FOO || {}?

I understand what the code does... See below for my reason(s) for asking.


Reason for asking:

I'm calling the passed in global like so:

;(function(foo){
    ... foo vs. FOO, anyone else potentially confused? ...
}(window.FOO = window.FOO || {}));

... but I just don't like calling that lowercase "foo", considering that the global is called capitalized FOO... It just seems confusing.

If I knew the technical name of this technique, I could say:

;(function(technicalname){
    ... do something with technicalname, not to be confused with FOO ...
}(window.FOO = window.FOO || {}));

I've seen a recent (awesome) example where they called it "exports":

;(function(exports){
    ...
}(window.Lib = window.Lib || {}));

I guess I'm just trying to standardize my coding conventions... I'd like to learn what the pros do and how they think (that's why I'm asking here)!

有帮助吗?

解决方案

Pattern
(function (foo) {
    ...code...
    foo.bar = baz;
    ...more code...
}(window.FOO = window.FOO || {});

There is no formal name for the pattern you describe, because it's three separate patterns combined. Each pattern goes by multiple names, but for this post I will use the following terminology:

  • closure
  • alias
  • namespace extension

Closure

The base of the entire pattern is the closure. It is simply a function that is used to scope variables and functions such that they don't pollute the global namespace:

No closure
//these declare window.foo and window.bar respectively
//as such, they pollute the global namespace
var foo;
function bar() {}
Closure, in this case, an Immediately Invoked Functional Expression (IIFE)
(function () {
    //these declare foo and bar within the function
    //but they are not accessible outside the function
    var foo;
    function bar() {}
}());

The advantage of keeping variables within a closure is that you won't have to worry about someone overwriting the variables that you're using. This is especially important for temporary variables such as i or j that are used often.

Alias

The second important part of this pattern is aliasing. Aliasing allows a variable to be defined and used within a closure without needing to worry about what global namespace it resides in.

Without Aliasing
(function () {
    ...
    foo = window.SomeFunction(bar, baz);
    ...
}());
With Aliasing
(function (sf) { //local name
    ...
    foo = sf(bar, baz);
    ...
}(window.SomeFunction)); //global namespace

This is especially important as it means that the global namespace can be changed across a large JavaScript file by changing the name in a single location. This is A Good Thing™. Additionally, minifiers can shorten the internal alias to a single letter variable name such as a, making for significant byte savings on minification.

Namespace Extension

The namespace extension pattern relies on the coalescing behavior of the or operator (||). In many languages, && and || return either true or false, but in JavaScript, && returns the first falsey value (false, 0, '', null, undefined), and || returns the first truthy value (anything that's not falsey). For both operators, if the respective type is not found, the last argument is returned. This makes the || operator a convenient way of defining a new namespace only if it doesn't already exist.

Without namespace extension
if (typeof window.Foo === 'undefined') {
    window.foo = {};
}
With namespace extension
window.foo = window.foo || {};

This is useful because it allows a namespace to be extended with additional properties and methods without having to worry about which order the properties and methods were defined in.

In this first example, FileA would need to be executed before FileB:

FileA.js
window.foo = {};
window.foo.bar = 'baz';
FileB.js
window.foo.fizz = 'buzz';

In this second example, File1 and File2 could be executed in any order:

File1.js
window.foo = window.foo || {};
window.foo.bar = 'baz';
File2.js
window.foo = window.foo || {};
window.foo.fizz = 'buzz';

All together now

Using each pattern together creates a very powerful modular script:

//use foo internally so that you don't have to worry about
//what the global namespace is called
(function (foo) {
    //declare variables internally that you want to keep local to the script
    var i,
        len,
        internal,
        qux;
    //declare functions/properties on the alias when you want to expose them
    foo.bar = function () {...};
//extend the global namespace so that existing extensions are persistent
}(window.FOO = window.FOO || {}));

其他提示

I've always understood it as "Null Coalescing".

As for affecting your IIFE, you are passing in window.FOO if it is already instantiated, or an empty object if it is not.

You could also read it as:

window.FOO = window.FOO || {};
;(function(foo){

    foo.init = function(baz) { ... }

    foo.other = function() { ... }

    return foo;

}(window.FOO));

Personally, I prefer a different pattern:

var FOO;
if (!FOO) {
    FOO = {};
}
(function () {
    "use strict";
    FOO.prop1 = 'bar';
    FOO.bar = function (z) {
        return z + 1;
    };
}());

I find it to be less confusing and helps me to ensure clean namespacing.

To complement pete's answer, an alternate form:

;(function() {
  var Foo = window.Foo = window.Foo || {};

  Foo.foo  = 'bar';
  Foo.baz  = function() {
    return "Hello World!";
  };
})();

I usually use the local var to give the minifier the opportunity to save a few bytes. This has a bigger effect when you are working on several levels deep in your namespace like on var MyView = window.MyApp.Views.MyView.

As others have noted, your first question is completely separate and independent of your second question. They have nothing to do with each other except for the fact that you have decided to combine them in a single statement rather than doing it in two statements.

For you first question, Douglas Crockford calls it default assignment. As in, if the variable exists then leave it alone otherwise initialize it to the specified default value. When you see something that looks like:

foo = foo || {};

Your mind's eye should read it as:

foo defaults to `{}`

Technically it's really "assign {} to foo if foo is falsy" but we've made the assumption that anything falsy such as zero, null or undefined is an invalid value for foo when we use this. Also, the word default already implies "if foo is not defined".

But, having said that and knowing your second question it's obvious that default is not really an appropriate word as the name of the parameter passed to your IIFE. What that parameter is doing is simply attach methods and attributes to the object passed in. In which case exports is appropriate as in "these are the publicly exported members of the object". I would think attach is also an appropriate name as in "attach these things to the object". My personal preference is simply to name it obj as in object, which is the type of thing you expect to be passed in.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top