From 5a9feb31b7ef1ff7a84ebe8059e41ee84fc89ef2 Mon Sep 17 00:00:00 2001 From: hochan222 Date: Wed, 28 Jul 2021 22:00:42 +0900 Subject: feat: convert extension html to md --- .../a_re-introduction_to_javascript/index.html | 1037 -------------------- .../a_re-introduction_to_javascript/index.md | 1037 ++++++++++++++++++++ .../ko/web/javascript/about_javascript/index.html | 59 -- files/ko/web/javascript/about_javascript/index.md | 59 ++ files/ko/web/javascript/closures/index.html | 455 --------- files/ko/web/javascript/closures/index.md | 455 +++++++++ files/ko/web/javascript/data_structures/index.html | 221 ----- files/ko/web/javascript/data_structures/index.md | 221 +++++ .../index.html | 331 ------- .../index.md | 331 +++++++ .../equality_comparisons_and_sameness/index.html | 503 ---------- .../equality_comparisons_and_sameness/index.md | 503 ++++++++++ files/ko/web/javascript/eventloop/index.html | 147 --- files/ko/web/javascript/eventloop/index.md | 147 +++ 14 files changed, 2753 insertions(+), 2753 deletions(-) delete mode 100644 files/ko/web/javascript/a_re-introduction_to_javascript/index.html create mode 100644 files/ko/web/javascript/a_re-introduction_to_javascript/index.md delete mode 100644 files/ko/web/javascript/about_javascript/index.html create mode 100644 files/ko/web/javascript/about_javascript/index.md delete mode 100644 files/ko/web/javascript/closures/index.html create mode 100644 files/ko/web/javascript/closures/index.md delete mode 100644 files/ko/web/javascript/data_structures/index.html create mode 100644 files/ko/web/javascript/data_structures/index.md delete mode 100644 files/ko/web/javascript/enumerability_and_ownership_of_properties/index.html create mode 100644 files/ko/web/javascript/enumerability_and_ownership_of_properties/index.md delete mode 100644 files/ko/web/javascript/equality_comparisons_and_sameness/index.html create mode 100644 files/ko/web/javascript/equality_comparisons_and_sameness/index.md delete mode 100644 files/ko/web/javascript/eventloop/index.html create mode 100644 files/ko/web/javascript/eventloop/index.md (limited to 'files/ko') diff --git a/files/ko/web/javascript/a_re-introduction_to_javascript/index.html b/files/ko/web/javascript/a_re-introduction_to_javascript/index.html deleted file mode 100644 index 36950fca58..0000000000 --- a/files/ko/web/javascript/a_re-introduction_to_javascript/index.html +++ /dev/null @@ -1,1037 +0,0 @@ ---- -title: JavaScript 재입문하기 (JS ​튜토리얼) -slug: Web/JavaScript/A_re-introduction_to_JavaScript -tags: - - CodingScripting - - Intermediate - - Intro - - JavaScript - - Learn - - Tutorial -translation_of: Web/JavaScript/A_re-introduction_to_JavaScript -original_slug: A_re-introduction_to_JavaScript ---- -
{{jsSidebar}}
- -

어째서 재입문일까요? 왜냐하면, JavaScript세계에서 가장 오해받고 있는 프로그래밍 언어로 악명이 높기 때문입니다. 종종 장난감같다고 조롱당하기도했지만, 이 거짓말같은 단순함 아래에는 몇 가지의 강력한 언어 기능이 숨어 있습니다. Javascript는 현재 엄청나게 많은, 요즘 가장 뜨고있는 애플리케이션들에 사용되고 있어서, 웹 또는 모바일 개발자 누구에게라도 이 기술에 대한 깊은 지식이 중요한 기량이 된다는 것을 보여주고 있습니다.

- -

이 이야기를 이해하는데는 이 언어의 역사를 먼저 보는 것이 도움이 됩니다. JavaScript는 1995년 Netscape의 엔지니어 Brendan Eich에 의해 만들어졌고, 1996년 초에 Netscape 2와 함께 처음 릴리즈 되었습니다. 이것은 원래 LiveScript로 불리기로 되어 있었습니다만 Sun Microsystem의 Java 언어의 성공에 편승해보려고 -두 언어 사이의 공통점이 매우 적음에도 불구하고- 불행이 예견된 마케팅 결정에 따라 이름이 바뀌게 됩니다. 이 결정은 역사상 유래가 없는 혼란의 근원이 되어버립니다.

- -

몇 달 후, Microsoft는 IE3와 함께 JScript를 발표했습니다. 이 JScript는 Javascript를 정말 닮았고 호환성이 좋았습니다. 몇 달 뒤에, Netscape는 1997년에 ECMAScript 표준의 첫번째 판이 되는 JavaScript를 유럽 표준화 단체인 Ecma International에 보냅니다. 이 표준은 1999년에 ECMAScript edition 3에 따라 큰 규모의 개정을 거친 후, 유례없이 아주 안정된 상태로 계속 유지되고 있습니다. 4번째 판은 중도 포기되었는데, 언어의 복잡성 증가에 관련한 정치적 문제 때문이었습니다. 이 4번째 판의 많은 파트들은 ECMAScript edition 5 (2009년 12월에 출간)와 6번째 개정판 규격(2015년에 출간)의 근간을 형성하고 있습니다. 

- -
-

 이제부터는 ECMAScript를 우리에게 좀 더 친근한 말인  "JavaScript"라고 부르겠습니다.

-
- -

대부분의 프로그래밍 언어와는 달리, JavaScript 언어는 입출력 개념이 없습니다. 이 언어는 호스트 환경 아래에서 스크립트 언어로서 동작하도록 디자인 되어있고, 따라서 외부 세계와 통신하기위해 호스트 환경이 제공하는 메커니즘에 의존합니다. 대부분의 경우 일반적인 호스트 환경은 브라우저이지만 JavaScript 인터프리터는 Adobe Acrobat, Photoshop, SVG images, Yahoo! 위젯 엔진 등의 제품에서도 발견할 수 있고, node.js 와 같은 서버 측 환경에서도 찾을 수 있습니다. 하지만 JavaScript가 사용되는 분야는 계속 더 넓혀지고 있습니다. NoSQL 데이터베이스, Apache CouchDB, 임베디드 컴퓨터, GNU/Linux OS의 가장 유명한 GUI 인 GNOME 과 같은 데스크톱 환경에서도 JavaScript가 사용됩니다.

- -

개요

- -

JavaScript는 유형 및 연산자, 표준 내장 객체 및 메소드가 있는 다중 패러다임, 동적 언어입니다. 구문은 Java 및 C 언어를 기반으로합니다. 이러한 언어의 많은 구조가 JavaScript에도 적용됩니다. JavaScript는 클래스 대신 객체 프로토 타입을 사용하여 객체 지향 프로그래밍을 지원합니다 (프로토 타입 상속 및 ES2015 {{jsxref("Classes")}}). JavaScript는 함수형 프로그래밍도 지원합니다. 함수는 객체이며, 함수는 실행 가능한 코드를 유지하고 다른 객체와 마찬가지로 전달 될 수 있습니다.

- -

어떤 언어에서라도 기초가 되는 부분인 타입을 살펴보는 것부터 시작해봅시다. JavaScript 프로그램은 값을 다루고 해당 값은 모두 타입을 가지고 있습니다. JavaScript의 타입은 다음과 같습니다:

- - - -

... 오, 그리고 약간 특별한 타입인 정의되지 않음(Undefined) 과 널(Null) 이 있습니다. 또한 객체의 특별한 종류인 배열(Array) 객체. 그리고 자유롭게 사용할 수 있는 날짜(Date) 객체정규식(RegExp) 객체가 있습니다. 그리고 기술적으로 정확히 말해 함수(Function)는 단지 객체의 특별한 타입으로 취급됩니다. 따라서 타입 구조도를 정리해보면 다음과 같습니다:

- - - -

그리고 또 몇 가지 오류 타입이 내장되어 있습니다. 그렇지만 처음 구조도를 기억하고만 있으면 다른 것들도 아주 쉽게 이해할 수 있을 것입니다.

- -

수 (Numbers)

- -

설계 명세서에 의하면 JavaScript에서 수는 "이중정밀도 64비트 형식 IEEE 754 값"으로 정의됩니다. 이것은 몇가지 흥미로운 결과를 가져옵니다. JavaScript에는 정수와 같은 것이 존재하지 않으므로 ({{jsxref("BigInt")}} 제외), 조금 조심해야 합니다. 이 예제를 보세요:

- -
console.log(3 / 2);             // 1이 아닌, 1.5
-console.log(Math.floor(3 / 2)); // 1
- -

명백한 정수는 사실 암묵적으로 실수입니다.

- -

또한, 다음과 같은 것들을 주의하세요:

- -
0.1 + 0.2 = 0.30000000000000004
-
- -

실제로 정수 값은 32 비트 정수로 처리되며 일부 구현은 32 비트 정수가 아닌 숫자에 유효한 명령어를 수행 할 때까지 이러한 방식으로 저장합니다. 이는 비트 단위 작업에 중요 할 수 있습니다.

- -

덧셈, 뺄셈, 계수 (또는 나머지) 연산을 포함하는 표준 산술 연산자가 지원됩니다. 또한 앞에서 언급하는 것을 깜박 잊은 고급 수학 함수와 상수를 다루기 위한 수학(Math)으로 불리는 내장 객체가 있습니다:

- -
Math.sin(3.5);
-var circumference = 2 * Math.PI * r;
- -

내장 parseInt() 함수를 사용하여 문자열을 정수로 변환할 수 있습니다. 이는 다음과 같이 옵션으로 주어지는 두번째 매개변수를 밑으로 하여 수행할 수 있습니다:

- -
parseInt('123', 10); // 123
-parseInt('010', 10); // 10
- -

구형 브라우저에서 "0"으로 시작하는 문자열은 8 진수 (기수 8)로 가정되지만, 2013 년 이후에는 그렇지 않습니다. 문자열 형식이 확실하지 않으면 이전 브라우저에서 놀라운 결과를 얻을 수 있습니다.

- -
parseInt('010');  //  8
-parseInt('0x10'); // 16
- -

이 같은 결과는 {{jsxref("Global_Objects/parseInt", "parseInt()")}} 함수가 0으로 시작되는 문자열을 8진수로, "0x"로 시작하는 문자열은 16진수로 취급하기 때문에 발생합니다. 16진수 표기법이 그대로 유지됩니다. 8진수는 제거되었습니다.

- -

만약 이진수를 정수로 변환하고 싶다면, 밑을 바꾸기만하면 됩니다:

- -
parseInt('11', 2); // 3
-
- -

이와 비슷하게, 내장 함수 {{jsxref("Global_Objects/parseFloat", "parseFloat()")}}를 사용하여 부동 소수점 숫자를 파싱 할 수 있습니다. {{jsxref("Global_Objects/parseInt", "parseInt()")}}과 달리 parseFloat()는 항상 10진수를 사용합니다.

- -

단항 연산자 + 를 사용하여 값을 숫자로 변환 할 수도 있습니다:

- -
+ '42';   // 42
-+ '010';  // 10
-+ '0x10'; // 16
- -

문자열이 수가 아닌 경우 NaN ("Not a Number" (수가 아님)을 줄인 약자)로 불리는 특별한 값을 돌려줍니다:

- -
parseInt('hello', 10); // NaN
-
- -

NaN 는 독성을 가지고 있습니다: 어떤 수학 연산의 입력값으로써 주어지면 그 결과는 역시 NaN가 되기 때문입니다:

- -
NaN + 5; // NaN
-
- -

내장 isNaN() 함수를 사용해서 NaN 인지 여부를 검사할 수 있습니다:

- -
isNaN(NaN); // true
-
- -

JavaScript는 또 특별한 값 Infinity-Infinity를 가지고 있습니다:

- -
 1 / 0; //  Infinity
--1 / 0; // -Infinity
- -

내장 함수 {{jsxref("Global_Objects/isFinite", "isFinite()")}}를 사용하여 Infinity, -Infinity 및 NaN 값을 테스트 할 수 있습니다.

- -
isFinite(1 / 0);     // false
-isFinite(-Infinity); // false
-isFinite(NaN);       // false
- -
-

{{jsxref("Global_Objects/parseInt", "parseInt()")}} 와 {{jsxref("Global_Objects/parseFloat", "parseFloat()")}} 함수는 숫자로 아닌 문자가 나올때까지 문자열을 파싱하고, 그 지점까지 파싱된 숫자를 반환합니다. 그런데 "+"연산자는 중간에 유효하지 않은 문자가 있으면 그대로 문자열을 NaN 으로 그냥 변환해버립니다. console에서 "10.2abc"를 파싱해보면 어떤점이 다른지 더 쉽게 이해할 수 있습니다.

-
- -

문자열 (Strings)

- -

JavaScript에서 문자열은 유니코드 문자들이 연결되어 만들어진 것입니다. 이는 국제화(i18n, internationalization) 하려하는 누구에게라도 환영받을만한 소식입니다. 좀 더 정확히 말하자면, 각각이 16비트 숫자로 표현된 UTF-16 코드 유닛이 길게 이어져있는 것입니다. 각 유니코드 문자는 1개나 2개의 코드 유닛으로 표현됩니다.

- -

한 개의 문자를 나타내려면 길이가 1인 문자열을 사용하면 됩니다.

- -

문자열의 길이를 알고싶다면, 해당 문자열의 length 속성(해당 객체가 소유하고 있는 성질을 나타내는 값)에 접근하면 됩니다:

- -
'hello'.length; // 5
-
- -

우리의 첫 JavaScript 객체입니다! 문자열도 역시 객체로 취급된다고 언급했던적이 있죠? 다음과 같이 메소드까지 있는 확실한 녀석입니다:

- -
'hello'.charAt(0); // "h"
-'hello, world'.replace('hello', 'goodbye'); // "goodbye, world"
-'hello'.toUpperCase(); // "HELLO"
- -

이외의 타입들

- -

JavaScript는 의도적으로 값이 없음을 가리키는 '객체' 타입의 객체인 null과 초기화되지 않은 값 — 아직 어떤 값도 주어지않은(할당되지않은) 변수임을 가리키는 '정의되지 않음' 타입의 객체인 undefined로 구분됩니다. 값에 대해서 나중에 언급할 것이지만 JavaScript에서 변수에 값을 주지않고 선언하는 것이 가능합니다. 이럴 경우, 변수의 타입은 undefined이 되는 것입니다.

- -

JavaScript는 truefalse 값 (둘은 모두 키워드로 예약되어있는 값)을 가질 수 있는 부울 타입을 가지고 있습니다. 다음과 같은 규칙에 따라 어떤 임의의 값을 부울값으로 변환할 수 있습니다:

- -
    -
  1. false, 0, 빈 문자열 (""), 수가 아님을 뜻하는 NaN, null, 와 undefined은 모두 false가 됩니다.
  2. -
  3. 다른 모든 값은 true가 됩니다.
  4. -
- -

이 변환은 Boolean() 함수를 써서 명시적으로 이 작업을 수행하실 수 있습니다:

- -
Boolean('');  // false
-Boolean(234); // true
- -

하지만 반드시 이렇게 할 필요는 거의 없습니다. JavaScript는 이러한 변환 작업을 if 문 (아래를 보세요)과 같이 부울값이 필요한 경우를 만나게되면 자동으로 사용자가 모르는 사이에 처리해버리기 때문입니다. 이러한 이유로 인해 우리는 가끔 부울 타입으로 변환되었을 때, truefalse이 됨을 의미하는 값들을 각각 "참 값"과 "거짓 값"으로 부를 것입니다. 또는 각각 "참으로 취급되다"와 "거짓으로 취급되다"라는 식으로 불릴 수도 있습니다.

- -

부울 연산자는 && (논리적와, 그리고 ), || (논리적또는 ), 그리고 ! (논리적부정 )이 지원됩니다. 아래에서 다시 언급하겠습니다.

- -

변수 (Variables)

- -

JavaScript에서 새로운 변수는 let, constvar 키워드로 선언됩니다.

- -

let을 사용하면 블록 유효 범위 변수를 선언 할 수 있습니다. 선언 된 변수는 변수가 포함 된 함수 블록에서 사용할 수 있습니다.

- -
let a;
-let name = 'Simon';
- -

아래는 let으로 선언한 변수가 가지는 유효 범위의 예제입니다. 

- -
// myLetVariable는 여기에서 보이지 *않습니다*
-
-for (let myLetVariable = 0; myLetVariable < 5; myLetVariable++) {
-  // myLetVariable는 여기에서 유효합니다
-}
-
-// myLetVariable는 여기에서 보이지 *않습니다*
- -

const는 값이 변경되지 않는 변수를 선언 할 수 있게 합니다. 변수는 변수가 선언 된 함수 블록에서 사용할 수 있습니다.

- -
const Pi = 3.14; // 변수 Pi 설정
-Pi = 1; // 상수로 설정된 변수는 변경 할 수 없기 때문에 애러 발생.
- -

var은 가장 일반적인 변수 선언 키워드입니다. let, const 키워드가 가지는 제한을 var은 갖지 않습니다. 이는 자바스크립트에서 변수를 선언하는 전통적인 유일한 방법이었기 때문입니다. var 키워드로 선언 된 변수는 변수가 선언 된 함수 블록에서 사용 할 수 있습니다.

- -
var a;
-var name = 'Simon';
- -

var로 선언한 변수의 유효 범위 예제입니다.

- -
// myVarVariable는 여기에서 사용 할 수 *있습니다*
-
-for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) {
-  // myVarVariable는 함수 전체에서 사용 할 수 있습니다.
-}
-
-// myVarVariable는 여기에서 사용 할 수 *있습니다*
- -

변수에 값을 지정하지 않고 변수를 선언하면, 타입은 undefined로 지정 됩니다.

- -

자바스크립트와 자바 같은 다른 언어 사이의 중요한 차이점은 자바스크립트는 블록에 범위가 없다는 것입니다. 함수에만 범위가 있습니다. 변수가 복합 문에서 (예를 들어 if 제어 구조 내에서) var를 사용하여 정의 된 경우 전체 함수에서 볼 수 있습니다. 그러나 ECMAScript 2015부터 letconst 선언을 사용하면 블록 범위 변수를 만들 수 있습니다.

- -

연산자 (Operators)

- -

JavaScript의 산술 연산자로는 +, -, *, /, %(나머지 연산자)가 있습니다. 값은 = 연산자로 할당할 수 있고, +=-=처럼 다른 연산자를 같이사용해서 할당할 수 있습니다. 이렇게 쓰인 연산자는 x = x연산자 y와 같은 결과를 나타냅니다.

- -
x += 5;
-x = x + 5;
-
- -

++-- 를 각각 점진적인 증가와 감소에 사용할 수 있습니다. 이들은 또한 전처리 또는 후처리 연산자로 사용될 수 있습니다.

- -

+ 연산자는 문자열 이어붙이기도 합니다:

- -
'hello' + ' world'; // "hello world"
-
- -

문자열에 어떤 수 (또는 다른 값)를 더하면 일단 모두 문자열로 바뀌게 됩니다. 다음 예를 보시면 무슨 말씀인지 아실 수 있을겁니다:

- -
'3' + 4 + 5;  // "345"
- 3 + 4 + '5'; // "75"
- -

빈 문자열에 어떤 값을 더하는 것은 해당 값을 문자열로 바꾸는 요령입니다.

- -

JavaScript에서 비교<, >, <=>= 를 통해 가능합니다. 이 연산자들은 문자열과 수 양쪽 모두에서 동작합니다. 상동은 약간 직관성이 떨어지는데 이중 등호 (==) 연산자는 서로 다른 타입을 줄 경우 타입 강제 변환을 수행하기 때문에 다음과 같이 때때로 기대하지 않은 결과를 내보내기 때문입니다:

- -
123 == '123'; // true
-1 == true;    // true
-
- -

타입 강제 변환을 하지 않게 하려면, 삼중 등호 연산자 (===)를 사용해야합니다:

- -
123 === '123'; // false
-1 === true;    // false
-
- -

이와 비슷하게 !=!== 연산자가 있습니다.

- -

JavaScript는 값을 비트로 취급하는 연산자도 가지고 있습니다. 사용하고 싶을 때 언제라도 사용할 수 있도록 말이죠.

- -

제어 구조

- -

JavaScript는 C 계열의 다른 언어들과 비슷한 제어 구조를 가지고 있습니다. 조건문은 ifelse를 지원하는데, 원하시는대로 얼마든지 중첩 시켜서 사용할 수 있습니다:

- -
var name = 'kittens';
-if (name == 'puppies') {
-  name += ' woof';
-} else if (name == 'kittens') {
-  name += ' meow';
-} else {
-  name += '!';
-}
-name == 'kittens meow';
-
- -

JavaScript는 while 반복문과 do-while 반복문도 사용할 수 있습니다. 첫번째 것은 단순 반복에 유용하게 사용할 수 있고, 두번째 것은 반복문이 반드시 적어도 한번이상 실행 되도록 하고 싶을 때 사용할 수 있습니다:

- -
while (true) {
-  // 무한루프!
-}
-
-var input;
-do {
-  input = get_input();
-} while (inputIsNotValid(input));
-
- -

JavaScript의 for 반복문은 C 와 Java의 반복문과 같습니다. 말하자면, 반복문에 필요한 제어 정보를 한 줄에 표현할 수 있다는 이야기지요.

- -
for (var i = 0; i < 5; i++) {
-  // 내부 동작을 5번 반복합니다
-}
-
- -

JavaScript에는 두개의 중요한 for 반복문 또한 포함됩니다. 첫번째로 for...of 입니다.

- -
for (let value of array) {
-  // value로 작업을 실행합니다
-}
-
- -

그리고 for ... in 입니다.

- -
for (let property in object) {
-  // object의 항목(property)으로 작업을 실행합니다
-}
-
- -

&&|| 연산자는 첫번째 식을 평가한 결과에 따라서 두번째 식을 평가를 실행하는 단축평가(short-circuit) 논리를 사용합니다. 이는 다음과 같이 객체에 접근하기 전에 null 객체인지, 아닌지를 검사하는데 유용하게 사용될 수 있습니다:

- -
var name = o && o.getName();
-
- -

또는 (틀린값이 유효하지 않은 값일때) 캐싱 값에 대해서도 사용합니다.:

- -
var name = cachedName || (cachedName = getName());
-
- -

JavaScript는 한줄로 조건문을 쓸 수 있게 해주는 삼중 연산자도 가지고 있습니다:

- -
var allowed = (age > 18) ? "yes" : "no";
-
- -

switch 문은 숫자나 문자열을 기반으로 다중 분기되는 문장을 작성하는데 사용될 수 있습니다:

- -
switch(action) {
-    case 'draw':
-        drawIt();
-        break;
-    case 'eat':
-        eatIt();
-        break;
-    default:
-        doNothing();
-}
-
- -

break 문장을 추가하지 않았다면, 다음 단계로 "넘어가서" 실행합니다. 이렇게 되는 것을 기대하는 것은 매우 드문경우 입니다. 실은 디버깅하는데 용이하도록 하기위해 주석으로서 일부러 붙여놓은 넘어가기 이름표 입니다:

- -
switch(a) {
-    case 1: // fallthrough
-    case 2:
-        eatIt();
-        break;
-    default:
-        doNothing();
-}
-
- -

default 구문의 적용은 선택사항입니다. switch와 case 부분에서 둘다 표현식을 사용할 수도 있습니다. switch부분과 case 부분의 표현식은  === 연산자로 비교됩니다.

- -
switch(1 + 3){
-    case 2 + 2:
-        yay();
-        break;
-    default:
-        neverhappens();
-}
-
- -

객체 (Objects)

- -

JavaScript 객체는 간단히 이름-값 쌍(name-value pairs)의 모임입니다. 그렇기 때문에, JavaScript의 객체의 모임은 다음과 비슷하다고 할 수 있습니다:

- - - -

이 데이터 구조가 매우 광범위하게 사용된다는 사실은 활용 방도가 다양함을 입증합니다. JavaScript내 모든 것 (코어 타입들은 제외)은 객체로 취급되기 때문에 어떤 JavaScript 프로그램도 기본적으로 해쉬 테이블을 검색하는데 필요한 출중한 성능을 가지고 있습니다. 매우 빠르기 때문에 장점이 됩니다!

- -

값은 객체를 포함하여 아무 JavaScript 값이 될 수 있는 반면, "이름" 부분은 JavaScript 문자열 입니다. 이는 무작위적인 복잡성을 가지는 데이터 구조를 만들 수 있도록 해줍니다.

- -

빈 객체를 생성하는데 두가지 방법이 있습니다:

- -
var obj = new Object();
-
- -

와:

- -
var obj = {};
-
- -

이들은 의미적으로 동치입니다. 두번째 방법은 객체 리터럴 구문이라고 부르며 더 편리합니다. 객체 리터럴 구문은 JSON 구문의 핵심이며 이 방법을 사용한 코드를 더 많이 볼 수 있습니다.

- -

객체 리터럴 구문으로 객체의 전체적인 구조를 초기화 할 수 있습니다:

- -
var obj = {
-    name: "Carrot",
-    "for": "Max",
-    details: {
-        color: "orange",
-        size: 12
-    }
-}
-
- -

속성에 연속적으로 접근할 수 있습니다:

- -
obj.details.color; // orange
-obj["details"]["size"]; // 12
-
- -

아래 예제는 객체 프로토타입(Person)과 프로토타입의 인스턴스(you)를 생성합니다.

- -
function Person(name, age) {
-  this.name = name;
-  this.age = age;
-}
-
-// 객체를 정의한다
-var you = new Person('You', 24);
-// "You"라는 이름의 24세인 새로운 사람을 생성중이다.
-
- -

일단 생성되면, 객체의 속성에 다음의 두가지 방법들 중 한가지로 접근할 수 있습니다:

- -
// dot 표기법
-obj.name = "Simon"
-var name = obj.name;
-
- -

그리고...

- -
// bracket 표기법
-obj["name"] = "Simon";
-var name = obj["name"];
-// key를 정의하기 위해 변수도 쓸수 있습니다.
-var user = prompt('what is your key?')
-obj[user] = prompt('what is its value?')
-
- -

이들은 의미적으로 역시 같습니다. 두번째 방법은 속성의 이름이 실행시간(run-time)에 계산될 수 있는 문자열로 주어집니다. 하지만 이방법을 사용하면 일부 JavaScript엔진과 압축기 최적화(minifier optimizations)를 적용할수 없습니다.또한 예약된 단어(키워드)로 되어있는 이름으로 객체의 속성을 설정하거나 얻어낼 수 있습니다:

- -
obj.for = "Simon"; // 구문 오류, for 가 예약된 단어(키워드)이기 때문에
-obj["for"] = "Simon"; // 정상 동작
-
- -
-

ECMAScript 5 이래로, 예약어는  객체 항목의 이름으로 "덧붙임없이" 사용할수도 있습니다. 이말은 객체 리터럴을 정의할때 따옴표로 "둘러쌀" 필요가 없다는 의미입니다.  ES5 Spec을 참고해 보십시오.

-
- -

객체나 프로토타입에 대한 좀더 상세한 내용은 Object.prototype 을 참조하십시오. 객체 프로토타입과 객체 프로토타입 체인에 대한 설명은 상속과 프로토타입 체인 을 참조하십시오.

- -
-

ECMAScript 2015 이래로, 객체의 key는 생성시의 대괄호 표기법(bracket notation)으로 정의될수 있습니다. 그냥 var userPhone = {}; userPhone[phoneType] = 12345. 처럼 표기하는 방법 대신 {[phoneType]: 12345} 와 같은 사용법도 가능합니다.

-
- -

배열 (Arrays)

- -

JavaScript에서 배열은 실제로는 특별한 타입의 객체입니다. (숫자로 나타낸 속성은 자연스럽게 [] 구문만을 사용해서 접근하게 되므로) 일반 객체와 많이 비슷하게 동작하지만, 이 객체는 'length'라는 한가지 마법적인 속성을 가집니다. 이는 항상 배열에서 가장 큰 인덱스보다 하나 더 큰 값으로 존재합니다.

- -

배열을 생성하는 예전 방법은 다음과 같습니다:

- -
var a = new Array();
-a[0] = "dog";
-a[1] = "cat";
-a[2] = "hen";
-a.length // 3
-
- -

한가지 더 편리한 배열 표현 방법은 배열 리터럴을 사용하는 것입니다:

- -
> var a = ["dog", "cat", "hen"];
-> a.length
-3
-
- -

배열 리터럴 끝에 콤마(",")를 꼬리로 남겨두는 것은 브라우저마다 다르게 처리하므로 그렇게 하지는 마시기 바랍니다.

- -

array.length 는 배열에 들어있는 항목의 수를 반드시 반영하지는 않는다는 점을 주의하시기 바랍니다. 다음과 같은 경우를 고려해보겠습니다:

- -
> var a = ["dog", "cat", "hen"];
-> a[100] = "fox";
-> a.length
-101
-
- -

기억해두세요 - 배열의 length 속성은 최대 인덱스에 하나를 더한 값일 뿐입니다.

- -

존재하지 않는 배열 인덱스를 참조하려고하면 다음과 같이 undefined 을 얻게됩니다:

- -
> typeof(a[90])
-undefined
-
- -

[]length에 관한 위의 사항들을 감안하면 배열을 for 반복문으로 처리할 때 다음과 같은 방법으로 처리하실 수 있을 것입니다:

- -
for (var i = 0; i < a.length; i++) {
-    // a[i] 로 뭔가를 수행
-}
-
- -

ES2015는 배열과 같은 이터러블 객체를 위해 좀더 간결한 for...of 루프를 소개했습니다.

- -
for (const currentValue of a) {
-    // currentValue 로 뭔가를 수행
-}
- -

또한 for...in 루프를 이용하여 배열에 루프를 돌릴수도 있지만, 이 방법은 배열 요소를 반복하는게 아니라 배열 인덱스를 반복합니다. 뿐만 아니라, 누군가 Array.prototype에 새로운 속성을 추가하면, 그 속성들 또한 이런 루프로 반복됩니다. 따라서 이런 반복 형태는 배열에는 추천되지 않습니다.

- -

배열에 대한 또다른 반복방법은 ECMAScript 5에 추가된 forEach() 입니다:

