Модульная система

Лекция 11

Scopes

Область видимости

Scopes - Global

  • переменные + функции
  • не находятся внутри function
  • являются свойствами глобального объекта

Scopes - Global


// Browser
var a = 5;
console.log(a);         // => 5
console.log(window.a);  // => 5
        

Scopes - Global


// Node
a = 5;
console.log(a);         // => 5
console.log(global.a);  // => 5
        

Scopes - LexicalEnvironment

  • все переменные внутри функции
  • создается при запуске
  • имеет ссылку на внешний контекст - скрытое свойство [[Scope]]

Scopes - LexicalEnvironment


function hello(name) {
  var phrase = "Hello, " + name;
  console.log(phrase);
}

hello('Jack');
        

Scopes - LexicalEnvironment


function hello(name) {
  // LexicalEnvironment
  //   = { name: 'Jack', phrase: undefined }
  var phrase = "Hello, " + name;
  console.log(phrase);
}

hello('Jack');
        

Scopes - LexicalEnvironment


function hello(name) {
  var phrase = "Hello, " + name;
  // LexicalEnvironment
  //   = { name: 'Jack', phrase: 'Hello, Jack' }
  console.log(phrase);
}

hello('Jack');
        

Scopes - LexicalEnvironment


var name = 'Jack';

function hello() {
  console.log(name); // => 'Jack'
}
        

Scopes - LexicalEnvironment


var a = 1;

function f1() {
    var b = 2;

    return function () {
        console.log(a); // => 1
        console.log(b); // => 2
    }
}

f1()();
        

Scopes - LexicalEnvironment


var a = 1;

function f1() {
    var a = 3;
    var b = 2;

    return function () {
        console.log(a); // => 3
        console.log(b); // => 2
    }
}

f1()();
        

Scopes - LexicalEnvironment


var phrase = 'Hi';

function say(name) {
  console.log(phrase + ', ' + name);
}

say('Jack');  // => Hi, Jack

phrase = 'Bye';

say('Jack');  // => Bye, Jack
        

Scopes - LexicalEnvironment


function makeCounter() {
  var currentCount = 1;

  return function() { return currentCount++; };
}

var counter = makeCounter();

console.log(counter());   // => 1
console.log(counter());   // => 2
console.log(counter());   // => 3

var counter2 = makeCounter();
console.log(counter2());  // => 1
        

Scopes - LexicalEnvironment


function makeCounter() {
  function counter() {
    return counter.currentCount++;
  };
  counter.currentCount = 1;

  return counter;
}

var counter = makeCounter();
console.log(counter());             // => 1
console.log(counter());             // => 2
console.log(counter.currentCount);  // => 2
        

Scopes - Variables


const a = 5;

a = 7; // => Error
        

Scopes - Variables


const a = { b: 5 };

a.b = 7;

a = { c: 7 }; // => Error
        

Scopes - Variables


{
    let a = 1;
}

console.log(a); // => Error: 'a' not defined
        

Scopes - Variables


{
    var a = 1;
}

console.log(a); // => 1
        

Scopes - Variables


for (var i = 0; i < 5; i++) { ... }

console.log(i); // => 5
        

Scopes - Variables


var a = 5;

if (true) {
  var a = 10;

  console.log(a);   // => 10
}

console.log(a);     // => 10
        

Scopes - Variables


let a = 5;

if (true) {
  let a = 10;

  console.log(a);   // => 10
}

console.log(a);     // => 5
        

Scopes - Variables


console.log(a); // => undefined

var a = 5;
        

Scopes - Variables


console.log(a); // => Error

let a = 5;
        

Scopes - Variables


var functions = [];

for (var i = 0; i < 9; i++) {
    functions[i] = function (a) { return a*i; }
}

// functions[1](5) => 45
// functions[5](6) => 54
        

Scopes - Variables


var functions = [];

for (var i = 0; i < 9; i++) {
    (function (b) {
        functions[i]
            = function (a) { return a*b; }
    })(i);
}
        

Scopes - Variables


var functions = [];

for (let i = 0; i < 9; i++) {
    functions[i] = function (a) { return a*i; }
}

// functions[1](5) => 5
// functions[5](6) => 30
        

Module

Паттерн "Модуль"

Module

  • инкапсуляция состояния и внутренней структуры
  • через замыкания
  • разрешение конфликтов
  • масштабирование
  • вовзращается публичное API

Module - IIFE


// Immediately-Invoked Function Expression
(function () {
  var a = 5;
})();

console.log(a); // => undefined
        

Module - IIFE


var counter = (function(){
  var i = 0;

  return {
    get: () => i,
    increment: () => i++
  };
})();

counter.get();         // => 0
counter.increment();
counter.get();         // => 1
        

Module - IIFE


const Counter = (function() {
  return function () {
    var i = 0;
    return {
      get: () => i,
      increment: () => i++
    };
  };
})();

let c1 = new Counter();
let c2 = new Counter();

c1.increment();
c1.get();         // => 1
c2.get();         // => 0
        

CommonJS

CommonJS

  • добровольная рабочая группа
  • проектирует, прототипирует и стандартизирует Javascript API
  • удобное API для модулей
  • синхронная загрузка
  • exports, require

