클로저(closure)는 자바스크립트 고유의 개념이 아니라 함수를 일급 객체로 취급하는 함수형 프로그래밍 언어(Erlang, Scala, Haskell, Lisp ...)에서 사용되는 특성이다.
MDN은 클로저를 다음과 같이 정의하고 있다.
클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.
function outerFunc() {
var x = 10;
var innerFunc = function () { console.log(x); };
innerFunc();
}
outerFunc(); // 10
위 예제에서 innerFunc은 outerFunc에서 정의된 x를 사용할 수 있는데, 이는 자신이 속한 렉시컬 스코프를 참조할 수 있기 때문이다.
위와 같은 경우에 실행 컨텍스트는 어떻게 작동될까?
이 때, 스코프 체인이 바인딩한 객체가 바로 렉시컬 스코프의 실체이다.
function outerFunc() {
var x = 10;
var innerFunc = function () { console.log(x); };
return innerFunc;
}
/**
* 함수 outerFunc를 호출하면 내부 함수 innerFunc가 반환된다.
* 그리고 함수 outerFunc의 실행 컨텍스트는 소멸한다.
*/
var inner = outerFunc();
inner(); // 10
innerFunc을 반환하는 함수로 outerFunc을 변경하고 실행하면, outerFunc이 호출 됐을 때 자신의 할 일을 마치며 실행 컨텍스트 스택에서 제거되므로 때문에 변수 x가 유효하지 않을 것 같다.
하지만 위 코드는 정상적으로 작동한다.
👉👉👉 이처럼 자신을 포함하고 있는 외부함수보다 자신(내부함수)이 더 오래 유지되는 경우, 외부함수 밖에서 내부함수가 호출되더라고 외부함수의 지역 변수에 접근할 수 있는 함수를 클로저라고 부른다.
이는 클로저(함수)가 자신이 선언됐을 때의 환경인 스코프를 기억하여 그 밖에서 호출되어도 해당 환경에 접근 할 수 있기 때문에 가능하다. 간단히 말하면 클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수이다.
클로저가 유용하게 사용되는 상황은 다음과 같다.
private
키워드를 흉내낼 수 있다.다음으로 클로저를 사용할 때 자주 발생할 수 있는 실수를 살펴보자 🤔
var arr = [];
for (var i = 0; i < 5; i++) {
arr[i] = function () {
return i;
};
}
for (var j = 0; j < arr.length; j++) {
console.log(arr[j]());
}
해당 예제에서 변수 i
가 전역 변수이므로 결과는 5 5 5 5 5
를 출력한다.
위 코드가 제대로 동작하게 하려면 다음처럼 수정해야한다.
var arr = [];
for (var i = 0; i < 5; i++){
arr[i] = (function (id) { // ②
return function () {
return id; // ③
};
}(i)); // ①
}
for (var j = 0; j < arr.length; j++) {
console.log(arr[j]());
}
i
를 인자로 전달받고, 매개변수 id
에 할당한 후 내부 함수를 반환하는데, 매개변수 id
가 자유변수가된다.물론 다음과 같은 방식으로도 해결할 수 있다.
✔️ let 키워드를 사용
const arr = [];
for (let i = 0; i < 5; i++) {
arr[i] = function () {
return i;
};
}
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]());
}
✔️ 함수형 프로그래밍으로 작성
const arr = new Array(5).fill();
arr.forEach((v, i, array) => array[i] = () => i);
// v: 값, i: 인덱스, array: arr 배열
arr.forEach(f => console.log(f()));
네이티브 객체 = Native objects = Built-in object = Global Objects
어플리케이션의 환경과 관계없이 사용할 수 있는 전역의 공통 기능을 제공하는 객체
Object, String, Number, Function, Array, RegExp, Date, Math와 같은 객체 생성에 관계가 있는 함수 객체와 메소드로 구성된다.
주의할 점은
window
, Server-side(Node.js)의 global
객체를 의미한다.var o = {};
new Object() | 동일한 생성자 |
---|---|
new Object('String') | new String('String') |
new Object(123) | new Number(123) |
new Object(true) | new Boolean(123) |
new Function(arg1, arg2, ... argN, functionBody)
var x = new Boolean(false);
if (x) { // x는 객체로서 존재한다. 따라서 참으로 간주된다.
// . . . 이 코드는 실행된다.
}
https://poiemaweb.com/js-number
new Number(value)
생성자로 생성할 수 있다.NaN
을 반환한다.new
연산자를 붙이지 않아 생성자로 사용하지 않으면 Number 객체가 아닌 number를 반환한다.✔️ 프로퍼티
static property로써 Number 객체를 생성하지 않고 Number.${propertyName}
의 형태로 바로 사용가능하다.
name | description |
---|---|
EPSILON | JS에서 표현할 수 있는 가장 작은 수 부동소수점 비교에서 사용한다. |
MAX_VALUE | JS에서 사용 가능한 가장 큰 숫자를 반환한다. 이보다 큰 수는 Infinity (전역 객체의 프로퍼티) 이다. |
MIN_VALUE | JS에서 사용가능한 가장 작은 숫자. 이보다 작은 수는 0으로 변환된다. |
POSITIVE_INFINITY | 양의 무한대 Infinity 를 반환한다. |
NEGATIVE_INFINITY | 음의 무한대 -Infinity 를 반환한다. |
NaN | 숫자가 아님(Not-a-Number)를 나타내는 숫자값. window.NaN 프로퍼티와 동일하다. |
✔️ 메소드
name | description |
---|---|
Number.isFinite(testValue: number): boolean | 매개변수에 전달된 값이 정상적인 유한수인지를 검사. 전역함수 isFinite()가 인수를 숫자로 변환하여 검사하는 반면 해당 메소드는 인수를 변환하지 않으므로 숫자가 아닌 인수에 대해서는 무조건 false를 반환한다. |
Number.isInteger(testValue: number): boolean | 매개변수에 전달된 값이 정수(Integer)인지 검사. 검사전에 인수를 숫자로 변환하지 않는다. |
Number.isNaN(testValue: number): boolean | 매개변수에 전달된 값이 정수(Integer)인지 검사. 검사전에 인수를 숫자로 변환하지 않는다. |
Number.isSafeInteger(testValue: number): boolean | 매개변수에 전달된 값이 안전한(safe) 정수값인지 검사. 검사전에 인수를 숫자로 변환하지 않는다. |
${Number.prototype}.toExponential(fractionDigits?: number): string | 대상을 지수표기법 (ex. 1234 = 1.234e+3 )으로 변환해 반환한다. |
${Number.prototype}.toFixed(fractionDigits?: number): string | 매개변수로 지정된 소숫점자리를 반올림하여 문자열로 반환한다. |
${Number.prototype}.toPrecision(precision?: number): string | 매개변수로 지정된 전체 자릿수까지 유효하도록 나머지 자릿수를 반올림하여 문자열로 반환한다. 지정된 전체 자릿수로 표현할 수 없는 경우 지수 표기법으로 결과를 반환한다. |
${Number.prototype}.toString(radix?: number): string | 숫자를 문자열로 변환하여 반환한다. |
${Number.prototype}.valueOf(): number | Number 객체의 원시 타입 값(primitive value)을 반환한다. |
https://poiemaweb.com/js-string
https://poiemaweb.com/js-regexp
https://poiemaweb.com/js-array
try {
// foo();
throw new Error('Whoops!');
} catch (e) {
console.log(e.name + ': ' + e.message);
}
네이티브 객체는 각자의 프로퍼티와 메소드를 가지는데 원시 타입 값에 대해 표준 빌트인 객체의 메소드를 호출해도 정상적으로 작동한다.
이는 원시 타입 값이 연관된 객체(Wrapper 객체)로 일시 변환 되기 때문에 가능하다. 메소드 호출이 종료되면 객체로 변환된 원시 타입 값은 다시 복귀한다.
Wrapper 객체는 String, Numver, Boolean이 있다.
참조 : Prototype: 6.원시 타입(Primitive data type)의 확장
브라우저 환경에서 제공하는 window, XmlHttpRequest, HTMLElement 등의 DOM 노드 객체와 같이 호스트 환경에 정의된 객체
(브라우저와 Node.js는 다른 호스트 객체를 사용할 수 있다.)
모든 객체의 유일한 객체
window
global
// window.alert('Hello world!');
alert('Hello world!');
var ga = 'Global variable';
console.log(ga);
console.log(window.ga);
function foo() {
console.log('invoked!');
}
window.foo();
✔️ 전역 프로퍼티 = 전역 객체의 프로퍼티
property | description |
---|---|
Infinity | 양/음의 무한대를 나타내는 숫자값 |
NaN | 숫자가 아님(NaN) |
undefined | 원시타입 undefined |
✔️ 전역 함수 = 전역 객체의 메소드
method | description |
---|---|
eval(string) | 매개변수에 전달된 문자열 구문 또는 표현식을 평가 또는 실행한다. var foo = eval('2 + 2'); console.log(foo); // 4 보안에 취약하므로 사용은 가급적으로 금지되고 있다. |
ifFinite(testValue) | 매개변수에 전달된 값이 정상적인 유한수인지 검사하여 Boolean으로 반환한다. |
isNaN(testValue) | 매개변수에 전달된 값이 NaN인지 검사하여 Boolean으로 반환한다. |
parseFloat(string) | 매개변수에 전달된 문자열을 부동소수점 숫자로 변환하여 반환한다. 문자열의 첫 숫자만 반환되며 전후 공백은 무시된다. parseFloat(' 60 99'); // 60 |
parseInt(string, radix) | 매개변수에 전달된 문자열을 정수형 숫자로 해석하여 반환한다. 첫번째 매개변수 : 파싱 대상 문자열 두번째 매개변수 : 진법을 나타내는 기수 (default 10, 2~36) radix는 파싱 대상 문자열의 진법을 표현하는 용도로써 반환값은 무조건 10진수이다. |
encodeURI(uri) decodeURI(encodedUri) | 매개변수로 전달된 URI를 인코딩/디코딩한다. |
encodeURIComponent(uri) decodeURIComponent(encodedUri) | 매개변수로 전달된 URI component를 인코딩/디코딩한다. encodeURIComponent()는 인수를 쿼리스트링의 일부로 간주하여 구분자인 '= ? &'를 인코딩하나 encodeURI()는 인수를 URI 전체라고 간주하여 구분자를 인코딩하지 않는다. |
인코딩이란❗️
URI 문자들을 이스케이프 처리하는 것을 의미한다. 즉, 네트워크를 통해 정보를 공유할 때 어떤 시스템에서도 읽을 수 있는 ASCII character-set으로 변환하는 것이다. UTF-8 특수문자의 경우, 1문자당 1~3byte, UTF-8 한글 표현의 경우, 1문자당 3btye이다. 예를 들어 특수문자 공백(space)은 %20, 한글 ‘가’는 %EC%9E%90으로 인코딩된다.
단, 알파벳, 0~9의 숫자, - _ . ! ~ * ' ()는 이스케이프 처리에서 제외된다.
브라우저 탭 또는 브라우저 창의 모델을 생성한다.
최상위 객체는 window
객체로 현재 브라우저 창 또는 탭을 표현하는 객체이다. 이 객체의 자식 객체들은 브라우저의 다른 기능들을 표현한다.
현재 웹페이지의 모델을 생성한다.
최상위 객체는 document
객체로 전체 문서를 표현한다. 이 객체의 자식 객체들은 문서의 다른 소요들을 표현한다.
브라우저의 렌더링 엔진은 웹 문서를 로드한 후, 파싱하여 웹 문서를 브라우저가 이해할 수 있는 구조로 구성하여 메모리에 적재하는데 이를 DOM이라 한다. 즉, 모든 요소와 요소의 attribute, 텍스트를 각각의 객체로 만들고 이들간의 관계를 표현할 수 있는 트리 구조로 구성된 것을 의미한다.
DOM API란 정적인 웹페이지에 접근하여 동적으로 변경시키기위해 필요한 프로퍼티와 메소드의 집합이다.
결론적으로 DOM은 다음 두 가지 기능을 담당한다. 🤔
DOM tree란 브라우저가 HTML 문서를 로드한 후 파싱하여 생성한 모델을 의미한다.
DOM에서 모든 요소, attribute, 텍스트는 하나의 객체이며 document 객체의 자식이다.
DOM tree의 진입점은 document 객체이며 최종점은 요소의 텍스트를 나타내는 객체이다.
🔎 DOME tree의 노드