자바스크립트 완벽 가이드 7판 스터디
책의 목차를 따라가며 관련 내용을 학습하고 공유하는 스터디입니다.

1. Function Definition - 함수 정의
2. 함수 호출
3. Default Parameters
4. Rest Parameters, Arguments
5. Arrow Function - 화살표 함수
6. Execution Context - 실행컨텍스트
7. This


1. Function Definition - 함수 정의

  • 함수는 일련의 과정을 문으로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것이다.
  • 함수는 입력을 받아서 출력을 한다.
    • 입력을 전달받는 변수 : Parameters - 매개변수
    • 입력된 값 : Argument - 인수
    • 출력 값: Return Value
  • 함수는 객체다.
    • 프로퍼티와 프로토타입을 가진다.
  • 함수는 Function Definition로 생성한다.
    • 4가지 방법: 함수 선언문, 함수 표현식, 화살표 함수, 생성자 함수

함수 선언문

  • 함수 이름을 생략할 수 없다.
  • 함수 이름은 소문자로 시작한다.
function doSomething(a, b, c) {
  return a + b + c;
}
  • 함수 선언문은 실제로는 함수 이름과 같은 식별자를 만들어 할당한다.
    • 함수 이름으로 호출하는 것이 아니라 함수를 가리키는 식별자로 호출하는 것이다.
  • 함수 선언문으로 정의하면 식별자 변수는 호이스팅된다.
    • 선언문 이전에 참조, 호출이 가능하다.
doSomething(1, 2, 3); // 호이스팅

var doSomething = function doSomething(a, b, c) {
  return a + b + c;
};

함수 표현식

익명함수

  • 할당한 변수명이 함수 이름이 된다.
doSomething(1, 2, 3); // EReferenceError: doSomething is not defined

const doSomething = function (a, b, c) {
  return a + b + c;
};

doSomething(1, 2, 3); // 6
doSomething.name; // 'doSomething'

기명함수

  • 함수 이름으로 호출할 수 없다.
const doSomething = function func(a, b, c) {
  return a + b + c;
};

doSomething(1, 2, 3); // 6
console.log(doSomething.name); // 'func'
func(1, 2, 3); // ReferenceError: func is not defined

생성자 함수

  • new 연산자와 함께 호출한다.
  • 생성자의 프로토타입 프로퍼티에서 지정된 객체를 상속하는 빈 객체를 새로 생성한다.
function Person(nickname, age) {
  this.nickname = nickname;
  this.age = age;
  this.sleep = function () {
    console.log(`${this.nickname} is sleeping`);
  };
}

const sangbeomheo = new Person('husbumps', 15);

sangbeomheo.nickname; // husbumps
sangbeomheo.age; // 15
sangbeomheo.sleep(); // husbumps is sleeping

화살표 함수


2. 함수 호출

  • 함수로 호출 : func(1,2,3)
  • 메서드로 호출 : obj.method(1,2,3)
  • 생성자로 호출 : new Person(1,2,3)
  • 간접적 호출 (call, apply) : func.call(thing)

  • 함수를 호출할 때 매개변수의 타입을 따로 지정하지 않아도 된다.
  • 매개변수에 값이 들어오지 않았다면(인자가 없다면) 해당 변수는 undefined가 된다.


3. Default Parameters

  • ES6 추가
  • 매개변수를 작성할 때 기본 값을 할당할 수 있다.
  • 인수가 전달되지 않은 경우, undefined인 경우에 할당한 기본 값이 인수가 된다.
function sum(x = 0, y = 0) {
  // Default Parameters가 없을 때는 이렇게 직접 체크를 해줬다.
  // x = x || 0;
  // y = y || 0;
  return x + y;
}

sum(10); // 10 + 0 = 10
sum(); // 0 + 0 = 0

4. Rest Parameters, Arguments

  • ES6에서 Rest Parameters 추가
  • Rest Parameters를 사용하면 매개변수의 개수가 동적일 때 인수 목록을 배열로 전달받을 수 있다.
  • arguments가 갖은 문제점과 번거로움을 보완
    • arguments는 유사배열이라서 배열 메소드를 사용하려면 배열로 변환해줘야한다.
    • 화살표 함수는 arguments 객체를 갖지 않는다.
function sum(...numbers) {
  console.log(numbers); // Array(5) [1,2,3,4,5]
  console.log(arguments); // Arguments(5) [1,2,3,4,5]
  return numbers.reduce((a, c) => a + c);
}

console.log(sum(1, 2, 3, 4, 5)); // 15

객체, 배열 분해해 매개변수로 사용할 수 있다.

function sum(...numbers) {
  console.log(numbers); // Array(5) [1,2,3,4,5]
  return numbers.reduce((a, c) => a + c);
}

const arr = [1, 2, 3, 4, 5];
console.log(sum(...arr)); // 15