CommonJS - Require

  • функция
  • принимает идентификатор модуля
  • возвращает экспортированное API

CommonJS - Exports

  • объект
  • объявление API

CommonJS - math.js


exports.add = function() {
    var sum = 0, i = 0,
      args = arguments,
      l = args.length;

    while (i < l) {
        sum += args[i++];
    }

    return sum;
};
        

CommonJS - increment.js


var add = require('math').add;

exports.increment = function(val) {
    return add(val, 1);
};
        

CommonJS - main.js


var inc = require('increment').increment;
var a = 1;

inc(a); // 2

module.id == "main";
        

AMD

AMD

  • Asynchronous Module Definition
  • define, exports, require
  • асинхронная загрузка

AMD


define(id?, dependencies?, factory);
        

AMD - Id

  • строка
  • имя модуля
  • camelCase + '/' + '.' + '..'
  • не добавлять расширение в конце имени модуля

AMD - Dependencies

  • массив строк
  • зависимости подгружаются до выполнения factory
  • зависимости передаются в factory в качестве аргументов

AMD - Factory

  • функция
  • выполняется один раз
  • возвращаемое значение считается экспортом
  • объект вместо функции считается экспортом

AMD


define("alpha",
  ["require", "exports", "beta"],
  function (require, exports, beta) {
    exports.verb = function() {
      return beta.verb();
    }
});
        

UMD

Universal Module Definition

UMD


// CommonJS
// dependencies
var $ = require('jquery');

// methods
function myFunc(){};

// exposed public method (single)
module.exports = myFunc;
        

UMD


// AMD
define(['jquery'], function ($) {
  // methods
  function myFunc(){};

  // exposed public methods
  return myFunc;
});
        

UMD


(function (root, factory) {
    if (typeof define === 'function'
      && define.amd) {
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        module.exports
          = factory(require('jquery'));
    } else {
        root.returnExports = factory(root.jQuery);
    }
}(this, function ($) {
    function myFunc(){};
    return myFunc;
}));
        

ES Modules

ES Modules


export let one = 1;
        

let two = 2;

export {two};
        

export {one, two};
        

export {one as once, two as twice};
        

ES Modules


export class User {
  constructor(name) {
    this.name = name;
  }
};

export function sayHi() {
  console.log("Hello!");
};

// => export {User, sayHi}
      

ES Modules


import {one, two} from "./nums";
      

import {one as item1, two as item2} 
  from "./nums";
      

import * as numbers from "./nums";
      

ES Modules


export default class User {
  constructor(name) {
    this.name = name;
  }
};
      

import User from './user';

new User('Jack');
      

ES Modules


System.import(
  ['module1', 'module2'],
  function (module1, module2) {
    ...
  },
  function (err) {
    ...
  }
);
      

Модули в Node.js

Node modules

  • CommonJS
  • exports, require, module
  • поиск модулей
  • кэширование

Node modules

Node modules


// foo.js
module.exports = {
  a: 5
};

// index.js
const foo = require('./foo');
      

Node modules


(function (exports, require,
  module, __filename, __dirname) {
    // Your module code actually lives in here
});
      

Node modules - Search


require(X) from module at path Y

1. If X is a core module,
   a. return the core module
   b. STOP

2. If X begins with './' or '/' or '../'
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)

3. LOAD_NODE_MODULES(X, dirname(Y))

4. THROW "not found"
      

Node modules - Search


LOAD_AS_FILE(X)

1. If X or X.js is a file - load it as JS text

2. If X.json is file - parse it

3. if X.node is a file - load as binary addon
      

Node modules - Search


LOAD_AS_DIRECTORY(X)

1. If X/package.json is a file
  a. Parse it and find "main" field
  b. LOAD_AS_FILE(X+main)

2. Check X/index.js, X/index.json, X/index.node,
  if exists - load it
      

Node modules - Search


LOAD_NODE_MODULES(X, START)

1. let DIRS=NODE_MODULES_PATHS(START)

2. for each DIR in DIRS:
  a. LOAD_AS_FILE(DIR/X)
  b. LOAD_AS_DIRECTORY(DIR/X)
      

Node modules - Search


NODE_MODULES_PATHS(START)

1. let PARTS = path split(START)

2. let I = count of PARTS - 1

3. let DIRS = []

4. while I >= 0,
  a. if PARTS[I] = "node_modules" CONTINUE
  c. DIR = path join(PARTS[0 .. I]
    + "node_modules")
  b. DIRS = DIRS + DIR
  c. let I = I - 1

5. return DIRS
      

Node modules - Cache


const foo1 = require('./foo');

foo1.a = 7;

const foo2 = require('./foo');

console.log(foo2.a); // => 7
      

Node modules


module.children // Array of dependencies
      

module.id
      

module.loaded
      

module.parent
      

module.main
      

Node modules


// index.js
global.rootRequire = function (path) {
  if (path.indexOf('/') !== -1) {
      path = `${__dirname}/${path}`
  }

  return require(path);
};

...

rootRequire('express');
rootRequire('/foo');
rootRequire('/models/user');
rootRequire('models/user');
      

Node.js + ESM

  • динамический vs лексический экспорт
  • как определить какой это модуль (*.mjs)
  • изменяемость модулей
  • Статья
  • Experimental API