자바스크립트

준비

노드 설치

노드(node.js)는 자바스크립트 프로그램을 실행시킬 수 있는 프레임워크입니다.

노드(node.js) 사이트에서 다운받아 설치합니다.

git 설치

깃(git)은 파일 및 프로젝트 버전을 관리할 수 있는 프로그램입니다.

깃 사이트에서 다운받아 설치합니다.

자바스크립트 에디터 설치

자바스크립트 에디터는 어떠한 텍스트 에디터를 사용해도 상관없습니다. 그중에서 편리하게 자바스크립트 코드를 작성하고 실행할 수 있는 프로그램인 비주얼 스튜디오 코드(vscode)가 있습니다.

Visual Studio Code 설치

이곳에서 다운받아 설치합니다.

SVN 플러그인 설치(선택 항목)

이 부분은 서브 버전을 이용해서 버전관리를 원하는 분들에게만 해당됩니다. 필요없는 사람은 무시해도 됩니다.

서브버전을 이용해 버전관리를 하고 있으면 SVN by Chris Johnston 플러그인을 설치합니다.

C:\Program Files\Microsoft VS Code\resources\app\product.json 파일 안에 다음과 같은 내용을 넣습니다.

"extensionAllowedProposedApi": [
  "ms-vsliveshare.vsliveshare", "johnstoncode.svn-scm",
  "ms-vscode.node-debug",
  "ms-vscode.node-debug2"
],

그리고 다시 vscode를 시작합니다.

자바스크립트 실행

자바스크립트 프로그래밍은 다음과 같은 방법을 이용해 실행할 수 있습니다. 웹브라우저(크롬, 파이어폭스, 엣지 등)를 이용해 실행하는 방법과 노드를 이용해서 실행하는 방법이 있습니다.

웹브라우저에서 실행

자바스크립트는 태생이 웹브라우저를 위해서 만들어졌기 때문에 웹브라우저에서 사용하는 HTML과 잘 어울리도록 설계되었습니다. 따라서 HTML 안에 자바스크립트 코드를 작성해서 실행할 수 있습니다.

HTML 파일 안의 자바스크립트

다음은 HTML body 안에 자바스크립트 코드를 중간에 삽입하여 웹페이지 출력을 제어하는 코드입니다. 다음 내용을 code_in_body.html로 저장하고 웹브라우저에서 열면 h1 태그 부분이 대체됩니다.스크립트에서 출력한 겁니다.로 대체됩니다.

<!DOCTYPE HTML>
<html>
  <head>
    <title>HTML에서 스크립트 실행</title>
  </head>
  <body>
    <h1>이 부분이 대체됩니다.</h1>
    <script>
      document.getElementsByTagName('h1')[0].innerHTML = "<p>스크립트에서 출력한 겁니다.</p>"
    </script>
  </body>
</html>
자바스크립트 파일

HTML 파일 안에서 자바스크립트 파일을 불러와 실행시킬 수 있습니다.

app.js 파일에 다음과 같은 내용을 입력하고 저장합니다.

let msg = "안녕하세요.";
console.log(msg);

app.html 파일을 만들어 다음과 같은 내용을 입력하고 저장합니다. 여기서 app.htmlapp.js 파일은 같은 폴더에 있어야 합니다.

<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="UTF-8">
    <title>HTML에서 스크립트 실행</title>
    <script src="app.js"></script>
  </head>
  <body>
    <h1>HTML 헤드에서 자바스크립트 실행</h1>
  </body>
</html>

app.html 파일을 클릭하여 크롬 웹브라우저에서 결과를 확인합니다. 크롬 브라우저의 F12를 눌러 Console 탭 부분을 확인하면 안녕하세요라는 결과가 출력이 된 것을 확인할 수 있을겁니다.

노드에서 실행

에디터를 이용해서 자바스크립트 프로그램을 작성한 후 명령창에서 노드 명령어를 이용하여 실행할 수 있습니다.

위에서 작성한 app.js 파일이 있는 폴더에서 명령창을 실행합니다. 그리고 다음과 같이 입력하면 원하는 결과가 출력됩니다.

node app.js

ES6 이상 실행