- -
['dog', 'cat', 'hen'].forEach(function(currentValue, index, array) {
-    // currentValue나 array[index]로 뭔가를 수행
-}
-
- -

배열에 항목 하나를 추가하길 원한다면 이렇게 하면 됩니다:

- -
a.push(item);
- -

배열은 몇가지 메서드가 제공됩니다. 배열 메서드에 대한 전체 문서를 참조하십시오.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
메서드 이름설명
a.toString()각 항목에 대한 toString()의 출력이 콤마로 구분된 한개의 문자열을 반환합니다.
a.toLocaleString()각 항목에 대한 toLocaleString()의 출력이 콤마로 구분된 한개의 문자열을 반환합니다.
a.concat(item1[, item2[, ...[, itemN]]])item들이 덧붙여진 한개의 배열을 반환합니다.
a.join(sep)배열의 값들을 sep 인자로 구분하여 합친 한개의 문자열로 변환합니다.
a.pop()배열의 마지막 항목을 반환하면서 제거합니다.
a.push(item1, ..., itemN)배열의 끝에 item들을 덧붙입니다.
a.shift()배열의 첫번째 항목을 반환하면서 제거합니다.
a.unshift(item1[, item2[, ...[, itemN]]])배열의 앞쪽에 item들을 덧붙입니다.
a.slice(start[, end])배열의 일부분을 새배열로 반환합니다.
a.sort([cmpfn])옵션으로 비교용도의 함수를 입력받습니다.
a.splice(start, delcount[, item1[, ...[, itemN]]])배열의 일부분을 제거하고 다른 항목으로 대체하여 배열을 변경합니다..
a.reverse()배열의 순서를 거꾸로 배열합니다.
- -

함수 (Functions)

- -

객체와 마찬가지로, 함수는 JavaScript를 이해하는데 핵심이 되는 컴포넌트입니다. 가장 기본적인 함수의 예는 다음과 같습니다:

- -
function add(x, y) {
-    var total = x + y;
-    return total;
-}
-
- -

이 예는 기본 함수에 대해 알아야 할 모든 것을 보여주고 있습니다. JavaScript 함수는 0 이상의 이름이 있는 매개변수를 가질 수 있습니다. 함수의 본체는 갯수 제한없이 구문을 포함할 수 있고 해당 함수에 지역적으로 변수를 보유하도록 선언할 수 있습니다. return 문은 언제나 값을 돌려주고 함수의 실행을 끝내는데 사용될 수 있습니다. 리턴 문이 없으면 (혹은 값이 없는 리턴이 사용되면), JavaScript는 undefined을 돌려줍니다.

- -

이름 붙여진 매개변수들은 다른 어떤 것보다도 해당 함수가 어떤 함수인지 설명해주는 좋은 역할을 할 수 있습니다. 해당 함수가 원하는 매개변수를 주지않고 함수를 호출할 수 있지만 그럴 경우 해당 변수들은 undefined로 설정됩니다.

- -
add(); //NaN
-// undefined에 대해 덧셈을 수행할 수 없습니다
-
- -

함수가 기대하는 원래의 매개변수보다 많은 매개변수를 넘겨줄 수도 있습니다:

- -
add(2, 3, 4); // 5
-// 처음의 두 수가 더해집니다. 4는 무시됨
-
- -

이 예는 조금 어리석어 보이지만, 함수는 추가적으로 주어진 매개변수를 함수 내부에서 접근할수 있습니다. 이 객체는 arguments라고 하며, 해당 함수에  매개변수로 넘겨진 모든 값을 가지고 있는 배열과 비슷한 객체입니다. 우리가 원하는만큼 값을 취하는 add 함수를 다시 써보겠습니다:

- -
function add() {
-    var sum = 0;
-    for (var i = 0, j = arguments.length; i < j; i++) {
-        sum += arguments[i];
-    }
-    return sum;
-}
-
-add(2, 3, 4, 5); // 14
-
- -

확실히 2 + 3 + 4 + 5를 직접쓰는 것보다 유용한 함수는아닙니다. 평균계산 함수를 만들어 보겠습니다:

- -
function avg() {
-    var sum = 0;
-    for (var i = 0, j = arguments.length; i < j; i++) {
-        sum += arguments[i];
-    }
-    return sum / arguments.length;
-}
-
-avg(2, 3, 4, 5); // 3.5
-
- -

이건 매우 유용합니다만, 좀 번잡해보입니다. 코드 크기를 다소 줄이기 위해, arguments 배열의 사용을 Rest 파라미터 문법으로 대체해볼 필요가 있습니다. 이 방법으로, 코드 크기는 최소한으로 유지 하면서, 갯수 제한없이 함수로 인자를 전달할수 있습니다. Rest 파라미터 연산자는 다음과 같은 포맷(...variable)으로 함수 파라미터 목록에 사용됩니다. 이 varaible 인자는 함수가 호출될때 전달되는 모든 인자를 포함합니다. variable 인자에서 반환되는 값을 사용하기 위해 위 코드에서 for 루프를 for..of 루프로 변경합니다.

- -
function avg(...args) {
-    var sum = 0;
-    for (let value of args) {
-        sum += value;
-    }
-    return sum / arr.length;
-}
-
-avg(2, 3, 4, 5); // 3.5
-
- -
-

위 코드에서,변수 args 는 함수로 전달된  모든 값을 가지고 있습니다.
-
- rest 파라미터 연산자가 함수 선언의 어느곳에 위치하든 선언 위치 이후에 모든 인자를 저장하는것이며, 이전이 아니라는 것이 중요합니다. 즉 , function avg(firstValue, ...args) 에서 함수로 전달된 첫번째 값은 firstValue 변수에 저장되며, 남은 변수들은 args에 저장됩니다.

-
- -

이건 또다른 유용한 언어 특성입니다만 우리를 새로운 문제점으로 인도합니다. avg() 함수는 콤마로 구분된 인자목록을 받지만, 배열의 평균을 알고싶은 경우라면요? 함수를 다음과 같이 재작성 하면 됩니다 :

- -
function avgArray(arr) {
-  var sum = 0;
-  for (var i = 0, j = arr.length; i < j; i++) {
-    sum += arr[i];
-  }
-  return sum / arr.length;
-}
-
-avgArray([2, 3, 4, 5]); // 3.5
- -

하지만 우리가 이미 만든 함수를 다시 사용할 수 있다면 좋을 것입니다. 운이 좋게도 JavaScript는 함수 객체라면 모두 가지게 되는 apply() 메소드를 사용해서 임의의 매개변수 배열을 함수에 넘겨줄 수 있습니다.

- -
> avg.apply(null, [2, 3, 4, 5])
-3.5
-
- -

apply()의 두번째 매개변수는 '매개변수들'로 사용하고자 하는 배열입니다. 첫번째 매개변수는 나중에 설명하도록 하겠습니다. 이는 함수가 역시 객체임을 명확히 해주는 사실입니다.

- -
-

함수 호출시 전개 연산자(spread operator) 를 이용하여 똑같은 결과를 얻을수 있습니다.

- -

예를 들면: avg(...numbers)

-
- -

JavaScript는 익명의 함수를 만들 수 있도록 허용하고 있습니다.

- -
var avg = function() {
-    var sum = 0;
-    for (var i = 0, j = arguments.length; i < j; i++) {
-        sum += arguments[i];
-    }
-    return sum / arguments.length;
-}
-
- -

이것은 의미적으로 function avg() 형식과 같습니다. 이 특징은 매우 강력한데, 일반적인 표현식(expression)을 사용할 수있는 어디에서나 완전한 함수 정의를 넣을 수 있도록 허용하는 것이기 때문입니다. 이 특징은 다양한 요령을 부릴 수 있게합니다. 다음 예는 C에서 블록 유효 범위를 적용 시킨 것 처럼 지역 변수를 "숨기는" 요령을 보여줍니다:

- -
var a = 1;
-var b = 2;
-
-(function() {
-    var b = 3;
-    a += b;
-})();
-
-a; // 4
-b; // 2
-
- -

JavaScript는 재귀적으로 함수를 부를 수 있습니다. 이는 브라우저 DOM 등에서 볼수 있는 트리 구조를 다루는데 유용합니다.

- -
function countChars(elm) {
-  if (elm.nodeType == 3) { // TEXT_NODE
-    return elm.nodeValue.length;
-  }
-  var count = 0;
-  for (var i = 0, child; child = elm.childNodes[i]; i++) {
-    count += countChars(child);
-  }
-  return count;
-}
-
- -

다음의 예는 익명 함수를 사용함에 있어 잠재적인 문제점을 보여줍니다: 이름이 없으면 어떻게 재귀적으로 부를 수 있을까요? JavaScript는 함수 표현식을 이렇게 이름붙이도록 지원합니다. 이름붙은 IIFEs (Immediately Invoked Function Expressions: 즉시 실행 함수 표현) 를 다음과 같이 사용할 수 있습니다:

- -
var charsInBody = (function counter(elm) {
-  if (elm.nodeType == 3) { // TEXT_NODE
-    return elm.nodeValue.length;
-  }
-  var count = 0;
-  for (var i = 0, child; child = elm.childNodes[i]; i++) {
-    count += counter(child);
-  }
-  return count;
-})(document.body);
-
- -

위와 같이 함수 표현식에 제공된 이름은 함수 자체 범위에서만 유효합니다. 이 특징은 엔진에 의한 최적화뿐만 아니라 코드 가독성을 높이는데 도움을 줍니다. 이 이름은 디버거와 스택 추적에서도 나타나므로 디버깅시간을 줄일수 있게합니다.

- -

JavaScript 함수는 - JavsScript 내의 다른 모든 것들과 마찬가지로 -  그 자체가 객체이며, 객체 섹션에서 이미 확인한 것처럼, 속성을 추가하거나 변경할수 있다는 점을 명심하십시오

- -

사용자 정의 객체

- -
-

JavaScript에서 객체 지향 프로그래밍에 대한 더 자세한 논의는 객체 지향 JavaScript 소개를 참조하십시오.

-
- -

고전 객체지향 프로그래밍에서 객체는 데이터와 해당 데이터들을 다루는 메소드의 집합이었습니다. JavaScript는 프로토타입 기반 언어로, C++ 이나 Java에서 발견할 수 있는 class 구문이 없습니다(이런 이유로 class 구문에 익숙한 프로그래머들이 때때로 혼란을 경험합니다). 그 대신, JavaScrip는 function을 class로 사용합니다. 이름과 성을 필드로 가지고 있는 'person' 객체를 고려해보도록 합시다. 이름을 표시하는 두가지 방법이 있을 수 있습니다. 예를 들어, "이름 성" 또는 "성, 이름" 이런 식으로 말이죠. 이전에 다룬 함수와 객체를 사용해서 이를 표현하면 다음과 같습니다:

- -
function makePerson(first, last) {
-  return {
-    first: first,
-    last: last
-  }
-}
-function personFullName(person) {
-  return person.first + ' ' + person.last;
-}
-function personFullNameReversed(person) {
-  return person.last + ', ' + person.first
-}
-
-var s = makePerson("Simon", "Willison");
-personFullName(s); // "Simon Willison"
-personFullNameReversed(s); // "Willison, Simon"
-
- -

이렇게 하면 작동하긴 하지만, 보기 안좋습니다. 이런 방법이라면 전역 이름공간(global namespace)에 관련 함수가 너무 많아집니다. 정말 우리에게 필요한 것은 객체에 함수를 붙여놓는 것입니다. 함수는 객체이기 때문에 이건 별로 어렵지 않습니다.

- -
function makePerson(first, last) {
-  return {
-    first: first,
-    last: last,
-    fullName: function() {
-      return this.first + ' ' + this.last;
-    },
-    fullNameReversed: function() {
-      return this.last + ', ' + this.first;
-    }
-  };
-}
-
-var s = makePerson('Simon', 'Willison');
-s.fullName(); // "Simon Willison"
-s.fullNameReversed(); // "Willison, Simon"
-
- -

this 키워드에 주목해 주십시오. 함수 안쪽에서 사용되면서, this는 현재 객체를 참조합니다. 그것이 실제로 의미하는 바는 당신이 부른 바로 그 함수를 지정하는 것입니다. 객체에서 dot 표기법이나 bracket 표기법을 사용해서 부른 경우, 해당 객체는 this가 됩니다. 해당 호출에서 dot 표기법을 사용하지 않은 경우, this는 전역 객체를 참조하게 됩니다.

- -

this가 실수의 잦은 원인이 된다는 것을 명심하십시오 . 예를 들면:

- -
var s = makePerson('Simon', 'Willison');
-var fullName = s.fullName;
-fullName(); // undefined undefined
-
- -

s.fullName()을 이용하지 않고 fullName()을 단독으로 호출하면, 'this'는 전역 객체로 묶이게(bind) 됩니다. first 또는 last 로 명명된 전역 변수가 없기 때문에, 각각에 대해 undefined 결과를 얻게됩니다.

- -

makePerson 함수를 개선하는데 'this' 키워드의 이점을 취할 수 있습니다:

- -
function Person(first, last) {
-  this.first = first;
-  this.last = last;
-  this.fullName = function() {
-    return this.first + ' ' + this.last;
-  };
-  this.fullNameReversed = function() {
-    return this.last + ', ' + this.first;
-  };
-}
-var s = new Person('Simon', 'Willison');
-
- -

여기서 new라는 또다른 키워드를 도입했습니다. newthis와 깊게 연관되어 있습니다. 새로운 빈 객체를 만든 다음 지정된 함수를 불러 새로운 객체를 this 에 설정합니다. this로 지정된 함수는 값을 반환하지 않고 단지 this 객체를 수정한다는 것을 명심하세요. this 객체를 호출하는 곳으로 반환하는 것은  new 입니다. 'new' 에 의해 호출되도록 설계된 함수는 컨스트럭터 함수라고 불립니다. 일반적으로 이러한 함수의 첫자를 대문자로 써서 new로 불릴 컨스트럭터 함수임을 나타냅니다.

- -

개선된 함수는 여전히 fullName() 을 단독으로 호출할 때의 함정이 존재합니다.

- -

우리의 person 객체가 점점 개선되고 있지만, 아직 좀 보기 안좋은 면이 있습니다. 매번 person 계열의 객체를 만들 때마다 내부에서 2개의 새로운 함수 객체를 만들고 있습니다. 이 코드가 객체간에 공유된다면 더 낫지 않을까요?

- -
function personFullName() {
-  return this.first + ' ' + this.last;
-}
-function personFullNameReversed() {
-  return this.last + ', ' + this.first;
-}
-function Person(first, last) {
-  this.first = first;
-  this.last = last;
-  this.fullName = personFullName;
-  this.fullNameReversed = personFullNameReversed;
-}
-
- -

더 좋아 보이네요: 메소드 함수를 한번만 만들고, 컨스트럭터 내에 해당 메소드들을 참조하도록 할당합니다. 이보다 더 개선 할 수 있을까요? 네, 그렇게 할 수 있습니다:

- -
function Person(first, last) {
-  this.first = first;
-  this.last = last;
-}
-Person.prototype.fullName = function() {
-  return this.first + ' ' + this.last;
-};
-Person.prototype.fullNameReversed = function() {
-  return this.last + ', ' + this.first;
-};
-
- -

Person.prototype은 모든 Person 인스턴스들간에 공유되는 객체입니다. 이는 lookup(찾아보기) 체인의 한 부분을 이룹니다. (이건 "prototype chain"이라는 특수한 이름을 따로 가지고 있습니다) 다시 말해, Person 객체의 설정되지 않은 속성에 접근을 시도할 때마다, 그것의 대체용도로 JavaScript는 Person.prototype에 그 속성이 존재하는지 살펴봅니다.그 결과,  Person.prototype에 할당된 모든 것은 this 객체를 통해 해당 컨스트럭터에 속한 모든 인스턴스들간에 사용 가능하게 됩니다.

- -

이것은 정말 강력한 도구입니다. JavaScript에서는 임의의 prototype을 프로그램 내에서 언제든 변형할 수 있습니다. 이미 존재하는 객체에 추가적인 메소드를 실시간으로 추가가할 수 있다는 이야기입니다:

- -
var s = new Person("Simon", "Willison");
-s.firstNameCaps(); //TypeError on line 1: s.firstNameCaps is not a function
-
-Person.prototype.firstNameCaps = function() {
-    return this.first.toUpperCase()
-};
-s.firstNameCaps(); // "SIMON"
-
- -

흥미롭게도, JavaScript의 빌트인 객체의 prototype에도 뭔가를 더 추가할 수 있습니다. String 객체에 문자열 순서를 거꾸로 배열하여 돌려주는 메소드를 추가해 봅시다.

- -
var s = "Simon";
-s.reversed(); // TypeError on line 1: s.reversed is not a function
-
-String.prototype.reversed = function() {
-    var r = "";
-    for (var i = this.length - 1; i >= 0; i--) {
-        r += this[i];
-    }
-    return r;
-};
-
-s.reversed(); // nomiS
-
- -

우리가 추가한 새로운 메소드는 심지어 문자열 상수에서도 동작합니다!

- -
"This can now be reversed".reversed(); // desrever eb won nac sihT
-
- -

기존에 언급한 바와같이, prototype은 체인의 한 부분을 이룹니다. 해당 체인의 루트는 Object.prototype 이며 toString() 메소드를 포함합니다. 이 메소드는 객체를 문자열로 나타내려할 때 호출됩니다. 이 메소드는 우리의 Person 객체의 디버깅에 유용합니다:

- -
var s = new Person("Simon", "Willison");
-s.toString(); // [object Object]
-
-Person.prototype.toString = function() {
-  return '<Person: ' + this.fullName() + '>';
-}
-
-s.toString(); // "<Person: Simon Willison>"
-
- -

avg.apply()의 첫번째 매개변수가 null 이었던걸 기억해봅시다. apply()에 적용되는 첫번째 인자는 당연히 `this'로 간주되는 객체입니다. 여기에 new 의 간단한 구현을 보시죠:

- -
function trivialNew(constructor, ...args) {
-    var o = {}; // 빈 객체를 생성
-    constructor.apply(o, args);
-    return o;
-}
-
- -

이것은 prototype 체인을 설정하지 않으므로 new의 완벽한 대체물이 될 수 없습니다.(이 부분은 설명하기 어렵습니다). 이 내용은 자주 사용하지는 않겠지만 알아두면 좋습니다. 이 부분에서 ...args (생략 부호를 포함해서)는 "rest arguments" 라고 불립니다. 이름이 암시하는 것처럼 매개변수의 나머지를 포함합니다.

- -

그러므로 이렇게 호출하는 것은

- -
var bill = trivialNew(Person, 'William', 'Orange');
- -

아래와 거의 동일합니다.

- -
var bill = new Person('William', 'Orange');
- -

apply() 와 비슷하게 this를 다시 설정할 수 있게 하는, call이라는 이름의 자매 함수가 있는데, 인자로 단일 배열이 아니라 확장된 인자 목록을 입력받습니다.

- -
function lastNameCaps() {
-  return this.last.toUpperCase();
-}
-var s = new Person('Simon', 'Willison');
-lastNameCaps.call(s);
-// 위의 구문은 다음과 같습니다:
-s.lastNameCaps = lastNameCaps;
-s.lastNameCaps();
-
- -

내장 함수 (Inner functions)

- -

다른 함수의 내부에서 JavaScript 함수를 선언할 수 있습니다. 우리는 makePerson() 함수 초기 버전에서 이것을 한번 본적이 있습니다. JavaScript에서 중첩 함수(nested functions)의 중요한 세부사항은 부모 함수 범위의 변수에 접근할 수 있다는 사실입니다:

- -
function parentFunc() {
-    var a = 1;
-
-    function nestedFunc() {
-        var b = 4; // parentFunc은 사용할 수 없는 변수
-        return a + b;
-    }
-    return nestedFunc();  // 5
-}
-
- -

좀 더 유지관리가 쉬운 코드를 작성하고자 할때 이 특성이 굉장히 유용합니다. 한개 혹은 두개의 정도의 함수에서만 호출되며 전체 코드중 다른 부분에서는 사용처가 없는 함수라면 그 함수내에 해당 함수를 중첩시키는 것이 좋습니다. 이렇게 전역 범위 함수의 갯수를 늘리지 않도록 하는 것은 언제나 좋은 습관입니다.

- -

이것은 또한 전역 변수에 대한 유혹을 뿌리칠 수 있는 좋은 대안이 됩니다. 복잡한 코드를 쓸 때, 다양한 함수들간에 값을 공유할 수 있도록 전역 변수를 사용하고 싶어집니다 - 전역 변수는 코드 유지 보수를 어렵게 만듭니다. 중첩 함수는 그 부모 함수의 범위에서 변수를 공유할 수 있으므로, 이 방법을 사용하면 전역 변수 이름공간을 건드리지 않고도 적절한 경우에 함수들을 연동시킬수 있습니다. - '지역 전역'이라고 불러도 괜찮겠네요. 이 기술을 사용할 때는 주의를 요하겠지만, 반드시 알아둬야할 유용한 기술입니다.

- -

클로져 (Closures)

- -

클로져 (역자주: 글자 그대로 한국어로 해석하면 닫힌 주머니)는 JavaScript가 제공해야만 하는 가장 막강한 추상 개념으로 우리를 이끕니다 - 하지만 동시에 잠재적으로 가장 혼란스럽기도 합니다. 다음 함수는 무엇을 하는 걸까요?

- -
function makeAdder(a) {
-  return function(b) {
-    return a + b;
-  };
-}
-var add5 = makeAdder(5);
-var add20 = makeAdder(20);
-add5(6); // ?
-add20(7); // ?
-
- -

makeAdder 함수의 이름은 다음과 같은 과정을 거쳐 반드시 없어집니다: 해당 함수가 한 매개변수를 받아 호출됐을 때, 생성될 때 주어진 매개변수를 더하는 새 'adder' 함수를 생성합니다.

- -

여기서 일어나는 일은 다른 함수의 내에 정의된 어떤 함수가 외부 함수의 변수에 액세스한다는 점에서 앞에 언급한 내장 함수에서 일어나는 일과 매우 비슷합니다. 한가지 다른 점은 외부 함수가 리턴 된다는 점인데, 상식적으로 그것에 들어 있는 변수는 사라진다고 볼 수 있습니다. 하지만 그들은 여전히존재합니다 - 그렇지 않으면 adder 함수는 동작하지 않겠지요. 게다가, makeAdder 지역 변수의 서로 다른 두 "복사본"이 존재합니다 - 하나의 a는 5이고, 다른 하나의 a는 20이죠. 따라서 해당 함수를 부른 결과는 다음과 같습니다:

- -
x(6) // 11을 돌려줌
-y(7) // 27을 돌려줌
-
- -

이건 실제로 일어나는 일입니다. JavaScript 함수가 실행될 때는 언제나, '범위' 객체가 생성되어 해당 함수내에서 생성된 지역 변수를 여기에 저장하고 있습니다. 함수 매개변수로서 넘겨진 어떤 변수라도 여기에 초기값으로 저장하고 있습니다. 이것은 모든 전역 변수와 함수가 들어있는 전역 객체와 비슷하지만, 두가지 중요한 차이점이 있습니다. 첫번째로, 함수가 실행될 때마다 새로운 범위 객체가 생성된다는 점과, 두번째로, (브라우저에서 window로 접근가능한) 전역 객체와 달리 범위 객체는 JavaScript 코드에서 직접적으로 액세스할 수 없다는 점입니다. 예를 들자면 현재 범위 객체의 속성에 반복 접근할 수 있는 수단이 없습니다.

- -

따라서 makeAdder 가 호출되면, 범위 객체는 makeAdder 함수에 매개변수로 넘겨진 하나의 속성 a를 가진 상태로 생성됩니다. 일반적으로 JavaScript의 가비지 컬렉터가 이때 makeAdder에 의해 생성된 범위 객체를 청소해야겠지만, 리턴된 함수가 여전히 범위 객체를 참조하고 있습니다. 결과적으로 범위 객체는 makeAdder에 의해 리턴된 함수 객체가 더는 참조되지 않을 때까지 가비지 컬렉터에 의해 정리되지 않게됩니다.

- -

범위 객체는 JavaScript 객체 체계에서 사용되는 prototype 사슬과 비슷한 범위 사슬이라고 불리는 사슬을 형성합니다.

- -

클로져는 함수와 함수에 의해 생성되는 범위 객체를 함께 지칭하는 용어입니다.

- -

또한 클로져는 상태를 저장할 수 있도록 허용합니다 - 그렇기 때문에, 객체의 내부에서 자주 사용될 수 있는 것입니다.

- -

메모리 누출

- -

클로져의 부작용은 Internet Explorer에서 심각하지는 않지만 쉽게 메모리 누출이 된다는 것입니다. JavaScript는 가비지 컬렉트를 하는 언어 입니다. 객체가 생성됨에 따라서 메모리가 할당되고, 사용하고난 메모리는 더 참조하는 다른 객체가 없을 때 되돌아가는 방식으로 동작하는 언어란 말이죠. 호스트 환경에서 제공되는 객체들은 해당 환경에 의해 다뤄집니다.

- -

브라우저 호스트는 HTML 페이지에 DOM 객체로서 표현되어있는 많은 수의 객체를 다뤄야 합니다. 이 객체들을 어떻게 할당하고 다시 거둬들일지는 브라우저 책임이죠.

- -

Internet Explorer는 이를 위해 자신만의 고유한, JavaScript의 그것과는 다른 가비지 컬렉션 방식을 사용합니다. 두 언어간에 상호작용이 일어날 수 있고 이 과정에서 메모리 누출이 발생할 수 있습니다.

- -

IE에서 메모리 누출은 JavaScript 객체와 고유 객체간에 참조하는 중 자기 자신을 참조 (circular reference, 순환 참조)하게 되는 일이 발생할 경우라면 언제든지 발생하게 됩니다. 다음을 고려해 보도록 합시다:

- -
function leakMemory() {
-    var el = document.getElementById('el');
-    var o = { 'el': el };
-    el.o = o;
-}
-
- -

위의 코드는 순환 참조로서 메모리 누출을 일으킵니다. IE는 완전히 다시 시작되기 전까지는 elo에 의해 사용되는 메모리를 반환하지 못합니다.

- -

위의 경우는 알아채지 못하고 지나갈 확률이 높습니다. 메모리 누출은 사실 오랫동안 실행되거나 큰 데이터 구조나 반복, 순환에 의해 누출된는 메모리 양이 많은 경우에서 실질적으로 고려할만한 가치가 생깁니다.

- -

누출이 이처럼 명확한 경우는 드뭅니다. 누출을 일으키는 데이터 구조는 수차례에 걸친 참조 구조를 가지고 있어서 순환 참조를 하고있는지 명확하지 않은 경우가 더 많습니다.

- -

클로져는 그렇게 되도록 하지않아도 간단하게 메모리 누출을 일으킬 수 있습니다. 다음을 고려해 봅시다:

- -
function addHandler() {
-    var el = document.getElementById('el');
-    el.onclick = function() {
-        this.style.backgroundColor = 'red';
-    }
-}
-
- -

위의 코드는 클릭했을때 배경색이 빨강으로 바뀌는 엘레멘트를 설정합니다. 그리고 메모리 누출도 일으킵니다. 어째서냐고요? el을 참조하면 의도와는 달리 익명 내부 함수 때문에 생성된 클로져 내에 붙잡혀 있게 되기 때문입니다. 이는 JavaScript 객체 (내부 함수)와 원시 객체 (el)간에 순환 참조를 만듭니다.

- -

이 문제를 피할 수 있는 많은 방법이 있습니다. 가장 간단한 건 이겁니다:

- -
function addHandler() {
-    var el = document.getElementById('el');
-    el.onclick = function() {
-        this.style.backgroundColor = 'red';
-    }
-    el = null;
-}
-
- -

이렇게 하면 순환 참조 고리를 끊을 수 있습니다.

- -

놀랍게도, 클로져에 의해 발생된 순환 참조를 고리를 끊기 위한 한 요령은 또다른 클로져를 추가하는 것입니다:

- -
function addHandler() {
-    var clickHandler = function() {
-        this.style.backgroundColor = 'red';
-    }
-    (function() {
-        var el = document.getElementById('el');
-        el.onclick = clickHandler;
-    })();
-}
-
- -

내부 함수는 실행되고 바로 사라지므로서, clickHandler와 함께 생성된 클로져로부터 그 내용을 숨깁니다.

- -

클로져를 피할 수 있는 또다른 좋은 요령은 window.onunload 이벤트가 발생하는 동안 순환 참조를 끊는 것입니다. 많은 이벤트 라이브러리가 이렇게 동작합니다. 주의할 것은 그렇게 하도록하면 Firefox 1.5의 bfcache를 비활성화 하게 되므로, 별 다른 이유가 없다면 Firefox에서 unload listener를 등록해서는 안 된다는 것입니다.

- -
-

원본 문서 정보

- - -
- -

{{ languages( { "en": "en/A_re-introduction_to_JavaScript", "fr": "fr/Une_reintroduction_a_JavaScript", "it": "it/Una_re-introduzione_a_Javascript", "ja": "ja/A_re-introduction_to_JavaScript", "pl": "pl/JavaScript/Na_pocz?tek", "zh-cn": "cn/A_re-introduction_to_JavaScript" } ) }}

diff --git a/files/ko/web/javascript/a_re-introduction_to_javascript/index.md b/files/ko/web/javascript/a_re-introduction_to_javascript/index.md new file mode 100644 index 0000000000..36950fca58 --- /dev/null +++ b/files/ko/web/javascript/a_re-introduction_to_javascript/index.md @@ -0,0 +1,1037 @@ +--- +title: JavaScript 재입문하기 (JS ​튜토리얼) +slug: Web/JavaScript/A_re-introduction_to_JavaScript +tags: + - CodingScripting + - Intermediate + - Intro + - JavaScript + - Learn + - Tutorial +translation_of: Web/JavaScript/A_re-introduction_to_JavaScript +original_slug: A_re-introduction_to_JavaScript +--- +
{{jsSidebar}}
+ +

어째서 재입문일까요? 왜냐하면, JavaScript세계에서 가장 오해받고 있는 프로그래밍 언어로 악명이 높기 때문입니다. 종종 장난감같다고 조롱당하기도했지만, 이 거짓말같은 단순함 아래에는 몇 가지의 강력한 언어 기능이 숨어 있습니다. Javascript는 현재 엄청나게 많은, 요즘 가장 뜨고있는 애플리케이션들에 사용되고 있어서, 웹 또는 모바일 개발자 누구에게라도 이 기술에 대한 깊은 지식이 중요한 기량이 된다는 것을 보여주고 있습니다.

+ +

이 이야기를 이해하는데는 이 언어의 역사를 먼저 보는 것이 도움이 됩니다. JavaScript는 1995년 Netscape의 엔지니어 Brendan Eich에 의해 만들어졌고, 1996년 초에 Netscape 2와 함께 처음 릴리즈 되었습니다. 이것은 원래 LiveScript로 불리기로 되어 있었습니다만 Sun Microsystem의 Java 언어의 성공에 편승해보려고 -두 언어 사이의 공통점이 매우 적음에도 불구하고- 불행이 예견된 마케팅 결정에 따라 이름이 바뀌게 됩니다. 이 결정은 역사상 유래가 없는 혼란의 근원이 되어버립니다.

+ +

몇 달 후, Microsoft는 IE3와 함께 JScript를 발표했습니다. 이 JScript는 Javascript를 정말 닮았고 호환성이 좋았습니다. 몇 달 뒤에, Netscape는 1997년에 ECMAScript 표준의 첫번째 판이 되는 JavaScript를 유럽 표준화 단체인 Ecma International에 보냅니다. 이 표준은 1999년에 ECMAScript edition 3에 따라 큰 규모의 개정을 거친 후, 유례없이 아주 안정된 상태로 계속 유지되고 있습니다. 4번째 판은 중도 포기되었는데, 언어의 복잡성 증가에 관련한 정치적 문제 때문이었습니다. 이 4번째 판의 많은 파트들은 ECMAScript edition 5 (2009년 12월에 출간)와 6번째 개정판 규격(2015년에 출간)의 근간을 형성하고 있습니다. 

+ +
+

 이제부터는 ECMAScript를 우리에게 좀 더 친근한 말인  "JavaScript"라고 부르겠습니다.

+
+ +

대부분의 프로그래밍 언어와는 달리, JavaScript 언어는 입출력 개념이 없습니다. 이 언어는 호스트 환경 아래에서 스크립트 언어로서 동작하도록 디자인 되어있고, 따라서 외부 세계와 통신하기위해 호스트 환경이 제공하는 메커니즘에 의존합니다. 대부분의 경우 일반적인 호스트 환경은 브라우저이지만 JavaScript 인터프리터는 Adobe Acrobat, Photoshop, SVG images, Yahoo! 위젯 엔진 등의 제품에서도 발견할 수 있고, node.js 와 같은 서버 측 환경에서도 찾을 수 있습니다. 하지만 JavaScript가 사용되는 분야는 계속 더 넓혀지고 있습니다. NoSQL 데이터베이스, Apache CouchDB, 임베디드 컴퓨터, GNU/Linux OS의 가장 유명한 GUI 인 GNOME 과 같은 데스크톱 환경에서도 JavaScript가 사용됩니다.

+ +

개요

+ +

JavaScript는 유형 및 연산자, 표준 내장 객체 및 메소드가 있는 다중 패러다임, 동적 언어입니다. 구문은 Java 및 C 언어를 기반으로합니다. 이러한 언어의 많은 구조가 JavaScript에도 적용됩니다. JavaScript는 클래스 대신 객체 프로토 타입을 사용하여 객체 지향 프로그래밍을 지원합니다 (프로토 타입 상속 및 ES2015 {{jsxref("Classes")}}). JavaScript는 함수형 프로그래밍도 지원합니다. 함수는 객체이며, 함수는 실행 가능한 코드를 유지하고 다른 객체와 마찬가지로 전달 될 수 있습니다.

+ +

어떤 언어에서라도 기초가 되는 부분인 타입을 살펴보는 것부터 시작해봅시다. JavaScript 프로그램은 값을 다루고 해당 값은 모두 타입을 가지고 있습니다. JavaScript의 타입은 다음과 같습니다:

+ + + +

... 오, 그리고 약간 특별한 타입인 정의되지 않음(Undefined) 과 널(Null) 이 있습니다. 또한 객체의 특별한 종류인 배열(Array) 객체. 그리고 자유롭게 사용할 수 있는 날짜(Date) 객체정규식(RegExp) 객체가 있습니다. 그리고 기술적으로 정확히 말해 함수(Function)는 단지 객체의 특별한 타입으로 취급됩니다. 따라서 타입 구조도를 정리해보면 다음과 같습니다:

+ + + +

그리고 또 몇 가지 오류 타입이 내장되어 있습니다. 그렇지만 처음 구조도를 기억하고만 있으면 다른 것들도 아주 쉽게 이해할 수 있을 것입니다.

+ +

수 (Numbers)

+ +

설계 명세서에 의하면 JavaScript에서 수는 "이중정밀도 64비트 형식 IEEE 754 값"으로 정의됩니다. 이것은 몇가지 흥미로운 결과를 가져옵니다. JavaScript에는 정수와 같은 것이 존재하지 않으므로 ({{jsxref("BigInt")}} 제외), 조금 조심해야 합니다. 이 예제를 보세요:

+ +
console.log(3 / 2);             // 1이 아닌, 1.5
+console.log(Math.floor(3 / 2)); // 1
+ +

명백한 정수는 사실 암묵적으로 실수입니다.

+ +

또한, 다음과 같은 것들을 주의하세요:

+ +
0.1 + 0.2 = 0.30000000000000004
+
+ +

실제로 정수 값은 32 비트 정수로 처리되며 일부 구현은 32 비트 정수가 아닌 숫자에 유효한 명령어를 수행 할 때까지 이러한 방식으로 저장합니다. 이는 비트 단위 작업에 중요 할 수 있습니다.

+ +

덧셈, 뺄셈, 계수 (또는 나머지) 연산을 포함하는 표준 산술 연산자가 지원됩니다. 또한 앞에서 언급하는 것을 깜박 잊은 고급 수학 함수와 상수를 다루기 위한 수학(Math)으로 불리는 내장 객체가 있습니다:

+ +
Math.sin(3.5);
+var circumference = 2 * Math.PI * r;
+ +

내장 parseInt() 함수를 사용하여 문자열을 정수로 변환할 수 있습니다. 이는 다음과 같이 옵션으로 주어지는 두번째 매개변수를 밑으로 하여 수행할 수 있습니다:

+ +
parseInt('123', 10); // 123
+parseInt('010', 10); // 10
+ +

구형 브라우저에서 "0"으로 시작하는 문자열은 8 진수 (기수 8)로 가정되지만, 2013 년 이후에는 그렇지 않습니다. 문자열 형식이 확실하지 않으면 이전 브라우저에서 놀라운 결과를 얻을 수 있습니다.

+ +
parseInt('010');  //  8
+parseInt('0x10'); // 16
+ +

이 같은 결과는 {{jsxref("Global_Objects/parseInt", "parseInt()")}} 함수가 0으로 시작되는 문자열을 8진수로, "0x"로 시작하는 문자열은 16진수로 취급하기 때문에 발생합니다. 16진수 표기법이 그대로 유지됩니다. 8진수는 제거되었습니다.

+ +

만약 이진수를 정수로 변환하고 싶다면, 밑을 바꾸기만하면 됩니다:

+ +
parseInt('11', 2); // 3
+
+ +

이와 비슷하게, 내장 함수 {{jsxref("Global_Objects/parseFloat", "parseFloat()")}}를 사용하여 부동 소수점 숫자를 파싱 할 수 있습니다. {{jsxref("Global_Objects/parseInt", "parseInt()")}}과 달리 parseFloat()는 항상 10진수를 사용합니다.

+ +

단항 연산자 + 를 사용하여 값을 숫자로 변환 할 수도 있습니다:

+ +
+ '42';   // 42
++ '010';  // 10
++ '0x10'; // 16
+ +

문자열이 수가 아닌 경우 NaN ("Not a Number" (수가 아님)을 줄인 약자)로 불리는 특별한 값을 돌려줍니다:

+ +
parseInt('hello', 10); // NaN
+
+ +

NaN 는 독성을 가지고 있습니다: 어떤 수학 연산의 입력값으로써 주어지면 그 결과는 역시 NaN가 되기 때문입니다:

+ +
NaN + 5; // NaN
+
+ +

내장 isNaN() 함수를 사용해서 NaN 인지 여부를 검사할 수 있습니다:

+ +
isNaN(NaN); // true
+
+ +

JavaScript는 또 특별한 값 Infinity-Infinity를 가지고 있습니다:

+ +
 1 / 0; //  Infinity
+-1 / 0; // -Infinity
+ +

내장 함수 {{jsxref("Global_Objects/isFinite", "isFinite()")}}를 사용하여 Infinity, -Infinity 및 NaN 값을 테스트 할 수 있습니다.

+ +
isFinite(1 / 0);     // false
+isFinite(-Infinity); // false
+isFinite(NaN);       // false
+ +
+

{{jsxref("Global_Objects/parseInt", "parseInt()")}} 와 {{jsxref("Global_Objects/parseFloat", "parseFloat()")}} 함수는 숫자로 아닌 문자가 나올때까지 문자열을 파싱하고, 그 지점까지 파싱된 숫자를 반환합니다. 그런데 "+"연산자는 중간에 유효하지 않은 문자가 있으면 그대로 문자열을 NaN 으로 그냥 변환해버립니다. console에서 "10.2abc"를 파싱해보면 어떤점이 다른지 더 쉽게 이해할 수 있습니다.

+
+ +

문자열 (Strings)

+ +

JavaScript에서 문자열은 유니코드 문자들이 연결되어 만들어진 것입니다. 이는 국제화(i18n, internationalization) 하려하는 누구에게라도 환영받을만한 소식입니다. 좀 더 정확히 말하자면, 각각이 16비트 숫자로 표현된 UTF-16 코드 유닛이 길게 이어져있는 것입니다. 각 유니코드 문자는 1개나 2개의 코드 유닛으로 표현됩니다.

+ +

한 개의 문자를 나타내려면 길이가 1인 문자열을 사용하면 됩니다.

+ +

문자열의 길이를 알고싶다면, 해당 문자열의 length 속성(해당 객체가 소유하고 있는 성질을 나타내는 값)에 접근하면 됩니다:

+ +
'hello'.length; // 5
+
+ +

우리의 첫 JavaScript 객체입니다! 문자열도 역시 객체로 취급된다고 언급했던적이 있죠? 다음과 같이 메소드까지 있는 확실한 녀석입니다:

+ +
'hello'.charAt(0); // "h"
+'hello, world'.replace('hello', 'goodbye'); // "goodbye, world"
+'hello'.toUpperCase(); // "HELLO"
+ +

이외의 타입들

+ +

JavaScript는 의도적으로 값이 없음을 가리키는 '객체' 타입의 객체인 null과 초기화되지 않은 값 — 아직 어떤 값도 주어지않은(할당되지않은) 변수임을 가리키는 '정의되지 않음' 타입의 객체인 undefined로 구분됩니다. 값에 대해서 나중에 언급할 것이지만 JavaScript에서 변수에 값을 주지않고 선언하는 것이 가능합니다. 이럴 경우, 변수의 타입은 undefined이 되는 것입니다.

+ +

JavaScript는 truefalse 값 (둘은 모두 키워드로 예약되어있는 값)을 가질 수 있는 부울 타입을 가지고 있습니다. 다음과 같은 규칙에 따라 어떤 임의의 값을 부울값으로 변환할 수 있습니다:

+ +
    +
  1. false, 0, 빈 문자열 (""), 수가 아님을 뜻하는 NaN, null, 와 undefined은 모두 false가 됩니다.
  2. +
  3. 다른 모든 값은 true가 됩니다.
  4. +
+ +

이 변환은 Boolean() 함수를 써서 명시적으로 이 작업을 수행하실 수 있습니다:

+ +
Boolean('');  // false
+Boolean(234); // true
+ +

하지만 반드시 이렇게 할 필요는 거의 없습니다. JavaScript는 이러한 변환 작업을 if 문 (아래를 보세요)과 같이 부울값이 필요한 경우를 만나게되면 자동으로 사용자가 모르는 사이에 처리해버리기 때문입니다. 이러한 이유로 인해 우리는 가끔 부울 타입으로 변환되었을 때, truefalse이 됨을 의미하는 값들을 각각 "참 값"과 "거짓 값"으로 부를 것입니다. 또는 각각 "참으로 취급되다"와 "거짓으로 취급되다"라는 식으로 불릴 수도 있습니다.

+ +

부울 연산자는 && (논리적와, 그리고 ), || (논리적또는 ), 그리고 ! (논리적부정 )이 지원됩니다. 아래에서 다시 언급하겠습니다.

+ +

변수 (Variables)

+ +

JavaScript에서 새로운 변수는 let, constvar 키워드로 선언됩니다.

+ +

let을 사용하면 블록 유효 범위 변수를 선언 할 수 있습니다. 선언 된 변수는 변수가 포함 된 함수 블록에서 사용할 수 있습니다.

+ +
let a;
+let name = 'Simon';
+ +

아래는 let으로 선언한 변수가 가지는 유효 범위의 예제입니다. 

+ +
// myLetVariable는 여기에서 보이지 *않습니다*
+
+for (let myLetVariable = 0; myLetVariable < 5; myLetVariable++) {
+  // myLetVariable는 여기에서 유효합니다
+}
+
+// myLetVariable는 여기에서 보이지 *않습니다*
+ +

const는 값이 변경되지 않는 변수를 선언 할 수 있게 합니다. 변수는 변수가 선언 된 함수 블록에서 사용할 수 있습니다.

+ +
const Pi = 3.14; // 변수 Pi 설정
+Pi = 1; // 상수로 설정된 변수는 변경 할 수 없기 때문에 애러 발생.
+ +

var은 가장 일반적인 변수 선언 키워드입니다. let, const 키워드가 가지는 제한을 var은 갖지 않습니다. 이는 자바스크립트에서 변수를 선언하는 전통적인 유일한 방법이었기 때문입니다. var 키워드로 선언 된 변수는 변수가 선언 된 함수 블록에서 사용 할 수 있습니다.

+ +
var a;
+var name = 'Simon';
+ +

var로 선언한 변수의 유효 범위 예제입니다.

+ +
// myVarVariable는 여기에서 사용 할 수 *있습니다*
+
+for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) {
+  // myVarVariable는 함수 전체에서 사용 할 수 있습니다.
+}
+
+// myVarVariable는 여기에서 사용 할 수 *있습니다*
+ +

변수에 값을 지정하지 않고 변수를 선언하면, 타입은 undefined로 지정 됩니다.

+ +

자바스크립트와 자바 같은 다른 언어 사이의 중요한 차이점은 자바스크립트는 블록에 범위가 없다는 것입니다. 함수에만 범위가 있습니다. 변수가 복합 문에서 (예를 들어 if 제어 구조 내에서) var를 사용하여 정의 된 경우 전체 함수에서 볼 수 있습니다. 그러나 ECMAScript 2015부터 letconst 선언을 사용하면 블록 범위 변수를 만들 수 있습니다.

+ +

연산자 (Operators)

+ +

JavaScript의 산술 연산자로는 +, -, *, /, %(나머지 연산자)가 있습니다. 값은 = 연산자로 할당할 수 있고, +=-=처럼 다른 연산자를 같이사용해서 할당할 수 있습니다. 이렇게 쓰인 연산자는 x = x연산자 y와 같은 결과를 나타냅니다.

+ +
x += 5;
+x = x + 5;
+
+ +

++-- 를 각각 점진적인 증가와 감소에 사용할 수 있습니다. 이들은 또한 전처리 또는 후처리 연산자로 사용될 수 있습니다.

+ +

+ 연산자는 문자열 이어붙이기도 합니다:

+ +
'hello' + ' world'; // "hello world"
+
+ +

문자열에 어떤 수 (또는 다른 값)를 더하면 일단 모두 문자열로 바뀌게 됩니다. 다음 예를 보시면 무슨 말씀인지 아실 수 있을겁니다:

+ +
'3' + 4 + 5;  // "345"
+ 3 + 4 + '5'; // "75"
+ +

빈 문자열에 어떤 값을 더하는 것은 해당 값을 문자열로 바꾸는 요령입니다.

+ +

JavaScript에서 비교<, >, <=>= 를 통해 가능합니다. 이 연산자들은 문자열과 수 양쪽 모두에서 동작합니다. 상동은 약간 직관성이 떨어지는데 이중 등호 (==) 연산자는 서로 다른 타입을 줄 경우 타입 강제 변환을 수행하기 때문에 다음과 같이 때때로 기대하지 않은 결과를 내보내기 때문입니다:

+ +
123 == '123'; // true
+1 == true;    // true
+
+ +

타입 강제 변환을 하지 않게 하려면, 삼중 등호 연산자 (===)를 사용해야합니다:

+ +
123 === '123'; // false
+1 === true;    // false
+
+ +

이와 비슷하게 !=!== 연산자가 있습니다.

+ +

JavaScript는 값을 비트로 취급하는 연산자도 가지고 있습니다. 사용하고 싶을 때 언제라도 사용할 수 있도록 말이죠.

+ +

제어 구조

+ +

JavaScript는 C 계열의 다른 언어들과 비슷한 제어 구조를 가지고 있습니다. 조건문은 ifelse를 지원하는데, 원하시는대로 얼마든지 중첩 시켜서 사용할 수 있습니다:

+ +
var name = 'kittens';
+if (name == 'puppies') {
+  name += ' woof';
+} else if (name == 'kittens') {
+  name += ' meow';
+} else {
+  name += '!';
+}
+name == 'kittens meow';
+
+ +

JavaScript는 while 반복문과 do-while 반복문도 사용할 수 있습니다. 첫번째 것은 단순 반복에 유용하게 사용할 수 있고, 두번째 것은 반복문이 반드시 적어도 한번이상 실행 되도록 하고 싶을 때 사용할 수 있습니다:

+ +
while (true) {
+  // 무한루프!
+}
+
+var input;
+do {
+  input = get_input();
+} while (inputIsNotValid(input));
+
+ +

JavaScript의 for 반복문은 C 와 Java의 반복문과 같습니다. 말하자면, 반복문에 필요한 제어 정보를 한 줄에 표현할 수 있다는 이야기지요.

+ +
for (var i = 0; i < 5; i++) {
+  // 내부 동작을 5번 반복합니다
+}
+
+ +

JavaScript에는 두개의 중요한 for 반복문 또한 포함됩니다. 첫번째로 for...of 입니다.

+ +
for (let value of array) {
+  // value로 작업을 실행합니다
+}
+
+ +

그리고 for ... in 입니다.

+ +
for (let property in object) {
+  // object의 항목(property)으로 작업을 실행합니다
+}
+
+ +

&&|| 연산자는 첫번째 식을 평가한 결과에 따라서 두번째 식을 평가를 실행하는 단축평가(short-circuit) 논리를 사용합니다. 이는 다음과 같이 객체에 접근하기 전에 null 객체인지, 아닌지를 검사하는데 유용하게 사용될 수 있습니다:

+ +
var name = o && o.getName();
+
+ +

또는 (틀린값이 유효하지 않은 값일때) 캐싱 값에 대해서도 사용합니다.:

+ +
var name = cachedName || (cachedName = getName());
+
+ +

JavaScript는 한줄로 조건문을 쓸 수 있게 해주는 삼중 연산자도 가지고 있습니다:

+ +
var allowed = (age > 18) ? "yes" : "no";
+
+ +

switch 문은 숫자나 문자열을 기반으로 다중 분기되는 문장을 작성하는데 사용될 수 있습니다:

+ +
switch(action) {
+    case 'draw':
+        drawIt();
+        break;
+    case 'eat':
+        eatIt();
+        break;
+    default:
+        doNothing();
+}
+
+ +

break 문장을 추가하지 않았다면, 다음 단계로 "넘어가서" 실행합니다. 이렇게 되는 것을 기대하는 것은 매우 드문경우 입니다. 실은 디버깅하는데 용이하도록 하기위해 주석으로서 일부러 붙여놓은 넘어가기 이름표 입니다:

+ +
switch(a) {
+    case 1: // fallthrough
+    case 2:
+        eatIt();
+        break;
+    default:
+        doNothing();
+}
+
+ +

default 구문의 적용은 선택사항입니다. switch와 case 부분에서 둘다 표현식을 사용할 수도 있습니다. switch부분과 case 부분의 표현식은  === 연산자로 비교됩니다.

+ +
switch(1 + 3){
+    case 2 + 2:
+        yay();
+        break;
+    default:
+        neverhappens();
+}
+
+ +

객체 (Objects)

+ +

JavaScript 객체는 간단히 이름-값 쌍(name-value pairs)의 모임입니다. 그렇기 때문에, JavaScript의 객체의 모임은 다음과 비슷하다고 할 수 있습니다:

+ + + +

이 데이터 구조가 매우 광범위하게 사용된다는 사실은 활용 방도가 다양함을 입증합니다. JavaScript내 모든 것 (코어 타입들은 제외)은 객체로 취급되기 때문에 어떤 JavaScript 프로그램도 기본적으로 해쉬 테이블을 검색하는데 필요한 출중한 성능을 가지고 있습니다. 매우 빠르기 때문에 장점이 됩니다!

+ +

값은 객체를 포함하여 아무 JavaScript 값이 될 수 있는 반면, "이름" 부분은 JavaScript 문자열 입니다. 이는 무작위적인 복잡성을 가지는 데이터 구조를 만들 수 있도록 해줍니다.

+ +

빈 객체를 생성하는데 두가지 방법이 있습니다:

+ +
var obj = new Object();
+
+ +

와:

+ +
var obj = {};
+
+ +

이들은 의미적으로 동치입니다. 두번째 방법은 객체 리터럴 구문이라고 부르며 더 편리합니다. 객체 리터럴 구문은 JSON 구문의 핵심이며 이 방법을 사용한 코드를 더 많이 볼 수 있습니다.

+ +

객체 리터럴 구문으로 객체의 전체적인 구조를 초기화 할 수 있습니다:

+ +
var obj = {
+    name: "Carrot",
+    "for": "Max",
+    details: {
+        color: "orange",
+        size: 12
+    }
+}
+
+ +

속성에 연속적으로 접근할 수 있습니다:

+ +
obj.details.color; // orange
+obj["details"]["size"]; // 12
+
+ +

아래 예제는 객체 프로토타입(Person)과 프로토타입의 인스턴스(you)를 생성합니다.

+ +
function Person(name, age) {
+  this.name = name;
+  this.age = age;
+}
+
+// 객체를 정의한다
+var you = new Person('You', 24);
+// "You"라는 이름의 24세인 새로운 사람을 생성중이다.
+
+ +

일단 생성되면, 객체의 속성에 다음의 두가지 방법들 중 한가지로 접근할 수 있습니다:

+ +
// dot 표기법
+obj.name = "Simon"
+var name = obj.name;
+
+ +

그리고...

+ +
// bracket 표기법
+obj["name"] = "Simon";
+var name = obj["name"];
+// key를 정의하기 위해 변수도 쓸수 있습니다.
+var user = prompt('what is your key?')
+obj[user] = prompt('what is its value?')
+
+ +

이들은 의미적으로 역시 같습니다. 두번째 방법은 속성의 이름이 실행시간(run-time)에 계산될 수 있는 문자열로 주어집니다. 하지만 이방법을 사용하면 일부 JavaScript엔진과 압축기 최적화(minifier optimizations)를 적용할수 없습니다.또한 예약된 단어(키워드)로 되어있는 이름으로 객체의 속성을 설정하거나 얻어낼 수 있습니다:

+ +
obj.for = "Simon"; // 구문 오류, for 가 예약된 단어(키워드)이기 때문에
+obj["for"] = "Simon"; // 정상 동작
+
+ +
+

ECMAScript 5 이래로, 예약어는  객체 항목의 이름으로 "덧붙임없이" 사용할수도 있습니다. 이말은 객체 리터럴을 정의할때 따옴표로 "둘러쌀" 필요가 없다는 의미입니다.  ES5 Spec을 참고해 보십시오.

+
+ +

객체나 프로토타입에 대한 좀더 상세한 내용은 Object.prototype 을 참조하십시오. 객체 프로토타입과 객체 프로토타입 체인에 대한 설명은 상속과 프로토타입 체인 을 참조하십시오.

+ +
+

ECMAScript 2015 이래로, 객체의 key는 생성시의 대괄호 표기법(bracket notation)으로 정의될수 있습니다. 그냥 var userPhone = {}; userPhone[phoneType] = 12345. 처럼 표기하는 방법 대신 {[phoneType]: 12345} 와 같은 사용법도 가능합니다.

+
+ +

배열 (Arrays)

+ +

JavaScript에서 배열은 실제로는 특별한 타입의 객체입니다. (숫자로 나타낸 속성은 자연스럽게 [] 구문만을 사용해서 접근하게 되므로) 일반 객체와 많이 비슷하게 동작하지만, 이 객체는 'length'라는 한가지 마법적인 속성을 가집니다. 이는 항상 배열에서 가장 큰 인덱스보다 하나 더 큰 값으로 존재합니다.

+ +

배열을 생성하는 예전 방법은 다음과 같습니다:

+ +
var a = new Array();
+a[0] = "dog";
+a[1] = "cat";
+a[2] = "hen";
+a.length // 3
+
+ +

한가지 더 편리한 배열 표현 방법은 배열 리터럴을 사용하는 것입니다:

+ +
> var a = ["dog", "cat", "hen"];
+> a.length
+3
+
+ +

배열 리터럴 끝에 콤마(",")를 꼬리로 남겨두는 것은 브라우저마다 다르게 처리하므로 그렇게 하지는 마시기 바랍니다.

+ +

array.length 는 배열에 들어있는 항목의 수를 반드시 반영하지는 않는다는 점을 주의하시기 바랍니다. 다음과 같은 경우를 고려해보겠습니다:

+ +
> var a = ["dog", "cat", "hen"];
+> a[100] = "fox";
+> a.length
+101
+
+ +

기억해두세요 - 배열의 length 속성은 최대 인덱스에 하나를 더한 값일 뿐입니다.

+ +

존재하지 않는 배열 인덱스를 참조하려고하면 다음과 같이 undefined 을 얻게됩니다:

+ +
> typeof(a[90])
+undefined
+
+ +

[]length에 관한 위의 사항들을 감안하면 배열을 for 반복문으로 처리할 때 다음과 같은 방법으로 처리하실 수 있을 것입니다:

+ +
for (var i = 0; i < a.length; i++) {
+    // a[i] 로 뭔가를 수행
+}
+
+ +

ES2015는 배열과 같은 이터러블 객체를 위해 좀더 간결한 for...of 루프를 소개했습니다.

+ +
for (const currentValue of a) {
+    // currentValue 로 뭔가를 수행
+}
+ +

또한 for...in 루프를 이용하여 배열에 루프를 돌릴수도 있지만, 이 방법은 배열 요소를 반복하는게 아니라 배열 인덱스를 반복합니다. 뿐만 아니라, 누군가 Array.prototype에 새로운 속성을 추가하면, 그 속성들 또한 이런 루프로 반복됩니다. 따라서 이런 반복 형태는 배열에는 추천되지 않습니다.

+ +

배열에 대한 또다른 반복방법은 ECMAScript 5에 추가된 forEach() 입니다:

+ +
['dog', 'cat', 'hen'].forEach(function(currentValue, index, array) {
+    // currentValue나 array[index]로 뭔가를 수행
+}
+
+ +

배열에 항목 하나를 추가하길 원한다면 이렇게 하면 됩니다:

+ +
a.push(item);
+ +

배열은 몇가지 메서드가 제공됩니다. 배열 메서드에 대한 전체 문서를 참조하십시오.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
메서드 이름설명
a.toString()각 항목에 대한 toString()의 출력이 콤마로 구분된 한개의 문자열을 반환합니다.
a.toLocaleString()각 항목에 대한 toLocaleString()의 출력이 콤마로 구분된 한개의 문자열을 반환합니다.
a.concat(item1[, item2[, ...[, itemN]]])item들이 덧붙여진 한개의 배열을 반환합니다.
a.join(sep)배열의 값들을 sep 인자로 구분하여 합친 한개의 문자열로 변환합니다.
a.pop()배열의 마지막 항목을 반환하면서 제거합니다.
a.push(item1, ..., itemN)배열의 끝에 item들을 덧붙입니다.
a.shift()배열의 첫번째 항목을 반환하면서 제거합니다.
a.unshift(item1[, item2[, ...[, itemN]]])배열의 앞쪽에 item들을 덧붙입니다.
a.slice(start[, end])배열의 일부분을 새배열로 반환합니다.
a.sort([cmpfn])옵션으로 비교용도의 함수를 입력받습니다.
a.splice(start, delcount[, item1[, ...[, itemN]]])배열의 일부분을 제거하고 다른 항목으로 대체하여 배열을 변경합니다..
a.reverse()배열의 순서를 거꾸로 배열합니다.
+ +

함수 (Functions)

+ +

객체와 마찬가지로, 함수는 JavaScript를 이해하는데 핵심이 되는 컴포넌트입니다. 가장 기본적인 함수의 예는 다음과 같습니다:

+ +
function add(x, y) {
+    var total = x + y;
+    return total;
+}
+
+ +

이 예는 기본 함수에 대해 알아야 할 모든 것을 보여주고 있습니다. JavaScript 함수는 0 이상의 이름이 있는 매개변수를 가질 수 있습니다. 함수의 본체는 갯수 제한없이 구문을 포함할 수 있고 해당 함수에 지역적으로 변수를 보유하도록 선언할 수 있습니다. return 문은 언제나 값을 돌려주고 함수의 실행을 끝내는데 사용될 수 있습니다. 리턴 문이 없으면 (혹은 값이 없는 리턴이 사용되면), JavaScript는 undefined을 돌려줍니다.

+ +

이름 붙여진 매개변수들은 다른 어떤 것보다도 해당 함수가 어떤 함수인지 설명해주는 좋은 역할을 할 수 있습니다. 해당 함수가 원하는 매개변수를 주지않고 함수를 호출할 수 있지만 그럴 경우 해당 변수들은 undefined로 설정됩니다.

+ +
add(); //NaN
+// undefined에 대해 덧셈을 수행할 수 없습니다
+
+ +

함수가 기대하는 원래의 매개변수보다 많은 매개변수를 넘겨줄 수도 있습니다:

+ +
add(2, 3, 4); // 5
+// 처음의 두 수가 더해집니다. 4는 무시됨
+
+ +

이 예는 조금 어리석어 보이지만, 함수는 추가적으로 주어진 매개변수를 함수 내부에서 접근할수 있습니다. 이 객체는 arguments라고 하며, 해당 함수에  매개변수로 넘겨진 모든 값을 가지고 있는 배열과 비슷한 객체입니다. 우리가 원하는만큼 값을 취하는 add 함수를 다시 써보겠습니다:

+ +
function add() {
+    var sum = 0;
+    for (var i = 0, j = arguments.length; i < j; i++) {
+        sum += arguments[i];
+    }
+    return sum;
+}
+
+add(2, 3, 4, 5); // 14
+
+ +

확실히 2 + 3 + 4 + 5를 직접쓰는 것보다 유용한 함수는아닙니다. 평균계산 함수를 만들어 보겠습니다:

+ +
function avg() {
+    var sum = 0;
+    for (var i = 0, j = arguments.length; i < j; i++) {
+        sum += arguments[i];
+    }
+    return sum / arguments.length;
+}
+
+avg(2, 3, 4, 5); // 3.5
+
+ +

이건 매우 유용합니다만, 좀 번잡해보입니다. 코드 크기를 다소 줄이기 위해, arguments 배열의 사용을 Rest 파라미터 문법으로 대체해볼 필요가 있습니다. 이 방법으로, 코드 크기는 최소한으로 유지 하면서, 갯수 제한없이 함수로 인자를 전달할수 있습니다. Rest 파라미터 연산자는 다음과 같은 포맷(...variable)으로 함수 파라미터 목록에 사용됩니다. 이 varaible 인자는 함수가 호출될때 전달되는 모든 인자를 포함합니다. variable 인자에서 반환되는 값을 사용하기 위해 위 코드에서 for 루프를 for..of 루프로 변경합니다.

+ +
function avg(...args) {
+    var sum = 0;
+    for (let value of args) {
+        sum += value;
+    }
+    return sum / arr.length;
+}
+
+avg(2, 3, 4, 5); // 3.5
+
+ +
+

위 코드에서,변수 args 는 함수로 전달된  모든 값을 가지고 있습니다.
+
+ rest 파라미터 연산자가 함수 선언의 어느곳에 위치하든 선언 위치 이후에 모든 인자를 저장하는것이며, 이전이 아니라는 것이 중요합니다. 즉 , function avg(firstValue, ...args) 에서 함수로 전달된 첫번째 값은 firstValue 변수에 저장되며, 남은 변수들은 args에 저장됩니다.

+
+ +

이건 또다른 유용한 언어 특성입니다만 우리를 새로운 문제점으로 인도합니다. avg() 함수는 콤마로 구분된 인자목록을 받지만, 배열의 평균을 알고싶은 경우라면요? 함수를 다음과 같이 재작성 하면 됩니다 :

+ +
function avgArray(arr) {
+  var sum = 0;
+  for (var i = 0, j = arr.length; i < j; i++) {
+    sum += arr[i];
+  }
+  return sum / arr.length;
+}
+
+avgArray([2, 3, 4, 5]); // 3.5
+ +

하지만 우리가 이미 만든 함수를 다시 사용할 수 있다면 좋을 것입니다. 운이 좋게도 JavaScript는 함수 객체라면 모두 가지게 되는 apply() 메소드를 사용해서 임의의 매개변수 배열을 함수에 넘겨줄 수 있습니다.

+ +
> avg.apply(null, [2, 3, 4, 5])
+3.5
+
+ +

apply()의 두번째 매개변수는 '매개변수들'로 사용하고자 하는 배열입니다. 첫번째 매개변수는 나중에 설명하도록 하겠습니다. 이는 함수가 역시 객체임을 명확히 해주는 사실입니다.

+ +
+

함수 호출시 전개 연산자(spread operator) 를 이용하여 똑같은 결과를 얻을수 있습니다.

+ +

예를 들면: avg(...numbers)

+
+ +

JavaScript는 익명의 함수를 만들 수 있도록 허용하고 있습니다.

+ +
var avg = function() {
+    var sum = 0;
+    for (var i = 0, j = arguments.length; i < j; i++) {
+        sum += arguments[i];
+    }
+    return sum / arguments.length;
+}
+
+ +

이것은 의미적으로 function avg() 형식과 같습니다. 이 특징은 매우 강력한데, 일반적인 표현식(expression)을 사용할 수있는 어디에서나 완전한 함수 정의를 넣을 수 있도록 허용하는 것이기 때문입니다. 이 특징은 다양한 요령을 부릴 수 있게합니다. 다음 예는 C에서 블록 유효 범위를 적용 시킨 것 처럼 지역 변수를 "숨기는" 요령을 보여줍니다:

+ +
var a = 1;
+var b = 2;
+
+(function() {
+    var b = 3;
+    a += b;
+})();
+
+a; // 4
+b; // 2
+
+ +

JavaScript는 재귀적으로 함수를 부를 수 있습니다. 이는 브라우저 DOM 등에서 볼수 있는 트리 구조를 다루는데 유용합니다.

+ +
function countChars(elm) {
+  if (elm.nodeType == 3) { // TEXT_NODE
+    return elm.nodeValue.length;
+  }
+  var count = 0;
+  for (var i = 0, child; child = elm.childNodes[i]; i++) {
+    count += countChars(child);
+  }
+  return count;
+}
+
+ +

다음의 예는 익명 함수를 사용함에 있어 잠재적인 문제점을 보여줍니다: 이름이 없으면 어떻게 재귀적으로 부를 수 있을까요? JavaScript는 함수 표현식을 이렇게 이름붙이도록 지원합니다. 이름붙은 IIFEs (Immediately Invoked Function Expressions: 즉시 실행 함수 표현) 를 다음과 같이 사용할 수 있습니다:

+ +
var charsInBody = (function counter(elm) {
+  if (elm.nodeType == 3) { // TEXT_NODE
+    return elm.nodeValue.length;
+  }
+  var count = 0;
+  for (var i = 0, child; child = elm.childNodes[i]; i++) {
+    count += counter(child);
+  }
+  return count;
+})(document.body);
+
+ +

위와 같이 함수 표현식에 제공된 이름은 함수 자체 범위에서만 유효합니다. 이 특징은 엔진에 의한 최적화뿐만 아니라 코드 가독성을 높이는데 도움을 줍니다. 이 이름은 디버거와 스택 추적에서도 나타나므로 디버깅시간을 줄일수 있게합니다.

+ +

JavaScript 함수는 - JavsScript 내의 다른 모든 것들과 마찬가지로 -  그 자체가 객체이며, 객체 섹션에서 이미 확인한 것처럼, 속성을 추가하거나 변경할수 있다는 점을 명심하십시오

+ +

사용자 정의 객체

+ +
+

JavaScript에서 객체 지향 프로그래밍에 대한 더 자세한 논의는 객체 지향 JavaScript 소개를 참조하십시오.

+
+ +

고전 객체지향 프로그래밍에서 객체는 데이터와 해당 데이터들을 다루는 메소드의 집합이었습니다. JavaScript는 프로토타입 기반 언어로, C++ 이나 Java에서 발견할 수 있는 class 구문이 없습니다(이런 이유로 class 구문에 익숙한 프로그래머들이 때때로 혼란을 경험합니다). 그 대신, JavaScrip는 function을 class로 사용합니다. 이름과 성을 필드로 가지고 있는 'person' 객체를 고려해보도록 합시다. 이름을 표시하는 두가지 방법이 있을 수 있습니다. 예를 들어, "이름 성" 또는 "성, 이름" 이런 식으로 말이죠. 이전에 다룬 함수와 객체를 사용해서 이를 표현하면 다음과 같습니다:

+ +
function makePerson(first, last) {
+  return {
+    first: first,
+    last: last
+  }
+}
+function personFullName(person) {
+  return person.first + ' ' + person.last;
+}
+function personFullNameReversed(person) {
+  return person.last + ', ' + person.first
+}
+
+var s = makePerson("Simon", "Willison");
+personFullName(s); // "Simon Willison"
+personFullNameReversed(s); // "Willison, Simon"
+
+ +

이렇게 하면 작동하긴 하지만, 보기 안좋습니다. 이런 방법이라면 전역 이름공간(global namespace)에 관련 함수가 너무 많아집니다. 정말 우리에게 필요한 것은 객체에 함수를 붙여놓는 것입니다. 함수는 객체이기 때문에 이건 별로 어렵지 않습니다.

+ +
function makePerson(first, last) {
+  return {
+    first: first,
+    last: last,
+    fullName: function() {
+      return this.first + ' ' + this.last;
+    },
+    fullNameReversed: function() {
+      return this.last + ', ' + this.first;
+    }
+  };
+}
+
+var s = makePerson('Simon', 'Willison');
+s.fullName(); // "Simon Willison"
+s.fullNameReversed(); // "Willison, Simon"
+
+ +

this 키워드에 주목해 주십시오. 함수 안쪽에서 사용되면서, this는 현재 객체를 참조합니다. 그것이 실제로 의미하는 바는 당신이 부른 바로 그 함수를 지정하는 것입니다. 객체에서 dot 표기법이나 bracket 표기법을 사용해서 부른 경우, 해당 객체는 this가 됩니다. 해당 호출에서 dot 표기법을 사용하지 않은 경우, this는 전역 객체를 참조하게 됩니다.

+ +

this가 실수의 잦은 원인이 된다는 것을 명심하십시오 . 예를 들면:

+ +
var s = makePerson('Simon', 'Willison');
+var fullName = s.fullName;
+fullName(); // undefined undefined
+
+ +

s.fullName()을 이용하지 않고 fullName()을 단독으로 호출하면, 'this'는 전역 객체로 묶이게(bind) 됩니다. first 또는 last 로 명명된 전역 변수가 없기 때문에, 각각에 대해 undefined 결과를 얻게됩니다.

+ +

makePerson 함수를 개선하는데 'this' 키워드의 이점을 취할 수 있습니다:

+ +
function Person(first, last) {
+  this.first = first;
+  this.last = last;
+  this.fullName = function() {
+    return this.first + ' ' + this.last;
+  };
+  this.fullNameReversed = function() {
+    return this.last + ', ' + this.first;
+  };
+}
+var s = new Person('Simon', 'Willison');
+
+ +

여기서 new라는 또다른 키워드를 도입했습니다. newthis와 깊게 연관되어 있습니다. 새로운 빈 객체를 만든 다음 지정된 함수를 불러 새로운 객체를 this 에 설정합니다. this로 지정된 함수는 값을 반환하지 않고 단지 this 객체를 수정한다는 것을 명심하세요. this 객체를 호출하는 곳으로 반환하는 것은  new 입니다. 'new' 에 의해 호출되도록 설계된 함수는 컨스트럭터 함수라고 불립니다. 일반적으로 이러한 함수의 첫자를 대문자로 써서 new로 불릴 컨스트럭터 함수임을 나타냅니다.

+ +

개선된 함수는 여전히 fullName() 을 단독으로 호출할 때의 함정이 존재합니다.

+ +

우리의 person 객체가 점점 개선되고 있지만, 아직 좀 보기 안좋은 면이 있습니다. 매번 person 계열의 객체를 만들 때마다 내부에서 2개의 새로운 함수 객체를 만들고 있습니다. 이 코드가 객체간에 공유된다면 더 낫지 않을까요?

+ +
function personFullName() {
+  return this.first + ' ' + this.last;
+}
+function personFullNameReversed() {
+  return this.last + ', ' + this.first;
+}
+function Person(first, last) {
+  this.first = first;
+  this.last = last;
+  this.fullName = personFullName;
+  this.fullNameReversed = personFullNameReversed;
+}
+
+ +

더 좋아 보이네요: 메소드 함수를 한번만 만들고, 컨스트럭터 내에 해당 메소드들을 참조하도록 할당합니다. 이보다 더 개선 할 수 있을까요? 네, 그렇게 할 수 있습니다:

+ +
function Person(first, last) {
+  this.first = first;
+  this.last = last;
+}
+Person.prototype.fullName = function() {
+  return this.first + ' ' + this.last;
+};
+Person.prototype.fullNameReversed = function() {
+  return this.last + ', ' + this.first;
+};
+
+ +

Person.prototype은 모든 Person 인스턴스들간에 공유되는 객체입니다. 이는 lookup(찾아보기) 체인의 한 부분을 이룹니다. (이건 "prototype chain"이라는 특수한 이름을 따로 가지고 있습니다) 다시 말해, Person 객체의 설정되지 않은 속성에 접근을 시도할 때마다, 그것의 대체용도로 JavaScript는 Person.prototype에 그 속성이 존재하는지 살펴봅니다.그 결과,  Person.prototype에 할당된 모든 것은 this 객체를 통해 해당 컨스트럭터에 속한 모든 인스턴스들간에 사용 가능하게 됩니다.

+ +

이것은 정말 강력한 도구입니다. JavaScript에서는 임의의 prototype을 프로그램 내에서 언제든 변형할 수 있습니다. 이미 존재하는 객체에 추가적인 메소드를 실시간으로 추가가할 수 있다는 이야기입니다:

+ +
var s = new Person("Simon", "Willison");
+s.firstNameCaps(); //TypeError on line 1: s.firstNameCaps is not a function
+
+Person.prototype.firstNameCaps = function() {
+    return this.first.toUpperCase()
+};
+s.firstNameCaps(); // "SIMON"
+
+ +

흥미롭게도, JavaScript의 빌트인 객체의 prototype에도 뭔가를 더 추가할 수 있습니다. String 객체에 문자열 순서를 거꾸로 배열하여 돌려주는 메소드를 추가해 봅시다.

+ +
var s = "Simon";
+s.reversed(); // TypeError on line 1: s.reversed is not a function
+
+String.prototype.reversed = function() {
+    var r = "";
+    for (var i = this.length - 1; i >= 0; i--) {
+        r += this[i];
+    }
+    return r;
+};
+
+s.reversed(); // nomiS
+
+ +

우리가 추가한 새로운 메소드는 심지어 문자열 상수에서도 동작합니다!

+ +
"This can now be reversed".reversed(); // desrever eb won nac sihT
+
+ +

기존에 언급한 바와같이, prototype은 체인의 한 부분을 이룹니다. 해당 체인의 루트는 Object.prototype 이며 toString() 메소드를 포함합니다. 이 메소드는 객체를 문자열로 나타내려할 때 호출됩니다. 이 메소드는 우리의 Person 객체의 디버깅에 유용합니다:

+ +
var s = new Person("Simon", "Willison");
+s.toString(); // [object Object]
+
+Person.prototype.toString = function() {
+  return '<Person: ' + this.fullName() + '>';
+}
+
+s.toString(); // "<Person: Simon Willison>"
+
+ +

avg.apply()의 첫번째 매개변수가 null 이었던걸 기억해봅시다. apply()에 적용되는 첫번째 인자는 당연히 `this'로 간주되는 객체입니다. 여기에 new 의 간단한 구현을 보시죠:

+ +
function trivialNew(constructor, ...args) {
+    var o = {}; // 빈 객체를 생성
+    constructor.apply(o, args);
+    return o;
+}
+
+ +

이것은 prototype 체인을 설정하지 않으므로 new의 완벽한 대체물이 될 수 없습니다.(이 부분은 설명하기 어렵습니다). 이 내용은 자주 사용하지는 않겠지만 알아두면 좋습니다. 이 부분에서 ...args (생략 부호를 포함해서)는 "rest arguments" 라고 불립니다. 이름이 암시하는 것처럼 매개변수의 나머지를 포함합니다.

+ +

그러므로 이렇게 호출하는 것은

+ +
var bill = trivialNew(Person, 'William', 'Orange');
+ +

아래와 거의 동일합니다.

+ +
var bill = new Person('William', 'Orange');
+ +

apply() 와 비슷하게 this를 다시 설정할 수 있게 하는, call이라는 이름의 자매 함수가 있는데, 인자로 단일 배열이 아니라 확장된 인자 목록을 입력받습니다.

+ +
function lastNameCaps() {
+  return this.last.toUpperCase();
+}
+var s = new Person('Simon', 'Willison');
+lastNameCaps.call(s);
+// 위의 구문은 다음과 같습니다:
+s.lastNameCaps = lastNameCaps;
+s.lastNameCaps();
+
+ +

내장 함수 (Inner functions)

+ +

다른 함수의 내부에서 JavaScript 함수를 선언할 수 있습니다. 우리는 makePerson() 함수 초기 버전에서 이것을 한번 본적이 있습니다. JavaScript에서 중첩 함수(nested functions)의 중요한 세부사항은 부모 함수 범위의 변수에 접근할 수 있다는 사실입니다:

+ +
function parentFunc() {
+    var a = 1;
+
+    function nestedFunc() {
+        var b = 4; // parentFunc은 사용할 수 없는 변수
+        return a + b;
+    }
+    return nestedFunc();  // 5
+}
+
+ +

좀 더 유지관리가 쉬운 코드를 작성하고자 할때 이 특성이 굉장히 유용합니다. 한개 혹은 두개의 정도의 함수에서만 호출되며 전체 코드중 다른 부분에서는 사용처가 없는 함수라면 그 함수내에 해당 함수를 중첩시키는 것이 좋습니다. 이렇게 전역 범위 함수의 갯수를 늘리지 않도록 하는 것은 언제나 좋은 습관입니다.

+ +

이것은 또한 전역 변수에 대한 유혹을 뿌리칠 수 있는 좋은 대안이 됩니다. 복잡한 코드를 쓸 때, 다양한 함수들간에 값을 공유할 수 있도록 전역 변수를 사용하고 싶어집니다 - 전역 변수는 코드 유지 보수를 어렵게 만듭니다. 중첩 함수는 그 부모 함수의 범위에서 변수를 공유할 수 있으므로, 이 방법을 사용하면 전역 변수 이름공간을 건드리지 않고도 적절한 경우에 함수들을 연동시킬수 있습니다. - '지역 전역'이라고 불러도 괜찮겠네요. 이 기술을 사용할 때는 주의를 요하겠지만, 반드시 알아둬야할 유용한 기술입니다.

+ +

클로져 (Closures)

+ +

클로져 (역자주: 글자 그대로 한국어로 해석하면 닫힌 주머니)는 JavaScript가 제공해야만 하는 가장 막강한 추상 개념으로 우리를 이끕니다 - 하지만 동시에 잠재적으로 가장 혼란스럽기도 합니다. 다음 함수는 무엇을 하는 걸까요?

+ +
function makeAdder(a) {
+  return function(b) {
+    return a + b;
+  };
+}
+var add5 = makeAdder(5);
+var add20 = makeAdder(20);
+add5(6); // ?
+add20(7); // ?
+
+ +

makeAdder 함수의 이름은 다음과 같은 과정을 거쳐 반드시 없어집니다: 해당 함수가 한 매개변수를 받아 호출됐을 때, 생성될 때 주어진 매개변수를 더하는 새 'adder' 함수를 생성합니다.

+ +

여기서 일어나는 일은 다른 함수의 내에 정의된 어떤 함수가 외부 함수의 변수에 액세스한다는 점에서 앞에 언급한 내장 함수에서 일어나는 일과 매우 비슷합니다. 한가지 다른 점은 외부 함수가 리턴 된다는 점인데, 상식적으로 그것에 들어 있는 변수는 사라진다고 볼 수 있습니다. 하지만 그들은 여전히존재합니다 - 그렇지 않으면 adder 함수는 동작하지 않겠지요. 게다가, makeAdder 지역 변수의 서로 다른 두 "복사본"이 존재합니다 - 하나의 a는 5이고, 다른 하나의 a는 20이죠. 따라서 해당 함수를 부른 결과는 다음과 같습니다:

+ +
x(6) // 11을 돌려줌
+y(7) // 27을 돌려줌
+
+ +

이건 실제로 일어나는 일입니다. JavaScript 함수가 실행될 때는 언제나, '범위' 객체가 생성되어 해당 함수내에서 생성된 지역 변수를 여기에 저장하고 있습니다. 함수 매개변수로서 넘겨진 어떤 변수라도 여기에 초기값으로 저장하고 있습니다. 이것은 모든 전역 변수와 함수가 들어있는 전역 객체와 비슷하지만, 두가지 중요한 차이점이 있습니다. 첫번째로, 함수가 실행될 때마다 새로운 범위 객체가 생성된다는 점과, 두번째로, (브라우저에서 window로 접근가능한) 전역 객체와 달리 범위 객체는 JavaScript 코드에서 직접적으로 액세스할 수 없다는 점입니다. 예를 들자면 현재 범위 객체의 속성에 반복 접근할 수 있는 수단이 없습니다.

+ +

따라서 makeAdder 가 호출되면, 범위 객체는 makeAdder 함수에 매개변수로 넘겨진 하나의 속성 a를 가진 상태로 생성됩니다. 일반적으로 JavaScript의 가비지 컬렉터가 이때 makeAdder에 의해 생성된 범위 객체를 청소해야겠지만, 리턴된 함수가 여전히 범위 객체를 참조하고 있습니다. 결과적으로 범위 객체는 makeAdder에 의해 리턴된 함수 객체가 더는 참조되지 않을 때까지 가비지 컬렉터에 의해 정리되지 않게됩니다.

+ +

범위 객체는 JavaScript 객체 체계에서 사용되는 prototype 사슬과 비슷한 범위 사슬이라고 불리는 사슬을 형성합니다.

+ +

클로져는 함수와 함수에 의해 생성되는 범위 객체를 함께 지칭하는 용어입니다.

+ +

또한 클로져는 상태를 저장할 수 있도록 허용합니다 - 그렇기 때문에, 객체의 내부에서 자주 사용될 수 있는 것입니다.

+ +

메모리 누출

+ +

클로져의 부작용은 Internet Explorer에서 심각하지는 않지만 쉽게 메모리 누출이 된다는 것입니다. JavaScript는 가비지 컬렉트를 하는 언어 입니다. 객체가 생성됨에 따라서 메모리가 할당되고, 사용하고난 메모리는 더 참조하는 다른 객체가 없을 때 되돌아가는 방식으로 동작하는 언어란 말이죠. 호스트 환경에서 제공되는 객체들은 해당 환경에 의해 다뤄집니다.

+ +

브라우저 호스트는 HTML 페이지에 DOM 객체로서 표현되어있는 많은 수의 객체를 다뤄야 합니다. 이 객체들을 어떻게 할당하고 다시 거둬들일지는 브라우저 책임이죠.

+ +

Internet Explorer는 이를 위해 자신만의 고유한, JavaScript의 그것과는 다른 가비지 컬렉션 방식을 사용합니다. 두 언어간에 상호작용이 일어날 수 있고 이 과정에서 메모리 누출이 발생할 수 있습니다.

+ +

IE에서 메모리 누출은 JavaScript 객체와 고유 객체간에 참조하는 중 자기 자신을 참조 (circular reference, 순환 참조)하게 되는 일이 발생할 경우라면 언제든지 발생하게 됩니다. 다음을 고려해 보도록 합시다:

+ +
function leakMemory() {
+    var el = document.getElementById('el');
+    var o = { 'el': el };
+    el.o = o;
+}
+
+ +

위의 코드는 순환 참조로서 메모리 누출을 일으킵니다. IE는 완전히 다시 시작되기 전까지는 elo에 의해 사용되는 메모리를 반환하지 못합니다.

+ +

위의 경우는 알아채지 못하고 지나갈 확률이 높습니다. 메모리 누출은 사실 오랫동안 실행되거나 큰 데이터 구조나 반복, 순환에 의해 누출된는 메모리 양이 많은 경우에서 실질적으로 고려할만한 가치가 생깁니다.

+ +

누출이 이처럼 명확한 경우는 드뭅니다. 누출을 일으키는 데이터 구조는 수차례에 걸친 참조 구조를 가지고 있어서 순환 참조를 하고있는지 명확하지 않은 경우가 더 많습니다.

+ +

클로져는 그렇게 되도록 하지않아도 간단하게 메모리 누출을 일으킬 수 있습니다. 다음을 고려해 봅시다:

+ +
function addHandler() {
+    var el = document.getElementById('el');
+    el.onclick = function() {
+        this.style.backgroundColor = 'red';
+    }
+}
+
+ +

위의 코드는 클릭했을때 배경색이 빨강으로 바뀌는 엘레멘트를 설정합니다. 그리고 메모리 누출도 일으킵니다. 어째서냐고요? el을 참조하면 의도와는 달리 익명 내부 함수 때문에 생성된 클로져 내에 붙잡혀 있게 되기 때문입니다. 이는 JavaScript 객체 (내부 함수)와 원시 객체 (el)간에 순환 참조를 만듭니다.

+ +

이 문제를 피할 수 있는 많은 방법이 있습니다. 가장 간단한 건 이겁니다:

+ +
function addHandler() {
+    var el = document.getElementById('el');
+    el.onclick = function() {
+        this.style.backgroundColor = 'red';
+    }
+    el = null;
+}
+
+ +

이렇게 하면 순환 참조 고리를 끊을 수 있습니다.

+ +

놀랍게도, 클로져에 의해 발생된 순환 참조를 고리를 끊기 위한 한 요령은 또다른 클로져를 추가하는 것입니다:

+ +
function addHandler() {
+    var clickHandler = function() {
+        this.style.backgroundColor = 'red';
+    }
+    (function() {
+        var el = document.getElementById('el');
+        el.onclick = clickHandler;
+    })();
+}
+
+ +

내부 함수는 실행되고 바로 사라지므로서, clickHandler와 함께 생성된 클로져로부터 그 내용을 숨깁니다.

+ +

클로져를 피할 수 있는 또다른 좋은 요령은 window.onunload 이벤트가 발생하는 동안 순환 참조를 끊는 것입니다. 많은 이벤트 라이브러리가 이렇게 동작합니다. 주의할 것은 그렇게 하도록하면 Firefox 1.5의 bfcache를 비활성화 하게 되므로, 별 다른 이유가 없다면 Firefox에서 unload listener를 등록해서는 안 된다는 것입니다.

+ +
+

원본 문서 정보

+ + +
+ +

{{ languages( { "en": "en/A_re-introduction_to_JavaScript", "fr": "fr/Une_reintroduction_a_JavaScript", "it": "it/Una_re-introduzione_a_Javascript", "ja": "ja/A_re-introduction_to_JavaScript", "pl": "pl/JavaScript/Na_pocz?tek", "zh-cn": "cn/A_re-introduction_to_JavaScript" } ) }}

diff --git a/files/ko/web/javascript/about_javascript/index.html b/files/ko/web/javascript/about_javascript/index.html deleted file mode 100644 index 434ecff1e1..0000000000 --- a/files/ko/web/javascript/about_javascript/index.html +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: JavaScript에 대하여 -slug: Web/JavaScript/About_JavaScript -tags: - - 비기너 - - 소개 - - 자바스크립트 -translation_of: Web/JavaScript/About_JavaScript -original_slug: Web/JavaScript/About ---- -

{{JsSidebar}}

- -

JavaScript란 무엇인가?

- -

JavaScript® (줄여서 JS)는 일급 함수를 사용하는 가벼운 객체 지향 인터프리터 언어이며 웹페이지의 스크립트 언어로 잘 알려져 있지만, 브라우저가 아닌 환경에서도 많이 사용된다. 프로토타입 기반, 다중 패러다임 스크립트 언어이며, 동적이고 명령어, 객체 지향, 함수 프로그래밍 스타일을 지원한다.

- -

자바스크립트는 클라이언트 측 웹(브라우저)에서 실행 되고, 웹 페이지가 이벤트 발생시 어떻게 작동하는 지 디자인 / 프로그래밍할 수 있다. 자바스크립트는 쉽게 배울 수 있고 강력한 스크립트 언어로 웹 페이지 동작을 제어하는 데 널리 사용된다.

- -

대중적인 오해와 달리, Javascript는 인터프리트 형태 자바가 아니다. 간단히 말하면, Javascript는 프로토 타입 기반 객체 생성을 지원하는 동적 스크립트 언어이다.  기본적인 문법은  언어 학습에 필요한 새로운 개념을 줄이기 위해 Java와 C++의 if문, for와 while문, switch문과 try...catch 구문을 사용하는 언어구조를 사용하며, 그와 같은 (거의 가까운) 동작을 한다.

- -

Javascript는 절차지향 (procedural) 언어와  객체지향 (object oriented) 언어 두가지 형태로 만들수 있다. 자바스크립트에서 객체는 실시간으로 빈 객체를 오버라이딩하여 메소드와 프로퍼티를 연결하는 (프로그래밍)방식으로 생성된다. C++ 및 Java와 같은 컴파일 언어에서 공통적인 구문 클래스 정의와 반대되는 개념이다. 한번 객체가 생성하면, 비슷한 객체를 생성할 때 프로토타입으로 사용할 수 있다.

- -

JavaScript의 동적인 성질은, 실행시의 오브젝트 구축, 가변 인수 리스트, 함수 변수, (eval 에 의한)동적 스크립트 작성, (for ... in 에 의한) 오브젝트의 내부참조, 또는 소스코드 복원 (JavaScript 의 프로그램은 함수본체를 소스텍스트에 역컴파일할 수 있다.) 을 포함하고 있다.

- -

자바스크립트 프로그래밍에 대한 더 자세한 설명은 아래의 자바스크립트 리소스 링크를 참조하면 된다.

- -

어떠한 JavaScript 구현이 유용한가?

- -

Mozilla 프로젝트는 두 가지 자바스크립트 구현을 제공한다. 최초의 Javascript는 Netscape의 Brendan Eich에 의해 만들었다. 이후 ECMA-262 에디션 5 와 최신 버전을 준수하도록 업데이트되었다. SpiderMonkey 라는 이름의 엔진은 C/C++로 구현되었다. Rhino 엔진은 주로 Norris Boys(또한 Netscape)가 만들었고 Java로 작성된 Javascript 구현체이다. SpiderMonkey와 마찬가지로 Rhino도 ECMA-262 에디션 5를 준거한다.

- -

TraceMonkey (Firefox 3.5), JägerMonkey (Firefox 4) 그리고 IonMonkey와 같은 몇가지 주요한 실시간 최적화는 차츰 SpiderMonkey 자바스크립트 엔진에 추가되었다. 자바스크립트 실행 성능 향상을 위한 작업은 지금도 진행 중이다.

- -

위의 구현 이외에도 다음과 같은 다른 인기있는 자바스크립트 엔진이 있다.

- - - -

mozilla.org의 각 JavaScript 엔진은, 애플리케이션이 JavaScript를 지원하기 위해 부를 수 있는 공개 API를 공개하고 있다. JavaScript 를 지원하는 가장 일반적인 호스트환경은 웹브라우저이다. 웹 브라우저는 일반적으로 공개 API를 사용하여 DOM을 Javascript로 반영하는 호스트 객체를 만든다.

- -

JavaScript 의 또 다른 일반적인 애플리케이션은 (웹) 서버사이드 스크립팅 언어이다. 자바스크립트 웹 서버는 HTTP 요청 및 응답 객체를 나타내는 호스트 객체를 공개하며 자바 스크립트 프로그램에 의해 조작되어 웹 페이지를 동적으로 생성 할 수 있다. Node.js는 이에 대한 대중적인 예이다.

- -

JavaScript 자원

- -
-
SpiderMonkey
-
애플리케이션에 임베드하는 방법을 포함하는, C/C++ 엔진(SpiderMonkey)으로 구현된 Mozilla의 JavaScript 구현체에 관한 정보.
-
Rhino
-
Java(Rhino)로 작성된 자바스크립트 구현체에 관련 정보.
-
Language resources
-
자바스크립트 표준을 출판하기 위한 포인터들.
-
A re-introduction to JavaScript
-
자바스크립트 가이드자바스크립트 레퍼런스.
-
- -

JavaScript® 는 미국 및 여러 나라의 Oracle 트레이드마크 또는 등록된 트레이드마크입니다.

diff --git a/files/ko/web/javascript/about_javascript/index.md b/files/ko/web/javascript/about_javascript/index.md new file mode 100644 index 0000000000..434ecff1e1 --- /dev/null +++ b/files/ko/web/javascript/about_javascript/index.md @@ -0,0 +1,59 @@ +--- +title: JavaScript에 대하여 +slug: Web/JavaScript/About_JavaScript +tags: + - 비기너 + - 소개 + - 자바스크립트 +translation_of: Web/JavaScript/About_JavaScript +original_slug: Web/JavaScript/About +--- +

{{JsSidebar}}

+ +

JavaScript란 무엇인가?

+ +

JavaScript® (줄여서 JS)는 일급 함수를 사용하는 가벼운 객체 지향 인터프리터 언어이며 웹페이지의 스크립트 언어로 잘 알려져 있지만, 브라우저가 아닌 환경에서도 많이 사용된다. 프로토타입 기반, 다중 패러다임 스크립트 언어이며, 동적이고 명령어, 객체 지향, 함수 프로그래밍 스타일을 지원한다.

+ +

자바스크립트는 클라이언트 측 웹(브라우저)에서 실행 되고, 웹 페이지가 이벤트 발생시 어떻게 작동하는 지 디자인 / 프로그래밍할 수 있다. 자바스크립트는 쉽게 배울 수 있고 강력한 스크립트 언어로 웹 페이지 동작을 제어하는 데 널리 사용된다.

+ +

대중적인 오해와 달리, Javascript는 인터프리트 형태 자바가 아니다. 간단히 말하면, Javascript는 프로토 타입 기반 객체 생성을 지원하는 동적 스크립트 언어이다.  기본적인 문법은  언어 학습에 필요한 새로운 개념을 줄이기 위해 Java와 C++의 if문, for와 while문, switch문과 try...catch 구문을 사용하는 언어구조를 사용하며, 그와 같은 (거의 가까운) 동작을 한다.

+ +

Javascript는 절차지향 (procedural) 언어와  객체지향 (object oriented) 언어 두가지 형태로 만들수 있다. 자바스크립트에서 객체는 실시간으로 빈 객체를 오버라이딩하여 메소드와 프로퍼티를 연결하는 (프로그래밍)방식으로 생성된다. C++ 및 Java와 같은 컴파일 언어에서 공통적인 구문 클래스 정의와 반대되는 개념이다. 한번 객체가 생성하면, 비슷한 객체를 생성할 때 프로토타입으로 사용할 수 있다.

+ +

JavaScript의 동적인 성질은, 실행시의 오브젝트 구축, 가변 인수 리스트, 함수 변수, (eval 에 의한)동적 스크립트 작성, (for ... in 에 의한) 오브젝트의 내부참조, 또는 소스코드 복원 (JavaScript 의 프로그램은 함수본체를 소스텍스트에 역컴파일할 수 있다.) 을 포함하고 있다.

+ +

자바스크립트 프로그래밍에 대한 더 자세한 설명은 아래의 자바스크립트 리소스 링크를 참조하면 된다.

+ +

어떠한 JavaScript 구현이 유용한가?

+ +

Mozilla 프로젝트는 두 가지 자바스크립트 구현을 제공한다. 최초의 Javascript는 Netscape의 Brendan Eich에 의해 만들었다. 이후 ECMA-262 에디션 5 와 최신 버전을 준수하도록 업데이트되었다. SpiderMonkey 라는 이름의 엔진은 C/C++로 구현되었다. Rhino 엔진은 주로 Norris Boys(또한 Netscape)가 만들었고 Java로 작성된 Javascript 구현체이다. SpiderMonkey와 마찬가지로 Rhino도 ECMA-262 에디션 5를 준거한다.

+ +

TraceMonkey (Firefox 3.5), JägerMonkey (Firefox 4) 그리고 IonMonkey와 같은 몇가지 주요한 실시간 최적화는 차츰 SpiderMonkey 자바스크립트 엔진에 추가되었다. 자바스크립트 실행 성능 향상을 위한 작업은 지금도 진행 중이다.

+ +

위의 구현 이외에도 다음과 같은 다른 인기있는 자바스크립트 엔진이 있다.

+ + + +

mozilla.org의 각 JavaScript 엔진은, 애플리케이션이 JavaScript를 지원하기 위해 부를 수 있는 공개 API를 공개하고 있다. JavaScript 를 지원하는 가장 일반적인 호스트환경은 웹브라우저이다. 웹 브라우저는 일반적으로 공개 API를 사용하여 DOM을 Javascript로 반영하는 호스트 객체를 만든다.

+ +

JavaScript 의 또 다른 일반적인 애플리케이션은 (웹) 서버사이드 스크립팅 언어이다. 자바스크립트 웹 서버는 HTTP 요청 및 응답 객체를 나타내는 호스트 객체를 공개하며 자바 스크립트 프로그램에 의해 조작되어 웹 페이지를 동적으로 생성 할 수 있다. Node.js는 이에 대한 대중적인 예이다.

+ +

JavaScript 자원

+ +
+
SpiderMonkey
+
애플리케이션에 임베드하는 방법을 포함하는, C/C++ 엔진(SpiderMonkey)으로 구현된 Mozilla의 JavaScript 구현체에 관한 정보.
+
Rhino
+
Java(Rhino)로 작성된 자바스크립트 구현체에 관련 정보.
+
Language resources
+
자바스크립트 표준을 출판하기 위한 포인터들.
+
A re-introduction to JavaScript
+
자바스크립트 가이드자바스크립트 레퍼런스.
+
+ +

JavaScript® 는 미국 및 여러 나라의 Oracle 트레이드마크 또는 등록된 트레이드마크입니다.

diff --git a/files/ko/web/javascript/closures/index.html b/files/ko/web/javascript/closures/index.html deleted file mode 100644 index dc6fdbc41c..0000000000 --- a/files/ko/web/javascript/closures/index.html +++ /dev/null @@ -1,455 +0,0 @@ ---- -title: 클로저 -slug: Web/JavaScript/Closures -tags: - - Closure - - ES5 - - Intermediate - - JavaScript - - Reference -translation_of: Web/JavaScript/Closures -original_slug: Web/JavaScript/Guide/Closures ---- -
{{jsSidebar("Intermediate")}}
- -

클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.

- -

어휘적 범위 지정(Lexical scoping)

- -

다음을 보자:

- -
function init() {
-  var name = "Mozilla"; // name은 init에 의해 생성된 지역 변수이다.
-  function displayName() { // displayName() 은 내부 함수이며, 클로저다.
-    alert(name); // 부모 함수에서 선언된 변수를 사용한다.
-  }
-  displayName();
-}
-init();
- -

init()은 지역 변수 name과 함수 displayName()을 생성한다. displayName()init() 안에 정의된 내부 함수이며 init() 함수 본문에서만 사용할 수 있다. 여기서 주의할 점은 displayName() 내부엔 자신만의 지역 변수가 없다는 점이다. 그런데 함수 내부에서 외부 함수의 변수에 접근할 수 있기 때문에 displayName() 역시 부모 함수 init()에서 선언된 변수 name에 접근할 수 있다. 만약 displayName()가 자신만의 name변수를 가지고 있었다면, name대신 this.name을 사용했을 것이다.

- -

{{JSFiddleEmbed("https://jsfiddle.net/78dg25ax/", "js,result", 250)}}

- -

위 코드를 실행하면 displayName() 함수 내의 alert()문이 부모 함수에서 정의한 변수 name의 값을 성공적으로 출력한다. 이 예시를 통해 함수가 중첩된 상황에서 파서가 어떻게 변수를 처리하는지 알 수 있다. 이는 어휘적 범위 지정(lexical scoping)의 한 예이다. 여기서 "lexical"이란, 어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미한다. 단어 "lexical"은 이런 사실을 나타낸다. 중첩된 함수는 외부 범위(scope)에서 선언한 변수에도 접근할 수 있다.

- -

클로저(Closure)

- -

이제 다음 예제를 보자:

- -
function makeFunc() {
-  var name = "Mozilla";
-  function displayName() {
-    alert(name);
-  }
-  return displayName;
-}
-
-var myFunc = makeFunc();
-//myFunc변수에 displayName을 리턴함
-//유효범위의 어휘적 환경을 유지
-myFunc();
-//리턴된 displayName 함수를 실행(name 변수에 접근)
- -

이 코드는 바로 전의 예제와 완전히 동일한 결과가 실행된다. 하지만 흥미로운 차이는 displayName()함수가 실행되기 전에 외부함수인 makeFunc()로부터 리턴되어 myFunc 변수에 저장된다는 것이다.

- -

한 눈에 봐서는 이 코드가 여전히 작동하는 것이 직관적으로 보이지 않을 수 있다. 몇몇 프로그래밍 언어에서, 함수 안의 지역 변수들은 그 함수가 처리되는 동안에만 존재한다. makeFunc() 실행이 끝나면(displayName함수가 리턴되고 나면) name 변수에 더 이상 접근할 수 없게 될 것으로 예상하는 것이 일반적이다.

- -

하지만 위의 예시와 자바스크립트의 경우는 다르다. 그 이유는 자바스크립트는 함수를 리턴하고, 리턴하는 함수가 클로저를 형성하기 때문이다. 클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다. 첫 번째 예시의 경우, myFuncmakeFunc이 실행될 때 생성된 displayName 함수의 인스턴스에 대한 참조다. displayName의 인스턴스는 변수 name 이 있는 어휘적 환경에 대한 참조를 유지한다. 이런 이유로 myFunc가 호출될 때 변수 name은 사용할 수 있는 상태로 남게 되고 "Mozilla" 가 alert 에 전달된다.

- -

다음은 조금 더 흥미로운 예제인 makeAdder 함수이다:

- -
function makeAdder(x) {
-  var y = 1;
-  return function(z) {
-    y = 100;
-    return x + y + z;
-  };
-}
-
-var add5 = makeAdder(5);
-var add10 = makeAdder(10);
-//클로저에 x와 y의 환경이 저장됨
-
-console.log(add5(2));  // 107 (x:5 + y:100 + z:2)
-console.log(add10(2)); // 112 (x:10 + y:100 + z:2)
-//함수 실행 시 클로저에 저장된 x, y값에 접근하여 값을 계산
-
-
- -

이 예제에서 단일 인자 x를 받아서 새 함수를 반환하는 함수 makeAdder(x)를 정의했다. 반환되는 함수는 단일 인자 z를 받아서 x와 y와 z의 합을 반환한다.

- -

본질적으로 makeAdder는 함수를 만들어내는 공장이다. 이는 makeAdder함수가 특정한 값을 인자로 가질 수 있는 함수들을 리턴한다는 것을 의미한다. 위의 예제에서 add5, add10 두 개의 새로운 함수들을 만들기 위해 makeAdder함수 공장을 사용했다. 하나는 매개변수 x에 5를 더하고 다른 하나는 매개변수 x에 10을 더한다.

- -

add5add10은 둘 다 클로저이다. 이들은 같은 함수 본문 정의를 공유하지만 서로 다른 맥락(어휘)적 환경을 저장한다. 함수 실행 시 add5의 맥락적 환경에서 클로저 내부의 x는 5 이지만 add10의 맥락적 환경에서 x는 10이다. 또한 리턴되는 함수에서 초기값이 1로 할당된 y에 접근하여 y값을 100으로 변경한 것을 볼 수 있다. (물론 x값도 동일하게 변경 가능하다.) 이는 클로저가 리턴된 후에도 외부함수의 변수들에 접근 가능하다는 것을 보여주는 포인트이며 클로저에 단순히 값 형태로 전달되는 것이 아니라는 것을 의미한다.

- -

실용적인 클로저

- -

클로저는 어떤 데이터(어휘적 환경)와 그 데이터를 조작하는 함수를 연관시켜주기 때문에 유용하다. 이것은 객체가 어떤 데이터와(그 객체의 속성) 하나 혹은 그 이상의 메소드들을 연관시킨다는 점에서 객체지향 프로그래밍과 분명히 같은 맥락에 있다.

- -

결론적으로 오직 하나의 메소드를 가지고 있는 객체를 일반적으로 사용하는 모든 곳에 클로저를 사용할 수 있다.

- -

이렇게 할 수 있는 상황은 특히 웹에서 일반적이다. 프론트 엔드 자바스크립트에서 우리가 쓰는 많은 코드가 이벤트 기반이다. 우리는 몇 가지 동작을 정의한 다음 사용자에 의한 이벤트에(클릭 혹은 키 누르기 같은) 연결한다. 우리의 코드는 일반적으로 콜백으로 첨부된다: 이벤트에 응답하여 실행되는 단일 함수다.

- -

예를 들면 페이지의 글자 크기를 조정하는 몇 개의 버튼을 추가한다고 가정하자. 이 작업을 수행하는 한 가지 방법은 body 요소의 font-size를 픽셀 단위로 지정하고 상대적인 em 단위를 사용하여 페이지의 다른 요소들의 (예: 헤더) 크기를 설정하는 것이다.

- -
body {
-  font-family: Helvetica, Arial, sans-serif;
-  font-size: 12px;
-}
-
-h1 {
-  font-size: 1.5em;
-}
-
-h2 {
-  font-size: 1.2em;
-}
-
- -

우리의 대화식 글자 크기 버튼들은 body 요소의 font-size 속성을 변경할 수 있고 이런 조정은 상대적 단위들 덕분에 페이지의 다른 요소에 의해 선택된다.

- -

여기 자바스크립트 코드가 있다.

- -
function makeSizer(size) {
-  return function() {
-    document.body.style.fontSize = size + 'px';
-  };
-}
-
-var size12 = makeSizer(12);
-var size14 = makeSizer(14);
-var size16 = makeSizer(16);
-
- -

size12, size14, size16은 body 요소의 글자 크기를 각각 12, 14, 16 픽셀로 바꾸는 함수이다. 이 함수들을 아래처럼 버튼들에(이 경우에는 링크) 연결할 수 있다.

- -
document.getElementById('size-12').onclick = size12;
-document.getElementById('size-14').onclick = size14;
-document.getElementById('size-16').onclick = size16;
-
- -
<a href="#" id="size-12">12</a>
-<a href="#" id="size-14">14</a>
-<a href="#" id="size-16">16</a>
-
- -

{{JSFiddleEmbed("https://jsfiddle.net/vnkuZ/","","200")}}

- -

클로저를 이용해서 프라이빗 메소드 (private method) 흉내내기

- -

자바와 같은 몇몇 언어들은 메소드를 프라이빗으로 선언할 수 있는 기능을 제공한다. 이는 같은 클래스 내부의 다른 메소드에서만 그 메소드들을 호출할 수 있다는 의미이다.

- -

자바스크립트는 태생적으로는 이런 방법을 제공하지 않지만 클로저를 이용하여 프라이빗 메소드를 흉내내는 것이 가능하다. 프라이빗 메소드는 코드에 제한적인 접근만을 허용한다는 점 뿐만 아니라 전역 네임 스페이스를 관리하는 강력한 방법을 제공하여 불필요한 메소드가 공용 인터페이스를 혼란스럽게 만들지 않도록 한다.

- -

아래 코드는 프라이빗 함수와 변수에 접근하는 퍼블릭 함수를 정의하기 위해 클로저를 사용하는 방법을 보여준다. 이렇게 클로저를 사용하는 것을 모듈 패턴이라 한다.

- -
var counter = (function() {
-  var privateCounter = 0;
-  function changeBy(val) {
-    privateCounter += val;
-  }
-  return {
-    increment: function() {
-      changeBy(1);
-    },
-    decrement: function() {
-      changeBy(-1);
-    },
-    value: function() {
-      return privateCounter;
-    }
-  };
-})();
-
-console.log(counter.value()); // logs 0
-counter.increment();
-counter.increment();
-console.log(counter.value()); // logs 2
-counter.decrement();
-console.log(counter.value()); // logs 1
-
- -

이전 예제에서 각 클로저들이 고유한 문법적 환경을 가졌지만 여기서 우리는 counter.increment, counter.decrement, counter.value 세 함수에 의해 공유되는 하나의 어휘적 환경을 만든다.

- -

공유되는 어휘적 환경은 실행되는 익명 함수 안에서 만들어진다. 이 익명 함수는 정의되는 즉시 실행된다. 이 어휘적 환경은 두 개의 프라이빗 아이템을 포함한다. 하나는 privateCounter라는 변수이고 나머지 하나는 changeBy라는 함수이다. 둘 다 익명 함수 외부에서 접근될 수 없다. 대신에 익명 래퍼에서 반환된 세 개의 퍼블릭 함수를 통해서만 접근되어야만 한다.

- -

위의 세 가지 퍼블릭 함수는 같은 환경을 공유하는 클로저다. 자바스크립트의 어휘적 유효 범위 덕분에 세 함수 각각 privateCounter 변수와 changeBy 함수에 접근할 수 있다.

- -

카운터를 생성하는 익명 함수를 정의하고 그 함수를 즉시 호출하고 결과를 counter 변수에 할당하는 것을 알아차렸을 것이다. 이 함수를 별도의 변수 makeCounter 저장하고 이 변수를 이용해 여러 개의 카운터를 만들 수 있다.

- -
var makeCounter = function() {
-  var privateCounter = 0;
-  function changeBy(val) {
-    privateCounter += val;
-  }
-  return {
-    increment: function() {
-      changeBy(1);
-    },
-    decrement: function() {
-      changeBy(-1);
-    },
-    value: function() {
-      return privateCounter;
-    }
-  }
-};
-
-var counter1 = makeCounter();
-var counter2 = makeCounter();
-alert(counter1.value()); /* 0 */
-counter1.increment();
-counter1.increment();
-alert(counter1.value()); /* 2 */
-counter1.decrement();
-alert(counter1.value()); /* 1 */
-alert(counter2.value()); /* 0 */
-
- -

두 개의 카운터가 어떻게 다른 카운터와 독립성을 유지하는지 주목해보자. 각 클로저는 그들 고유의 클로저를 통한 privateCounter 변수의 다른 버전을 참조한다. 각 카운터가 호출될 때마다; 하나의 클로저에서 변수 값을 변경해도 다른 클로저의 값에는 영향을 주지 않는다.

- -

이런 방식으로 클로저를 사용하여 객체지향 프로그래밍의 정보 은닉과 캡슐화 같은 이점들을 얻을 수 있다.

- -

클로저 스코프 체인

- -

모든 클로저에는 세가지 스코프(범위)가 있다:-

- - - -

따라서, 우리는 클로저에 대해 세가지 범위 모두 접근할 수 있지만, 중첩된 내부 함수가 있는 경우 종종 실수를 저지른다. 아래 예제를 확인해보자:

- -
// 전역 범위 (global scope)
-var e = 10;
-function sum(a){
-  return function(b){
-    return function(c){
-      // 외부 함수 범위 (outer functions scope)
-      return function(d){
-        // 지역 범위 (local scope)
-        return a + b + c + d + e;
-      }
-    }
-  }
-}
-
-console.log(sum(1)(2)(3)(4)); // log 20
-
-// 익명 함수 없이 작성할 수도 있다.
-
-// 전역 범위 (global scope)
-var e = 10;
-function sum(a){
-  return function sum2(b){
-    return function sum3(c){
-      // 외부 함수 범위 (outer functions scope)
-      return function sum4(d){
-        // 지역 범위 (local scope)
-        return a + b + c + d + e;
-      }
-    }
-  }
-}
-
-var s = sum(1);
-var s1 = s(2);
-var s2 = s1(3);
-var s3 = s2(4);
-console.log(s3) //log 20
- -

위의 예제를 보면 일련의 중첩된 함수들을 확인할 수 있다. 이 함수들은 전부 외부 함수의 스코프에 접근할 수 있다. 그런데 문제는 즉각적인 외부 함수의 스코프만을 추측한다는 것이다. 이 문맥에서는 모든 클로저가 선언된 외부 함수의 스코프에 접근한다라고 말할 수 있다.

- -

루프에서 클로저 생성하기: 일반적인 실수

- -

ECMAScript 2015의 let 키워드 소개 전에는 클로저와 관련된 일반적인 문제는 루프 안에서 클로저가 생성되었을 때 발생한다.다음 예제를 보자.

- -
<p id="help">Helpful notes will appear here</p>
-<p>E-mail: <input type="text" id="email" name="email"></p>
-<p>Name: <input type="text" id="name" name="name"></p>
-<p>Age: <input type="text" id="age" name="age"></p>
-
- -
function showHelp(help) {
-  document.getElementById('help').innerHTML = help;
-}
-
-function setupHelp() {
-  var helpText = [
-      {'id': 'email', 'help': 'Your e-mail address'},
-      {'id': 'name', 'help': 'Your full name'},
-      {'id': 'age', 'help': 'Your age (you must be over 16)'}
-    ];
-
-  for (var i = 0; i < helpText.length; i++) {
-    var item = helpText[i];
-    document.getElementById(item.id).onfocus = function() {
-      showHelp(item.help);
-    }
-  }
-}
-
-setupHelp();
-
- -

{{JSFiddleEmbed("https://jsfiddle.net/v7gjv/", "", 200)}}

- -

helpText 배열은 세 개의 도움말 힌트를 정의한다. 각 도움말은 문서의 입력 필드의 ID와 연관된다. 루프를 돌면서 각 입력 필드 ID에 해당하는 엘리먼트의 onfocus 이벤트에 관련된 도움말을 보여주는 메소드에 연결한다.

- -

이 코드를 사용하면 제대로 동작하지 않는 것을 알게 된다. 어떤 필드에 포커스를 주더라도 나이에 관한 도움말이 표시된다.

- -

onfocus 이벤트에 연결된 함수가 클로저이기 때문이다. 이 클로저는 함수 정의와 setupHelp 함수 범위에서 캡처된 환경으로 구성된다. 루프에서 세 개의 클로저가 만들어졌지만 각 클로저는 값이 변하는 변수가 (item.help) 있는 같은 단일 환경을 공유한다. onfocus 콜백이 실행될 때 콜백의 환경에서 item 변수는 (세개의 클로저가 공유한다) helpText 리스트의 마지막 요소를 가리키고 있을 것이다.

- -

이 경우 한 가지 해결책은 더 많은 클로저를 사용하는 것이다: 특히 앞에서 설명한 함수 팩토리를 사용하는 것이다.

- -
function showHelp(help) {
-  document.getElementById('help').innerHTML = help;
-}
-
-function makeHelpCallback(help) {
-  return function() {
-    showHelp(help);
-  };
-}
-
-function setupHelp() {
-  var helpText = [
-      {'id': 'email', 'help': 'Your e-mail address'},
-      {'id': 'name', 'help': 'Your full name'},
-      {'id': 'age', 'help': 'Your age (you must be over 16)'}
-    ];
-
-  for (var i = 0; i < helpText.length; i++) {
-    var item = helpText[i];
-    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
-  }
-}
-
-setupHelp();
-
- -

{{JSFiddleEmbed("https://jsfiddle.net/v7gjv/1/", "", 300)}}

- -

이것은 예상대로 동작한다. 모두 단일 환경을 공유하는 콜백대신, makeHelpCallback 함수는 각각의 콜백에 새로운 어휘적 환경을 생성한다. 여기서 help는 helpText 배열의 해당 문자열을 나타낸다.

- -

익명 클로저를 사용하여 위 코드를 작성하는 또 다른 방법은 다음과 같다.

- -
function showHelp(help) {
-  document.getElementById('help').innerHTML = help;
-}
-
-function setupHelp() {
-  var helpText = [
-      {'id': 'email', 'help': 'Your e-mail address'},
-      {'id': 'name', 'help': 'Your full name'},
-      {'id': 'age', 'help': 'Your age (you must be over 16)'}
-    ];
-
-  for (var i = 0; i < helpText.length; i++) {
-    (function() {
-       var item = helpText[i];
-       document.getElementById(item.id).onfocus = function() {
-         showHelp(item.help);
-       }
-    })(); // Immediate event listener attachment with the current value of item (preserved until iteration).
-  }
-}
-
-setupHelp();
- -

더 많은 클로저를 사용하는 것이 싫다면 ES2015의 let 키워드를 사용할 수 있다.

- -
function showHelp(help) {
-  document.getElementById('help').innerHTML = help;
-}
-
-function setupHelp() {
-  var helpText = [
-      {'id': 'email', 'help': 'Your e-mail address'},
-      {'id': 'name', 'help': 'Your full name'},
-      {'id': 'age', 'help': 'Your age (you must be over 16)'}
-    ];
-
-  for (var i = 0; i < helpText.length; i++) {
-    let item = helpText[i];
-    document.getElementById(item.id).onfocus = function() {
-      showHelp(item.help);
-    }
-  }
-}
-
-setupHelp();
- -

위의 경우 var 대신 let을 사용하여 모든 클로저가 블록 범위 변수를 바인딩할 것이므로 추가적인 클로저를 사용하지 않아도 완벽하게 동작할 것이다.

- -

성능 관련 고려 사항

- -

특정 작업에 클로저가 필요하지 않는데 다른 함수 내에서 함수를 불필요하게 작성하는 것은 현명하지 않다. 이것은 처리 속도와 메모리 소비 측면에서 스크립트 성능에 부정적인 영향을 미칠 것이다.

- -

예를 들어, 새로운 객체/클래스를 생성 할 때, 메소드는 일반적으로 객체 생성자에 정의되기보다는 객체의 프로토타입에 연결되어야 한다. 그 이유는 생성자가 호출 될 때마다 메서드가 다시 할당되기 때문이다 (즉, 모든 개체가 생성 될 때마다).

- -

비실용적이지만 시범적인 다음 예를 고려하라:

- -
function MyObject(name, message) {
-  this.name = name.toString();
-  this.message = message.toString();
-  this.getName = function() {
-    return this.name;
-  };
-
-  this.getMessage = function() {
-    return this.message;
-  };
-}
-
- -

앞의 코드는 클로저의 이점을 이용하지 않음으로 다음과 같이 다시 쓸 수 있다.

- -
function MyObject(name, message) {
-  this.name = name.toString();
-  this.message = message.toString();
-}
-MyObject.prototype = {
-  getName: function() {
-    return this.name;
-  },
-  getMessage: function() {
-    return this.message;
-  }
-};
-
- -

그러나 프로토타입을 다시 정의하는 것은 권장되지 않음으로 기존 프로토타입에 추가하는 다음 예제가 더 좋다.

- -
function MyObject(name, message) {
-  this.name = name.toString();
-  this.message = message.toString();
-}
-MyObject.prototype.getName = function() {
-  return this.name;
-};
-MyObject.prototype.getMessage = function() {
-  return this.message;
-};
-
- -

위의 코드는 같은 결과를 가진 더 깨끗한 방법으로 작성할 수도 있다:

- -
function MyObject(name, message) {
-    this.name = name.toString();
-    this.message = message.toString();
-}
-(function() {
-    this.getName = function() {
-        return this.name;
-    };
-    this.getMessage = function() {
-        return this.message;
-    };
-}).call(MyObject.prototype);
-
- -

앞의 두 가지 예제에서 상속된 프로토타입은 모든 객체에서 공유될 수 있으며 메소드 정의는 모든 객체 생성시 발생할 필요가 없다. 객체 모델의 세부 사항을 참고하라.

diff --git a/files/ko/web/javascript/closures/index.md b/files/ko/web/javascript/closures/index.md new file mode 100644 index 0000000000..dc6fdbc41c --- /dev/null +++ b/files/ko/web/javascript/closures/index.md @@ -0,0 +1,455 @@ +--- +title: 클로저 +slug: Web/JavaScript/Closures +tags: + - Closure + - ES5 + - Intermediate + - JavaScript + - Reference +translation_of: Web/JavaScript/Closures +original_slug: Web/JavaScript/Guide/Closures +--- +
{{jsSidebar("Intermediate")}}
+ +

클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.

+ +

어휘적 범위 지정(Lexical scoping)

+ +

다음을 보자:

+ +
function init() {
+  var name = "Mozilla"; // name은 init에 의해 생성된 지역 변수이다.
+  function displayName() { // displayName() 은 내부 함수이며, 클로저다.
+    alert(name); // 부모 함수에서 선언된 변수를 사용한다.
+  }
+  displayName();
+}
+init();
+ +

init()은 지역 변수 name과 함수 displayName()을 생성한다. displayName()init() 안에 정의된 내부 함수이며 init() 함수 본문에서만 사용할 수 있다. 여기서 주의할 점은 displayName() 내부엔 자신만의 지역 변수가 없다는 점이다. 그런데 함수 내부에서 외부 함수의 변수에 접근할 수 있기 때문에 displayName() 역시 부모 함수 init()에서 선언된 변수 name에 접근할 수 있다. 만약 displayName()가 자신만의 name변수를 가지고 있었다면, name대신 this.name을 사용했을 것이다.

+ +

{{JSFiddleEmbed("https://jsfiddle.net/78dg25ax/", "js,result", 250)}}

+ +

위 코드를 실행하면 displayName() 함수 내의 alert()문이 부모 함수에서 정의한 변수 name의 값을 성공적으로 출력한다. 이 예시를 통해 함수가 중첩된 상황에서 파서가 어떻게 변수를 처리하는지 알 수 있다. 이는 어휘적 범위 지정(lexical scoping)의 한 예이다. 여기서 "lexical"이란, 어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미한다. 단어 "lexical"은 이런 사실을 나타낸다. 중첩된 함수는 외부 범위(scope)에서 선언한 변수에도 접근할 수 있다.

+ +

클로저(Closure)

+ +

이제 다음 예제를 보자:

+ +
function makeFunc() {
+  var name = "Mozilla";
+  function displayName() {
+    alert(name);
+  }
+  return displayName;
+}
+
+var myFunc = makeFunc();
+//myFunc변수에 displayName을 리턴함
+//유효범위의 어휘적 환경을 유지
+myFunc();
+//리턴된 displayName 함수를 실행(name 변수에 접근)
+ +

이 코드는 바로 전의 예제와 완전히 동일한 결과가 실행된다. 하지만 흥미로운 차이는 displayName()함수가 실행되기 전에 외부함수인 makeFunc()로부터 리턴되어 myFunc 변수에 저장된다는 것이다.

+ +

한 눈에 봐서는 이 코드가 여전히 작동하는 것이 직관적으로 보이지 않을 수 있다. 몇몇 프로그래밍 언어에서, 함수 안의 지역 변수들은 그 함수가 처리되는 동안에만 존재한다. makeFunc() 실행이 끝나면(displayName함수가 리턴되고 나면) name 변수에 더 이상 접근할 수 없게 될 것으로 예상하는 것이 일반적이다.

+ +

하지만 위의 예시와 자바스크립트의 경우는 다르다. 그 이유는 자바스크립트는 함수를 리턴하고, 리턴하는 함수가 클로저를 형성하기 때문이다. 클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다. 첫 번째 예시의 경우, myFuncmakeFunc이 실행될 때 생성된 displayName 함수의 인스턴스에 대한 참조다. displayName의 인스턴스는 변수 name 이 있는 어휘적 환경에 대한 참조를 유지한다. 이런 이유로 myFunc가 호출될 때 변수 name은 사용할 수 있는 상태로 남게 되고 "Mozilla" 가 alert 에 전달된다.

+ +

다음은 조금 더 흥미로운 예제인 makeAdder 함수이다:

+ +
function makeAdder(x) {
+  var y = 1;
+  return function(z) {
+    y = 100;
+    return x + y + z;
+  };
+}
+
+var add5 = makeAdder(5);
+var add10 = makeAdder(10);
+//클로저에 x와 y의 환경이 저장됨
+
+console.log(add5(2));  // 107 (x:5 + y:100 + z:2)
+console.log(add10(2)); // 112 (x:10 + y:100 + z:2)
+//함수 실행 시 클로저에 저장된 x, y값에 접근하여 값을 계산
+
+
+ +

이 예제에서 단일 인자 x를 받아서 새 함수를 반환하는 함수 makeAdder(x)를 정의했다. 반환되는 함수는 단일 인자 z를 받아서 x와 y와 z의 합을 반환한다.

+ +

본질적으로 makeAdder는 함수를 만들어내는 공장이다. 이는 makeAdder함수가 특정한 값을 인자로 가질 수 있는 함수들을 리턴한다는 것을 의미한다. 위의 예제에서 add5, add10 두 개의 새로운 함수들을 만들기 위해 makeAdder함수 공장을 사용했다. 하나는 매개변수 x에 5를 더하고 다른 하나는 매개변수 x에 10을 더한다.

+ +

add5add10은 둘 다 클로저이다. 이들은 같은 함수 본문 정의를 공유하지만 서로 다른 맥락(어휘)적 환경을 저장한다. 함수 실행 시 add5의 맥락적 환경에서 클로저 내부의 x는 5 이지만 add10의 맥락적 환경에서 x는 10이다. 또한 리턴되는 함수에서 초기값이 1로 할당된 y에 접근하여 y값을 100으로 변경한 것을 볼 수 있다. (물론 x값도 동일하게 변경 가능하다.) 이는 클로저가 리턴된 후에도 외부함수의 변수들에 접근 가능하다는 것을 보여주는 포인트이며 클로저에 단순히 값 형태로 전달되는 것이 아니라는 것을 의미한다.

+ +

실용적인 클로저

+ +

클로저는 어떤 데이터(어휘적 환경)와 그 데이터를 조작하는 함수를 연관시켜주기 때문에 유용하다. 이것은 객체가 어떤 데이터와(그 객체의 속성) 하나 혹은 그 이상의 메소드들을 연관시킨다는 점에서 객체지향 프로그래밍과 분명히 같은 맥락에 있다.

+ +

결론적으로 오직 하나의 메소드를 가지고 있는 객체를 일반적으로 사용하는 모든 곳에 클로저를 사용할 수 있다.

+ +

이렇게 할 수 있는 상황은 특히 웹에서 일반적이다. 프론트 엔드 자바스크립트에서 우리가 쓰는 많은 코드가 이벤트 기반이다. 우리는 몇 가지 동작을 정의한 다음 사용자에 의한 이벤트에(클릭 혹은 키 누르기 같은) 연결한다. 우리의 코드는 일반적으로 콜백으로 첨부된다: 이벤트에 응답하여 실행되는 단일 함수다.

+ +

예를 들면 페이지의 글자 크기를 조정하는 몇 개의 버튼을 추가한다고 가정하자. 이 작업을 수행하는 한 가지 방법은 body 요소의 font-size를 픽셀 단위로 지정하고 상대적인 em 단위를 사용하여 페이지의 다른 요소들의 (예: 헤더) 크기를 설정하는 것이다.

+ +
body {
+  font-family: Helvetica, Arial, sans-serif;
+  font-size: 12px;
+}
+
+h1 {
+  font-size: 1.5em;
+}
+
+h2 {
+  font-size: 1.2em;
+}
+
+ +

우리의 대화식 글자 크기 버튼들은 body 요소의 font-size 속성을 변경할 수 있고 이런 조정은 상대적 단위들 덕분에 페이지의 다른 요소에 의해 선택된다.

+ +

여기 자바스크립트 코드가 있다.

+ +
function makeSizer(size) {
+  return function() {
+    document.body.style.fontSize = size + 'px';
+  };
+}
+
+var size12 = makeSizer(12);
+var size14 = makeSizer(14);
+var size16 = makeSizer(16);
+
+ +

size12, size14, size16은 body 요소의 글자 크기를 각각 12, 14, 16 픽셀로 바꾸는 함수이다. 이 함수들을 아래처럼 버튼들에(이 경우에는 링크) 연결할 수 있다.

+ +
document.getElementById('size-12').onclick = size12;
+document.getElementById('size-14').onclick = size14;
+document.getElementById('size-16').onclick = size16;
+
+ +
<a href="#" id="size-12">12</a>
+<a href="#" id="size-14">14</a>
+<a href="#" id="size-16">16</a>
+
+ +

{{JSFiddleEmbed("https://jsfiddle.net/vnkuZ/","","200")}}

+ +

클로저를 이용해서 프라이빗 메소드 (private method) 흉내내기

+ +

자바와 같은 몇몇 언어들은 메소드를 프라이빗으로 선언할 수 있는 기능을 제공한다. 이는 같은 클래스 내부의 다른 메소드에서만 그 메소드들을 호출할 수 있다는 의미이다.

+ +

자바스크립트는 태생적으로는 이런 방법을 제공하지 않지만 클로저를 이용하여 프라이빗 메소드를 흉내내는 것이 가능하다. 프라이빗 메소드는 코드에 제한적인 접근만을 허용한다는 점 뿐만 아니라 전역 네임 스페이스를 관리하는 강력한 방법을 제공하여 불필요한 메소드가 공용 인터페이스를 혼란스럽게 만들지 않도록 한다.

+ +

아래 코드는 프라이빗 함수와 변수에 접근하는 퍼블릭 함수를 정의하기 위해 클로저를 사용하는 방법을 보여준다. 이렇게 클로저를 사용하는 것을 모듈 패턴이라 한다.

+ +
var counter = (function() {
+  var privateCounter = 0;
+  function changeBy(val) {
+    privateCounter += val;
+  }
+  return {
+    increment: function() {
+      changeBy(1);
+    },
+    decrement: function() {
+      changeBy(-1);
+    },
+    value: function() {
+      return privateCounter;
+    }
+  };
+})();
+
+console.log(counter.value()); // logs 0
+counter.increment();
+counter.increment();
+console.log(counter.value()); // logs 2
+counter.decrement();
+console.log(counter.value()); // logs 1
+
+ +

이전 예제에서 각 클로저들이 고유한 문법적 환경을 가졌지만 여기서 우리는 counter.increment, counter.decrement, counter.value 세 함수에 의해 공유되는 하나의 어휘적 환경을 만든다.

+ +

공유되는 어휘적 환경은 실행되는 익명 함수 안에서 만들어진다. 이 익명 함수는 정의되는 즉시 실행된다. 이 어휘적 환경은 두 개의 프라이빗 아이템을 포함한다. 하나는 privateCounter라는 변수이고 나머지 하나는 changeBy라는 함수이다. 둘 다 익명 함수 외부에서 접근될 수 없다. 대신에 익명 래퍼에서 반환된 세 개의 퍼블릭 함수를 통해서만 접근되어야만 한다.

+ +

위의 세 가지 퍼블릭 함수는 같은 환경을 공유하는 클로저다. 자바스크립트의 어휘적 유효 범위 덕분에 세 함수 각각 privateCounter 변수와 changeBy 함수에 접근할 수 있다.

+ +

카운터를 생성하는 익명 함수를 정의하고 그 함수를 즉시 호출하고 결과를 counter 변수에 할당하는 것을 알아차렸을 것이다. 이 함수를 별도의 변수 makeCounter 저장하고 이 변수를 이용해 여러 개의 카운터를 만들 수 있다.

+ +
var makeCounter = function() {
+  var privateCounter = 0;
+  function changeBy(val) {
+    privateCounter += val;
+  }
+  return {
+    increment: function() {
+      changeBy(1);
+    },
+    decrement: function() {
+      changeBy(-1);
+    },
+    value: function() {
+      return privateCounter;
+    }
+  }
+};
+
+var counter1 = makeCounter();
+var counter2 = makeCounter();
+alert(counter1.value()); /* 0 */
+counter1.increment();
+counter1.increment();
+alert(counter1.value()); /* 2 */
+counter1.decrement();
+alert(counter1.value()); /* 1 */
+alert(counter2.value()); /* 0 */
+
+ +

두 개의 카운터가 어떻게 다른 카운터와 독립성을 유지하는지 주목해보자. 각 클로저는 그들 고유의 클로저를 통한 privateCounter 변수의 다른 버전을 참조한다. 각 카운터가 호출될 때마다; 하나의 클로저에서 변수 값을 변경해도 다른 클로저의 값에는 영향을 주지 않는다.

+ +

이런 방식으로 클로저를 사용하여 객체지향 프로그래밍의 정보 은닉과 캡슐화 같은 이점들을 얻을 수 있다.

+ +

클로저 스코프 체인

+ +

모든 클로저에는 세가지 스코프(범위)가 있다:-

+ + + +

따라서, 우리는 클로저에 대해 세가지 범위 모두 접근할 수 있지만, 중첩된 내부 함수가 있는 경우 종종 실수를 저지른다. 아래 예제를 확인해보자:

+ +
// 전역 범위 (global scope)
+var e = 10;
+function sum(a){
+  return function(b){
+    return function(c){
+      // 외부 함수 범위 (outer functions scope)
+      return function(d){
+        // 지역 범위 (local scope)
+        return a + b + c + d + e;
+      }
+    }
+  }
+}
+
+console.log(sum(1)(2)(3)(4)); // log 20
+
+// 익명 함수 없이 작성할 수도 있다.
+
+// 전역 범위 (global scope)
+var e = 10;
+function sum(a){
+  return function sum2(b){
+    return function sum3(c){
+      // 외부 함수 범위 (outer functions scope)
+      return function sum4(d){
+        // 지역 범위 (local scope)
+        return a + b + c + d + e;
+      }
+    }
+  }
+}
+
+var s = sum(1);
+var s1 = s(2);
+var s2 = s1(3);
+var s3 = s2(4);
+console.log(s3) //log 20
+ +

위의 예제를 보면 일련의 중첩된 함수들을 확인할 수 있다. 이 함수들은 전부 외부 함수의 스코프에 접근할 수 있다. 그런데 문제는 즉각적인 외부 함수의 스코프만을 추측한다는 것이다. 이 문맥에서는 모든 클로저가 선언된 외부 함수의 스코프에 접근한다라고 말할 수 있다.

+ +

루프에서 클로저 생성하기: 일반적인 실수

+ +

ECMAScript 2015의 let 키워드 소개 전에는 클로저와 관련된 일반적인 문제는 루프 안에서 클로저가 생성되었을 때 발생한다.다음 예제를 보자.

+ +
<p id="help">Helpful notes will appear here</p>
+<p>E-mail: <input type="text" id="email" name="email"></p>
+<p>Name: <input type="text" id="name" name="name"></p>
+<p>Age: <input type="text" id="age" name="age"></p>
+
+ +
function showHelp(help) {
+  document.getElementById('help').innerHTML = help;
+}
+
+function setupHelp() {
+  var helpText = [
+      {'id': 'email', 'help': 'Your e-mail address'},
+      {'id': 'name', 'help': 'Your full name'},
+      {'id': 'age', 'help': 'Your age (you must be over 16)'}
+    ];
+
+  for (var i = 0; i < helpText.length; i++) {
+    var item = helpText[i];
+    document.getElementById(item.id).onfocus = function() {
+      showHelp(item.help);
+    }
+  }
+}
+
+setupHelp();
+
+ +

{{JSFiddleEmbed("https://jsfiddle.net/v7gjv/", "", 200)}}

+ +

helpText 배열은 세 개의 도움말 힌트를 정의한다. 각 도움말은 문서의 입력 필드의 ID와 연관된다. 루프를 돌면서 각 입력 필드 ID에 해당하는 엘리먼트의 onfocus 이벤트에 관련된 도움말을 보여주는 메소드에 연결한다.

+ +

이 코드를 사용하면 제대로 동작하지 않는 것을 알게 된다. 어떤 필드에 포커스를 주더라도 나이에 관한 도움말이 표시된다.

+ +

onfocus 이벤트에 연결된 함수가 클로저이기 때문이다. 이 클로저는 함수 정의와 setupHelp 함수 범위에서 캡처된 환경으로 구성된다. 루프에서 세 개의 클로저가 만들어졌지만 각 클로저는 값이 변하는 변수가 (item.help) 있는 같은 단일 환경을 공유한다. onfocus 콜백이 실행될 때 콜백의 환경에서 item 변수는 (세개의 클로저가 공유한다) helpText 리스트의 마지막 요소를 가리키고 있을 것이다.

+ +

이 경우 한 가지 해결책은 더 많은 클로저를 사용하는 것이다: 특히 앞에서 설명한 함수 팩토리를 사용하는 것이다.

+ +
function showHelp(help) {
+  document.getElementById('help').innerHTML = help;
+}
+
+function makeHelpCallback(help) {
+  return function() {
+    showHelp(help);
+  };
+}
+
+function setupHelp() {
+  var helpText = [
+      {'id': 'email', 'help': 'Your e-mail address'},
+      {'id': 'name', 'help': 'Your full name'},
+      {'id': 'age', 'help': 'Your age (you must be over 16)'}
+    ];
+
+  for (var i = 0; i < helpText.length; i++) {
+    var item = helpText[i];
+    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
+  }
+}
+
+setupHelp();
+
+ +

{{JSFiddleEmbed("https://jsfiddle.net/v7gjv/1/", "", 300)}}

+ +

이것은 예상대로 동작한다. 모두 단일 환경을 공유하는 콜백대신, makeHelpCallback 함수는 각각의 콜백에 새로운 어휘적 환경을 생성한다. 여기서 help는 helpText 배열의 해당 문자열을 나타낸다.

+ +

익명 클로저를 사용하여 위 코드를 작성하는 또 다른 방법은 다음과 같다.

+ +
function showHelp(help) {
+  document.getElementById('help').innerHTML = help;
+}
+
+function setupHelp() {
+  var helpText = [
+      {'id': 'email', 'help': 'Your e-mail address'},
+      {'id': 'name', 'help': 'Your full name'},
+      {'id': 'age', 'help': 'Your age (you must be over 16)'}
+    ];
+
+  for (var i = 0; i < helpText.length; i++) {
+    (function() {
+       var item = helpText[i];
+       document.getElementById(item.id).onfocus = function() {
+         showHelp(item.help);
+       }
+    })(); // Immediate event listener attachment with the current value of item (preserved until iteration).
+  }
+}
+
+setupHelp();
+ +

더 많은 클로저를 사용하는 것이 싫다면 ES2015의 let 키워드를 사용할 수 있다.

+ +
function showHelp(help) {
+  document.getElementById('help').innerHTML = help;
+}
+
+function setupHelp() {
+  var helpText = [
+      {'id': 'email', 'help': 'Your e-mail address'},
+      {'id': 'name', 'help': 'Your full name'},
+      {'id': 'age', 'help': 'Your age (you must be over 16)'}
+    ];
+
+  for (var i = 0; i < helpText.length; i++) {
+    let item = helpText[i];
+    document.getElementById(item.id).onfocus = function() {
+      showHelp(item.help);
+    }
+  }
+}
+
+setupHelp();
+ +

위의 경우 var 대신 let을 사용하여 모든 클로저가 블록 범위 변수를 바인딩할 것이므로 추가적인 클로저를 사용하지 않아도 완벽하게 동작할 것이다.

+ +

성능 관련 고려 사항

+ +

특정 작업에 클로저가 필요하지 않는데 다른 함수 내에서 함수를 불필요하게 작성하는 것은 현명하지 않다. 이것은 처리 속도와 메모리 소비 측면에서 스크립트 성능에 부정적인 영향을 미칠 것이다.

+ +

예를 들어, 새로운 객체/클래스를 생성 할 때, 메소드는 일반적으로 객체 생성자에 정의되기보다는 객체의 프로토타입에 연결되어야 한다. 그 이유는 생성자가 호출 될 때마다 메서드가 다시 할당되기 때문이다 (즉, 모든 개체가 생성 될 때마다).

+ +

비실용적이지만 시범적인 다음 예를 고려하라:

+ +
function MyObject(name, message) {
+  this.name = name.toString();
+  this.message = message.toString();
+  this.getName = function() {
+    return this.name;
+  };
+
+  this.getMessage = function() {
+    return this.message;
+  };
+}
+
+ +

앞의 코드는 클로저의 이점을 이용하지 않음으로 다음과 같이 다시 쓸 수 있다.

+ +
function MyObject(name, message) {
+  this.name = name.toString();
+  this.message = message.toString();
+}
+MyObject.prototype = {
+  getName: function() {
+    return this.name;
+  },
+  getMessage: function() {
+    return this.message;
+  }
+};
+
+ +

그러나 프로토타입을 다시 정의하는 것은 권장되지 않음으로 기존 프로토타입에 추가하는 다음 예제가 더 좋다.

+ +
function MyObject(name, message) {
+  this.name = name.toString();
+  this.message = message.toString();
+}
+MyObject.prototype.getName = function() {
+  return this.name;
+};
+MyObject.prototype.getMessage = function() {
+  return this.message;
+};
+
+ +

위의 코드는 같은 결과를 가진 더 깨끗한 방법으로 작성할 수도 있다:

+ +
function MyObject(name, message) {
+    this.name = name.toString();
+    this.message = message.toString();
+}
+(function() {
+    this.getName = function() {
+        return this.name;
+    };
+    this.getMessage = function() {
+        return this.message;
+    };
+}).call(MyObject.prototype);
+
+ +

앞의 두 가지 예제에서 상속된 프로토타입은 모든 객체에서 공유될 수 있으며 메소드 정의는 모든 객체 생성시 발생할 필요가 없다. 객체 모델의 세부 사항을 참고하라.

diff --git a/files/ko/web/javascript/data_structures/index.html b/files/ko/web/javascript/data_structures/index.html deleted file mode 100644 index 14967ae250..0000000000 --- a/files/ko/web/javascript/data_structures/index.html +++ /dev/null @@ -1,221 +0,0 @@ ---- -title: 자바스크립트의 자료형 -slug: Web/JavaScript/Data_structures -translation_of: Web/JavaScript/Data_structures ---- -
{{jsSidebar("More")}}
- -

모든 프로그래밍 언어는 내장 자료형이 있지만, 종종 이러한 내장 자료형은 언어마다 다르다. 이 문서에서는 자바스크립트의 내장 자료형과, 내장 자료형에서 사용할 수 있는 속성들에 대해 알아본다. 이로써 내장 자료형들로 더 복잡한 자료형을 만드는데 사용할 수 있을 것이다. 가능하다면 다른 언어와도 비교해보자.

- -

동적 타이핑

- -

자바스크립트는 느슨한 타입 (loosely typed) 언어, 혹은 동적 (dynamic) 언어이다. 그 말은, 변수의 타입을 미리 선언할 필요가 없다는 뜻이다. 타입은 프로그램이 처리되는 과정에서 자동으로 파악될 것이다. 또한 그 말은 같은 변수에 여러 타입의 값을 넣을 수 있다는 뜻이다.

- -
var foo = 42;    // foo 는 이제 Number 임
-var foo = "bar"; // foo 는 이제 String 임
-var foo = true;  // foo 는 이제 Boolean 임
-
- -

데이터 타입

- -

최신 ECMAScript 표준은 다음과 같은 7개의 자료형을  정의한다.

- - - -

다음 장에서 이 여섯개의 자료형을 사용하는 방법과 자료형을 조합하여 더 복잡한 자료형을 만드는 방법에 대해 알아보자.

- -

기본 타입 (Primitive value)

- -

오브젝트를 제외한 모든 값은 변경 불가능한 값 (immutable value) 이다. 예를 들자면, 특히 C 언어와는 다르게도, 문자열은 불변값 (immutable) 이다. 이런 값을 "primitive values" 라고 일컫는다. 아래의  {{ anch("Strings") }} 장에서 더 자세히 설명할 것이다.

- -

Boolean 타입

- -

Boolean 은 논리적인 요소를 나타내고, truefalse 의 두 가지 값을 가질 수 있다.

- -

Null 타입

- -

Null 타입은 딱 한 가지 값, null 을 가질 수 있다. 더 알아보려면 {{jsxref("null")}} 와 {{Glossary("Null")}} 을 보라.

- -

Undefined 타입

- -

값을 할당하지 않은 변수는 undefined 값을 가진다. 더 알아보려면 {{jsxref("undefined")}} 와 {{Glossary("Undefined")}} 을 보라.

- -

Number 타입

- -

ECMAScript 표준에 따르면, 숫자의 자료형은 배정밀도 64비트 형식 IEEE 754 값 (-(253 -1) 와 253 -1 사이의 숫자값) 단 하나만 존재한다. 정수만을 표현하기 위한 특별한 자료형은 없다. 부동 소수점을 표현할 수 있는 것 말고도, Number 타입은 세 가지 의미있는 몇가지 상징적인 값들도 표현할 수 있다. 이 값에는 +무한대, -무한대, and NaN (숫자가 아님)이 있다.

- -

+/-Infinity 보다 크거나 작은지 확인하는 용도로 상수값인 {{jsxref("Number.MAX_VALUE")}} 나 {{jsxref("Number.MIN_VALUE")}} 을 사용할 수 있다. 또한, ECMAScript 6 부터는 숫자가 배정밀도 부동소수점 숫자인지 확인하는 용도로 {{jsxref("Number.isSafeInteger()")}} 과 {{jsxref("Number.MAX_SAFE_INTEGER")}}, {{jsxref("Number.MIN_SAFE_INTEGER")}} 을 사용할 수 있다. 이 범위를 넘어서면, 자바스크립트의 숫자는 더 이상 안전하지 않다.

- -

Number 타입의 값 중에는 두 가지 방식으로 표현할 수 있는 유일한 값이 있는데, 0 이다. 0 은 -0 이나 +0 으로 표시할 수 있다. ("0" 은 물론 +0 이다.) 실제로는 이러한 사실은 거의 효력이 없다. 그 예로, +0 === -0true 이다. 하지만 0으로 나누는 경우 그 차이가 눈에 띌 것이다.

- -
> 42 / +0
-Infinity
-> 42 / -0
--Infinity
- -

숫자가 보통 값만으로 표현되긴 하지만, 자바스크립트는 몇 가지 이진 연산자도 제공한다. 이러한 이진 연산자들은 비트 마스킹(bit masking) 기법으로 한 숫자 안에 여러 Boolean 값을 저장하는데도 쓸 수 있다. 일반적으로 이런 방법은 나쁜 방법이지만, 자바스크립트에서는 (Boolean 값의 배열이나 Boolean 값들을 이름있는 속성들로 가지는 객체 같은) Boolean 덩어리를 나타낼 다른 방법이 없다. 비트 마스킹은 또한 코드를 이해하고, 읽고, 유지보수하는데에 있어서 좀 더 어렵게 만드는 경향이 있다. 하지만 이러한 기법은 local storage 의 저장공간이 부족해서 절약하려고 하거나, 네트워크 상에서 각각의 비트를 전송하는 등의 극단적인 상황 같은 굉장히 제한적인 환경에서 필요할 수도 있다. 그래서 비트 마스킹 기법은 크기를 최대한 줄여야하는 상황에서만 사용을 고려해야 한다.

- -

String 타입

- -

자바스크립트의 {{jsxref("Global_Objects/String", "String")}} 타입은 텍스트 데이터를 나타내는데 사용한다. 이는 16비트 부호없는 정수 값 요소들의 집합이다. String의 각 요소는 String의 위치를 차지한다. 첫 번째 요소는 0번 인덱스에 있고, 다음 요소는 1번, 그 다음 요소는 2번... 같은 방식이다. String 의 길이는 String이 가지고있는 요소의 갯수이다.

- -

C 같은 언어와는 다르게, 자바스크립트의 문자열은 변경 불가능 (immutable) 하다. 이것은 한 번 문자열이 생성되면, 그 문자열을 수정할 수 없다는걸 의미한다. 그러나 원래 문자열에서 일부가 수정된 다른 문자열을 만드는건 가능하다. 예를 들자면 이렇다.

- - - -

"문자열의 자료형화"를 조심하라!

- -

문자열을 복잡한 자료형을 표현하는 용도로 쓰는 방법이 꽤나 매력적일 수 있다. 단기적으로 이런 장점들이 있다.

- - - -

규칙만 잘 정의해놓는다면, 어떤 자료구조가 되던 문자열로 표시할 수 있다. 그렇다고 해서 이게 좋은 방법이 되는 건 아니다. 예를 들자면, 구분자로 리스트 자료형을 흉내낼 수 있을 것이다 (하지만 자바스크립트의 배열을 사용하는게 더 알맞을 것이다). 불행하게도, 리스트의 요소중에 구분자가 들어있는 요소가 있다면 리스트는 엉망진창이 될 것이다. 물론 탈출 문자 (escape character) 등을 사용하거나 할 수도 있을 것이다. 하지만 이런 것들은 모두 미리 정해놓은 규칙을 필요로 하고, 덕분에 불필요한 관리 부담을 낳는다.

- -

문자열은 텍스트 데이터에만 사용하자. 복잡한 데이터를 나타낼때는, 문자열을 분석해서 적합한 추상화를 선택해 사용하자.

- -

Symbol 타입

- -

Symbol 은 ECMAScript 6 에서 추가되었다. Symbol은 유일하고 변경 불가능한 (immutable) 기본값 (primitive value) 이다. 또한, 객체 속성의 key 값으로도 사용될 수 있다 (아래를 볼 것). 몇몇 프로그래밍 언어에서는 Symbol을 atom 이라고 부른다. 또, C 언어의 이름있는 열거형 (enum) 과도 비슷하다. 좀 더 자세히 알아보려면, 자바스크립트의  {{Glossary("Symbol")}} 와 {{jsxref("Symbol")}} 객체 래퍼 (wrapper) 를 보라.

- -

객체 (Objects)

- -

컴퓨터 과학에서, 객체는 {{Glossary("Identifier", "식별자 (Identifier)")}} 로 참조할 수 있는, 메모리에 있는 값이다.

- -

속성 (Properties)

- -

자바스크립트에서, 객체는 속성들을 담고있는 가방 (collection) 으로 볼 수 있다. 객체 리터럴 문법 (object literal syntax) 으로 제한적으로 몇 가지 속성을 초기화할 수 있고, 그러고 나서 속성들을 추가하거나 제거할 수도 있다. 속성 값은 객체를 포함해 어떠한 자료형도 될 수 있고, 그 덕분에  복잡한 데이터 구조를 형성하는게 가능해진다. 속성은 키 (key) 값으로 식별된다. 키 값은 String 이거나 Symbol 값이다.

- -

두 종류의 객체 속성이 있는데, 이들은 종류에 따라 특성값들을 가질 수 있다. 데이터 (data) 속성과 접근자 (accessor) 속성이 그것이다.

- -

데이터 속성 (Data property)

- -

키에 값을 연결하고, 아래와 같은 특성들 (attribute) 이 있다.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
데이터 속성의 특성들
특성 (Attribute)자료형설명기본값
[[Value]]JavaScript 타입 모두 가능이 속성에 대한 get 접근으로 반환되는 값.undefined
[[Writable]]Booleanfalse 라면, 이 속성의 [[Value]] 을 바꿀 수 없다.false
[[Enumerable]]Booleantrue 라면, 이 속성은 for...in 루프에서 열거될 수 있다.false
[[Configurable]]Booleanfalse 라면, 이 속성은 제거될 수 없고, [[Value]]와 [[Writable]] 외에는 수정될 수 없다.false
- -

접근자 속성 (Accessor property)

- -

값을 가져오거나 값을 저장하기 위해 키에 하나 혹은 두 개의 접근자 함수 (get, set) 연결짓는다. 아래와 같은 특성이 있다.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
접근자 속성
특성 (Attribute)자료형설명기본값
[[Get]]Function 객체 혹은 undefined이 속성의 값에 접근할 때마다, 인자 목록 없이 함수가 호출되고, 함수의 반환된 값으로 속성값을 가져온다. get 을 볼 것undefined
[[Set]]Function 객체 혹은 undefined -

이 속성의 값이 바뀌려고 할 때마다, 할당된 값을 인자로 함수가 호출된다. set 을 볼 것

-
undefined
[[Enumerable]]Booleantrue 라면, 이 속성은 for...in 루프에서 열거될 수 있다.false
[[Configurable]]Booleanfalse 라면, 이 속성은 제거될 수 없고, 데이터 속성을 수정할 수 없다.false
- -

"Normal" objects, and functions

- -

자바스크립트 오브젝트는 키와 값의 매핑이다. 키는 문자열이고 값은 다른 어떤 값도 될 수 있다. 오브젝트는 hashmaps을 표현하는데 적합하다. 표준이 아닌 __proto__  슈도 프로퍼티를 사용할 때는 주의하자. 이것을 지원하는 환경에서는 '__proto__'는 오브젝트의 프로토타입을 의미하므로 이 이름을 키로 사용할 수 없다.   속성을 사용할 수 없다. 문자열의 출처가 분명하지 않을 때(입력 필드의 입력값 등)주의가 필요하다. 이런 일이 생길 수도 있다. 이 때는  StringMap abstraction 같은 대안을 사용해보자.함수는 일반 오브젝트에서 호출 가능한 특성을 추가한 오브젝트이다.

- -

Dates

- -

시간을 나타내려면 Date utility를 사용하자. 최고의 선택이다.

- -

Arrays

- -

배열(Arrays) 는 정수키를 가지는 일련의 값들을 표현하기 위한 오브젝트이다. 배열 오브젝트에는 길이를 나타내는 'length'란 속성도 있다. 배열은 Array.prototype을 상속받으므로 배열을 다룰 때 편한 indexOf (배열에서 값 검색)와 push (새로운 값 추가) 같은 함수를 사용할 수 있다. 배열은 리스트나 집합을 표현하는데 적합하다.

- -

WeakMaps, Maps, Sets

- -

표준이 아니지만 ECMAScript 6에서 표준이 될 것 같다.

- -

이 자료형들에서는 키가 문자열 뿐만 아니라 오브젝트도 될 수 있다. Set은 오브젝트의 집합을 나타내는 반면에 WeakMaps와 Maps는 오브젝트에 값을 연관시킬 수 있다. Map과 WeakMap의 차이는 전자는 오브젝트 키를 열거할 수 있다는 것이다. 이것은 가비지 콜렉션에서 이점을 준다.

- -

ECMAScript 5를 이용해서 Map과 Set을 구현할 수 있을 것이다. 그러나 오브젝트는 크기 비교가 안된다는 점 때문에(예를들어 어떤 오브젝트는 다른 오브젝트보다 '작다'라고 할 수 없다) look-up에 소요되는 시간이 선형 시간이지 않을 것이다. 네이티브 구현은(WeakMaps를 포함해서) look-up 시간이 거의 로그 시간에서 상수 시간이다.

- -

DOM 노드에 데이터를 지정하기 위해서 직접 속성을 지정할 수도 있지만 data-* 속성을 사용할 수도 있다. 여기에는 다른 스크립트도 모두 그 속성에 접근할 수 있다는 나쁜 점이 있다. Map과 WeakMap은 오브젝트만 사용할 수 있는 개인 데이터를 쉽게 만들 수 있게 해준다.

- -

TypedArrays

- -

표준이 아니지만 ECMAScript 6에서 표준이 될 것 같다.

- -

더 보기

- - diff --git a/files/ko/web/javascript/data_structures/index.md b/files/ko/web/javascript/data_structures/index.md new file mode 100644 index 0000000000..14967ae250 --- /dev/null +++ b/files/ko/web/javascript/data_structures/index.md @@ -0,0 +1,221 @@ +--- +title: 자바스크립트의 자료형 +slug: Web/JavaScript/Data_structures +translation_of: Web/JavaScript/Data_structures +--- +
{{jsSidebar("More")}}
+ +

모든 프로그래밍 언어는 내장 자료형이 있지만, 종종 이러한 내장 자료형은 언어마다 다르다. 이 문서에서는 자바스크립트의 내장 자료형과, 내장 자료형에서 사용할 수 있는 속성들에 대해 알아본다. 이로써 내장 자료형들로 더 복잡한 자료형을 만드는데 사용할 수 있을 것이다. 가능하다면 다른 언어와도 비교해보자.

+ +

동적 타이핑

+ +

자바스크립트는 느슨한 타입 (loosely typed) 언어, 혹은 동적 (dynamic) 언어이다. 그 말은, 변수의 타입을 미리 선언할 필요가 없다는 뜻이다. 타입은 프로그램이 처리되는 과정에서 자동으로 파악될 것이다. 또한 그 말은 같은 변수에 여러 타입의 값을 넣을 수 있다는 뜻이다.

+ +
var foo = 42;    // foo 는 이제 Number 임
+var foo = "bar"; // foo 는 이제 String 임
+var foo = true;  // foo 는 이제 Boolean 임
+
+ +

데이터 타입

+ +

최신 ECMAScript 표준은 다음과 같은 7개의 자료형을  정의한다.

+ + + +

다음 장에서 이 여섯개의 자료형을 사용하는 방법과 자료형을 조합하여 더 복잡한 자료형을 만드는 방법에 대해 알아보자.

+ +

기본 타입 (Primitive value)

+ +

오브젝트를 제외한 모든 값은 변경 불가능한 값 (immutable value) 이다. 예를 들자면, 특히 C 언어와는 다르게도, 문자열은 불변값 (immutable) 이다. 이런 값을 "primitive values" 라고 일컫는다. 아래의  {{ anch("Strings") }} 장에서 더 자세히 설명할 것이다.

+ +

Boolean 타입

+ +

Boolean 은 논리적인 요소를 나타내고, truefalse 의 두 가지 값을 가질 수 있다.

+ +

Null 타입

+ +

Null 타입은 딱 한 가지 값, null 을 가질 수 있다. 더 알아보려면 {{jsxref("null")}} 와 {{Glossary("Null")}} 을 보라.

+ +

Undefined 타입

+ +

값을 할당하지 않은 변수는 undefined 값을 가진다. 더 알아보려면 {{jsxref("undefined")}} 와 {{Glossary("Undefined")}} 을 보라.

+ +

Number 타입

+ +

ECMAScript 표준에 따르면, 숫자의 자료형은 배정밀도 64비트 형식 IEEE 754 값 (-(253 -1) 와 253 -1 사이의 숫자값) 단 하나만 존재한다. 정수만을 표현하기 위한 특별한 자료형은 없다. 부동 소수점을 표현할 수 있는 것 말고도, Number 타입은 세 가지 의미있는 몇가지 상징적인 값들도 표현할 수 있다. 이 값에는 +무한대, -무한대, and NaN (숫자가 아님)이 있다.

+ +

+/-Infinity 보다 크거나 작은지 확인하는 용도로 상수값인 {{jsxref("Number.MAX_VALUE")}} 나 {{jsxref("Number.MIN_VALUE")}} 을 사용할 수 있다. 또한, ECMAScript 6 부터는 숫자가 배정밀도 부동소수점 숫자인지 확인하는 용도로 {{jsxref("Number.isSafeInteger()")}} 과 {{jsxref("Number.MAX_SAFE_INTEGER")}}, {{jsxref("Number.MIN_SAFE_INTEGER")}} 을 사용할 수 있다. 이 범위를 넘어서면, 자바스크립트의 숫자는 더 이상 안전하지 않다.

+ +

Number 타입의 값 중에는 두 가지 방식으로 표현할 수 있는 유일한 값이 있는데, 0 이다. 0 은 -0 이나 +0 으로 표시할 수 있다. ("0" 은 물론 +0 이다.) 실제로는 이러한 사실은 거의 효력이 없다. 그 예로, +0 === -0true 이다. 하지만 0으로 나누는 경우 그 차이가 눈에 띌 것이다.

+ +
> 42 / +0
+Infinity
+> 42 / -0
+-Infinity
+ +

숫자가 보통 값만으로 표현되긴 하지만, 자바스크립트는 몇 가지 이진 연산자도 제공한다. 이러한 이진 연산자들은 비트 마스킹(bit masking) 기법으로 한 숫자 안에 여러 Boolean 값을 저장하는데도 쓸 수 있다. 일반적으로 이런 방법은 나쁜 방법이지만, 자바스크립트에서는 (Boolean 값의 배열이나 Boolean 값들을 이름있는 속성들로 가지는 객체 같은) Boolean 덩어리를 나타낼 다른 방법이 없다. 비트 마스킹은 또한 코드를 이해하고, 읽고, 유지보수하는데에 있어서 좀 더 어렵게 만드는 경향이 있다. 하지만 이러한 기법은 local storage 의 저장공간이 부족해서 절약하려고 하거나, 네트워크 상에서 각각의 비트를 전송하는 등의 극단적인 상황 같은 굉장히 제한적인 환경에서 필요할 수도 있다. 그래서 비트 마스킹 기법은 크기를 최대한 줄여야하는 상황에서만 사용을 고려해야 한다.

+ +

String 타입

+ +

자바스크립트의 {{jsxref("Global_Objects/String", "String")}} 타입은 텍스트 데이터를 나타내는데 사용한다. 이는 16비트 부호없는 정수 값 요소들의 집합이다. String의 각 요소는 String의 위치를 차지한다. 첫 번째 요소는 0번 인덱스에 있고, 다음 요소는 1번, 그 다음 요소는 2번... 같은 방식이다. String 의 길이는 String이 가지고있는 요소의 갯수이다.

+ +

C 같은 언어와는 다르게, 자바스크립트의 문자열은 변경 불가능 (immutable) 하다. 이것은 한 번 문자열이 생성되면, 그 문자열을 수정할 수 없다는걸 의미한다. 그러나 원래 문자열에서 일부가 수정된 다른 문자열을 만드는건 가능하다. 예를 들자면 이렇다.

+ + + +

"문자열의 자료형화"를 조심하라!

+ +

문자열을 복잡한 자료형을 표현하는 용도로 쓰는 방법이 꽤나 매력적일 수 있다. 단기적으로 이런 장점들이 있다.

+ + + +

규칙만 잘 정의해놓는다면, 어떤 자료구조가 되던 문자열로 표시할 수 있다. 그렇다고 해서 이게 좋은 방법이 되는 건 아니다. 예를 들자면, 구분자로 리스트 자료형을 흉내낼 수 있을 것이다 (하지만 자바스크립트의 배열을 사용하는게 더 알맞을 것이다). 불행하게도, 리스트의 요소중에 구분자가 들어있는 요소가 있다면 리스트는 엉망진창이 될 것이다. 물론 탈출 문자 (escape character) 등을 사용하거나 할 수도 있을 것이다. 하지만 이런 것들은 모두 미리 정해놓은 규칙을 필요로 하고, 덕분에 불필요한 관리 부담을 낳는다.

+ +

문자열은 텍스트 데이터에만 사용하자. 복잡한 데이터를 나타낼때는, 문자열을 분석해서 적합한 추상화를 선택해 사용하자.

+ +

Symbol 타입

+ +

Symbol 은 ECMAScript 6 에서 추가되었다. Symbol은 유일하고 변경 불가능한 (immutable) 기본값 (primitive value) 이다. 또한, 객체 속성의 key 값으로도 사용될 수 있다 (아래를 볼 것). 몇몇 프로그래밍 언어에서는 Symbol을 atom 이라고 부른다. 또, C 언어의 이름있는 열거형 (enum) 과도 비슷하다. 좀 더 자세히 알아보려면, 자바스크립트의  {{Glossary("Symbol")}} 와 {{jsxref("Symbol")}} 객체 래퍼 (wrapper) 를 보라.

+ +

객체 (Objects)

+ +

컴퓨터 과학에서, 객체는 {{Glossary("Identifier", "식별자 (Identifier)")}} 로 참조할 수 있는, 메모리에 있는 값이다.

+ +

속성 (Properties)

+ +

자바스크립트에서, 객체는 속성들을 담고있는 가방 (collection) 으로 볼 수 있다. 객체 리터럴 문법 (object literal syntax) 으로 제한적으로 몇 가지 속성을 초기화할 수 있고, 그러고 나서 속성들을 추가하거나 제거할 수도 있다. 속성 값은 객체를 포함해 어떠한 자료형도 될 수 있고, 그 덕분에  복잡한 데이터 구조를 형성하는게 가능해진다. 속성은 키 (key) 값으로 식별된다. 키 값은 String 이거나 Symbol 값이다.

+ +

두 종류의 객체 속성이 있는데, 이들은 종류에 따라 특성값들을 가질 수 있다. 데이터 (data) 속성과 접근자 (accessor) 속성이 그것이다.

+ +

데이터 속성 (Data property)

+ +

키에 값을 연결하고, 아래와 같은 특성들 (attribute) 이 있다.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
데이터 속성의 특성들
특성 (Attribute)자료형설명기본값
[[Value]]JavaScript 타입 모두 가능이 속성에 대한 get 접근으로 반환되는 값.undefined
[[Writable]]Booleanfalse 라면, 이 속성의 [[Value]] 을 바꿀 수 없다.false
[[Enumerable]]Booleantrue 라면, 이 속성은 for...in 루프에서 열거될 수 있다.false
[[Configurable]]Booleanfalse 라면, 이 속성은 제거될 수 없고, [[Value]]와 [[Writable]] 외에는 수정될 수 없다.false
+ +

접근자 속성 (Accessor property)

+ +

값을 가져오거나 값을 저장하기 위해 키에 하나 혹은 두 개의 접근자 함수 (get, set) 연결짓는다. 아래와 같은 특성이 있다.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
접근자 속성
특성 (Attribute)자료형설명기본값
[[Get]]Function 객체 혹은 undefined이 속성의 값에 접근할 때마다, 인자 목록 없이 함수가 호출되고, 함수의 반환된 값으로 속성값을 가져온다. get 을 볼 것undefined
[[Set]]Function 객체 혹은 undefined +

이 속성의 값이 바뀌려고 할 때마다, 할당된 값을 인자로 함수가 호출된다. set 을 볼 것

+
undefined
[[Enumerable]]Booleantrue 라면, 이 속성은 for...in 루프에서 열거될 수 있다.false
[[Configurable]]Booleanfalse 라면, 이 속성은 제거될 수 없고, 데이터 속성을 수정할 수 없다.false
+ +

"Normal" objects, and functions

+ +

자바스크립트 오브젝트는 키와 값의 매핑이다. 키는 문자열이고 값은 다른 어떤 값도 될 수 있다. 오브젝트는 hashmaps을 표현하는데 적합하다. 표준이 아닌 __proto__  슈도 프로퍼티를 사용할 때는 주의하자. 이것을 지원하는 환경에서는 '__proto__'는 오브젝트의 프로토타입을 의미하므로 이 이름을 키로 사용할 수 없다.   속성을 사용할 수 없다. 문자열의 출처가 분명하지 않을 때(입력 필드의 입력값 등)주의가 필요하다. 이런 일이 생길 수도 있다. 이 때는  StringMap abstraction 같은 대안을 사용해보자.함수는 일반 오브젝트에서 호출 가능한 특성을 추가한 오브젝트이다.

+ +

Dates

+ +

시간을 나타내려면 Date utility를 사용하자. 최고의 선택이다.

+ +

Arrays

+ +

배열(Arrays) 는 정수키를 가지는 일련의 값들을 표현하기 위한 오브젝트이다. 배열 오브젝트에는 길이를 나타내는 'length'란 속성도 있다. 배열은 Array.prototype을 상속받으므로 배열을 다룰 때 편한 indexOf (배열에서 값 검색)와 push (새로운 값 추가) 같은 함수를 사용할 수 있다. 배열은 리스트나 집합을 표현하는데 적합하다.

+ +

WeakMaps, Maps, Sets

+ +

표준이 아니지만 ECMAScript 6에서 표준이 될 것 같다.

+ +

이 자료형들에서는 키가 문자열 뿐만 아니라 오브젝트도 될 수 있다. Set은 오브젝트의 집합을 나타내는 반면에 WeakMaps와 Maps는 오브젝트에 값을 연관시킬 수 있다. Map과 WeakMap의 차이는 전자는 오브젝트 키를 열거할 수 있다는 것이다. 이것은 가비지 콜렉션에서 이점을 준다.

+ +

ECMAScript 5를 이용해서 Map과 Set을 구현할 수 있을 것이다. 그러나 오브젝트는 크기 비교가 안된다는 점 때문에(예를들어 어떤 오브젝트는 다른 오브젝트보다 '작다'라고 할 수 없다) look-up에 소요되는 시간이 선형 시간이지 않을 것이다. 네이티브 구현은(WeakMaps를 포함해서) look-up 시간이 거의 로그 시간에서 상수 시간이다.

+ +

DOM 노드에 데이터를 지정하기 위해서 직접 속성을 지정할 수도 있지만 data-* 속성을 사용할 수도 있다. 여기에는 다른 스크립트도 모두 그 속성에 접근할 수 있다는 나쁜 점이 있다. Map과 WeakMap은 오브젝트만 사용할 수 있는 개인 데이터를 쉽게 만들 수 있게 해준다.

+ +

TypedArrays

+ +

표준이 아니지만 ECMAScript 6에서 표준이 될 것 같다.

+ +

더 보기

+ + diff --git a/files/ko/web/javascript/enumerability_and_ownership_of_properties/index.html b/files/ko/web/javascript/enumerability_and_ownership_of_properties/index.html deleted file mode 100644 index 41252c39d1..0000000000 --- a/files/ko/web/javascript/enumerability_and_ownership_of_properties/index.html +++ /dev/null @@ -1,331 +0,0 @@ ---- -title: Enumerability and ownership of properties -slug: Web/JavaScript/Enumerability_and_ownership_of_properties -translation_of: Web/JavaScript/Enumerability_and_ownership_of_properties ---- -
{{JsSidebar("More")}}
- - - -

'Enumerable properties'(열거 가능한 속성)는 내부 열거 형 플래그가 true로 설정된 property로, 이는 간단한 할당 또는 property initializer (Object.defineProperty를 통해 정의 된 특성 및 이러한 기본 열거 형을 false로 정의한 특성)를 통해 작성된 property의 기본값입니다. 등록 정보의 키가 Symbol이 아니면 열거 가능한 등록 정보가 for...in 루프에 표시됩니다. 'Ownership of properties' (속성의 소유권)은 속성이 프로토 타입 체인이 아닌 개체에 직접 속하는지 여부에 따라 결정됩니다. 객체의 속성도 전체적으로 검색 할 수 있습니다. 개체 속성을 감지, 반복 / 열거 및 검색하는 여러 가지 기본 제공 방법이 있으며 아래 표와 같이 사용할 수 있습니다. 누락 된 범주를 얻는 방법을 보여주는 샘플 코드는 다음과 같습니다.

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Property enumerability and ownership - built-in methods of detection, retrieval, and iteration
FunctionalityOwn objectOwn object and its prototype chainPrototype chain only
Detection - - - - - - - - - - - - - - - -
EnumerableNonenumerableEnumerable and Nonenumerable
-

propertyIsEnumerable

- -

hasOwnProperty

-
-

hasOwnProperty – filtered to exclude enumerables using propertyIsEnumerable

-
hasOwnProperty
-
- - - - - - - - - - - - - - - -
EnumerableNonenumerableEnumerable and Nonenumerable
Not available without extra codeNot available without extra codein
-
Not available without extra code
Retrieval - - - - - - - - - - - - - - - -
EnumerableNonenumerableEnumerable and Nonenumerable
-

Object.keys

- -

getOwnPropertyNames 

- -

getOwnPropertySymbols

-
getOwnPropertyNames, getOwnPropertySymbols – filtered to exclude enumerables using propertyIsEnumerable -

getOwnPropertyNames

- -

getOwnPropertySymbols

-
-
Not available without extra codeNot available without extra code
Iterable - - - - - - - - - - - - - - - -
EnumerableNonenumerableEnumerable and Nonenumerable
-

Object.keys

- -

getOwnPropertyNames 

- -

getOwnPropertySymbols

-
getOwnPropertyNames, getOwnPropertySymbols – filtered to exclude enumerables using propertyIsEnumerable -

getOwnPropertyNames

- -

getOwnPropertySymbols

-
-
- - - - - - - - - - - - - - - -
EnumerableNonenumerableEnumerable and Nonenumerable
-

for..in

- -

(excluding symbols)

-
Not available without extra codeNot available without extra code
-
Not available without extra code
-
- -

Obtaining properties by enumerability/ownership

- - - -

아래는 모든 경우에 가장 효율적인 알고리즘은 아니지만 빠르게 코드를 작성하여 확인하기 좋습니다.

- - - -
var SimplePropertyRetriever = {
-    getOwnEnumerables: function(obj) {
-        return this._getPropertyNames(obj, true, false, this._enumerable);
-         // Or could use for..in filtered with hasOwnProperty or just this: return Object.keys(obj);
-    },
-    getOwnNonenumerables: function(obj) {
-        return this._getPropertyNames(obj, true, false, this._notEnumerable);
-    },
-    getOwnEnumerablesAndNonenumerables: function(obj) {
-        return this._getPropertyNames(obj, true, false, this._enumerableAndNotEnumerable);
-        // Or just use: return Object.getOwnPropertyNames(obj);
-    },
-    getPrototypeEnumerables: function(obj) {
-        return this._getPropertyNames(obj, false, true, this._enumerable);
-    },
-    getPrototypeNonenumerables: function(obj) {
-        return this._getPropertyNames(obj, false, true, this._notEnumerable);
-    },
-    getPrototypeEnumerablesAndNonenumerables: function(obj) {
-        return this._getPropertyNames(obj, false, true, this._enumerableAndNotEnumerable);
-    },
-    getOwnAndPrototypeEnumerables: function(obj) {
-        return this._getPropertyNames(obj, true, true, this._enumerable);
-        // Or could use unfiltered for..in
-    },
-    getOwnAndPrototypeNonenumerables: function(obj) {
-        return this._getPropertyNames(obj, true, true, this._notEnumerable);
-    },
-    getOwnAndPrototypeEnumerablesAndNonenumerables: function(obj) {
-        return this._getPropertyNames(obj, true, true, this._enumerableAndNotEnumerable);
-    },
-    // Private static property checker callbacks
-    _enumerable: function(obj, prop) {
-        return obj.propertyIsEnumerable(prop);
-    },
-    _notEnumerable: function(obj, prop) {
-        return !obj.propertyIsEnumerable(prop);
-    },
-    _enumerableAndNotEnumerable: function(obj, prop) {
-        return true;
-    },
-    // Inspired by http://stackoverflow.com/a/8024294/271577
-    _getPropertyNames: function getAllPropertyNames(obj, iterateSelfBool, iteratePrototypeBool, includePropCb) {
-        var props = [];
-
-        do {
-            if (iterateSelfBool) {
-                Object.getOwnPropertyNames(obj).forEach(function(prop) {
-                    if (props.indexOf(prop) === -1 && includePropCb(obj, prop)) {
-                        props.push(prop);
-                    }
-                });
-            }
-            if (!iteratePrototypeBool) {
-                break;
-            }
-            iterateSelfBool = true;
-        } while (obj = Object.getPrototypeOf(obj));
-
-        return props;
-    }
-};
- -

Detection Table

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
infor..inobj.hasOwnPropertyobj.propertyIsEnumerableObject.keysObject.getOwnPropertyNamesObject.getOwnPropertyDescriptorsReflect.ownKeys()
Enumerabletruetruetruetruetruetruetruetrue
Nonenumerabletruefalsetruefalsefalsetruetruetrue
Symbols keystruefalsetruetruefalsefalsetruetrue
Inherited Enumerabletruetruefalsefalsefalsefalsefalsefalse
Inherited Nonenumerabletruefalsefalsefalsefalsefalsefalsefalse
Inherited Symbols keystruefalsefalsefalsefalsefalsefalsefalse
-
- -

See also

- - diff --git a/files/ko/web/javascript/enumerability_and_ownership_of_properties/index.md b/files/ko/web/javascript/enumerability_and_ownership_of_properties/index.md new file mode 100644 index 0000000000..41252c39d1 --- /dev/null +++ b/files/ko/web/javascript/enumerability_and_ownership_of_properties/index.md @@ -0,0 +1,331 @@ +--- +title: Enumerability and ownership of properties +slug: Web/JavaScript/Enumerability_and_ownership_of_properties +translation_of: Web/JavaScript/Enumerability_and_ownership_of_properties +--- +
{{JsSidebar("More")}}
+ + + +

'Enumerable properties'(열거 가능한 속성)는 내부 열거 형 플래그가 true로 설정된 property로, 이는 간단한 할당 또는 property initializer (Object.defineProperty를 통해 정의 된 특성 및 이러한 기본 열거 형을 false로 정의한 특성)를 통해 작성된 property의 기본값입니다. 등록 정보의 키가 Symbol이 아니면 열거 가능한 등록 정보가 for...in 루프에 표시됩니다. 'Ownership of properties' (속성의 소유권)은 속성이 프로토 타입 체인이 아닌 개체에 직접 속하는지 여부에 따라 결정됩니다. 객체의 속성도 전체적으로 검색 할 수 있습니다. 개체 속성을 감지, 반복 / 열거 및 검색하는 여러 가지 기본 제공 방법이 있으며 아래 표와 같이 사용할 수 있습니다. 누락 된 범주를 얻는 방법을 보여주는 샘플 코드는 다음과 같습니다.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Property enumerability and ownership - built-in methods of detection, retrieval, and iteration
FunctionalityOwn objectOwn object and its prototype chainPrototype chain only
Detection + + + + + + + + + + + + + + + +
EnumerableNonenumerableEnumerable and Nonenumerable
+

propertyIsEnumerable

+ +

hasOwnProperty

+
+

hasOwnProperty – filtered to exclude enumerables using propertyIsEnumerable

+
hasOwnProperty
+
+ + + + + + + + + + + + + + + +
EnumerableNonenumerableEnumerable and Nonenumerable
Not available without extra codeNot available without extra codein
+
Not available without extra code
Retrieval + + + + + + + + + + + + + + + +
EnumerableNonenumerableEnumerable and Nonenumerable
+

Object.keys

+ +

getOwnPropertyNames 

+ +

getOwnPropertySymbols

+
getOwnPropertyNames, getOwnPropertySymbols – filtered to exclude enumerables using propertyIsEnumerable +

getOwnPropertyNames

+ +

getOwnPropertySymbols

+
+
Not available without extra codeNot available without extra code
Iterable + + + + + + + + + + + + + + + +
EnumerableNonenumerableEnumerable and Nonenumerable
+

Object.keys

+ +

getOwnPropertyNames 

+ +

getOwnPropertySymbols

+
getOwnPropertyNames, getOwnPropertySymbols – filtered to exclude enumerables using propertyIsEnumerable +

getOwnPropertyNames

+ +

getOwnPropertySymbols

+
+
+ + + + + + + + + + + + + + + +
EnumerableNonenumerableEnumerable and Nonenumerable
+

for..in

+ +

(excluding symbols)

+
Not available without extra codeNot available without extra code
+
Not available without extra code
+
+ +

Obtaining properties by enumerability/ownership

+ + + +

아래는 모든 경우에 가장 효율적인 알고리즘은 아니지만 빠르게 코드를 작성하여 확인하기 좋습니다.

+ + + +
var SimplePropertyRetriever = {
+    getOwnEnumerables: function(obj) {
+        return this._getPropertyNames(obj, true, false, this._enumerable);
+         // Or could use for..in filtered with hasOwnProperty or just this: return Object.keys(obj);
+    },
+    getOwnNonenumerables: function(obj) {
+        return this._getPropertyNames(obj, true, false, this._notEnumerable);
+    },
+    getOwnEnumerablesAndNonenumerables: function(obj) {
+        return this._getPropertyNames(obj, true, false, this._enumerableAndNotEnumerable);
+        // Or just use: return Object.getOwnPropertyNames(obj);
+    },
+    getPrototypeEnumerables: function(obj) {
+        return this._getPropertyNames(obj, false, true, this._enumerable);
+    },
+    getPrototypeNonenumerables: function(obj) {
+        return this._getPropertyNames(obj, false, true, this._notEnumerable);
+    },
+    getPrototypeEnumerablesAndNonenumerables: function(obj) {
+        return this._getPropertyNames(obj, false, true, this._enumerableAndNotEnumerable);
+    },
+    getOwnAndPrototypeEnumerables: function(obj) {
+        return this._getPropertyNames(obj, true, true, this._enumerable);
+        // Or could use unfiltered for..in
+    },
+    getOwnAndPrototypeNonenumerables: function(obj) {
+        return this._getPropertyNames(obj, true, true, this._notEnumerable);
+    },
+    getOwnAndPrototypeEnumerablesAndNonenumerables: function(obj) {
+        return this._getPropertyNames(obj, true, true, this._enumerableAndNotEnumerable);
+    },
+    // Private static property checker callbacks
+    _enumerable: function(obj, prop) {
+        return obj.propertyIsEnumerable(prop);
+    },
+    _notEnumerable: function(obj, prop) {
+        return !obj.propertyIsEnumerable(prop);
+    },
+    _enumerableAndNotEnumerable: function(obj, prop) {
+        return true;
+    },
+    // Inspired by http://stackoverflow.com/a/8024294/271577
+    _getPropertyNames: function getAllPropertyNames(obj, iterateSelfBool, iteratePrototypeBool, includePropCb) {
+        var props = [];
+
+        do {
+            if (iterateSelfBool) {
+                Object.getOwnPropertyNames(obj).forEach(function(prop) {
+                    if (props.indexOf(prop) === -1 && includePropCb(obj, prop)) {
+                        props.push(prop);
+                    }
+                });
+            }
+            if (!iteratePrototypeBool) {
+                break;
+            }
+            iterateSelfBool = true;
+        } while (obj = Object.getPrototypeOf(obj));
+
+        return props;
+    }
+};
+ +

Detection Table

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
infor..inobj.hasOwnPropertyobj.propertyIsEnumerableObject.keysObject.getOwnPropertyNamesObject.getOwnPropertyDescriptorsReflect.ownKeys()
Enumerabletruetruetruetruetruetruetruetrue
Nonenumerabletruefalsetruefalsefalsetruetruetrue
Symbols keystruefalsetruetruefalsefalsetruetrue
Inherited Enumerabletruetruefalsefalsefalsefalsefalsefalse
Inherited Nonenumerabletruefalsefalsefalsefalsefalsefalsefalse
Inherited Symbols keystruefalsefalsefalsefalsefalsefalsefalse
+
+ +

See also

+ + diff --git a/files/ko/web/javascript/equality_comparisons_and_sameness/index.html b/files/ko/web/javascript/equality_comparisons_and_sameness/index.html deleted file mode 100644 index 31d1778d5d..0000000000 --- a/files/ko/web/javascript/equality_comparisons_and_sameness/index.html +++ /dev/null @@ -1,503 +0,0 @@ ---- -title: 동치 비교 및 동일성 -slug: Web/JavaScript/Equality_comparisons_and_sameness -tags: - - Comparison - - Equality - - Intermediate - - JavaScript - - SameValue - - SameValueZero - - Sameness -translation_of: Web/JavaScript/Equality_comparisons_and_sameness ---- -
{{jsSidebar("Intermediate")}}
- -
-

ES2015에는 4가지 같음(equality) 알고리즘이 있습니다:

- - - -

JavaScript는 3가지 서로 다른 값 비교 연산을 제공합니다:

- - - -

어느 연산을 쓸 지 그 선택은 당신이 어떤 종류의 비교를 수행하기 위해 찾고 있는 지에 달렸습니다.

- -

 

- - - -

 

-
- -

이들 사이의 구분은 모두 원시형(primitive) 처리와 관련이 있습니다. 매개 변수가 구조적, 개념적으로 유사한 지 비교하는 것이 없습니다. 같은 구조를 가지지만 개체 자체가 각각인 비원시형(non-primitive) 개체 x 및 y의 경우 위의 모든 형태(form)는 false로 평가됩니다.

- -

===를 사용하는 엄격한 같음

- -

엄격한 같음(strict equality)은 두 값이 같은 지 비교합니다. 어느 값도 비교되기 전에 어떤 다른 값으로 남몰래 변환되지 않습니다. 둘이 서로 다른 형이면, 둘은 같지 않다고 여깁니다. 그렇지 않고 둘이 같은 형이고 숫자가 아닌 경우, 같은 값이면 같다고 여깁니다. 끝으로, 둘이 숫자인 경우, 둘 다 NaN이 아닌 같은 값이거나 하나는 +0 또 하나는 -0인 경우 같다고 여깁니다.

- -
var num = 0;
-var obj = new String("0");
-var str = "0";
-var b = false;
-
-console.log(num === num); // true
-console.log(obj === obj); // true
-console.log(str === str); // true
-
-console.log(num === obj); // false
-console.log(num === str); // false
-console.log(obj === str); // false
-console.log(null === undefined); // false
-console.log(obj === null); // false
-console.log(obj === undefined); // false
-
- -

엄격한 같음은 거의 항상 사용하는 올바른 비교 연산입니다. 숫자를 뺀 모든 값에 대해, 분명한 의미(semantics)를 사용합니다: 값은 그 자체와만 같습니다(/ 단지 그 자체입니다). 숫자는 서로 다른 두 극단 상황(edge case)을 얼버무리기(gloss over) 위해 약간 다른 의미를 사용합니다. 첫째는 부동 소수점 0은 양이든 음이든 하나의 부호를 지닙니다. 이는 특정 수학상의 해결책을 나타내는 데 유용하지만, 대부분의 상황에 +0-0의 차이에 신경쓰지 않기에, 엄격한 같음은 둘을 같은 값으로 다룹니다. 둘째는 부동 소수점은 not-a-number 값(NaN) 개념을 포함합니다, 특정 잘못 정의된(ill-defined) 수학 문제의 해결책을 보여주기 위해: 예를 들어, 양의 무한대(infinity)에 추가된 음의 무한대. 엄격한 같음은 NaN을 다른 모든 값과 같지 않게 다룹니다 -- 자신 포함. ((x !== x)true인 유일한 경우는 xNaN일 때입니다.)

- -

==를 사용하는 느슨한 같음

- -

느슨한 같음(loose equality)은 두 값이 같은 지 비교합니다, 두 값을 공통(common) 형으로 변환한 에. 변환 후 (하나 또는 양쪽이 변환을 거칠 수 있음), 최종 같음 비교는 꼭 ===처럼 수행됩니다. 느슨한 같음은 대칭(symmetric)입니다: A == BAB가 어떤 값이든 항상 B == A와 같은 의미를 갖습니다 (적용된 변환의 순서 말고는).

- -

같음 비교는 다양한 형의 피연산자에 대해 다음과 같이 수행됩니다:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 피연산자 B
  UndefinedNullNumberStringBooleanObject
피연산자 AUndefinedtruetruefalsefalsefalsefalse
Nulltruetruefalsefalsefalsefalse
NumberfalsefalseA === BA === ToNumber(B)A === ToNumber(B)A == ToPrimitive(B)
StringfalsefalseToNumber(A) === BA === BToNumber(A) === ToNumber(B)A == ToPrimitive(B)
BooleanfalsefalseToNumber(A) === BToNumber(A) === ToNumber(B)A === BToNumber(A) == ToPrimitive(B)
ObjectfalsefalseToPrimitive(A) == BToPrimitive(A) == BToPrimitive(A) == ToNumber(B)A === B
- -

위 표에서, ToNumber(A)는 비교 전에 그 인수를 숫자로 변환하려고 시도합니다. 그 동작(behavior)은 +A(단항 + 연산자)에 해당합니다. ToPrimitive(A)는 그 객체 인수를 원시형 값으로 변환하려고 시도합니다, 다양한 순서로 AA.toStringA.valueOf 메서드 호출을 시도하여.

- -

전통 및 ECMAScript에 따르면, 모든 객체는 undefinednull과 느슨하게 같지 않습니다. 그러나 대부분의 브라우저는 일부 문맥(context)에서 undefined값을 모방하는(emulate) 것처럼 행동하기 위해 매우 좁은 부류의 객체(특히, 모든 페이지에 대한 document.all 객체)에 허용합니다. 느슨한 같음이 그러한 문맥 중 하나입니다: null == Aundefined == A는 A가 undefined모방하는 객체인 경우, 그리고 그 경우에만 true로 평가합니다. 다른 모든 경우에 객체는 결코 undefined 또는 null과 느슨하게 같지 않습니다.

- -

 

- -

전통적으로 ECMAScript에 따르면 모든 객체는 undefinednull과 느슨하게 같지 않습니다. 그러나 대부분의 브라우저는 일부 문맥(context)에서 정의되지 않은 값(undefined)을 모방하는(emulate) 것처럼 동작하는 매우 좁은 개체 클래스 (특히 모든 페이지의 document.all 개체)를 허용합니다. Loose equality는 다음과 같은 컨텍스트 중 하나입니다. null == Aundefined == Aundefined를 에뮬레이트하는 객체 인 경우에만 true로 평가됩니다. 다른 모든 경우에는 객체가 undefined거나 null이 될 수 없습니다.

- -
var num = 0;
-var obj = new String("0");
-var str = "0";
-var b = false;
-
-console.log(num == num); // true
-console.log(obj == obj); // true
-console.log(str == str); // true
-
-console.log(num == obj); // true
-console.log(num == str); // true
-console.log(obj == str); // true
-console.log(null == undefined); // true
-
-// 둘 다 false, 드문 경우를 제외하고는
-console.log(obj == null);
-console.log(obj == undefined);
-
- -

대부분의 경우 느슨한 같음을 사용하는 것은 바람직하지 않습니다. strict equality를 사용한 비교의 결과는 예측하기가 쉽고 형 강제(coercion) 변환이 일어나지 않기에 평가가 빠를 수 있습니다.

- -

 

- -

Same-value equality

- -

등가(same-value) 같음은 최종 사용 사례(use case)를 다룹니다: 두 값이 모든 문맥에서 기능상 같은지 여부를 결정하는. (이 사용 사례는 리스코프 치환 원칙의 실례를 보입니다.) 다음은 불변 속성(property)을 변화시키려 시도할 때 일어나는 한 사례입니다:

- -
// 불변(immutable) NEGATIVE_ZERO 속성을 Number 생성자에 추가.
-Object.defineProperty(Number, "NEGATIVE_ZERO",
-                      { value: -0, writable: false, configurable: false, enumerable: false });
-
-function attemptMutation(v)
-{
-  Object.defineProperty(Number, "NEGATIVE_ZERO", { value: v });
-}
-
- -

Object.defineProperty는 변경 불가능한 속성을 변경하려고 시도 할 때 예외를 throw하지만 실제 변경이 요청되지 않으면 아무 것도 수행하지 않습니다. v-0이면, 변경 사항이 요청되지 않고 오류가 발생하지 않습니다. 내부적으로, 불변의 property가 재정의 (redefined)되었을 때, 새롭게 지정된 값은 같은 값의 동등성을 사용해 현재의 값과 비교됩니다.

- -

Same-value equality는 {{jsxref("Object.is")}} 메서드로 제공됩니다.

- -

Same-value-zero equality

- -

등가 같음과 비슷하지만 +0과 -0이 같다고 여깁니다.

- -

스펙 내 추상적 같음, 엄격한 같음 및 등가

- -

In ES5, the comparison performed by == is described in Section 11.9.3, The Abstract Equality Algorithm. The === comparison is 11.9.6, The Strict Equality Algorithm. (Go look at these. They're brief and readable. Hint: read the strict equality algorithm first.) ES5 also describes, in Section 9.12, The SameValue Algorithm for use internally by the JS engine. It's largely the same as the Strict Equality Algorithm, except that 11.9.6.4 and 9.12.4 differ in handling {{jsxref("Number")}}s. ES2015 simply proposes to expose this algorithm through {{jsxref("Object.is")}}.

- -

We can see that with double and triple equals, with the exception of doing a type check upfront in 11.9.6.1, the Strict Equality Algorithm is a subset of the Abstract Equality Algorithm, because 11.9.6.2–7 correspond to 11.9.3.1.a–f.

- -

같음 비교를 이해하기 위한 모델은?

- -

ES2015 이전에, 이중 등호 및 삼중 등호에 대해 하나가 다른 하나의 "확장"판이라고 (말)했을 지 모릅니다. 예를 들어, 누군가는 이중 등호는 삼중 등호의 확장판이라고 합니다, 전자는 후자가 하는 모든 것을 하지만 그 피연산자에 형 변환을 하기에. 가령, 6 == "6". (대신에, 이중 등호는 기준선이고 삼중 등호는 향상판이라고 하는 이도 있습니다, 두 피연산자가 같은 형이길 요구하고 그래서 별도 제약을 추가하기에. 어느 게 더 이해하기 좋은 모델인지는 당신이 상태(things)를 보기 위해 선택한 방법에 달렸습니다.)

- -

However, this way of thinking about the built-in sameness operators is not a model that can be stretched to allow a place for ES2015's {{jsxref("Object.is")}} on this "spectrum". {{jsxref("Object.is")}} isn't simply "looser" than double equals or "stricter" than triple equals, nor does it fit somewhere in between (i.e., being both stricter than double equals, but looser than triple equals). We can see from the sameness comparisons table below that this is due to the way that {{jsxref("Object.is")}} handles {{jsxref("NaN")}}. Notice that if Object.is(NaN, NaN)evaluated to false, we could say that it fits on the loose/strict spectrum as an even stricter form of triple equals, one that distinguishes between -0 and +0. The {{jsxref("NaN")}} handling means this is untrue, however. Unfortunately, {{jsxref("Object.is")}} simply has to be thought of in terms of its specific characteristics, rather than its looseness or strictness with regard to the equality operators.

- -

 

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Sameness Comparisons
xy=====Object.isSameValueZero
undefinedundefinedtruetruetruetrue
nullnulltruetruetruetrue
truetruetruetruetruetrue
falsefalsetruetruetruetrue
'foo''foo'truetruetruetrue
00truetruetruetrue
+0-0truetruefalsetrue
+00truetruetruetrue
-00truetruefalsetrue
0falsetruefalsefalsefalse
""falsetruefalsefalsefalse
""0truefalsefalsefalse
'0'0truefalsefalsefalse
'17'17truefalsefalsefalse
[1, 2]'1,2'truefalsefalsefalse
new String('foo')'foo'truefalsefalsefalse
nullundefinedtruefalsefalsefalse
nullfalsefalsefalsefalsefalse
undefinedfalsefalsefalsefalsefalse
{ foo: 'bar' }{ foo: 'bar' }falsefalsefalsefalse
new String('foo')new String('foo')falsefalsefalsefalse
0nullfalsefalsefalsefalse
0NaNfalsefalsefalsefalse
'foo'NaNfalsefalsefalsefalse
NaNNaNfalsefalsetruetrue
- -

 

- -

{{jsxref("Object.is")}} 대신 삼중 등호를 사용하는 경우

- -

In general, the only time {{jsxref("Object.is")}}'s special behavior towards zeros is likely to be of interest is in the pursuit of certain meta-programming schemes, especially regarding property descriptors, when it is desirable for your work to mirror some of the characteristics of {{jsxref("Object.defineProperty")}}. If your use case does not require this, it is suggested to avoid {{jsxref("Object.is")}} and use === instead. Even if your requirements involve having comparisons between two {{jsxref("NaN")}} values evaluate to true, generally it is easier to special-case the {{jsxref("NaN")}} checks (using the {{jsxref("isNaN")}} method available from previous versions of ECMAScript) than it is to work out how surrounding computations might affect the sign of any zeros you encounter in your comparison.

- -

여기 당신 코드에서 그 자체를 드러내기 위해 -0+0 사이의 구별을 일으킬 수도 있는 철저하지 않은(in-exhaustive) 내장 메서드 및 연산자 목록이 있습니다:

- -
-
- (unary negation) 
-
- -
let stoppingForce = obj.mass * -obj.velocity
- -
-
-

obj.velocity0인 (또는 0으로 계산하는) 경우, -0이 그 자리에 소개되고 stoppingForce로 전해집니다. 

-
-
{{jsxref("Math.atan2")}}
-
{{jsxref("Math.ceil")}}
-
{{jsxref("Math.pow")}}
-
{{jsxref("Math.round")}} 
-
In some cases,it's possible for a -0 to be introduced into an expression as a return value of these methods even when no -0 exists as one of the parameters. For example, using {{jsxref("Math.pow")}} to raise {{jsxref("Infinity", "-Infinity")}} to the power of any negative, odd exponent evaluates to -0. Refer to the documentation for the individual methods. 
-
- -
-
{jsxref("Math.floor")}}
-
{{jsxref("Math.max")}}
-
{{jsxref("Math.min")}}
-
{{jsxref("Math.sin")}}
-
{{jsxref("Math.sqrt")}}
-
{{jsxref("Math.tan")}} 
-
It's possible to get a -0 return value out of these methods in some cases where a -0 exists as one of the parameters. E.g., Math.min(-0, +0) evaluates to -0. Refer to the documentation for the individual methods.
-
- -
-
~
-
<<
-
>>
-
이러한 연산자 각각은 내부에서 ToInt32 알고리즘을 사용합니다. 내부 32-bit 정수형에는 0에 대해 한 표현만 있기에, -0은 역(inverse) 연산 후 왕복 여행(round trip, 이중 역 연산)에 살아남지 못합니다. 가령, Object.is(~~(-0), -0)Object.is(-0 << 2 >> 2, -0)false로 평가합니다.
-
- -

Relying on {{jsxref("Object.is")}} when the signedness of zeros is not taken into account can be hazardous. Of course, when the intent is to distinguish between -0 and +0, it does exactly what's desired.

- -

Caveat: {{jsxref("Object.is")}} and NaN

- -

The {{jsxref("Object.is")}} specification treats all instances of {{jsxref("NaN")}} as the same object. However, since typed arrays are available, we can have distinct instances, which don't behave identically in all contexts. For example:

- -
var f2b = x => new Uint8Array(new Float64Array([x]).buffer);
-var b2f = x => new Float64Array(x.buffer)[0];
-var n = f2b(NaN);
-n[0] = 1;
-var nan2 = b2f(n);
-nan2;
-// > NaN
-Object.is(nan2, NaN);
-// > true
-f2b(NaN);
-// > Uint8Array(8) [0, 0, 0, 0, 0, 0, 248,127)
-f2b(nan2);
-// > Uint8Array(8) [1, 0, 0, 0, 0, 0, 248,127)
- -

참조

- - diff --git a/files/ko/web/javascript/equality_comparisons_and_sameness/index.md b/files/ko/web/javascript/equality_comparisons_and_sameness/index.md new file mode 100644 index 0000000000..31d1778d5d --- /dev/null +++ b/files/ko/web/javascript/equality_comparisons_and_sameness/index.md @@ -0,0 +1,503 @@ +--- +title: 동치 비교 및 동일성 +slug: Web/JavaScript/Equality_comparisons_and_sameness +tags: + - Comparison + - Equality + - Intermediate + - JavaScript + - SameValue + - SameValueZero + - Sameness +translation_of: Web/JavaScript/Equality_comparisons_and_sameness +--- +
{{jsSidebar("Intermediate")}}
+ +
+

ES2015에는 4가지 같음(equality) 알고리즘이 있습니다:

+ + + +

JavaScript는 3가지 서로 다른 값 비교 연산을 제공합니다:

+ + + +

어느 연산을 쓸 지 그 선택은 당신이 어떤 종류의 비교를 수행하기 위해 찾고 있는 지에 달렸습니다.

+ +

 

+ + + +

 

+
+ +

이들 사이의 구분은 모두 원시형(primitive) 처리와 관련이 있습니다. 매개 변수가 구조적, 개념적으로 유사한 지 비교하는 것이 없습니다. 같은 구조를 가지지만 개체 자체가 각각인 비원시형(non-primitive) 개체 x 및 y의 경우 위의 모든 형태(form)는 false로 평가됩니다.

+ +

===를 사용하는 엄격한 같음

+ +

엄격한 같음(strict equality)은 두 값이 같은 지 비교합니다. 어느 값도 비교되기 전에 어떤 다른 값으로 남몰래 변환되지 않습니다. 둘이 서로 다른 형이면, 둘은 같지 않다고 여깁니다. 그렇지 않고 둘이 같은 형이고 숫자가 아닌 경우, 같은 값이면 같다고 여깁니다. 끝으로, 둘이 숫자인 경우, 둘 다 NaN이 아닌 같은 값이거나 하나는 +0 또 하나는 -0인 경우 같다고 여깁니다.

+ +
var num = 0;
+var obj = new String("0");
+var str = "0";
+var b = false;
+
+console.log(num === num); // true
+console.log(obj === obj); // true
+console.log(str === str); // true
+
+console.log(num === obj); // false
+console.log(num === str); // false
+console.log(obj === str); // false
+console.log(null === undefined); // false
+console.log(obj === null); // false
+console.log(obj === undefined); // false
+
+ +

엄격한 같음은 거의 항상 사용하는 올바른 비교 연산입니다. 숫자를 뺀 모든 값에 대해, 분명한 의미(semantics)를 사용합니다: 값은 그 자체와만 같습니다(/ 단지 그 자체입니다). 숫자는 서로 다른 두 극단 상황(edge case)을 얼버무리기(gloss over) 위해 약간 다른 의미를 사용합니다. 첫째는 부동 소수점 0은 양이든 음이든 하나의 부호를 지닙니다. 이는 특정 수학상의 해결책을 나타내는 데 유용하지만, 대부분의 상황에 +0-0의 차이에 신경쓰지 않기에, 엄격한 같음은 둘을 같은 값으로 다룹니다. 둘째는 부동 소수점은 not-a-number 값(NaN) 개념을 포함합니다, 특정 잘못 정의된(ill-defined) 수학 문제의 해결책을 보여주기 위해: 예를 들어, 양의 무한대(infinity)에 추가된 음의 무한대. 엄격한 같음은 NaN을 다른 모든 값과 같지 않게 다룹니다 -- 자신 포함. ((x !== x)true인 유일한 경우는 xNaN일 때입니다.)

+ +

==를 사용하는 느슨한 같음

+ +

느슨한 같음(loose equality)은 두 값이 같은 지 비교합니다, 두 값을 공통(common) 형으로 변환한 에. 변환 후 (하나 또는 양쪽이 변환을 거칠 수 있음), 최종 같음 비교는 꼭 ===처럼 수행됩니다. 느슨한 같음은 대칭(symmetric)입니다: A == BAB가 어떤 값이든 항상 B == A와 같은 의미를 갖습니다 (적용된 변환의 순서 말고는).

+ +

같음 비교는 다양한 형의 피연산자에 대해 다음과 같이 수행됩니다:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 피연산자 B
  UndefinedNullNumberStringBooleanObject
피연산자 AUndefinedtruetruefalsefalsefalsefalse
Nulltruetruefalsefalsefalsefalse
NumberfalsefalseA === BA === ToNumber(B)A === ToNumber(B)A == ToPrimitive(B)
StringfalsefalseToNumber(A) === BA === BToNumber(A) === ToNumber(B)A == ToPrimitive(B)
BooleanfalsefalseToNumber(A) === BToNumber(A) === ToNumber(B)A === BToNumber(A) == ToPrimitive(B)
ObjectfalsefalseToPrimitive(A) == BToPrimitive(A) == BToPrimitive(A) == ToNumber(B)A === B
+ +

위 표에서, ToNumber(A)는 비교 전에 그 인수를 숫자로 변환하려고 시도합니다. 그 동작(behavior)은 +A(단항 + 연산자)에 해당합니다. ToPrimitive(A)는 그 객체 인수를 원시형 값으로 변환하려고 시도합니다, 다양한 순서로 AA.toStringA.valueOf 메서드 호출을 시도하여.

+ +

전통 및 ECMAScript에 따르면, 모든 객체는 undefinednull과 느슨하게 같지 않습니다. 그러나 대부분의 브라우저는 일부 문맥(context)에서 undefined값을 모방하는(emulate) 것처럼 행동하기 위해 매우 좁은 부류의 객체(특히, 모든 페이지에 대한 document.all 객체)에 허용합니다. 느슨한 같음이 그러한 문맥 중 하나입니다: null == Aundefined == A는 A가 undefined모방하는 객체인 경우, 그리고 그 경우에만 true로 평가합니다. 다른 모든 경우에 객체는 결코 undefined 또는 null과 느슨하게 같지 않습니다.

+ +

 

+ +

전통적으로 ECMAScript에 따르면 모든 객체는 undefinednull과 느슨하게 같지 않습니다. 그러나 대부분의 브라우저는 일부 문맥(context)에서 정의되지 않은 값(undefined)을 모방하는(emulate) 것처럼 동작하는 매우 좁은 개체 클래스 (특히 모든 페이지의 document.all 개체)를 허용합니다. Loose equality는 다음과 같은 컨텍스트 중 하나입니다. null == Aundefined == Aundefined를 에뮬레이트하는 객체 인 경우에만 true로 평가됩니다. 다른 모든 경우에는 객체가 undefined거나 null이 될 수 없습니다.

+ +
var num = 0;
+var obj = new String("0");
+var str = "0";
+var b = false;
+
+console.log(num == num); // true
+console.log(obj == obj); // true
+console.log(str == str); // true
+
+console.log(num == obj); // true
+console.log(num == str); // true
+console.log(obj == str); // true
+console.log(null == undefined); // true
+
+// 둘 다 false, 드문 경우를 제외하고는
+console.log(obj == null);
+console.log(obj == undefined);
+
+ +

대부분의 경우 느슨한 같음을 사용하는 것은 바람직하지 않습니다. strict equality를 사용한 비교의 결과는 예측하기가 쉽고 형 강제(coercion) 변환이 일어나지 않기에 평가가 빠를 수 있습니다.

+ +

 

+ +

Same-value equality

+ +

등가(same-value) 같음은 최종 사용 사례(use case)를 다룹니다: 두 값이 모든 문맥에서 기능상 같은지 여부를 결정하는. (이 사용 사례는 리스코프 치환 원칙의 실례를 보입니다.) 다음은 불변 속성(property)을 변화시키려 시도할 때 일어나는 한 사례입니다:

+ +
// 불변(immutable) NEGATIVE_ZERO 속성을 Number 생성자에 추가.
+Object.defineProperty(Number, "NEGATIVE_ZERO",
+                      { value: -0, writable: false, configurable: false, enumerable: false });
+
+function attemptMutation(v)
+{
+  Object.defineProperty(Number, "NEGATIVE_ZERO", { value: v });
+}
+
+ +

Object.defineProperty는 변경 불가능한 속성을 변경하려고 시도 할 때 예외를 throw하지만 실제 변경이 요청되지 않으면 아무 것도 수행하지 않습니다. v-0이면, 변경 사항이 요청되지 않고 오류가 발생하지 않습니다. 내부적으로, 불변의 property가 재정의 (redefined)되었을 때, 새롭게 지정된 값은 같은 값의 동등성을 사용해 현재의 값과 비교됩니다.

+ +

Same-value equality는 {{jsxref("Object.is")}} 메서드로 제공됩니다.

+ +

Same-value-zero equality

+ +

등가 같음과 비슷하지만 +0과 -0이 같다고 여깁니다.

+ +

스펙 내 추상적 같음, 엄격한 같음 및 등가

+ +

In ES5, the comparison performed by == is described in Section 11.9.3, The Abstract Equality Algorithm. The === comparison is 11.9.6, The Strict Equality Algorithm. (Go look at these. They're brief and readable. Hint: read the strict equality algorithm first.) ES5 also describes, in Section 9.12, The SameValue Algorithm for use internally by the JS engine. It's largely the same as the Strict Equality Algorithm, except that 11.9.6.4 and 9.12.4 differ in handling {{jsxref("Number")}}s. ES2015 simply proposes to expose this algorithm through {{jsxref("Object.is")}}.

+ +

We can see that with double and triple equals, with the exception of doing a type check upfront in 11.9.6.1, the Strict Equality Algorithm is a subset of the Abstract Equality Algorithm, because 11.9.6.2–7 correspond to 11.9.3.1.a–f.

+ +

같음 비교를 이해하기 위한 모델은?

+ +

ES2015 이전에, 이중 등호 및 삼중 등호에 대해 하나가 다른 하나의 "확장"판이라고 (말)했을 지 모릅니다. 예를 들어, 누군가는 이중 등호는 삼중 등호의 확장판이라고 합니다, 전자는 후자가 하는 모든 것을 하지만 그 피연산자에 형 변환을 하기에. 가령, 6 == "6". (대신에, 이중 등호는 기준선이고 삼중 등호는 향상판이라고 하는 이도 있습니다, 두 피연산자가 같은 형이길 요구하고 그래서 별도 제약을 추가하기에. 어느 게 더 이해하기 좋은 모델인지는 당신이 상태(things)를 보기 위해 선택한 방법에 달렸습니다.)

+ +

However, this way of thinking about the built-in sameness operators is not a model that can be stretched to allow a place for ES2015's {{jsxref("Object.is")}} on this "spectrum". {{jsxref("Object.is")}} isn't simply "looser" than double equals or "stricter" than triple equals, nor does it fit somewhere in between (i.e., being both stricter than double equals, but looser than triple equals). We can see from the sameness comparisons table below that this is due to the way that {{jsxref("Object.is")}} handles {{jsxref("NaN")}}. Notice that if Object.is(NaN, NaN)evaluated to false, we could say that it fits on the loose/strict spectrum as an even stricter form of triple equals, one that distinguishes between -0 and +0. The {{jsxref("NaN")}} handling means this is untrue, however. Unfortunately, {{jsxref("Object.is")}} simply has to be thought of in terms of its specific characteristics, rather than its looseness or strictness with regard to the equality operators.

+ +

 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sameness Comparisons
xy=====Object.isSameValueZero
undefinedundefinedtruetruetruetrue
nullnulltruetruetruetrue
truetruetruetruetruetrue
falsefalsetruetruetruetrue
'foo''foo'truetruetruetrue
00truetruetruetrue
+0-0truetruefalsetrue
+00truetruetruetrue
-00truetruefalsetrue
0falsetruefalsefalsefalse
""falsetruefalsefalsefalse
""0truefalsefalsefalse
'0'0truefalsefalsefalse
'17'17truefalsefalsefalse
[1, 2]'1,2'truefalsefalsefalse
new String('foo')'foo'truefalsefalsefalse
nullundefinedtruefalsefalsefalse
nullfalsefalsefalsefalsefalse
undefinedfalsefalsefalsefalsefalse
{ foo: 'bar' }{ foo: 'bar' }falsefalsefalsefalse
new String('foo')new String('foo')falsefalsefalsefalse
0nullfalsefalsefalsefalse
0NaNfalsefalsefalsefalse
'foo'NaNfalsefalsefalsefalse
NaNNaNfalsefalsetruetrue
+ +

 

+ +

{{jsxref("Object.is")}} 대신 삼중 등호를 사용하는 경우

+ +

In general, the only time {{jsxref("Object.is")}}'s special behavior towards zeros is likely to be of interest is in the pursuit of certain meta-programming schemes, especially regarding property descriptors, when it is desirable for your work to mirror some of the characteristics of {{jsxref("Object.defineProperty")}}. If your use case does not require this, it is suggested to avoid {{jsxref("Object.is")}} and use === instead. Even if your requirements involve having comparisons between two {{jsxref("NaN")}} values evaluate to true, generally it is easier to special-case the {{jsxref("NaN")}} checks (using the {{jsxref("isNaN")}} method available from previous versions of ECMAScript) than it is to work out how surrounding computations might affect the sign of any zeros you encounter in your comparison.

+ +

여기 당신 코드에서 그 자체를 드러내기 위해 -0+0 사이의 구별을 일으킬 수도 있는 철저하지 않은(in-exhaustive) 내장 메서드 및 연산자 목록이 있습니다:

+ +
+
- (unary negation) 
+
+ +
let stoppingForce = obj.mass * -obj.velocity
+ +
+
+

obj.velocity0인 (또는 0으로 계산하는) 경우, -0이 그 자리에 소개되고 stoppingForce로 전해집니다. 

+
+
{{jsxref("Math.atan2")}}
+
{{jsxref("Math.ceil")}}
+
{{jsxref("Math.pow")}}
+
{{jsxref("Math.round")}} 
+
In some cases,it's possible for a -0 to be introduced into an expression as a return value of these methods even when no -0 exists as one of the parameters. For example, using {{jsxref("Math.pow")}} to raise {{jsxref("Infinity", "-Infinity")}} to the power of any negative, odd exponent evaluates to -0. Refer to the documentation for the individual methods. 
+
+ +
+
{jsxref("Math.floor")}}
+
{{jsxref("Math.max")}}
+
{{jsxref("Math.min")}}
+
{{jsxref("Math.sin")}}
+
{{jsxref("Math.sqrt")}}
+
{{jsxref("Math.tan")}} 
+
It's possible to get a -0 return value out of these methods in some cases where a -0 exists as one of the parameters. E.g., Math.min(-0, +0) evaluates to -0. Refer to the documentation for the individual methods.
+
+ +
+
~
+
<<
+
>>
+
이러한 연산자 각각은 내부에서 ToInt32 알고리즘을 사용합니다. 내부 32-bit 정수형에는 0에 대해 한 표현만 있기에, -0은 역(inverse) 연산 후 왕복 여행(round trip, 이중 역 연산)에 살아남지 못합니다. 가령, Object.is(~~(-0), -0)Object.is(-0 << 2 >> 2, -0)false로 평가합니다.
+
+ +

Relying on {{jsxref("Object.is")}} when the signedness of zeros is not taken into account can be hazardous. Of course, when the intent is to distinguish between -0 and +0, it does exactly what's desired.

+ +

Caveat: {{jsxref("Object.is")}} and NaN

+ +

The {{jsxref("Object.is")}} specification treats all instances of {{jsxref("NaN")}} as the same object. However, since typed arrays are available, we can have distinct instances, which don't behave identically in all contexts. For example:

+ +
var f2b = x => new Uint8Array(new Float64Array([x]).buffer);
+var b2f = x => new Float64Array(x.buffer)[0];
+var n = f2b(NaN);
+n[0] = 1;
+var nan2 = b2f(n);
+nan2;
+// > NaN
+Object.is(nan2, NaN);
+// > true
+f2b(NaN);
+// > Uint8Array(8) [0, 0, 0, 0, 0, 0, 248,127)
+f2b(nan2);
+// > Uint8Array(8) [1, 0, 0, 0, 0, 0, 248,127)
+ +

참조

+ + diff --git a/files/ko/web/javascript/eventloop/index.html b/files/ko/web/javascript/eventloop/index.html deleted file mode 100644 index c7911fdbfb..0000000000 --- a/files/ko/web/javascript/eventloop/index.html +++ /dev/null @@ -1,147 +0,0 @@ ---- -title: 동시성 모델과 이벤트 루프 -slug: Web/JavaScript/EventLoop -tags: - - 이벤트 루프 -translation_of: Web/JavaScript/EventLoop ---- -
{{JsSidebar("Advanced")}}
- -

자바스크립트는 코드 실행, 이벤트 수집과 처리, 큐에 놓인 하위 작업들을 담당하는 이벤트 루프에 기반한 동시성(concurrency) 모델을 가지고 있습니다. 이 모델은 C 또는 Java와 같은 언어와 완전히 다릅니다.

- -

런타임 개념

- -

이어지는 섹션에서는 이론적 모델을 설명합니다. 모던 자바스크립트 엔진들은 아래 묘사된 개념들을 구현하고 최적화 합니다.

- -

시각적 표현

- -

Stack, heap, queue

- -

Stack

- -

함수 호출은 프레임들의 스택을 형성합니다.

- -
function foo(b) {
-  var a = 10;
-  return a + b + 11;
-}
-
-function bar(x) {
-  var y = 3;
-  return foo(x * y);
-}
-
-console.log(bar(7)); //returns 42
- -

bar를 호출할 때, bar의 인자와 지역 변수를 포함하는 첫 번째 프레임이 생성됩니다. barfoo를 호출하면 두 번째 프레임이 만들어져 foo의 인수와 지역 변수가 들어있는 첫 번째 프레임의 맨 위에 푸시됩니다. foo가 반환되면, 최상위 프레임 요소는 bar의 호출 프레임만을 남겨둔 채로 스택 밖으로 빠져나옵니다. bar가 반환되면, 스택은 비워집니다.

- -

Heap

- -

객체들은 힙 안에 할당됩니다. 힙은 구조화되지 않은 넓은 메모리 영역을 지칭합니다.

- -

Queue

- -

JavaScript 런타임은 처리 할 메시지 목록 인 메시지 대기열을 사용합니다. 각 메시지에는 메시지를 처리하기 위해 호출되는 관련 함수가 있습니다.

- -

{{anch("Event loop", "event loop")}} 중 어떤 시점에서 런타임은 대기열에서 가장 오래된 메시지부터 처리하기 시작합니다. 그렇게하기 위해, 메시지는 큐에서 제거되고 해당 기능이 메시지를 입력 매개 변수로 호출됩니다. 언제나 그렇듯이, 함수를 호출하면 그 함수의 사용을위한 새로운 스택 프레임이 생성됩니다.

- -

함수의 처리는 스택이 다시 비워 질 때까지 계속됩니다. 이벤트 루프는 큐의 다음 메시지를 처리합니다(존재할 경우).

- -

Event loop

- -

Event loop는 그 구현 방식 때문에 붙은 이름이며 보통 다음과 유사합니다 :

- -
while(queue.waitForMessage()){
-  queue.processNextMessage();
-}
- -

queue.waitForMessage() 함수는 현재 아무 메시지도 없다면 새로운 메시지 도착을 동기적으로 기다립니다.

- -

"Run-to-completion"

- -

각 메시지는 다른 메시지가 처리되기 전에 완전히 처리됩니다.

- -

이것은 함수가 실행될 때마다 미리 비워질 수 없고 다른 코드가 실행되기 전에 완전히 실행되며 함수가 조작하는 데이터를 수정할 수 있다는 사실을 포함하여 프로그램에 대한 추론을 할 때 좋은 속성을 제공합니다. 이것은 C와는 다릅니다. 예를 들어 함수가 쓰레드에서 실행된다면 런타임 시스템이 다른 쓰레드에서 다른 코드를 실행하기 위해 어느 시점에서 멈출 수 있습니다.

- -

이 모델의 부정적인 면은 어떤 메시지가 완료되기 까지 지나치게 오래 걸린다면 웹 어플리케이션은 클릭이나 스크롤과 같은 사용자 인터랙션을 처리할 수 없게 됩니다. 브라우저는 이러한 상황을 "a script is taking too long to run"과 같은 대화상자로 완화 합니다. 추천되는 좋은 방법은 메시지 처리를 짧도록 만드는 것과 하나의 메시지를 여러개의 메시지로 나누는 것 입니다.

- -

메시지 추가하기

- -

웹 브라우저에서 이벤트 리스너가 부착된 이벤트가 발생할 때마다 메시지가 추가됩니다. 리스너가 없으면 이벤트는 손실됩니다. 클릭 이벤트 핸들러가 있는 요소를 클릭하면 다른 이벤트와 마찬가지로 메시지가 추가됩니다.

- -

setTimeout 함수는 2 개의 인수, 대기열에 추가 할 메시지와 시간값(선택사항, 기본값은 0)으로 호출됩니다. 시간 값은 메시지가 실제로 큐에 푸시 될 때까지의 (최소) 지연을 나타냅니다. 대기열에 다른 메시지가 없으면 지연 직후에 메시지가 처리됩니다. 그러나 메시지가있는 경우 setTimeout 메시지는 다른 메시지가 처리 될 때까지 기다려야합니다. 따라서 두 번째 인수는 최소 시간을 나타내지 만 보장 된 시간은 아닙니다.

- -

다음은 이 개념에 대한 예제입니다 (setTimeout은 타이머 만료 직후에 실행되지 않습니다).

- -
const s = new Date().getSeconds();
-
-setTimeout(function() {
-  // prints out "2", meaning that the callback is not called immediately after 500 milliseconds.
-  console.log("Ran after " + (new Date().getSeconds() - s) + " seconds");
-}, 500);
-
-while(true) {
-  if(new Date().getSeconds() - s >= 2) {
-    console.log("Good, looped for 2 seconds");
-    break;
-  }
-}
- -

Zero delays

- -

Zero delay는 실제로 0ms 후에 콜백이 시작된다는 의미는 아닙니다. 0ms 지연된 {{domxref("WindowTimers.setTimeout", "setTimeout")}}은 주어진 간격 후에 콜백 함수를 실행하지 않습니다.

- -

실행은 큐에 대기중인 작업 수에 따라 다릅니다. 아래 예에서, 콜백의 메시지가 처리되기 전에 콘솔에 "this is just message"메시지가 기록됩니다. 왜냐하면 지연(delay)은 보장된 시간이 아니라 요청을 처리하기 위해 필요한 최소의 시간이기 때문입니다.

- -

기본적으로 setTimeout은 setTimeout에 대한 특정 시간 제한을 지정 했더라도 대기중인 메시지의 모든 코드가 완료 될 때까지 대기해야합니다.

- -
(function() {
-
-  console.log('this is the start');
-
-  setTimeout(function cb() {
-    console.log('this is a msg from call back');
-  });
-
-  console.log('this is just a message');
-
-  setTimeout(function cb1() {
-    console.log('this is a msg from call back1');
-  }, 0);
-
-  console.log('this is the end');
-
-})();
-
-// "this is the start"
-// "this is just a message"
-// "this is the end"
-// note that function return, which is undefined, happens here
-// "this is a msg from call back"
-// "this is a msg from call back1"
- -

몇가지 런타임 통신

- -

웹워커 또는 크로스 오리진 iframe은 자신의 스택, 힙, 메시지 큐를 가지고 있습니다. 두 별개의 런타임들은 postMessage method를 통해서만 서로 통신할 수 있습니다. 이 메서드는 다른 런타임이 message 이벤트 핸들러를 등록하고 있다면 해당 런타임의 큐에 메시지를 추가합니다.

- -

Never blocking

- -

이벤트 루프 모델의 무척 재밌는 부분은 다른 언어와 달리 자바스크립트는 결코 Block하지 않는다는 것입니다. I/O 처리는 흔히 이벤트와 콜백으로 처리 됩니다. 그래서 응용프로그램이 IndexedDB query 반환을 기다리고 있거나 XHR 요청 반환을 기다릴 때에도 여전히 사용자 입력과 같은 다른 것을을 처리할 수 있습니다.

- -

alert 또는 synchronous XHR과 같은 구현 예외가 존재 합니다. 그러나 그것들은 사용되지 않는 것이 좋다라고 여겨집니다. 예외를 위한 예외를 조심하세요 (그러나 보통 구현 버그일뿐 그이상 아무것도 아닙니다).

- -

명세

- - - - - - - - - - - - - -
명세
{{SpecName('HTML WHATWG', 'webappapis.html#event-loops', 'Event loops')}}
Node.js Event Loop
diff --git a/files/ko/web/javascript/eventloop/index.md b/files/ko/web/javascript/eventloop/index.md new file mode 100644 index 0000000000..c7911fdbfb --- /dev/null +++ b/files/ko/web/javascript/eventloop/index.md @@ -0,0 +1,147 @@ +--- +title: 동시성 모델과 이벤트 루프 +slug: Web/JavaScript/EventLoop +tags: + - 이벤트 루프 +translation_of: Web/JavaScript/EventLoop +--- +
{{JsSidebar("Advanced")}}
+ +

자바스크립트는 코드 실행, 이벤트 수집과 처리, 큐에 놓인 하위 작업들을 담당하는 이벤트 루프에 기반한 동시성(concurrency) 모델을 가지고 있습니다. 이 모델은 C 또는 Java와 같은 언어와 완전히 다릅니다.

+ +

런타임 개념

+ +

이어지는 섹션에서는 이론적 모델을 설명합니다. 모던 자바스크립트 엔진들은 아래 묘사된 개념들을 구현하고 최적화 합니다.

+ +

시각적 표현

+ +

Stack, heap, queue

+ +

Stack

+ +

함수 호출은 프레임들의 스택을 형성합니다.

+ +
function foo(b) {
+  var a = 10;
+  return a + b + 11;
+}
+
+function bar(x) {
+  var y = 3;
+  return foo(x * y);
+}
+
+console.log(bar(7)); //returns 42
+ +

bar를 호출할 때, bar의 인자와 지역 변수를 포함하는 첫 번째 프레임이 생성됩니다. barfoo를 호출하면 두 번째 프레임이 만들어져 foo의 인수와 지역 변수가 들어있는 첫 번째 프레임의 맨 위에 푸시됩니다. foo가 반환되면, 최상위 프레임 요소는 bar의 호출 프레임만을 남겨둔 채로 스택 밖으로 빠져나옵니다. bar가 반환되면, 스택은 비워집니다.

+ +

Heap

+ +

객체들은 힙 안에 할당됩니다. 힙은 구조화되지 않은 넓은 메모리 영역을 지칭합니다.

+ +

Queue

+ +

JavaScript 런타임은 처리 할 메시지 목록 인 메시지 대기열을 사용합니다. 각 메시지에는 메시지를 처리하기 위해 호출되는 관련 함수가 있습니다.

+ +

{{anch("Event loop", "event loop")}} 중 어떤 시점에서 런타임은 대기열에서 가장 오래된 메시지부터 처리하기 시작합니다. 그렇게하기 위해, 메시지는 큐에서 제거되고 해당 기능이 메시지를 입력 매개 변수로 호출됩니다. 언제나 그렇듯이, 함수를 호출하면 그 함수의 사용을위한 새로운 스택 프레임이 생성됩니다.

+ +

함수의 처리는 스택이 다시 비워 질 때까지 계속됩니다. 이벤트 루프는 큐의 다음 메시지를 처리합니다(존재할 경우).

+ +

Event loop

+ +

Event loop는 그 구현 방식 때문에 붙은 이름이며 보통 다음과 유사합니다 :

+ +
while(queue.waitForMessage()){
+  queue.processNextMessage();
+}
+ +

queue.waitForMessage() 함수는 현재 아무 메시지도 없다면 새로운 메시지 도착을 동기적으로 기다립니다.

+ +

"Run-to-completion"

+ +

각 메시지는 다른 메시지가 처리되기 전에 완전히 처리됩니다.

+ +

이것은 함수가 실행될 때마다 미리 비워질 수 없고 다른 코드가 실행되기 전에 완전히 실행되며 함수가 조작하는 데이터를 수정할 수 있다는 사실을 포함하여 프로그램에 대한 추론을 할 때 좋은 속성을 제공합니다. 이것은 C와는 다릅니다. 예를 들어 함수가 쓰레드에서 실행된다면 런타임 시스템이 다른 쓰레드에서 다른 코드를 실행하기 위해 어느 시점에서 멈출 수 있습니다.

+ +

이 모델의 부정적인 면은 어떤 메시지가 완료되기 까지 지나치게 오래 걸린다면 웹 어플리케이션은 클릭이나 스크롤과 같은 사용자 인터랙션을 처리할 수 없게 됩니다. 브라우저는 이러한 상황을 "a script is taking too long to run"과 같은 대화상자로 완화 합니다. 추천되는 좋은 방법은 메시지 처리를 짧도록 만드는 것과 하나의 메시지를 여러개의 메시지로 나누는 것 입니다.

+ +

메시지 추가하기

+ +

웹 브라우저에서 이벤트 리스너가 부착된 이벤트가 발생할 때마다 메시지가 추가됩니다. 리스너가 없으면 이벤트는 손실됩니다. 클릭 이벤트 핸들러가 있는 요소를 클릭하면 다른 이벤트와 마찬가지로 메시지가 추가됩니다.

+ +

setTimeout 함수는 2 개의 인수, 대기열에 추가 할 메시지와 시간값(선택사항, 기본값은 0)으로 호출됩니다. 시간 값은 메시지가 실제로 큐에 푸시 될 때까지의 (최소) 지연을 나타냅니다. 대기열에 다른 메시지가 없으면 지연 직후에 메시지가 처리됩니다. 그러나 메시지가있는 경우 setTimeout 메시지는 다른 메시지가 처리 될 때까지 기다려야합니다. 따라서 두 번째 인수는 최소 시간을 나타내지 만 보장 된 시간은 아닙니다.

+ +

다음은 이 개념에 대한 예제입니다 (setTimeout은 타이머 만료 직후에 실행되지 않습니다).

+ +
const s = new Date().getSeconds();
+
+setTimeout(function() {
+  // prints out "2", meaning that the callback is not called immediately after 500 milliseconds.
+  console.log("Ran after " + (new Date().getSeconds() - s) + " seconds");
+}, 500);
+
+while(true) {
+  if(new Date().getSeconds() - s >= 2) {
+    console.log("Good, looped for 2 seconds");
+    break;
+  }
+}
+ +

Zero delays

+ +

Zero delay는 실제로 0ms 후에 콜백이 시작된다는 의미는 아닙니다. 0ms 지연된 {{domxref("WindowTimers.setTimeout", "setTimeout")}}은 주어진 간격 후에 콜백 함수를 실행하지 않습니다.

+ +

실행은 큐에 대기중인 작업 수에 따라 다릅니다. 아래 예에서, 콜백의 메시지가 처리되기 전에 콘솔에 "this is just message"메시지가 기록됩니다. 왜냐하면 지연(delay)은 보장된 시간이 아니라 요청을 처리하기 위해 필요한 최소의 시간이기 때문입니다.

+ +

기본적으로 setTimeout은 setTimeout에 대한 특정 시간 제한을 지정 했더라도 대기중인 메시지의 모든 코드가 완료 될 때까지 대기해야합니다.

+ +
(function() {
+
+  console.log('this is the start');
+
+  setTimeout(function cb() {
+    console.log('this is a msg from call back');
+  });
+
+  console.log('this is just a message');
+
+  setTimeout(function cb1() {
+    console.log('this is a msg from call back1');
+  }, 0);
+
+  console.log('this is the end');
+
+})();
+
+// "this is the start"
+// "this is just a message"
+// "this is the end"
+// note that function return, which is undefined, happens here
+// "this is a msg from call back"
+// "this is a msg from call back1"
+ +

몇가지 런타임 통신

+ +

웹워커 또는 크로스 오리진 iframe은 자신의 스택, 힙, 메시지 큐를 가지고 있습니다. 두 별개의 런타임들은 postMessage method를 통해서만 서로 통신할 수 있습니다. 이 메서드는 다른 런타임이 message 이벤트 핸들러를 등록하고 있다면 해당 런타임의 큐에 메시지를 추가합니다.

+ +

Never blocking

+ +

이벤트 루프 모델의 무척 재밌는 부분은 다른 언어와 달리 자바스크립트는 결코 Block하지 않는다는 것입니다. I/O 처리는 흔히 이벤트와 콜백으로 처리 됩니다. 그래서 응용프로그램이 IndexedDB query 반환을 기다리고 있거나 XHR 요청 반환을 기다릴 때에도 여전히 사용자 입력과 같은 다른 것을을 처리할 수 있습니다.

+ +

alert 또는 synchronous XHR과 같은 구현 예외가 존재 합니다. 그러나 그것들은 사용되지 않는 것이 좋다라고 여겨집니다. 예외를 위한 예외를 조심하세요 (그러나 보통 구현 버그일뿐 그이상 아무것도 아닙니다).

+ +

명세

+ + + + + + + + + + + + + +
명세
{{SpecName('HTML WHATWG', 'webappapis.html#event-loops', 'Event loops')}}
Node.js Event Loop
-- cgit v1.2.3-54-g00ecf