본문 바로가기
  • soldonii's devlog
Javascript 공부/Zero To Mastery(-)

(10) 자바스크립트 심화 1 - scope, 삼항조건 연산자, 화살표 함수 등

by soldonii 2019. 8. 27.

*Udemy의 "The Complete Web Developer in 2019 : Zero To Mastery" 강의에서 학습한 내용을 정리한 포스팅입니다.

*https://soldonii.github.io에서 2019년 7월 16일(화)에 작성한 글을 티스토리로 옮겨온 포스팅입니다.

*자바스크립트를 배우는 단계라 오류가 있을 수 있습니다. 틀린 내용은 댓글로 말씀해주시면 수정하겠습니다. 감사합니다. :)


1. 스코프(Scope)

Scope는 변수에 대한 접근 권한을 가진 layer를 의미하며, window 객체가 default root scope이다. 함수는 root scope에 있는 어떤 변수에라도 접근이 가능하다. 아래 사례를 보면, 변수(b)와 함수(bb) 모두 root scope인 window 객체를 부모로 가진 children이기 때문이다.

let b = "Can I access this?"
function bb() {
  let a = 'hello';
}

window.bb;
window.b;

 

아래와 같이 특정 함수 내에서 변수를 선언할 경우, 해당 변수는 함수 scope 내에 종속되기 때문에 함수 외부에서는 접근이 불가능하다.

function bb() {
  let a = 'hello';
}

console.log(a); // ReferenceError: a is not defined

 

전역 변수와 함수 내 변수의 변수명이 동일할 경우에는?
// Root Scope(window)
let fun = 5;

function funFunction() {
  // child scope
  let fun = "hellooo";
  console.log(1, fun);
}

function funerFunction() {
  // child scope
  let fun = "Bye";
  console.log(2, fun);
}

function funestFunction() {
  // child scope
  let fun = "AHHHHH";
  console.log(3, fun);
}

console.log("window", fun); // "window 5"
funFunction(); // "1 hellooo"
funerFunction(); // "2 Bye"
funestFunction(); // "3 AHHHHH"

console.log("window", fun); // "window 5"

 

위 사례를 보면 전역 scope(root scope)에서 변수 fun 에 숫자 5를 할당한 후, 각 함수들 내에서는(child scope) 변수 fun에 각기 다른 값을 다시 할당했다. 이 같은 경우에 변수 fun 에는 어떤 값이 담겨 있을까?

함수가 실행될 때마다 각 함수 속 변수 fun은 새로운 실행 컨텍스트 내의 변수 환경에서 새롭게 정의된다.

  • 최초 root scope(window)에서 변수 fun은 5의 값을 가지게 된다.
  • 두번째 funFunction을 실행하면 새로운 실행 컨텍스트가 콜스택에 쌓이고, 그 안에서 변수환경에 변수 fun이 hellooo로 저장된다. 따라서 hellooo가 출력된 후, 콜스택에서 사라진다. 즉,  hellooo가 값으로 할당된 변수 fun은 funFunction 내부에서만 사용이 가능하고, 콜스택에서 pop up 되는 순간 사라지기 때문에 global에서 변수 fun은 여전이 5이다.
  • 나머지 funerFunction, funestFunction도 마찬가지로 각 함수가 실행되면서 생성되는 콜스택 내에서만 각 변수 사용이 가능하며, 따라서 funerFunction 내부에서 Bye가 할당된 변수 fun은 콜스택에서 funerFunction이 사라짐과 동시에 이 변수 값 또한 사용이 불가능하게 된다.
  • 따라서 funFunction, funerFunction, funestFunction 함수의 실행이 끝난 후에 여전히 root scope에서 변수 fun은 5이므로 최종적으로도 5가 출력된다.

# Conflict?

funFunction, funerFunction, funestFunction 에서는 root scope에 존재하는, 값이 5인 변수 fun 에 절대로 접근이 불가능하다. 함수가 실행됨과 동시에 root scope에 있는 전역 변수 fun 을 함수 내부에서 다른 값으로 변경하기 때문이다. 이처럼 root scope의 변수명과 child scope의 변수명이 동일해서 발생하는 오류를 conflict라고 한다.

