Help understanding javascript global abatement tec

2020-05-24 05:58发布

问题:

From the DailyJS "Let's build a JavaScript Framework" I'm not quite sure on the following code, obviously used as a global abatement technique.

My understanding so far balks at (function(){}) . I understand setting the turing var up, setting global.turing to turing, and return either window or this (if not in a browser), but the (function(global){})(this or window) thing confuses me...I've seen things like

var mything = {} and setting all your code up under mything, but this idiom confuses me a little.

I really want to understand the reasoning here vs memorizing that it "works out"

(function(global) {
  var turing = {
    VERSION: '0.0.1',
    lesson: 'Part 1: Library Architecture'
  };

  if (global.turing) {
    throw new Error('turing has already been defined');
  } else {
    global.turing = turing;
  }
})(typeof window === 'undefined' ? this : window);

回答1:

(This answer is over 4 years old (as of April, 2015) and while it's still correct I think it needs a more general explanation - see below)

Original answer

Think of this:

(function (x) {
    // ...
})(y);

as:

function functionName(x) {
    // ...
}
functionName(y);

but without the need to give it a name (like functionName).

So this:

(function(global) {
    // ...
})(typeof window === 'undefined' ? this : window);

is really just:

function functionName(global) {
    // ...
}
functionName(typeof window === 'undefined' ? this : window);

It is a function with one argument (called global within the function) and it is called with typeof window === 'undefined' ? this : window which means the same as:

function functionName(global) {
    // ...
}
if (typeof window === 'undefined') {
    functionName(this);
} else {
    functionName(window);
}

but using a shorter notation (and without naming the function).

More general explanation

I wrote this answer over 4 years ago and I think it's time to add some more general explanation of the concepts that are involved here.

As I explained above, this:

(function (x) {
    // ...
})(y);

is an anonymous version of this:

function functionName(x) {
    // ...
}
functionName(y);

which (if you call it only once) is usually (see below for exceptions) also the same as this:

function functionName() {
    var x = y;
    // ...
}
functionName();

Going back to the anonymous immediately invoked function, this:

(function (x) {
    // ...
})(y);

is the same as this:

(function () {
    var x = y;
    // ...
})();

which may be have a more obvious meaning for most people. (Here the immediately invoked function has no arguments and serves only the purpose of giving us an isolated scope for variables and other nested functions, so that we don't pollute the outer or global scope - which is also the main reason of using arguments in immediately invoked anonymous functions in the first place.)

On parentheses

By the way, this:

(function () {
    // ...
})();

is the same as this:

(function () {
    // ...
}());

The parentheses around the function are required because of a language ambiguity but they may include the () that actually invoke the function or they may not - though some people argue that the second form here looks more clear. See this explanation by Douglas Crockford for more details and why he thinks that the first version looks like "dog balls."

Exceptions

Previously I said that this:

(function (x) {
    // ...
}(y));

is the same as this:

(function () {
    var x = y;
    // ...
}());

and this is most of the time true for one argument (that doesn't mask the variable of the same name in the outer scope while depending on its value at the same time) and usually true for more than one argument (if they don't depend on each other as well). I hope it will get more clear on the examples.

When we have this code:

(function (x) {
    // ...
}(x + 1));

then we can't translate it into:

(function () {
    var x = x + 1;
    // ...
}());

because in the first version we add 1 to the outer x and bind the result to the inner x and as soon as we get inside the function we have only the inner x to work with (and even the new let statement will not help us with that).

Another example:

(function (x, outer_x) {
    // ...
}(1, x));

Here you can set old_x to the value of x from the outer scope and x in the inner scope to a new value of 1, and you don't have to worry about the order. But if you did:

Summary

So as you see there are some situations where you cannot simply translate:

(function (x) {
    // ...
}(y));

into:

(function () {
    var x = y;
    // ...
}());

but I would argue that if it can be translated into the second form then it should be for readability. Especially for larger functions it is inconvenient that you have to scroll to the end of the function to know what the variable used at the top of the function mean.

Back to the question

That means that I would translate this code from the question:

(function(global) {
  var turing = {
    VERSION: '0.0.1',
    lesson: 'Part 1: Library Architecture'
  };

  if (global.turing) {
    throw new Error('turing has already been defined');
  } else {
    global.turing = turing;
  }
})(typeof window === 'undefined' ? this : window);

into this:

(function () {
  var global = typeof window === 'undefined' ? this : window;
  var turing = {
    VERSION: '0.0.1',
    lesson: 'Part 1: Library Architecture'
  };

  if (global.turing) {
    throw new Error('turing has already been defined');
  } else {
    global.turing = turing;
  }
}());

I hope this answer explains why those two are equivalent, and that it could even be written as:

(function (global, turing) {
  if (global.turing) {
    throw new Error('turing has already been defined');
  } else {
    global.turing = turing;
  }
})(typeof window === 'undefined' ? this : window,
   {VERSION: '0.0.1', lesson: 'Part 1: Library Architecture'});

and it would still mean the same, while being much less readable at the same time.

See also my other answer where I explain similar concepts.



回答2:

This is an anonymous or lambda function. Most languages support this.

Using an anonymous function is really a matter of preference for the author. Without setting up an anonymous function to deal with the return value of a prior call, you would have to allocate additional memory on the client side by initializing a variable to contain the returned object and then pass it to a globally declared function later on in a serial manner.

If it is a 1 time function, it saves space, memory, time.

As a side-note: You can probably get a better feeling for this by googling around for some of the concepts behind functional programming. Lambda functions seem to be all the rage lately, especially in Python.