node 에서 es6 문법은 아직 실행시킬 수 없습니다. 다음과 같이 바벨(babel)을 이용하여 코드를 es6 버전으로 바꾼 후 실행해야 합니다.

만일 package.json 파일이 없는 상태라면 다음과 같이 만듭니다. 이것은 단순히 파일 package.json을 하나 만들어 프로젝트 형식을 같도록 합니다.

npm init -f
바벨(babel) 설치

babel-clibabel 명령어를 명령줄에서 실행할 수 있습니다.

npm install --save-dev @babel/core @babel/cli
preset 설치

@babel/preset-env는 자바스크립트 es6 문법을 바벨 기존 설정을 이용해서 변경할 수 있도록 합니다.

npm install --save-dev @babel/preset-env
babel-node 설치

babel-node를 이용해서 명령줄에서 컴파일을 자동으로 하고 실행까지 할 수 있습니다.

npm install --save-dev @babel/node
자바스크립트 파일 실행

실행하고자 하는 하는 파일이 있는 폴더에 들어가서 스크립트 파일을 실행시킬 수 있습니다. 여기서는 main.js 파일을 실행시킨다고 가정했습니다.

npx babel-node --presets=env main.js

presetpackage.json 파일에 지정해 놓으면 기본값으로 package.json 파일의 내용으로 컴파일 합니다. 즉, --presets=env 부분을 생략할 수 있습니다.

다음은 package.json 파일 일부입니다.

"devDependencies": {
  "@babel/cli": "^7.2.3",
  "@babel/core": "^7.4.0",
  "@babel/node": "^7.2.2",
  "@babel/preset-env": "^7.4.2"
},
"babel": {
  "presets": [
    "@babel/preset-env"
  ]
}

npx <명령어>명령어를 노드 패키지 중에서 찾아서 실행시킵니다. 자세한 사용법은 여기를 참조하세요.

디버그(Debug)

VSCode 이용 디버그

코드 변환 설정

ES5 파일에서 ES6 파일로 변환시키는 작업을 합니다.

package.json 파일 안에 스크립트 부분에 다음과 같이 compile 부분을 삽입합니다.

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "compile": "babel LearningJS/ --out-dir LearningJS/.compiled --source-maps --watch"
},

babelbabel 명령어를 사용하겠다는 뜻이고 LearningJS는 폴더 이름으로 그 폴더 아래에 있는 모든 *.js 파일을 --out-dir 옵션 뒤에 있는 폴더 LearningJS/.compiled 폴더로 컴파일해서 놓겠다는 뜻입니다. 모든 폴더 이름들의 위치는 package.json 파일을 기준으로 상대적 위치입니다. --source-map 옵션은 ES5, CommonJS 파일과 ES6 파일간의 소스의 위치를 대응시키기 위한 것이고 --watch 옵션을 사용하면 소스 변경시 마다 변경된 파일만 자동으로 재빌드합니다.

VSCode 디버깅 설정

vscode에서 디버그 탭을 누른 후 초록색 실행 버튼 오른쪽에 있는 설정 버튼(기어 아이콘)을 누릅니다. 다음과 같은 launch.json 파일이 하나 생성됩니다.

{
  // IntelliSense를 사용하여 가능한 특성에 대해 알아보세요.
  // 기존 특성에 대한 설명을 보려면 가리킵니다.
  // 자세한 내용을 보려면 https://go.microsoft.com/fwlink/?linkid=830387을(를)  방문하세요.
  "version": "0.2.0",
  "configurations": [


    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "program": "${workspaceFolder}\\ClassOop\\protoChange.js"
    },
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Current Program",
      "program": "${file}",
      "outFiles": [ "${workspaceFolder}\\.compiled\\**\\*.js" ]
    }
  ]
}

name은 실행 버튼에 표시될 이름으로 적당히 지정하면 됩니다. program은 실행될 파일을 의미하는 것으로 디버그 실행 버튼(초록색 버튼)을 누르면 실행될 파일입니다. ${file}은 현재 열려 있는 파일을 의미합니다. outFiles는 변환된 자바스크립트 파일의 위치를 의미합니다. 만일 다른 형식으로 변환되었다면 변환된 파일이 위치한 곳을 지정합니다. 위의 코드변환설정 부분에서 package.json 파일에 --out-dir에 지정했던 위치를 적어주면 됩니다.

