Skip to content

Instantly share code, notes, and snippets.

@chrisrzhou
Last active November 14, 2016 07:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisrzhou/d4192d58531295e13e573fe06b27c966 to your computer and use it in GitHub Desktop.
Save chrisrzhou/d4192d58531295e13e573fe06b27c966 to your computer and use it in GitHub Desktop.
closure and module examples in JS (from https://github.com/getify/You-Dont-Know-JS)

Closure

// no closure
for (var i=1; i<=5; i++) {
  setTimeout(() => {
    console.log(i);  // 6, 6, 6, 6, 6
  }, i*1000);
}

// with closure
for (var i=1; i<=5; i++) {
  ((j) => {
    setTimeout(() => {
      console.log(j);  // 1, 2, 3, 4, 5
    }, j*1000);
  })(i);
}

// using let (declared for each iteration with a new initialized value)
for (let i=1; i<=5; i++) {
  setTimeout( function timer(){  // no need for explicity closure
    console.log( i );
  }, i*1000 );
}

Module

// no module/closure
const foo = () => {
  const something = 'cool';
  const another = [1, 2, 3];
  
  const doSomething = () => {
    console.log(something);
  };
  const doAnother = () => {
    console.log(another.join(' ! '));
  };
};

// module pattern
const CoolModule = () => {
  const something = 'cool';
  const another = [1, 2, 3];
  
  const doSomething = () => {
    console.log(something);
  };
  const doAnother = () => {
    console.log(another.join(' ! '));
  };
  return {
    doSomething,
    doAnother,
  };
};

Firstly, CoolModule() is just a function, but it has to be invoked for there to be a module instance created. Without the execution of the outer function, the creation of the inner scope and the closures would not occur.

Secondly, the CoolModule() function returns an object, which has references to our inner functions, but not to our innter data variables. We keep those hidden and private. We can think of this object return value as a public API for our module.

Requirements for a Module

  • There must be an outer enclosing function, and it must be invoked at least once (each time creates a new module instance).
  • The enclosing function must return back at least one inner function, so that this inner function has closure over the private scope, and can access and/or modify that private state.

Modules with parameters

// modules are just functions, so they can receive parameters
const CoolModule = (id) => {
  const identify = () => {
    console.log( id );
  };
  return {
    identify,
  };
};

const foo1 = CoolModule( "foo 1" );
const foo2 = CoolModule( "foo 2" );

foo1.identify(); // "foo 1"
foo2.identify(); // "foo 2"

Modern modules

const MyModules = (() => {  // manager function
  const modules = {};
  const define = (name, deps, impl) => {
    deps.forEach((dep, i) => {
      deps[i] = modules[dep];
    });
    modules[name] = impl.apply( impl, deps );
  };
  const get = (name) => {
    return modules[name];
  };
  return {
    define,
    get,
  };
})();

// defining example modules
MyModules.define('bar', [], () => {
  const hello = (who) => {
    return `Let me introduce: ${who}`;
  };
  return {
    hello,
  };
});

MyModules.define('foo', ['bar'], (bar) => {
  const hungry = 'hippo';
  const awesome = () => {
    console.log(bar.hello(hungry).toUpperCase());
  };
  return {
    awesome,
  };
});

const bar = MyModules.get( "bar" );
const foo = MyModules.get( "foo" );

console.log(bar.hello( "hippo" )); // Let me introduce: hippo
foo.awesome(); // LET ME INTRODUCE: HIPPO

ES6

ES6 adds first-class syntax support for modules. It's even better because the ES6 module API is static (i.e. compiled). Regular modules pattern in JS are not statically recognized pattern, so their API semantics are not considered until run-time.

ES6 modules have to be created in separate file (one per module). Browser/engines have a default 'module loader'.

// bar.js
const hello = (who) => {
  return `Let me introduce: ${who}`;
};

// foo.js
import hello from 'bar';

const hungry = 'hippo';
const awesome = () => {
  console.log(hello(hungry).toUpperCase());
};
  
export awesome;

// app.js
module foo from 'foo';
module bar from 'bar';

console.log(bar.hello('rhino'));
foo.awesome();

The contents inside the module file are treated as if enclosed in a scope closure, just like with the function-closure modules seen earlier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment