可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I imitated a library and was able to write following code. This code created 'c'
object to which 'a'
function is assigned. So, to call 'a'
, I will have to write c.a()
.
Also, I was able to add more functions to this 'c'
object. I want to understand what is happening in this code. It doesn't look like normal object oriented programming. What is this type of javascript programming called?
var c = (function(c) {
if (c === undefined) {
c = {};
}
function a() {
alert(1);
}
c.a = a;
return c;
}(c));
回答1:
It's a module pattern. You'll see many variants of that pattern, so it's essential to understand what really happens, you can't just imitate one.
The point of this piece of code is to complete an object c
(typically your global library). You probably have many similar pieces of code in your application, all building pieces of c
, probably each of those in its own file.
In case the library object c
, which is passed as argument to the function, doesn't exist yet ( c === undefined
), it is created. This makes it possible to not depend of the execution order or of a preexecuted file.
The right part of the assignment is an IIFE (Immediately Invoked Function Expression), that is a function which is immediately called. The advantage of this construction is that it creates a scope in which variables (for example the a
function) can be declared without polluting the external (global) scope. Here the point is moot as a
is externalized anyway but a module typically depends on several internal (private) functions and variables.
A detail that might need an explanation : all those files look like they define a new variable c
but there's no problem here, even if the files are concatenated : a var
statements doesn't define a new variable if it already exists (a variable is defined for the whole scope, here globally, even before the point of declaration).
Another way to write this would have been
var c = c || {}; // ensure the c variable is defined, and initialize its value it if necessary
(function() { // let's use an IIFE to have a protected scope and not pollute the global one
function a() {
alert(1);
}
c.a = a; // let's augment c
})();
This one is probably clearer as
- it explicitly separates the two steps (
c
initialization and c
completion using an IIFE)
- it doesn't depend on two
c
variables with the same name
- it is less verbose
回答2:
Here's the same code with added comments on what every line does, and what happens when we pass it.
//Here, we're defining a function that will return an object.
//its only parameter is named 'c'
//this is confusing, since the parameter has the same name as the global definition of the function.
//the var c definition is the global definition. the function parameter is not.
//so every reference to anything named 'c' inside the function definition is local.
var c = (function(c)
{
//if c (the c we passed as a parameter) is not yet defined
if (c === undefined) {
//define c as an object
c = {};
}
//define a function
function a() {
alert(1);
}
//attach it to the object
c.a = a;
//return the object
return c;
}(c)); // call the constructor we just defined, using the global definition of `c`.
// the first time we pass this point, c as a variable is still undefined.
回答3:
var c = (function(c) {
if (c === undefined) {
c = {};
}
function a() {
alert(1);
}
c.a = a;
return c;
}(c));
Let us take it step by step.
var c =
initializing a variable named c. Note that at this point, if a variable named as c is already initialized, c
would refer to that value only until we reach the end of this declaration.
( .... )
This means that whatever is inside should be treated as an expression.
function(c)
means that this "expression" is a function which takes an argument. This argument would henceforth be referred by the name c
until the function is over.
Any variable with the name c
which was declared outside the function scope would thus not be accessible directly here. Although if it is in the global scope, and the global scope happens to be the window object, it can referred to as window.c
.
if (c === undefined) { ... }
checks if the argument passed to it is undefined or not. Would return true if it is undefined, and thus execute what ever is inside the if block.
c = {}
set the variable c
to an empty object. So if the argument was (passed or) undefined, we define it ourselves here (and we define it to an empty object....).
function a() { alert(1); }
declared a function with the name a
calling which willl result in alerting the numeral 1. Note that this is just a function declaration. We haven't called the function yet.
c.a = a
The argument c
is now assigned a property named a
which refers to the function a
we just created.
return c
break out of the function with the return value as the final value of c
, after undergoing the changes we did to the argument passed.
(fun...}(c))
call the function that we just created, and pass to it as an argument the present value of c
. since we called the function as an expression, the result of this expression would be whatever is the return value of the function. And our function returns an object (which we passed to it), after assigning a property to it.
Since this expression was equated to the variable c
, the return value of the expression (which is the return value of the function), is now held with the variable c
.
If you read all this properly, you would know that the variable c
would now hold an object, which would have a property a
which is a function which alerts the number 1. And that object also retains the properties it might have had before.
If we de-uglify this code to make it readable just by making the variable names descriptive:
var myObject = (function(someObject) {
if (someObject === undefined) {
someObject = {};
}
function alertOne () {
alert(1);
}
someObject.alertOne = alertOne;
return someObject;
}(myObject));
This show is an episode from the series module revealing pattern, which tells us how to add additional properties to an object, which was declared previously, in an elegant manner without polluting the global scope.
Starring Immediately Invoked Function Expressions (IIFEs).
回答4:
Why at the end, we wrote (c)) again?
This is named immediately invoked function.
(function(received argument){
//some code here.......
})(passed argument) //<-- here the function is invoked
c
is the passed argument. The function is invoked immediately when it is created. This type of declaration is used to keep variables private and to keep the global namespace clean.
The pattern in your code is used to create modules.
............Now come to your code :............
if passed argument is undefined : .............
Firstly, ...}(c))
this part of the immediately invoked function is invoked.It is passed with an argument called c
which is not defined yet. (function(c){...
part receive this c
argument .
Here at first passed argument c
is undefined.So if(c==undefined)
is triggered .At this point using c={}
statement,undefined object c
is assigned an empty object
.
Here function a() { //... }
is a private method created inside a module.It can't be accessed globally.
Private method a
is is made globally available by assigning it to c using c.a=a
statement.Thus when the object will return you can call this method on global context.
Thus newly created empty object c
is assigned a method called a
.Then it returns and var c
receive this object.
if passed argument is not undefined : ............
But if passed c
is not undefined
say,if an object is passed,then no new object is created .I mean if if(c==undefined)
is falsy.So , it is not executed.I mean no new empty object is created.Then Passed object is assigned a new method called a
using c.a=a
It is simple as that.
The code below is much simpler version of your code. It will automatically send an empty object if it is initially undefined.So you don't have to take the trouble of checking whether it is undefined or not.It is called loose augmentation.
var c = (function(c) {
function a() {
alert(1);
}
c.a = a;
return c;
}(c || {} ));
回答5:
This article written by Ben Cherry helped me understand this type of pattern: http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html
Excerpt from the article:
Anonymous Closures
This is the fundamental construct that makes it all possible, and really is the single best feature of JavaScript. We’ll simply create an anonymous function, and execute it immediately. All of the code that runs inside the function lives in a closure, which provides privacy and state throughout the lifetime of our application.
(function () {
// ... all vars and functions are in this scope only
// still maintains access to all globals
}());
Notice the ()
around the anonymous function. This is required by the language, since statements that begin with the token function are always considered to be function declarations. Including ()
creates a function expression instead.
Global Import
JavaScript has a feature known as implied globals. Whenever a name is used, the interpreter walks the scope chain backwards looking for a var statement for that name. If none is found, that variable is assumed to be global. If it’s used in an assignment, the global is created if it doesn’t already exist. This means that using or creating global variables in an anonymous closure is easy. Unfortunately, this leads to hard-to-manage code, as it’s not obvious (to humans) which variables are global in a given file.
Luckily, our anonymous function provides an easy alternative. By passing globals as parameters to our anonymous function, we import them into our code, which is both clearer and faster than implied globals. Here’s an example:
(function ($, YAHOO) {
// now have access to globals jQuery (as $) and YAHOO in this code
}(jQuery, YAHOO));
Module Export
Sometimes you don’t just want to use globals, but you want to declare them. We can easily do this by exporting them, using the anonymous function’s return value. Doing so will complete the basic module pattern, so here’s a complete example:
var MODULE = (function () {
var my = {},
privateVariable = 1;
function privateMethod() {
// ...
}
my.moduleProperty = 1;
my.moduleMethod = function () {
// ...
};
return my;
}());
Notice that we’ve declared a global module named MODULE
, with two public properties: a method named MODULE.moduleMethod
and a variable named MODULE.moduleProperty
. In addition, it maintains private internal state using the closure of the anonymous function. Also, we can easily import needed globals, using the pattern we learned above
回答6:
What you are doing is declaring an anonymous function and then calling it with a parameter called c
and assigning it to a variable also called c
, which is very confusing :-)
Renaming variables this is what you have:
var result=(function (input_parameter){...} (parameter_used_to_call_my_function));
The final (c)
you ask about is the parameter to call the function. It's easier to see if you use a longer syntax:
var my_function=function(input_parameter){...};
var result=my_function(result);
It is also worth noting that you are calling my_function
using result
(although you called it c
) as a parameter, which is also the name of the variable you just created to store the return value of the function. JS goes along with this because it is not strict with how you handle variables, but this is a confusing way to write code. You are declaring a variable and passing it (the variable with that name, which at that point should not exist) as a parameter to your function just in case it was declared earlier (and taking care of that case inside the function, which at least is consistent).
What happens inside my_function
is that you check is your parameter had a previous value (what I explained if the previous paragraph); if it was undefined you initialize it to an empty object. Then you append the a function to input_parameter
and return it.
I don't know if there is a name for this type of programming, but using the same variable name for different things doesn't look like a good idea :-)