위에서는 두 개의 디버그 설정을 했습니다. 첫번째는 작업폴더(workspaceFolder) 아래 ClassOop\protoChange.js 파일만을 디버그할 수 있는 설정입니다.

두번째 설정은 작업폴더 밑에 .compiled 폴더 아래에 있는 모든 js 파일들을 디버그할 수 있는 것입니다.

디버그 실행

디버깅 실행은 디버그 탭을 눌렀을 때 나오는 초록색 버튼을 눌러주면 됩니다. 누르기 전에 중단점(break point)를 미리 설정해야 합니다. 중단점 설정은 파일 왼쪽 부분으로 마우스를 움직이면 중단점(빨간색 점)을 설정할 수 있습니다.

참조 사이트

[LearningJavaScript]Learning JavaScript Data Structures and Algorithms by Loiane Groner - Third Edition
[AdvancedJavascript]Advanced JavaScript by Zachary Shute Publisher: Packt Publishing Release Date: January 2019 ISBN: 9781789800104
기초

변수와 상수

변수

변수(variable)란 값을 대입할 수 있는 이름(식별자)이다. 변수의 값은 언제든 바뀔 수 있다. 변수를 선언할 때 let 키워드를 사용한다. 자바스크립트 이전 버전에서는 var을 사용하던지 키워드를 사용하지 않아도 됐었다. 하지만 스코프에 대한 혼동으로 let이란 블록 영역 변수 선언으로 ES6부터 도입하게 되었다.

let 온도 = 28;

여기서 온도가 변수가 되며 let을 이용해 선언하면서 초기값을 할당하고 있다. 선언만 하고 나중에 값을 할당할 수도 있다. 만일 값을 할당하지 않고 선언만하면 기본적으로 undefined라는 값이 할당이 된다.

또한 언제든 변수에 다른 타입을 할당할 수도 있다.

온도 = "오늘은 너무 덥네요!";

위와 같이 온도 변수에 문자열 타입을 할당해도 된다. 즉, 변수는 어떤 타입의 값도 할당할 수 있다.

let 문 하나로 여러 개의 변수들을 선언할 수도 있다.

let 온도 = 28, 방1 = "세미나", 방2;

여기서 변수 온도, 방1, 방2가 선언되었고 방2에는 값이 할당되지 않았으므로 undefined가 할당이 된다.

상수(const)

상수 const는 ES6에서 도입된 키워드로 한번 할당되면 값을 변경할 수 없다.

const ROOM_TEMP = 26, MAX_TEMP = 30;

상수 이름은 일반적으로 대문자와 밑줄로만 사용한다. 될 수 있으면 변수보다 상수를 사용해야 한다.

식별자(identifier)

변수와 상수, 함수 이름들을 식별자라고 한다. 식별자는 다음과 같은 규칙이 있다.

  • 식별자는 반드시 글자나 달러 기호($), 밑줄(_)로 시작해야 한다.
  • 식별자는 글자, 숫자, 달러 기호, 밑줄만 사용할 수 있다.
  • 한글을 포함 유니코드를 사용할 수 있다.
  • 예약어는 식별자로 사용할 수 없다.

식별자를 사용할 때 일반적으로 다음과 규칙을 따른다.

  • 클래스를 정의할 때 사용하는 식별자를 제외하고는 첫글자로 대문자를 쓰지 않는다.
  • 밑줄 한 개 또는 두 개로 시작하는 식별자는 특별한 상황, 자신 만이 내부에서 사용할 목적이외로는 사용하지 않는다.
  • jQuery를 사용할 때는 달러 기호로 시작하는 식별자는 jQuery 객체 이름으로 사용된다.

리터럴(literal)

리터럴이란 문자 그대로 정해진 객체가 되는 것을 말한다. 즉, 숫자 10은 숫자형으로 인식을 하고 따옴표로 묶은 것은 문자열로 객체로 인식된다.

let num = 20; // 숫자 리터럴
let room = "세미나실"; // 따옴표로 둘러싼 것은 문자열 리터럴

원시 타입과 객체

자바스크립트에는 6가지 원시타입(primitive type)이 있다.

  • 숫자
  • 문자열
  • 불리언(booolean)
  • null
  • undefined
  • 심볼(Symbol)

원시타입은 불변(immutable)이라 한 번 만들어지면 바꿀 수 없다.

원시타입과 달리 변할 수 있는(mutable) 객체 타입이 있다. 내장 객체 타입으로 다음과 같은 것들이 있다.

  • Array
  • Date
  • RegExp
  • Map과 WeakMap
  • Set과 WeakSet
  • Number
  • String
  • Boolean

숫자

자바스크립트는 숫자형이 배정도 부동소숫점 형식 하나밖에 없다. 즉 정수형 타입이 존재하지 않는다. 하지만 10진수, 2진수, 8진수, 16진수 리터럴을 인식할 수 있다. 또한 과학용 지수 표현과 무한대, 음의 무한대, NaN 값 등이 있다.

10;
0x0000ff; // 16진수
0o0022; // 8진수
21.5;
3.1e-10;
-1.3E20;
Infinity;
-Infinity;
Nan;

문자열

자바스크립트 문자열은 유니코드 텍스트이다. 문자열 리터를에는 작은 따옴표 ', 큰따옴표 ", 백틱(backtick) `을 사용한다. 백틱은 템플릿 문자열에서 사용한다.

탈출문자

문자열 리터럴 안에 같은 따옴표를 반복할 필요가 있을 때나 특수한 문자를 입력하기 위해서 탈출문자 역슬래시 \를 사용한다.

"Sam said "don't do that!" to Mike"; // 에러 발생

큰따옴표 안에 큰따옴표가 중복되어 에러가 발생한다. 이러한 것을 피하기 위해 탈출문자를 사용한다.

"Sam said \"don't do that!\" to Mike";

특수문자

코드 줄바꿈 예제
\n 줄바꿈 " 첫줄 \n 둘째줄 "
\r 캐리지리턴 " 윈도즈첫줄 \r\n 윈도즈 둘째줄 "
\' 작은따옴표 " Don \'t "
\" 큰따옴표 " \"안녕하세요 \"라고 말했다 "
\` 백틱 ` ES6 도입: \` 문자열 `
\$ 달러 기호 ` ES6: ${변수명} `
\\ 역슬래시 " 역슬래시 \\\\\\ 사용 "

템플릿 문자열

문자열 템플릿을 이용해서 문자열 안에 변수나 상수를 사용할 수 있다. 템플릿 문자열은 백틱을 이용해서 만들고 달러 기호와 중괄호 안에 변수를 사용한다.

let 온도 = 28;

const msg = `현재 온도는 ${온도}도 입니다.`;

여러 줄 문자열

문자열을 여러 줄에 걸쳐서 사용하고 싶으면 따옴표 문자열 줄 끝에 \n\을 삽입한다.

const multiline = "line1\n\
line2";

다음과 같이 백틱을 사용하면 탈출문자 없이도 사용할 수 있다.

const mutliline = `line1
line2`

하지만 다음과 같이 다음줄 앞에 빈 공백이 있으면 그대로 출력된다.

let multiline = `line1
  line2
  line3`;

이것을 피하기 위해서는 문자열 병합을 이용한다.

let multiline = "line1\n" +
  "line2\n" +
  "line3";

숫자와 문자열

숫자를 따옴표 안에 넣으면 그것은 문자열 객체이다. 그리고 숫자 리터럴과 문자열과 더하면 문자열 리터럴로 변경된다. 결과 문자열은 두 문자열을 합친 것이 된다.

1 + '2'; // 결과는 문자열이고 '12'

하지만 더하기를 제외한 문자열 숫자와 리터럴 숫자의 연산은 문자열이 숫자로 바뀌어 계산된다.

5 * '10'; // 결과는 숫자 50
'10' / 5; // 결과는 숫자 2
5 - '10'; // 결과는 숫자 -5

불리언(boolean)

불리언은 true 또는 false 두 가지 값을 갖는 데이터 타입이다. 불리언 사용시 따옴표 안에 넣으면 문자열로 인식하므로 주의하여야 한다.

심볼(Symbol)

심볼은 ES6에서 도입된 것으로 유일한 토큰을 나타낸다. 심볼은 다음과 같이 Symbol() 생성자를 이용하여 만든다.

const RED = Symbol("red");
const red = Symbol("red");
RED === red; // false

nullundefined

nullundefined는 둘 다 값이 존재하지 않는 것을 의미한다. 사용자들은 null을 이용하는 것으로 충분하다. 변수를 선언하고 값을 할당하지 않으면 암시적으로 undefined가 설정된다.

객체

객체는 리터럴하게 사용하여 중괄호를 이용하여 만들 수 있다. 객체는 프로퍼티와 메소드로 구성된다. 프로퍼티는 객체가 갖는 값을 나타내며 메소드는 객체와 관련된 함수를 나타낸다. 객체의 내용은 동적으로 언제든 바꿀 수 있다. 다음과 같이 빈 객체로 시작하자.

const obj = {};

다음과 같이 프로퍼티 color를 추가할 수 있다.

obj.color = "Red";

생성된 프로퍼티는 접근 연산자 . 또는 대괄호와 따옴표를 이용하여 사용할 수 있다.

console.log(obj.color);
console.log(obj['color']);

심볼을 프로퍼티로 사용할 수 있고 접근할 때는 대괄호를 사용한다.

const SIZE = Symbol();
obj[SIZE] = 10;
console.log(obj[SIZE]);

프로퍼티 식별자로 공백을 사용할 수도 있다. 그 때는 따옴표로 식별자 이름을 묶어주어야 한다. 그리고 접근할 때는 반드시 대괄호와 따옴표를 사용한다.

obj["Not a Number"] = NaN;
console.log(obj["Not a Number"]);

객체를 만들면서 프로퍼티를 동시에 만들 수 있다.

const kim1 = {
  name: "Kim",
  age: 6,
};

const kim2 = {
  name: "Kim",
  age: 10,
};

const kim3 = {
  name: 'Kim',
  classification: {
    kingdom: 'Anamalia',
    class: 'Mamalia',
    family: 'Felidae',
  };
}

kim3에서 보는 바와 같이 객체도 프로퍼티의 값이 될 수 있다. classificationfamily 프로퍼티에 접근하는 방법은 다음과 같이 여러 가지 방법이 있다.

kim3.classification.family;
kim3['classification'].family;
kim3.classification['family'];
kim3['classification']['family'];

객체의 메소드를 넣는 방법도 비슷하다.

kim3.speak = function() {return "Meow!";};

객체의 프로퍼티와 메소드는 delete 연산자를 사용한다.

delete kim3.classification;
delete kim3.speak;

Number, String, Boolean 객체

배열

배열의 요소는 어떤 객체도 될 수 있고 배열의 크기는 고정되지 않는다. 배열의 인덱스는 0부터 시작한다. 배열을 만들 때는 대괄호를 이용하고 쉼표로 요소를 구분한다.

const a1 = [1, 2, 3, 4];
const a2 = [1, 'two', 3, null];
const a3 = ['안녕', '만나서', '반가워!'];
const a4 = [
  {name: "Diamond", hardenss: 10},
  {name: "Ruby", hardenss: 9},
  {name: "Topaz", hardenss: 8},
];
const a5 = [
  [1, 2, 3],
  [4, 5, 6],
]

배열은 length 프로퍼티가 있어서 배열의 요소의 갯수를 반환한다.

const arr = ['a', 'b', 'c'];
arr.length; // 3

배열 요소 접근은 인덱스를 이용한다.

const arr = ['a', 'b', 'c'];
arr[0]; // 'a'

배열 요소의 값을 덮어 쓸 때는 값을 할당하면 된다.

arr[0] = 10; // [10, 'b', 'c']

메소드들

concat

Array.prototype.concat() 배열과, 인자로 주어진 배열/값을 결합해 새로운 배열을 만들고, 이 새 배열을 반환합니다.

reduce

Array.prototype.reduce() reduce()메서드는 누산기(accumulator)와 배열의 각 요소(왼쪽에서 오른쪽으로)에 대해 하나의 단일 값(single value)으로 줄이는 함수를 적용합니다.

const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15

날짜

자바스크립트 날짜는 내장된 Date 객체에 담당한다. 날짜 객체는 new Date()를 사용한다.

const now = new Date();
now; // 2018-07-02T07:39:37.773Z UTC 표현식이다.

특정 날자와 시간에 해당하는 객체는 다음과 같이 쓴다.

const christmas = new Date(2018, 12, 25);

각부분 값을 가져오기 위해서는 메소드를 사용한다.

christmas.getFullYear();
christmas.getMonth(); // 11; 1월은 0부터 시작한다.
christmas.getDate(); // 25
christmas.getDay(); // 2(화요일. 0은 일요일)

정규 표현식

정규 표현식은 슬래시 / 사이에 넣는 리터럴 문법을 사용할 수 있다.

const email = /\b[a-z0-9._-]+@[a-z_-]+(?:\.[a-z]+)+\b/;

맵(Map)과 셋(Set)

ES6에서 도입된 것으로 맵은 객체와 마찬가지로 키와 값을 연결하지만 특정한 부분에서 객체와 다른 성질을 갖는다. Set은 중복을 허락하지 않는다. WeakMap과 WeakSet은 Map과 Set과 비슷하지만 일부 기능을 제거해 성능을 향상시킨 버전이다.

데이터 타입 변환

숫자로 변환

문자열을 숫자로 변환하려면 Number 객체를 이용한다.

const num = '22.12';
Number(num);

숫자로 변환할 수 없으면 NaN이 반환된다.

또 다른 방법으로는 parseIntparseFloat 함수를 이용할 수 있다.

parseInt("223 393 mail", 10); // 223; 앞에서부터 공백을 제외한 숫자인 부분까지만 변환한다.
parseFloat("123.45kg"); // 123.45;

문자열로 변환

자바스크립트의 모든 객체는 문자열로 변환할 수 있는 toString() 메소드를 가지고 있다. 숫자를 문자열로 변환하기 위해서 toString() 메소드를 사용한다.

num = 123.34;
num.toString(); // 문자열 '123.34'

불리언으로 변환

자바스크립트는 true, false 외에 ‘참값은 값’과 ‘거짓 같은 값’이 존재한다. 부정연산자를 사용하면 모든 값을 불리언으로 변경할 수 있다.

n = 0;
!!n; // false
Boolean(n); // false

연산자

자바스크립트에서는 다음과 같은 연산자들이 있습니다.

  • 대입 연산자
  • 비교 연산자
  • 산술 연산자
  • 비트 연산자
  • 논리 연산자

대입 연산자

확산(spread) 연산자

확산 또는 전개 연산자라고도 불립니다. 2개 이상의 인수나 2개 이상의 요소 또는 2개 이상의 변수가 예상되는 곳에서 확장되는데 사용됩니다.

함수의 인수에 사용

> function myFun(x, y, z) {console.log(x, y, z)}
> let args = [1, 2, 3]
> myFun(...args)
1 2 3

배열에서 사용

> parts = ['shoulders', 'knees']
> lyrics = ['head', ...parts, 'and', 'toes']
[ 'head', 'shoulders', 'knees', 'and', 'toes' ]

MDN 자바스크립트 튜토리얼: https://developer.mozilla.org/ko/docs/A_re-introduction_to_JavaScript

함수를 생성(create)하는 방법은 여러 가지가 있습니다. 함수 선언(function declaration, or function definition, for function statement)을 이용하는 방법과 함수 표현식(function expression), 화살표 기호를 이용하는 방법 등이 있습니다.

함수 선언을 이용해서 함수 만들기

함수 선언을 통해 함수를 정의하는 방법은 다음과 같습니다.

function 함수이름(매개변수1, 매개변수2, ..., 매개변수N) {
      ...생략...
}

선언 함수는 다음과 같이 사용합니다.

function 함수() {
    console.log('매개변수:', )
}

함수('매개변수입니다.')

위의 결과는 다음과 같습니다.

매개변수: 매개변수입니다.

반환값

함수 호출의 반환값은 return을 이용합니다. return 문이 없으면 undefined를 반환합니다.

호출과 참조

자바스크립트는 함수도 객체입니다. 따라서 함수를 다른 변수에 할당할 수 있고 함수의 매개변수로 넘길 수 있으며 함수의 반환값으로 함수를 넘길 수 있습니다.