// Root Scope(window)
let fun = 5;

function funFunction() {
  // child scope
  console.log(1, fun);
}

 

위 사례에서 funFunction을 실행하면 우선 function scope 내에서 변수 fun을 찾는다.

  • function scope 내에서 변수를 찾을 경우 해당 변수를 찾아서 그 값을 return하지만, 찾지 못할 경우 한 단계 상위 scope로 올라가 변수를 찾게 된다.
  • 이 과정을 반복하며, 만약 계속 상위 scope에서 변수를 찾지 못하면 최종적으로는 root scope까지 거슬러 올라간다. 만일 root scope에서 해당 변수를 찾으면 그 값을 return하지만, root scope에서도 찾지 못할 경우에는 ReferenceError가 발생한다.

2. 조건문 심화(Advanced Control Flow)

if, else if, else statement와 마찬가지로 ternary operator, switch 또한 Javascript의 조건문 중 하나이다.

 

# Ternary Operator

기본 문법은 condition ? expr1 : expr2; 이다.

function isUserValid(bool) {
  return bool;
}
let answer = isUserValid(true) ? "You may enter" : "Access Denied";
console.log(answer); // "You may enter"

만일 isUserValid 의 값이 true이면 expr1(“You may enter”)를, false면 expr2(“Access Denied”)를 변수 answer에 할당한다.

 

# Switch Statement

function moveCommand(direction) {
  let whatHappens;
  switch (direction) {
    case "forward":
      whatHappens = "you encounter a monster";
      break;
    case "back":
      whatHappens = "you arrived home";
      break;
    case "right":
      whatHappens = "you found a river";
      break;
    case "left":
      whatHappens = "you run into a troll";
      break;
    default:
      whatHappens = "please enter a valid direction";
  }
  return whatHappens;
}

 

1) whatHappens 라는 변수를 만든 후, 2) switch: direction 이 무엇인지에 따라서, 3) 만일 direction 이 forward면 whatHappens 에 “you encounter a monster”를 할당하는 식으로 direction  의 값에 따라 어떤 값을 변수 whatHappens 에 할당할지를 결정한다. 4) 만일 direction 의 값이 모든 case와도 일치하지 않을 경우, default로 설정한 값을 할당한다.

 

break 는 switch statement를 멈추라는 의미이다. 따라서 switch statement에서 나와서, 바로 return whatHappens 로 넘어간다. switch statement는 조건이 많을 때 if와 else if를 수없이 늘어놓는 대신 사용하면 좋은 구문이다.

 

3. ES5 and ES6

새로운 ES가 update될 때마다, 각 browser가 해당 기능을 support할 때까지 기다려야만 할까? 과거에는 그랬을지 모르지만, 현재는 BABEL을 통해 바로 사용이 가능하다.

  • BABEL은 새로운 ES문법을 모든 browser에서 작동하도록 자동으로 변환해준다.

# let, const

let은 {} 가 사용되는 순간 새로운 scope가 생성되지만, var는 함수 선언 시에만 새로운 scope가 생성된다.(중괄호 {} 에서는 새로운 scope가 생성되지 않는다.)

// 1번 사례 : let
const player = 'bobby';
let experience = 100;
let wizardLevel = false;

if (experience > 90) {
  let wizardLevel = true;
  console.log('inside', wizardLevel); // "inside true"
}
console.log('outside', wizardLevel); // "outside false"
// 2번 사례 : var
const player = 'bobby';
let experience = 100;
var wizardLevel = false;

if (experience > 90) {
  var wizardLevel = true;
  console.log('inside', wizardLevel); // "inside true"
}
console.log('outside', wizardLevel); // "outside true"

 

1번 사례 let의 경우 {} 를 사용하는 순간 scope가 새로 생성되기 때문에, 현재 if문 내에 child scope가 형성된 상태이고, scope 내에서 먼저 변수를 referencing하기 때문에 {} scope 내에서는 wizardLevel true를 return하지만 {} scope 외부에서는 root scope에 할당된 false를 return한다.

2번 사례 var의 경우 함수가 아닌 경우에는 {} 를 사용해도 scope가 생성되지 않는다. 따라서 wizardLevel 은 더 뒤에서 선언된 true를 가지게 되므로 if문 안과 밖 모두 true를 return한다.

 

const : 절대 변동되지 않아야 하는 값을 할당할 때 사용한다.

const를 사용해 변수를 선언하는 것이 더 안전하다. 한 번 const로 값이 할당된 변수에 다른 값을 재할당하는 것은 불가능하다.(let, var는 가능) 하지만 const로 할당한 변수 내의 특정 property(ex. 객체에서 key 또는 value 등)를 변경하는 것은 가능하다.

const obj = {
  player: "bobby",
  experience: 100,
  wizardLevel: false
}

obj = 5; // TypeError: Assignment to constant variable.
obj.wizardLevel = true;

console.log(obj); // {player: bobby, experience: 100, wizardLevel : true}

let, const, var의 차이

 

# Destructuring(구조 분해 문법)

Destructuring은 구조화된 배열 또는 객체를 destructuring, 즉 비구조화하여 개별 변수에 할당하는 것을 의미한다. 배열 또는 객체에서 필요한 값만을 추출하여 변수에 할당하거나 반환할 때 유용하다.(출처 : poiemaweb.com) 아래 사례에서 방식 1과 방식 2는 완전히 같은 의미이다.

const obj = {
  player: bobby,
  experience: 100,
  wizardLevel: false
}

// 방식 1
const player = obj.player;
const experience = obj.experience;
let wizardLevel = obj.wizardLevel;

// 방식 2
const { player, experience } = obj;
let { wizardLevel } = obj;

 

# Object properties

ES6에서는 객체에 아래와 같이 [] 를 이용하여 값을 할당할 수도 있다. [] 내부에 특정 변수를 가져와서 쓰거나, 계산을 하는 등 다양한 방식으로 활용이 가능하다.

 

const name = "john snow";

const obj = {
  [name]: 'hello',
  ['ray' + 'smith']: 'hihi',
  [3 * 6]: 18
}

 

또 만약 새로운 객체를 만들 때, 변수가 key, 할당된 값이 value가 될 경우에는 아래 방식 2처럼 선언할 수 있다.

const a = "simon";
const b = true;
const c = {};

/* 방식 1
const obj = {
  a: a,
  b: b,
  c: c
} 
*/

// 방식 2
const obj = {
  a, b, c
}
console.log(obj); // {a: "simon", b: true, c: {…}}

 

# 템플릿 문자열(Template strings)

Template String은 backtick(``)을 사용하여 더욱 쉽게 string을 표현하는 것을 말한다.

const name = "Sally";
const age = 34;
const pet = "horse";

const greeting = "Hello " + name + " you seem to be doing" + greeting + "!";

const greetingBest = `Hello ${name} you seem to be ${age-10}. What a lovely ${pet} you have.`;

 

# Default parameter

함수 선언 시 필요한 parameter에 대해서 default parameter 값을 설정할 수 있다. 만일 함수 실행 시 어떤 argument도 수신받지 않을 경우, default로 설정한 값을 활용해 함수를 실행하게 된다.

const name = "Sally";
const age = 34;
const pet = "horse";

function greet(name='', age=30, pet='cat') {
	return `Hello ${name} you seem to be ${age-10}. What a lovely ${pet} you have.`;
}

 

# 심볼(Symbol(Javascript Type 중 하나))

let sym1 = Symbol();
let sym2 = Symbol('foo');
let sym3 = Symbol('foo');

sym2 === sym3; // false

 

Symbol은 완전하게 unique한 변수를 만들 때 사용하며, 이 방식을 사용하면 conflict가 발생하지 않는다. Symbol은 객체 property의 식별자로 주로 사용된다.

 poiemaweb symbol

 

# 화살표 함수(Arrow Functions)

function add(a, b) {
  return a + b;
}

const add = (a, b) => a + b;

댓글