问题
To start, I'm coming from a .NET world, where there are static classes (C#, also known as modules in VB) and instance classes - those you can instantiate.
This question is about Javascript, where I'm trying to recreate the pattern I already know and make a module / static class. Here is the code:
var MyModule = {
variable1: 0,
variable2: 1,
method1: function() {
//code goes here
},
method2: function() {
//mode code goes here
},
method3: function() {
//and some more just to prove the pattern
}
}
method1(); //error - no such method - ok, good, I expect that
variable1 = 5; //silently assigns a variable inside MyModule to 5 ?
Please explain why methods declared inside a namespace are not visible, but variables are ? Also, is there a way to prevent those variables from being visible outside of MyModule
, even in the same script ?
回答1:
I will start by trying to address some misconceptions - your module is actually a plain JavaScript object initialised using a literal. An object does not hide any information - JavaScript does not have private
and public
scope the same way as, say Java does. Furthermore, the only way to access properties of an object is to access them through it
method1();
This fails because you are trying to get a variable called method1
and execute it. This does not exist, hence the error. What you can do, however, is MyModule.method1()
. This is because MyModule
is an actual object. In Java, this would be similar to having Object MyModule = new Object()
. You can thus call methods on it, like MyModule.toString()
- the only "unconventional" thing, is that the instance is named with a capital letter - variables in Java and JavaScript normally start with lower case but only by convention - there is no language feature that mandates it.
Next, there is this line:
variable1 = 5; //silently assigns a variable inside MyModule to 5 ?
To answer the comment on the line - no, it doesn't. The above line is equivalent to doing window.variable1 = 5
. What you have there is called an "implied global". The name is fairly self descriptive, but just to clarify window.foo = 42
is an explicit global foo = 42
is implied because it falls back to the window
object. However, var foo = 42
is not a global - the var keyword does not attach to or use the global window
object. If you use the "use strict" directive you will cause the JavaScript engine to throw an error when it encounters an implied global.
Now, with these misconceptions aside, there is another minor one that I will merely gloss over but it's actually a major thing - MyModule.method1
and so on are functions rather than methods. A method is an Object Oriented terminology for a subroutine that also carries an implied state, which is the object it's assigned to. A function, however, is a free floating executable code that does not intrinsically "belong" to an object. The distinction may seem rather academic, but it has a big impact. However, I'll ask you to trust me on this one, as this is too broad a topic to cover here.
So, that's what's wrong. How do we make it right? First, on scopes, as that is needed for the rest of it:
JavaScript has two scopes - global and functional. Here is what this means, exactly:
var foo = 42;
function bar(input) {
var foo = input;
return foo;
}
console.log(bar(5)); //5
console.log(foo); //42
We have too variables called foo
- one inside a function one not. Assigning one does not change the other - it's because they are in different scopes. That's thanks to the var
keyword - if that was omitted inside the function, then we would be accessing the same thing
var foo = 42;
function bar(input) {
foo = input;
return foo;
}
console.log(bar(5)); //5
console.log(foo); //5
This is because the inner foo
is not declared to be in the functional scope, so it accesses the outer (and with no outer - the window.foo
property).
That's simple enough. However, there is a hidden aspect here that is not immediately obvious yet it bears mentioning: there are only two scopes. Here is what I mean
function bar(condition) {
var foo = 5;
if (condition) {
var foo = 42;
}
return foo;
}
console.log( bar(false) ); //5
console.log( bar(true) ); //42
So, even though we seemingly declare a new foo
property inside the if statement that's actually the same one as the first one. Other languages have a block scope inside { }
so, variables declared there, stay there. Not true in JavaScript. Easy to miss, so it's important to note.
Now, onto your problem. There are excellent resources about the JavaScript module pattern, to mention but a few: Addy Osmani's "Learning JavaScript Design Patterns" book has a very good description, Todd Motto also has, what I believe is, a good article for beginners while the Adequately Good post goes more in-depth yet it's still accessible. They all cover the Module pattern. You will actually found many Module patterns - in those resources and around the web. What the Module allows you to do is have something similar to public
and private
from other languages. I will not try to explain it, since I think other people have done it way better than I can. However, I will just do a very brief explanation what a Module is. Again, other resources can cover this better.
A Module leverages a JavaScript feature called Closure more information here and here (one of the most upvoted StackOverflow questions). Remember how I said, there are two scopes in JavaScript? A closure, to put it simply, provides a functional scope. Here is how it looks like:
(function() { /* your code goes here */ })()
To dissect this briefly you have a function with your code an that is enclosed in (/* function goes here */)()
. In effect what this does is that executes the function so everything inside is put into functional scope. It is very powerful, however, and here is an example
var closure = (function() {
var privateVar = "secret";
return {
publicVar: 42,
getPrivateVar() { return privateVar; }
}
})()
console.log(closure.publicVar); //42
console.log(closure.getPrivateVar()); //"secret"
//let's change this
closure.publicVar = 5;
closure.privateVar = "changed";
console.log(closure.publicVar); //5
console.log(closure.getPrivateVar()); //"secret"
And just like that we have (something resembling) a private field. Again, var privateVar
will stay "bottled up" inside the function scope and would not be accessible outside. Incidentally, this is an example of the Module pattern. Instead of var closure
I could have called it myModule
. It is a rather simplistic take on it but, here is the thing - it doesn't get more complicated. All the different Modules boil down to very similar things - you just expose data in different manners. Yet, the concept is similar - the "private" fields stay inside, and the "public" ones, you can access from elsewhere.
This is long, but hopefully should cover the basics you need to get going.
回答2:
As always before asking a question on Stack Overflow, I created a JSFiddle for attaching to the question, and several steps later figured what the problem was. This happens often actually. But this time I would like to share my findings with the public. The JS gurus probably already know this, while some less experienced developers may find this information helpful.
I had two instances of variable variable1
, one belonging to MyModule
and the other one belonging to window
(global scope). When I hover over variable1
in debugger, even though it's inside MyModule
, the debugger will display a global value. Looks like a bug in Visual Studio 2015, but this is how it works. Then I put a breakpoint in one of the MyModule
's functions and noticed it shows a different value if a variable is prefixed with this
. Obviously, JS syntax does not allow putting this
during declaration of MyModule
, so just have to live with how JS debugger works in Visual Studio.
If my explanation makes little sense, try hovering over a variable at the top of any class definition inside a C# project, for example. At any point during debugging, regardless of who declared what, you will see latest and correct values there. The difference of IDE behavior is what caused confusion for me. Also that Javascript has a tendency of creating global variables, if you were not careful enough with variable declaration.
来源:https://stackoverflow.com/questions/38575664/variables-in-a-javascript-module-visible-outside-of-it