5. Arrow Function - 화살표 함수

  • function 키워드 대신 화살표 => 를 사용해 함수를 정의한다.
  • 내부 동작이 기존의 함수보다 간략하다.
  • 함수 선언문으로 정의할 수 없고 표현식으로만 정의해야 한다.
const sum = (x, y) => {
  return s + y;
};

const sum = (x, y) => s + y; // {}를 생략할 수 있다. 이 경우 'return'도 생략

화살표 함수 vs 일반 함수

  • 화살표 함수는 인스턴스를 생성할 수 없다. non-constuctor
const Arrow = () => {};
new Arrow(); // TypeError: Arrow is not a constructor
  • this, arguments, super, new.target 바인딩을 갖지 않는다.
    • 스코프 체인 상 가장 가까운 상위 함수 중에서 화살표 함수가 아닌 함수의 this, arguments, super, new,target을 참조한다.
// Arguments

function outer(a, b, c) {
  console.log(arguments); // 1

  function inner1(a, b, c) {
    console.log(arguments); // 2
  }

  const inner2 = (a, b, c) => {
    console.log(arguments); // 3
  };

  inner1(4, 5, 6);
  inner2(7, 8, 9);
}

outer(1, 2, 3);

// 1 [Arguments] { '0': 1, '1': 2, '2': 3 }
// 2 [Arguments] { '0': 4, '1': 5, '2': 6 }
// 3 [Arguments] { '0': 1, '1': 2, '2': 3 } // 상위함수 바인딩

6. Execution Context - 실행컨텍스트

실행컨텍스트 란?

함수를 실행할 때 필요한 환경정보를 담은 객체

  • 동일한 조건, 환경을 지니는 코드뭉치가 있을 때 그 내용을 실행할 때 필요한 조건, 환경정보를 말한다.
  • 자바스크립트에서 동일한 조건을 지닐 수 있는 조건은 4가지다. 전역공간 함수 eval module
    • 전역공간 : 자바스크립트 코드가 실행되는 순간에 행성되고 전체 코드가 끝날 때 종료
    • module : import 될 때 행성, 해당 코드가 전부 끝날 때 종료
    • 함수 : 함수가 실행될 때 생성, 함수 내부 코드가 전부 끝날 때 종료
    • eval을 절대 사용하지 말 것!
  • 조건문, 반복문 등은 블록스코프에서 독립된 공간에서 실행되지만 실행컨텍스트를 생성하지 않는다.
  • 동일한 조건, 환경을 지니는 코드뭉치는 함수 라고 묶어서 볼 수 있다.
  • 실행컨텍스트는 함수를 실행할 때 필요한 환경정보를 담은 객체

콜스택(Call Stock)에 쌓이는 실행 컨텍스트

// global start
var a = '1';

function foo() {
  // foo start

  function bar() {
    // bar start
    console.log(a); // undefined
    var a = '3';
    console.log(a); // 3
    // bar end
  }
  bar();
  console.log(a); // 1

  // foo end
}

foo();
console.log(a); // 1
// global end

실행 컨텍스트 객체 내부

Variable Environment

  • 현재 환경과 관련된 식별자 정보들이 담긴다.
  • 최초 실행의 스냅샷을 유지한다. 값의 변경이 반영되지 않는다.

Lexical Environment

  • Variable Environment에 먼저 정보를 담고 이를 복사해서 Lexical Environment를 만든다.
  • environmentRecord : 환경 기록. 현재 컨텍스트 내부의 식별자 정보 (호이스팅)
  • outerEnvironmentReference : 외부 환경 참조. (스코프 체인)

This

  • this 참조 값을 결정한다.

environmentRecord

  • 실행컨텍스트가 최초 실행될 때 현재 컨텍스트의 식별자 정보를 수집해 environmentRecord에 담는다.
  • 이 담는 과정을 Hoisting(호이스팅)이라고 한다. Chapter3. 호이스팅 정리 부분
a();

function a() {
  return 'a';
}

var b = 123;

var c = function () {
  return 'c';
};
  • 위의 코드가 실행되면, 대략적으로 아래와 같이 호이스팅 된다. (실제 코드가 이렇게 변환되는게 아님)
// environmentRecord start
function a() {
  return 'a';
}
var b;
var c;
// environmentRecord end

a();

var b = 123;

var c = function () {
  return 'c';
};

outerEnvironmentReference

  • 외부 식별자 정보를 참조한다.
  • 외부의 Lexical Environment를 참조한다.
  • Scope Chain(스코프 체인) 현상은 outerEnvironmentReference에 의해서 만들어진다.


7. This

  • 실행 컨텍스트가 생성되는 순간에 this를 binding 한다.
  • 함수를 어떤 식으로 호출했느냐에 따라서 this가 결정된다.

전역공간에서 this

전역공간에서의 **this****전역 객체**를 가리킨다.

  • window(document) / global(node)
  • 자바스크립트를 실행하는 환경에 따라 달라진다.
// 브라우저에서
console.log(this); // Window {...}

var a = 1;
console.log(a); // 1
console.log(window.a); // 1
console.log(this.a); // 1

// node.js에서
console.log(this); // Object [global] {...}

함수 호출 시 this

함수 호출은 전역객체가 하기때문에 this전역 객체 를 가리킨다.

  • 함수를 실행하는 순간에 함수를 실행해주는 주체는? ⇒ 전역 객체
  • 함수호출시 this는 언제나 전역 객체를 가리킨다.
  • Arrow Function(화살표 함수) 디스 바인딩을 하지 않는다.
function a() {
  console.log(this);
}
a(); // Window (브라우저 실행 시)

function b() {
  function c() {
    console.log(this);
  }
  c(); // Window (브라우저 실행 시)
}

b();

메서드 호출 시 this

메서드를 호출한 주체this가 된다.

  • . 앞이 this!
  • 메서드는 자신을 호출한 대상 객체에 관한 동작을 수행
  • 클래스, 인스턴스도 객체이기 때문에 똑같이 동작한다.
  • 함수인데, 어떤 객체와 관련된 동작을 하게 되면 메서드가 된다.
var func = function (x) {
  console.log(this, x);
};
func(1);

var obj = {
  method: func,
};
obj.method(2);
obj['method'](2);

// Window
// { method: f } 2
// { method: f } 2

// 익명함수는 그대로인데 이를 변수에 담아 호출한 경우,
// obj객체의 프로퍼티에 할당해서 메소드로 호출한 경우
// 어디에 작성됐는지가 아니라 어디서 어떻게 호출됐는지가 This를 결정
const outer = {
  inner: function () {
    console.log(this);

    function innerFunc() {
      console.log(this);
    }
    innerFunc();
  },
};
outer.inner();

// {inner: ƒ}
// Window
const a = {
  b: {
    c: function () {
      console.log(this);
    },
  },
};
a.b.c();

// {c: f}
var a = 10;

const obj = {
  a: 20,
  b: function () {
    console.log(this.a);

    function c() {
      console.log(this.a);
    }
    c();
  },
};
obj.b();

// 20
// 10

둘 다 20이 나오게 하려면? (c함수에서 this가 b를 가리키게 하려면)

  • 아예 다른 변수를 사용 (ES5, ES6에서 대체할 수 있는 기능들이 추가되면서 사용x)
  • 화살표 함수 사용 (ES6)
  • call, apply, bind 를 사용 (ES5)
var a = 10;

const obj = {
  a: 20,
  b: function () {
    var _this = this;
    console.log(this.a);

    function c() {
      console.log(_this.a);
    }
    c();

    const _c = () => {
      console.log(this.a);
    };
    _c();
  },
};

obj.b();

// 20
// 20
// 20

call, apply, bind 메소드

  • this를 명시적으로 바인딩하는 3가지 방법
function example(a, b, c) {
  console.log(this, a, b, c);
}

const obj = {
  x: 'x',
};

example(1, 2, 3); // Window, 1, 2, 3

example.call(obj, 1, 2, 3); // {x: 'x'}, 1, 2, 3
example.apply(obj, [1, 2, 3]); // {x: 'x'}, 1, 2, 3

const boundExample1 = example.bind(obj);
boundExample1(1, 2, 3); // {x: 'x'}, 1, 2, 3

const boundExample2 = example.bind(obj, 1, 2);
boundExample2(3); // {x: 'x'}, 1, 2, 3

callback 호출시 this

기본적으로는 함수내부에서와 동일 ⇒ this는 전역객체

  • 콜백함수를 넘겨받은 대상이 해당 콜백함수를 어떻게 처리하느냐에 따라서 다르다.
const callback = function () {
  console.log(this);
};

const obj = {
  a: 1,
  b: function (fn) {
    fn();
  },
};

obj.b(callback);

// Window
  • 이벤트핸들러의 경우
function b() {
  console.log(this);
}

document.body.innerHTML = '<div id="a">Hello</div>';

document.querySelector('#a').addEventListener('click', function () {
  console.log(this);
});

// div#a (DOM Element)
// 이벤트리스너 함수는 콜백함수를 실행할 때 이벤트가 발생한 타겟 엘리먼트로 한다고 정해져있음

생성자 함수로 호출시 this

새로 만드는 인스턴스 객체 그 자체가 **this**가 된다.

function Person(name, age) {
  this.name = name;
  this.age = age;
}
const sangbeomheo = Person('상범', 10);

console.log(sangbeomheo.name, sangbeomheo.age); // 상범, 10


참고자료

0개의 댓글