this is a curiosity type of question.
Assuming a file library.js as follows:
function alpha(){
console.log("in alpha");
beta();
console.log("back in alpha");
}
function beta(){
console.log("in beta")
}
module.exports = {
alpha:alpha
}
When this library is required from another file say main.js then calling the alpha function from this module will look as follows:
var lib = require("library.js")
lib.alpha();
//outputs:
//in alpha
//in beta
//back in alpha
However the beta() function is not really exported. Does this mean NodeJS looks in the library.js file for beta()? If anyone could explain the process in technical detail it would be great.
It depends on the Scope of the function, you can always call functions or variables from the same Scope or all surrounding Scope.
A Scope is usually a function-Statement and it is surrounded by maybe the global Scope (in Browsers the global Scope is window). You can nest functions and the inner functions have access to their own variables as well as the variables of their surrounding Scopes. Take a look at this example:
var varInGlobalScope = 'global'; // This is always accessible
var functionInGlobalNamespace = function () {
// This is accessible inside the function 'functionInGlobalNamespace'
// and all nested functions but not from the global Scope
var varInFunction = 'in function';
var functionInFunction = function () {
// This is accessible inside the function 'functionInFunction'
// but not from 'functionInGlobalNamespace' or the global Scope
var varInFunctionInFunction = 'in function in function';
console.log(varInFunctionInFunction); // works
console.log(varInFunction); // works
console.log(varInGlobalScope); // works
}
console.log(varInFunctionInFunction); // fails
console.log(varInFunction); // works
console.log(varInGlobalScope); // works
}
console.log(varInFunctionInFunction); // fails
console.log(varInFunction); // fails
console.log(varInGlobalScope); // works
This Scope is always there, this means if you build a function as a public member of another function and call a private Member from that it works. But if you are not in the correct Scope it fails. This behaviour is important if you extend the prototypes of an object.
var Demo = function () {
var privateFunction = function () {
console.log('private');
};
this.publicMethod = function () {
console.log('public');
};
this.publicMethodToCallPrivateFunction = function () {
privateFunction();
};
}
Demo.prototype.tryCallingPublicMethod = function () {
this.publicMethod();
};
Demo.prototype.tryCallingPrivateFunction = function () {
privateFunction();
};
var demo = new Demo();
demo.publicMethod(); // works
demo.publicMethodToCallPrivateFunction(); // works
demo.tryCallingPublicMethod(); // works
demo.tryCallingPrivateFunction(); // fails
And now comes the "magic"-part of Node.js because if you build a module to require it in another module or file you have to put all exportable things into the special object module.exports. This is basically a object which is returned by the require function, but as we learned in the examples before the whole Scope of this function is still there.
There are a few other aspects which are maybe important in this handling, but to explain these will bloat the answer.
As a tip you can use this perfectly to implement private functionality and only provide a clean interface to the user. For example to split a huge function into several small chunks to maintain a clean code base.
The reason why this works it because of closure and hoisting.
First of all nodejs creates a surrounding block around the content of a file that looks like this:
(function (exports, require, module, __filename, __dirname) {
function alpha(){
console.log("in alpha");
beta();
console.log("back in alpha");
}
function beta(){
console.log("in beta")
}
module.exports = {
alpha:alpha
}
});
Function declarations are always parsed before the code execution and always hoisted to top. So they are known by each other.
A function Definition like this:
function beta(){
console.log("in beta")
}
can be seen as a shorthand for this:
var beta = function beta(){
console.log("in beta")
}
With the difference that both the declaration and the definition of the function are hoisting to the beginning of the scope.
The resulting code will look this way:
(function (exports, require, module, __filename, __dirname) {
var alpha, beta;
alpha = function alpha(){
console.log("in alpha");
beta();
console.log("back in alpha");
}
beta = function beta(){
console.log("in beta")
}
module.exports = {
alpha:alpha
}
});
Functions which are not exported in the module, works as private method that can not be called from outside. They may only be used if some other function exported use it, else will be inaccessible code.
Related
I thought the JavaScript, below, would causeobj.fnto run in the context ofentryPoint().
But whenobj.fn()runs, a'locVar is undefined'exception is raised.
function entryPoint () {
var locVar = "xxx";
var obj = {};
PassRcvr(obj);
obj.fn();
}
function PassRcvr(passedObj) { // Being an object, the arg is passed by reference.
passedObj.fn = function () {
alert(locVar);
}
}
The JScript, below, however does run with an awareness oflocVar.
function entryPoint () {
var locVar = "xxx";
var obj = {};
PassRcvr(obj);
obj.fn();
function PassRcvr(passedObj) {
passedObj.fn = function () {
alert(locVar);
}
}
}
Why isn’tobj.fn()aware of the context in which it is called, in both cases, rather than just the one?
The context is the value of this. You aren't using it, so it isn't relevant to your problem.
The scope is not passed. A function keeps the scope it was declared in (that is why we can use closures in JS). Since PassRcvr isn't defined inside entryPoint in the first example, it doesn't have access to variables that are scoped to entryPoint.
In the first case PassRcvr is located in global scope, while the locVar is within entryPoint function's local scope - that is why it cannot be accessed.
In the second case both PassRcvr and locVar are in one local scope.
I recently search in the code of the library of knockout to find how observables are able to create dependencies with computed functions when we call it.
In the source code, I found the function linked to observables creation:
ko.observable = function (initialValue) {
var _latestValue = initialValue;
function observable() {
if (arguments.length > 0) {
// Write
// Ignore writes if the value hasn't changed
if (observable.isDifferent(_latestValue, arguments[0])) {
observable.valueWillMutate();
_latestValue = arguments[0];
if (DEBUG) observable._latestValue = _latestValue;
observable.valueHasMutated();
}
return this; // Permits chained assignments
}
else {
// Read
ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
return _latestValue;
}
}
ko.subscribable.call(observable);
ko.utils.setPrototypeOfOrExtend(observable, ko.observable['fn']);
if (DEBUG) observable._latestValue = _latestValue;
observable.peek = function() { return _latestValue };
observable.valueHasMutated = function () { observable["notifySubscribers"](_latestValue); }
observable.valueWillMutate = function () { observable["notifySubscribers"](_latestValue, "beforeChange"); }
ko.exportProperty(observable, 'peek', observable.peek);
ko.exportProperty(observable, "valueHasMutated", observable.valueHasMutated);
ko.exportProperty(observable, "valueWillMutate", observable.valueWillMutate);
return observable;
}
What I think is very weird is the returns of 'observable' where I don't found any declaration of this variable. Sure that great men who created this library don't forget to declared it.
How it is possible to use a variable without declared it and prevent it to be put in a global scope?
My feeling is we can used a function declaration as a variable when this function declaration is declared inside another function but I'm really not sure about how it works.
Edit:
After searching on the web, I found this article.
In this article, the guy write this:
Use declarations, please
"In the code of unexperienced developers, functions are often declared by expressions:
... code ... var f = function() { ... } ...
Function Declarations are much more readable and shorter. Use them instead.
... code ... function f() { ... } ...
Besides, functions declared this way can be called before it’s definition.
Use expressions only if you mean it. E.g for conditional function definition."
Ok, Am I an unexperienced developer? I don't think so. I just don't read all the odds of Javascript. :)
observable is a variable. It is declared by a function declaration.
function observable() {
...
}
In Javascript, functions can also be returned. Within the function, he defines the function "observable" which is returned at the end of the function.
Sort to speak, functions are variables too. With a function inside.
Is it possible to call the function locally defined in another function in JavaScript? I have a code pretty much like this:
var module = function () {
function inner() {
// Some code here
}
// Some code here
}
var originalModule = module;
var module = function () {
originalModule();
// inner() must be called here
}
So I'm overriding the original module implementation, but at some point in the new implementation I need to call inner() function. I cannot edit the original implementation. So far the only way I see is to copy inner() function from original and define it in the new one. Is there another way?
As inner() is defined inside of module() scope, you can't access it outside of this scope..
This pattern is used to implement private methods of module().
It's usually not a good practice, but you could do something like this:
function a(x) { // <-- function
function b(y) { // <-- inner function
return x + y; // <-- use variables from outer scope
}
return b; // <-- you can even return a function.
}
a(3)(4); // == 7.
I've wrapped all my functions around an immediately-invoked function expression as shown:
(function(){
"use strict";
function toggleComment(parentCommentID) {
$("form#" + parentCommentID).toggle();
}
function scrollBottom() {
window.scrollTo(0, document.body.scrollHeight);
}
})();
However, upon calling one of these functions through a link:
Reply
The Chrome console outputs Uncaught ReferenceError: toggleComment is not defined. Am I mistaken in thinking that an immediately-invoked function expression, as its name suggests, should be invoked immediately and therefore toggleComment should be called? Should I call the function differently?
The function toggleComment is not visible. It's enclosed in the ready function you're using; if you want to be able to call it like that (which is not recommended, in most cases), you have to hoist it outside of that function and make it globally accessible.
And this has nothing to do with strict. If you remove the strict line, this problem will still be the same.
The functions are no longer declared in the global scope. Try
window.toggleComment = function(parentCommentID) {
$("form#" + parentCommentID).toggle();
};
You have declared the functions inside a closure. They're outside of the scope of the HTML tag.
You could set an id to your <a> tag as well as publish your function to the global scope, so you can do this:
(function(){
"use strict";
var toggleComment = function(parentCommentID) {
$("form#" + parentCommentID).toggle();
}
function scrollBottom() {
window.scrollTo(0, document.body.scrollHeight);
}
document.getElementById("yourATagId").onclick(function() {
toggleComment(159);
});
window.toggleComment = toggleComment;
})();
Maybe you could benefit from this simple singleton pattern:
(function() {
var controller = {};
controller = new function() {
this.sampleProperty = "my property";
}
controller.yourFunction = function() {
var localVariable;
console.log("I can access " + this.property);
};
window.controller = controller;
})();
This way, controller will be known to your global scope.
My javascript file is getting pretty big (3000+ lines) and I'm getting confused as to how to layout my file and delare functions so that they can called anywhere in the file.
To summarise my JS file looks a little like this at the moment:
//ALL GLOBAL VARIABLES FIRST DECLARED HERE
var var1 , var2 ,var3
$(document).ready(function(){
//JQUERY STUFF
});
//ALL FUNCTIONS THAT NEED TO BE GLOBAL DECLARED HERE
function myFunction(){
//do some stuff here
}
I am running into problems with this as some functions I call in places don't seem to be declared at the time of calling or aren't available globaly. It's all very confusing now!
Could someone suggest the best way to layout a big js/jquery file with certain JS Functions, Objects and Variables available to be referenced anywhere in the file.
UPDATE:
So to simplify it this correct (see my comments)?
window.MainModule = (function($, win, doc, undefined) {//WHAT IS BEING PASSED IN HERE?
var foo, bar, modules; //VARIABLES ACCESSIBLE ANYWHERE
var modules["foobar"] = (function() {//WHAT IS A MODULE? WHEN WOULD I USE A SEPERATE MODULE?
var someFunction = function() { ... };//DECLARING MY FUNCTIONS?
...
return {
init: someFunction,//IS THIS WHERE I USE/BIND MY FUNCTIONS TO EVENTS AND ELEMENTS?
...
};
}());
// hoist a variable into global scope
window.Global = someLocal;
return {
init: function() {//FUNCTION TO INIT ALL MODULES?
for (var key in modules) {
modules[key].init();
}
}
};
}(jQuery, this, document));
The modules section isn't properly defined ... here's a slightly tidied up example.
window.MainModule = (function($, win, doc, undefined) {
var modules = {};
// -- Create as many modules as you need ...
modules["alerter"] = (function(){
var someFunction = function(){ alert('I alert first'); };
return {
init: someFunction
};
}());
modules["alerter2"] = (function(){
var someFunction = function(){ alert('I alert second'); };
return {
init: someFunction
};
}());
return {
init: function(){
for (var key in modules){
modules[key].init();
}
}
};
}(jQuery, this, document));
$(window.MainModule.init);
// We always use closures don't we?
window.MainModule = (function($, win, doc, undefined) {
var foo, bar, modules; // List of local variables.
var modules["foobar"] = (function() {
var someFunction = function() { ... };
...
return {
init: someFunction,
...
};
}());
// hoist a variable into global scope
window.Global = someLocal;
return {
init: function() {
for (var key in modules) {
modules[key].init();
}
}
};
}(jQuery, this, document));
// Let's kick off the MainModule on $.ready
// I recommend you do this in your `html` with page specific data.
$(window.MainModule.init);
[[Disclaimer]]: This is a pseudo-code module with some standard code excluded for brevity.
Anything declared with var x inside your main closure is available throughout the entire function. Of course it won't be set to what you expect it to be set unless you set it.
To control loading and flow split code into what's automatically executed in your self executing closure and what needs to manually inited by your controller with page/user specific parameters.
You can either declare them in Window scope:
window.variableName = myVariable;
or you can omit the var, which is the same as declaring something in window scope:
variableName = myVariable;