함수를 호출할 때는 항상 소괄호를 이용합니다.

function getGreeting() {
  return "Hello World";
}

getGreeting();

다음과 같이 함수를 다른 변수에 할당할 수 있습니다.

const f = getGreeting;
f(); // "Hello World"

함수를 객체 프로퍼티에 할당할 수 있습니다.

const o = {};
o.f = getGreeting;
o.f(); // "Hello World"

배열 요소로도 할당할 수도 있습니다.

const arr = [1, 2, 3];
arr[1] = getGreeting;
arr[1](); // "Hello World"

함수와 매개변수

함수의 매개변수로 원시 타입이 건네 줄 때 원시 타입의 값은 복사가 되서 넘겨집니다. 하지만 객체가 인자로 넘겨질 때는 객체 자체가 넘겨져 함수 안에서 객체가 변경되면 함수 호출 후에도 변경된 값이 유지됩니다.

function f(o) {
  o.message = `f 안에서 수정함(이전 값: '${o.message}'`;
}

let o = {
  message: '초기값',
}

console.log(`f를 호출하기 전: o.message="${o.message}"`);
f(o);
console.log(`f를 호출한 다음: o.message="${o.message}"`);

실행한 결과는 다음과 같습니다다.

f를 호출하기 : o.message="초기값"
f를 호출한 다음: o.message="f 안에서 수정함(이전 값: '초기값'"

매개변수 기본값

매개변수에 기본값을 지정하는 것도 가능합니다.

function f(a, b=10, c="default") {
  console.log("a:", a, "b:", b, "c:", c);
}

객체의 프로퍼티인 함수

앞에서보 언급했듯이 객체의 프로퍼티로 함수를 취할 수 있는데 이 함수를 메소드라고 부릅니다.

const o = {
  name: "Mike",
  bark: function() {return "Woof";},
}

this 키워드

일반적으로 this는 객체의 프로퍼티인 메소드에서 의미가 있습니다. 메소드를 호출하면 this는 호출한 메소드를 소유하는 객체를 의미합니다.

const 명함 = {
  이름: "길동",
  이름은() {return `내 이름은 ${this.이름}입니다.`},
}

명함.이름은()을 호출하면 this가 가리키는 것은 이름은() 메소드를 소유하고 있는 객체인 명함이 됩니다. 따라서 다음과 같은 결과가 나옵니다.

console.log(명함.이름은()); // "내 이름은 길동입니다"

다음을 보면 호출하는 객체에 따라 다른 결과가 나오는 것을 알 수 있습니다.

const 이름은 = 명함.이름은;
console.log(이름은()); // "내 이름은 undefined입니다."

이름은() 메소드를 소유하고 있는 객체가 없기 때문에 this가 가리키는 것이 없게 되어 undefined가 나오게 됩니다.

함수 표현식을 이용해서 함수 만들기

함수 표현식은 호이스팅(hoisting)이 되질 않습니다.

익명 함수(anonymous function)

함수의 이름이 없는 함수를 익명 함수라고 합니다.

function([매개변수]) {
    ...생략...
}

함수는 객체이기 때문에 변수에 할당해서 사용할 수 있습니다.

var  = function(매개) {
    console.log('매개변수:', 매개);
}

('매개변수입니다.');

익명 함수를 이라는 변수에 할당해서 함수를 호출하고 있는 것을 알 수 있습니다.

익명 함수는 주로 클로저(closure) 또는 다른 함수의 인자로 사용됩니다.

클로저(closure)

클로저

프라미스(Promise)

Promise 개체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.

프라미스 정의는

let promise1 = new Promise((resolve, reject) => {
  // resolve 또는 reject 구현
  resolve('반환값')
  // 또는
  reject('오류값')
})

첫 번째 함수(resolve)는 비동기 작업이 성공적으로 완료되어 결과를 값으로 반환하면 호출되고, 두 번째 함수(reject)는 작업이 실패하여 오류의 원인을 반환하면 호출됩니다. 두 번째 함수는 주로 오류 객체를 받습니다. resolve 또는 reject의 반환값이 없으면 계속 대기 상태가 유지됩니다.

프라미스 실행은

