IIFE(Immediately Invoked Function Expressions) - 즉시 호출 함수 표현식
(function () {
// Do fun stuff
}
)()
위의 예제가 가장 기본적인 형태입니다.
함수 선언(Declaration)과 표현(Expression)
-
함수 표현식은 선언과 동일한 문법을 가지고 단지 표현식에서는 함수명이 생략될 수 있다고만 기술하고 있습니다.
-
함수 선언(declaration)은 미리 자바 스크립트의 실행 컨텍스트(execution context)에 로딩 되어 있으므로 언제든지 호출할 수 있지만, 표현식(Expression)은 인터프리터가 해당 라인에 도달 하였을때만 실행이 됩니다.
// [함수표현식] - 호이스팅관련 성공
a(); // success!
function a() {
alert('a');
}
// [함수표현식] - 호이스팅관련 실패
b(); // "b" is not defined.
var b = function() {
alert('b');
};
// [즉시호출함수]
(function c () {});
alert(c); // "c" is not defined.즉시 호출 함수 표현식 작동방법-
괄호쌍이 익명의 함수를 감싸서 함수 선언(declaration)을 함수 표현식(expression)으로 표현될 수 있습니다.
-
단순한 익명의 함수를 global 스코프에 선언하지 않고 어디서든 익명의 함수 표현식을 가질 수 있습니다.
// 괄호 사용 안함
x = function () {};
// 괄호 사용
(x = function () {});
// 변수 x에는 함수의 값이 할당됩니다. 괄호로 둘러쌓인 함수는 익명의 함수 표현식이 됩니다. -
-
이와 유사하게 이름을 부여하고, 즉시 호출된 함수 표현식으로 생성할 수 도 있습니다.
(showName = function (name) {
console.log(name || "No Name")
}
) (); // No Name
showName("Rich"); // Rich
showName(); // No Name
-
참조할 방법이 없기 때문에 익명의 표현식은 나중에 다시 호출할 수 없습니다.
-
showName 함수는 선언과 동시에 실행이 되며, 이름이 있으므로 나중에 호출 할수도 있습니다.
주로 언제 IIFE를 사용하는가?
-
괄호쌍이 익명의 함수를 감싸서 함수 선언(declaration)을 함수 표현식(expression)으로 표현될 수 있습니다.
-
단순한 익명의 함수를 global 스코프에 선언하지 않고 어디서든 익명의 함수 표현식을 가질 수 있습니다.
- 필요한 경우 마지막 괄호안에 외부 객체나 변수를 넣어 익명의 함수에 전달할 수 도 있습니다.
Module
-
모듈은 프로그램의 일부분, 프로그램은 하나 이상의 모듈의 조합으로 구성됩니다.
-
모듈은 하나 또는 그 이상의 함수, 메서드, 프로시져 로 구성됩니다.
모듈이 나온 배경
-
자바 스크립트 변수는 전역 범위안에 있어 캡슐화의 필요성을 느낌
-
한 프로그램에서 점점 늘어가는 코드를 정리 하기 위함
-
역할에 맞는 기능들을 묶기 위함
- Module Pattern
-
Module Pattern은 클래스의 컨샙을 모방해서 private, public 메서드 모두를 포함시킬수가 있고 단일 객체 내의 변수를 포함할수있습니다.
-
특정부분의 전역 스코프로 부터 보호하는 것이 가능해집니다.
-
동일 페이지 내에서도 함수이름이나 변수가 충돌하는 것을 예방할수있습니다.
var counter = 0;
return {
incrementCounter: function () {
return counter++;
},
resetCounter: function () {
console.log( "counter value prior to reset: " + counter ); counter = 0;
}
};
})();
testModule.incrementCounter();
testModule.resetCounter(); -
CommonJS
-
AMD (Asychronous Moudle Definition)
-
ES6 Module
AMD 와 CommonJS 자세한 내용이 궁금 하면 참고
-
탄생 배경
-
창시자인 Kevin Eangoor가 자바스크립트의 문제점을 지적
-
-
자바스크립트는 모듈 시스템이 없다.
-
자바스크립트는 표준 라이브러리가 없다. (API, data, math 등 기본라이브러리만)
-
웹 서버 또는 DB등을 위한 표준 인터페이스가 없다.
-
자바스크립트는 의존성을 관리하고 설치 할 수 있도록 하는 패키지 관리 시스템이 없다.
-
-
이와 같은 문제들을 극복하기 위해 시작한 그룹
- module.exports
- // 📁 utils.js
const PI = 3.14;
module.exports.PI = PI;
// 📁 app.js
const utils = require('./utils');
console.log(utils.PI); // 3.14 -
위의 예제와 같이 export에 값이 들어가게 됩니다.
- module.export에 값을 넣는경우
- // 📁 utils.js
module.exports = 3.14;
console.log(module);
Module {
...
exports: 3.14, // exports 값은 3.14 입니다
parent: Module {/*...*/},
filename: '...',
loaded: false,
children: [],
paths: [ /*...*/ ]
} -
exports
-
// 📁 utils.js
exports.PI = 3.14;
console.log(module.exports === exports); // true
console.log(exports); // { PI: 3.14 } -
차이점 : exports의 리턴 값은 module.exports 입니다.
-
module.exports 와 exports 는 완전히 동일합니다.
-
module.exports 처럼 같은 동작을 하는 exports 키워드가 있습니다.
- require
-
require 는 다른 모듈 파일을 볼러옵니다.
-
공식 문서에 따르면 대표적으로 모듈을 불러오는 방법이 4가지 있습니다.
1. File Modules
2. Folders as Modules
3. node_modules
4. Global Directory
- file Modules// 📁 data.json
{ "data": "Hello World!" }
// 📁 utils.js
module.exports.PI = 3.14;
// 📁 app.js
const data = require('./data');
const utils = require('./utils');
console.log(data); // { data: "Hello World!" }
console.log(utils.PI); // 3.14 -
상대경로에 있는 파일 중 확장자가 .js, .json, .node 인 파일을 모듈로써 불러옵니다.
-
확장자는 생략할 수 있습니다.
- Folders as Modules
- 폴더를 상대 · 절대 경로의 형태(/, ./, ../) 로 require 할 때는 다음과 같은 순서로 탐색합니다.
1. package.json 파일에 정의된 name 과 main 의 값을 활용
2. 1번에서 탐색에 실패하면, 해당 폴더에 index.js 또는 index.node 파일이 있는지 확인
3. 모두 실패하면 Cannot find module 에러 throw
- node_modules
-
만약 상대, 절대 경로에 대한 표시 없이 (/, ./, ../) require 를 호출하면 Node.js 는 현재 모듈의 최상위 디렉토리에서 시작하여 /node_modules 라는 경로를 앞에 붙이고 탐색합니다.
-
만약 require('a') 를 호출하면 아래와 같은 순서로 탐색합니다.
-
/home/사용자이름/project/node_modules/a.js
-
/home/사용자이름/node_modules/a.js
-
/home/node_modules/a.js
-
/node_modules/a.js
-
- GLOBAL_DIRECTORIES
-
Node.js 는 위 3 가지 방법으로 찾지 못하면 OS의 글로벌 파일 경로를 탐색합니다.
-
Node.js 는 기본적으로 다음 GLOBAL_DIRECTORIES 를 탐색합니다.
-
$HOME/.node_modules
-
$HOME/.node_libraries
-
$PREFIX/lib/node
- $PREFIX의 경우는 Node.js 가 설정한node_prefix 경로입니다
- AMD(Asynchronous Module Definition)
- AMD 그룹은 비동기 상황에서도 JavaScript 모듈을 쓰기 위해 CommonJS에서 함께 논의하다 합의점을 이루지 못하고 독립한 그룹입니다.
- CommonJS가 JavaScript를 브라우저 밖으로 꺼내기 위한 노력의 일환으로 탄생했기 때문에 브라우저 내에서의 실행에 중점을 두었던 AMD와는 합의를 이끌어 내지 못하고 결국 둘이 분리되었습니다.
- AMD가 목표로 하는 것은 필요한 모듈을 네트워크를 이용해 내려받아야 하는 브라우저 환경에서도 모듈을 사용할 수 있도록 표준을 만드는 것입니다.
- 따라서 현재 JavaScript 모듈화에 대한 논의는 크게 CommonJS 진영과 AMD 진영으로 나뉘게 되었습니다.
- ES6 Module
-
ES6 이전까지는, 브라우저 환경에서 사용 가능한 표준 모듈 시스템은 없었습니다.
-
ES6에서는 정식으로 모듈 시스템이 도입되었습니다.
- export// 1. 변수 선언 즉시 내보내기
export let name1;
export const name2;
export var name3;
export function name4 () {/*...*/}
export class MyClass {/*...*/}
// 2. 변수 먼저 정의하고, 모아서 내보내기
const var1;
let var2;
var var3;
export { var1, var2, var3 }
// 3. 먼저 정의된 함수를 별칭으로 변경해서 내보내기
let var4;
export { var4 as var5 } // 다른 모듈에서 import 할 때에는 var5 로 import 해야 함-
named export는 모듈 내에 여러개 존재 할 수 있습니다.
-
변수 선언과 동시에 내보내는 것과 정의된 변수들을 모아서 내보내는 방법이 있습니다.
-
- default export export default expression;
export default function () {/*...*/} // 익명함수
export default function myFunction () {/*...*/} // 기명함수
export default class {/*...*/}
export default class MyClass {/*...*/}
export default function* () {/*...*/} // 제너레이터도 동일
// 🙅•♂️ Uncaught SyntaxError: Unexpected token const
export default const test = /*...*/
// named export 처럼 묶어 내보내기도 가능합니다
const myModule = {/*...*/}
const var1 = () => {}
export { myModule as default, var1 } // as 를 이용해 default export 하였습니다
-
-
default export 는 모듈에서 하나만 존재할 수 있습니다.
-
export default { left, const, var } 도 안됩니다.
-
- export - from// math 모듈에서 일부만 import 한 뒤 다시 export 합니다
export { add, subtract } from './math';
// 같은 파일 내에서 사용하고 내보낼 수 없습니다
add(1, 2); // 🙅•♂️ Uncaught ReferenceError: add is not defined
-
import 와 export 를 한 번에 처리 할 수 있습니다.
-
패키지의 다른 모듈을 모아서 일관된 형태로 내보내거나 관리하고자 할 때 주로사용 합니다.
-
export - from 을 처리하는 파일 스코프에 식별자를 바인딩 하지 않습니다.
-
- import import name from "module-name";
import * as name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import defaultMember, { member [ , [...] ] } from "module-name";
import defaultMember, * as alias from "module-name";
import defaultMember from "module-name";
import "module-name";
-
다른 파일에서 모듈을 불러 올때 씁니다.
-
default export, named export를 불러 올 수 있습니다.
-
as를 통해 alias 처리 하여 부분 및 전체를 불러 올 수도 있습니다.
네임스페이스
-
네임스페이스(namespace)는 구분이 가능하도록 정해놓은 범위나 영역을 뜻합니다.
-
말 그대로 이름 공간을 선언하여 다른 공간과 구분하도록 합니다.
-
결국 네임스페이싱(namespacing)은 객체나 변수가 겹치지 않는 안전한 소스코드를 만드는 개념입니다.
-
JavaScript는 아직까지 네임스페이싱을 위한 기능을 지원하지 않기 때문에 다음의 특성을 통해 네임스페이스와 비슷한 효과를 얻을 수 있습니다.
-
JavaScript의 모든 객체는 프로퍼티를 가집니다.
-
그 프로퍼티는 다시 다른 객체를 담을 수 있습니다.
-
-
이러한 네임스페이싱 코딩 기법들을 네임스페이스 패턴 이라고 합니다.
- 객체 리터럴 네임스페이싱
-
-
가장 기본적인 네임스페이스 패턴은 객체 리터럴 네임스페이싱 방식 입니다.
-
하나의 전역 객체(global object)를 만든 후, 모든 함수, 객체, 변수를 여기에 추가하여 구현합니다.
var MYAPP = {};
MYAPP.Parent = function() { console.log('Parent'); };
MYAPP.Child = function() { console.log('Child'); };
MYAPP.variable = 1;
// 객체 컨테이너
MYAPP.modules = {};
// 객체들을 컨테이너 안에 추가합니다.
MYAPP.modules.module1 = {};
MYAPP.modules.module1.data = {a: 1, b: 2};
MYAPP.modules.module2 = {};
MYAPP.Parent(); // Parent 출력
console.log(MYAPP.modules.module1.data.a); // 1 출력
MYAPP.Child(); // Child 출력 -
-
-
코드 안, 같은 페이지에 존재하는 JS라이브러리의 이름 충돌도 방지해 주며 체계적이라는 장점이 있습니다.
-
단점
1. 모든 변수와 함수에, 상위 객체 명을 모두 붙여야 하기 때문에 소스코드량, 다운로드량이 늘어납니다.
2. 전역 인스턴스가 단 하나뿐이기 때문에 코드의 어느 한 부분이 수정되어도 전역 인스턴스를 수정하게 됩니다.
3. 매번 객체에 접근하는데다, 이름이 중첩되고 길어지므로 검색이 느려지게 됩니다.
-
- 범용 네임스페이스 함수
-
프로그램이 복잡해짐에 따라, 코드의 각 부분들이 별개의 파일로 분리되어 선택적으로 문서에 포함되는 경우가 많다.
-
네임스페이스로 사용할 객체를 선언할 때나, 이미 있는 것을 재정의하는 일도 생길 수 있습니다.
var MYAPP = {};
// 개선안
if (typeof MYAPP === "undefined") {
var MYAPP = {};
}
// 짧게 작성
var MYAPP = MYAPP || {};
-
네임스페이스가 존재하면 덮어쓰지 않기 때문에 기존 코드를 망가 뜨리지 않습니다.
-
네임스페이스 함수를 구현한 함수입니다.
MYAPP.namespace = function (ns_string) {
var parts = ns_string.split('.'), parent = MYAPP, i; // 처음에 중복되는 전역 객체명은 제거
if (parts[0] === 'MYAPP') {
parts = parts.slice(1);
}
for (i = 0; i < parts.length; i += 1) {
if (typeof parent[parts[i]] === 'undefined') {
parent[parts[i]] = {};
} parent = parent[parts[i]];
}
return parent;
};
console.log(module2);
console.log(module2 === MYAPP.modules.module2); // true 가 기록
// 첫 부분의 'MYAPP' 을 생략하고도 사용할 수 있습니다.
MYAPP.namespace('modules.module10');
// 매우 긴 네임스페이스를 만들 수 있습니다.
MYAPP.namespace('a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z');
console.log(MYAPP); -
- 샌드박스 패턴
-
코드들이 전역 범위를 더럽히지 않고 마음껏 쓰일 수 있도록 유효범위를 정해줍니다.
-
생성자를 유일한 전역으로 사용
-
생성자에게 콜백 함수를 전달해 모든 기능을 샌드박스 내부 환경으로 격리 시키는 방법
-
장점
-
단 하나의 전역변수에 의존하는 네임스페이스 패턴의 단점을 여러 개의 샌드박스 객체를 생성으로 극복할수있다.
-
.으로 연결된 긴 이름을 쓸 필요 없다.
-
런타임때 탐색 작업을 거치지 않게 해준다.
-