// resolve  
promise1.then((result) => {
  ...
})
// 또는 reject  때는
promise1.catch((res) => {
  ...
})

result는 promise1의 상태가 완료(resolve)가 발생했을 때 결과값이 result로 전달되어 실행됩니다.

프라미스가 한번 결정되면 더 이상 결정은 이루어지지 않습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/**
 * 프라미스가 한 번 결정(resolved)되면 더 이상의 결정은 일어나지 않습니다.
 */

p2 = new Promise((resolve, reject) => {
  // resolve(new Date());
  console.log("start at: ", new Date());
  setTimeout(() => resolve(new Date()), 1000);
});

p2.then((res) => {
  console.log("first then: ", res);
});

p2.then((res) => {
  console.log("next then: ", res);
});

result = p2.then((res) => res);
console.log("result: ", result);

위 코드를 실행시키면 다음과 같이 나옵니다.

start at:  2019-06-23T12:18:40.700Z
result:  Promise { <pending> }
first then:  2019-06-23T12:18:41.709Z
next then:  2019-06-23T12:18:41.709Z
  1. 제일 먼저 5라인 프라미스 객체가 만들어지면서 7라인이 실행됩니다.
  2. 8라인이 실행되면서 1초 후에 resolve가 실행되도록 예약을 합니다.
  3. 11, 15 라인이 실행이 되려고 하지만 아직 1초가 지나지 않았으므로 프라미스의 상태가 결정되지 않아 보류를 하고 넘어 갑니다.
  4. 19라인도 1초가 지나지 않은 상태이므로 프라미스의 then을 실행하지 않고 result에는 p2의 상태인 Promise { <pending> }을 넘겨주게 됩니다.
  5. 20라인이 실행되면 Promise { <pending> }이라는 메시지가 출력이 되면서 아직 대기(pending) 상태라는 것이 찍힙니다.
  6. 1초가 지나면 드디어 프라미스의 상태가 이행(resolved)으로 변경되어 11, 15 라인이 작동이 되면서 시각을 출력하게 됩니다. 그런데 같은 값을 출력하는 볼 수 있습니다. 이것은 프라미스가 한번 이행이 되면 더이상 반복해서 이행이되질 않는 것을 알 수 있습니다.

프라미스의 then, catch에 의해서 반환된 값도 역시 프라미스이므로 계속 연결(chaining)해서 사용할 수 있습니다.

컨텍스트(Context)

zero cho의 컨텍스트

함수 안에서 this

poiemaweb this 참조

function 으로 정의한 함수를 일반함수라고 하고 화살표 =>로 정의한 함수를 화살표함수(arrow function)이라고 부르겠습니다.

일반함수의 this와 화살표함수의 this는 다르게 동작합니다. 생성자함수객체의 메소드를 제외한 모든 함수(내부 함수, 콜백 함수 포함) 내부의 this는 전역 객체를 가리킵니다.

일반함수의 this

자바스크립트의 경우 함수 호출 방식에 의해 this에 바인딩할 어떤 객체가 동적으로 결정됩니다. 다시 말해, 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정되는 것이 아니고, 함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정됩니다.

콜백 함수 내부의 this는 전역 객체 window를 가리킵니다.

화살표함수의 this

화살표함수는 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정됩니다. 동적으로 결정되는 일반 함수와는 달리 화살표함수의 this는 언제나 상위 스코프의 this를 가리킵니다. 이를 Lexical this라고 합니다.

자바스크립트의 객체(object)는 프로퍼티(property)와 메소드로 구성된다. 프로퍼티는 한 개의 값을 갖는 것이고 메소드란 행위에 해당하는 것으로 함수이다.

객체를 생성하는 쉬운 방법은 중괄호 안에 프로퍼티 또는 메소드들을 쉼표로 나열하는 것이다.

{
 color: "red",
 count: 5
 log: function () {
   console.log('수량: ' + this.count, '색상: ' + this.color);
 }
}

객체를 생성하는 다른 방법으로는 new Object()를 이용하는 것이다.

let 내객체 = new Object();
내객체.색상 = "빨강";
내객체.수량 = 5;
내객체.로그 = function () {
  console.log('수량: ' + this.수량, '색상: ' + this.색상);
}