Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags
more
Archives
Today
Total
관리 메뉴

동그란 도그린

[자바스크립트 완벽 가이드🦏] 4장 : 표현식과 연산자 본문

FrontEnd/Javascript

[자바스크립트 완벽 가이드🦏] 4장 : 표현식과 연산자

도그rin 2023. 3. 22. 18:00

📍 표현식이란?

  • 어떤 값으로 평가되는 구절

 

📍 기본 표현식

  • 가장 단순한 표현식
  • 상수, 리터럴 값, 일부 키워드, 변수 참조 등이 있음
true     // 불 true로 평가
false    // 불 false로 평가
null     // null로 평가
this     // '현재' 객체로 평가 (일정한 값이 아니며, 사용한 위치에 따라 다른 값으로 평가)

 

📍 객체와 배열 초기화 배열식

  • 값이 새로 생성된 객체나 배열인 표현식 (객체 리터럴 / 배열 리터럴)

 

  • 배열 초기화 표현식
    • 대괄호 안에 콤마로 구분된 리스트를 쓰는 형태
    • 배열 초기화 표현식 내부의 요소 표현식도 배열 초기화 표현식이 될 수 있음, 이 표현식은 배열 초기화 표현식을 평가할 때마다 평가되므로 결과가 달라질 수 있음
    • 배열 리터럴에서 콤마 사이의 값을 생략하면 정의되지 않은 요소가 들어감
    • 배열 리터럴 내부의 마지막 표현식 다음에 콤마가 단 하나 있으면 정의되지 요소가 만들어지지는 않으나, 그 마지막 표현식 다음의 인덱스로 배열에 접근하려는 표현식은 undefined
    [ ]            // 빈 배열
    [1+2, 3+4]     // 요소가 두개인 배열
    
    // 배열 초기화 표현식 내부의 요소 표현식
    // 이 표현식은 중첩된 배열 반환함
    let matrix = [[1,2,3], [4,5,6], [7,8,9]];
    
    // 다음 요소 다섯개 중에 세 개는 정의되지 않음
    let sparseArray = [1,,,,5];
    

 

  • 객체 초기화 표현식
    • 배열 초기화 표현식과는 달리 중괄호를 사용하고, 각 하위 표현식은 프로퍼티 이름과 콜론(:)으로 시작
    • ES6 이후에는 객체 리터럴 문법의 기능이 풍부해짐 ⇒ 중첩 가능해짐
    let p = { x: 2.3, y: -1.2 };
    let q = {};
    
    // 중첩 가능
    let rectangle = {
    	upperLeft: { x: 2, y: 2 },
    	lowerRight: { x: 4, y: 5 }
    }
    

 

📍 함수 정의 표현식

  • 함수를 정의하며, 그 값이 함수
  • ‘함수 리터럴’이라고도 부름
  • function 키워드로 시작하고 괄호 안에 0개 이상의 식별자(매개변수 이름)을 쓴 뒤, 중괄호 안에 자바스크립트 코드(함수 바디)를 쓰는 형태
  • 함수 정의 표현식에 함수 이름을 쓸 수도 있음
  • ES6 이후에는 함수 표현식을 간결한 ‘화살표 함수’문법으로 쓸 수 있음
let square = function(x) { return x*x; };

 

📍 프로퍼티 접근 표현식

  • 객체 프로퍼티나 배열 요소의 값으로 평가됨
  • 자바스크립트에는 2가지 프로퍼티 접근 문법이 있음 (마침표 사용, 대괄호 사용)
  • 해당되는 이름의 프로퍼티가 존재하지 않는다면 프로퍼티 접근 표현식의 값은 undefined
  • 어떤 스타일의 프로퍼티 접근 표현식을 쓰든 .이나 [ 앞에 있는 표현식을 첫 번째로 평가 ⇒ 그 값이 null이나 undefined이면 이 둘은 프로퍼티를 가질 수 없는 값이므로 표현식은 TypeError 발생시킴
  • .식별자 문법 : 더 간결하지만, 접근하고자 하는 이름이 유효한 식별자이고 그 이름을 알고 있을 때만 사용 가능
  • 대괄호 표기법 : 프로퍼티 이름에 스페이스나 구두점이 들어 있거나, 숫자인 경우(배열), 프로퍼티 이름이 고정되어 있지 않고 계산 결과일 때 사용해야 함
// 2가지 프로퍼티 접근 문법
expression.identifier     // 마침표 사용 (expression: 객체, identifier: 원하는 프로퍼티 이름)
expressiont[expression]   // 대괄호 사용 (대괄호 안의 expression: 프로퍼티 이름/배열 요소 인덱스)

// 예제
let o = { x: 1, y: { z: 3 }};     // 예제 객체
let a = [o, 4, [5, 6]];           // 객체를 담고 있는 예제 배열
o.x            // 표현식 o의 프로퍼티 x
o["x"]         // 표현식 o의 프로퍼티 x
o.y.z          // 표현식 o.y의 프로퍼티 z
a[1]           // 표현식 a의 인덱스 1에 있는 요소
a[2]["1"]      // 표현식 a[2]의 인덱스 1에 있는 요소

 

  • 조건부 프로퍼티 접근
    • ES2020에서 추가된 새로운 프로퍼티 접근 표현식
    • ?.과 ?.[] 문법을 사용해 TypeError 방지 가능(왼쪽 표현식이 null / undefined일 때 일어나는 TypeError)
    • ?.와 ?.[] 문법을 사용한 프로퍼티 접근은 ‘단축 평가’ ⇒ ?.의 왼쪽에 있는 하위 표현식이 null/undefined로 평가되면 더 이상 프로퍼티에 접근하려 하지 않고 전체 표현식을 즉시 undefined로 평가
    expression ?. identifier    // expression이 null/undefined이면 프로퍼티 identifier에 접근하려는 시도 없이 undefined로 평가
    expression ?.[expression]
    
    // 예제
    let a = { b: null };
    a.b?.c.d   // => undefined (a.b는 유효한 프로퍼티 접근 표현식이지만 값이 null이므로 즉시 undefined로 평가)
    
    let a;              // 변수 초기화 안함
    let index = 0;
    try {
    	a[index++];       // TypeError 발생
    } catch(e) {
    	index             // => 1: index는 TypeError 발생하기 전에 증가함
    }
    a?.[index++]        // => undefined
    index               // => 1: ?.[]는 단축 평가이므로 index가 증가하지 않음
    a[index++]          // TypeError 발생
    

 

📍 호출 표현식

  • 함수나 메서드를 호출(실행)하는 문법
  • 호출할 함수로 평가되는 함수 표현식으로 시작하며, 그 다음에 여는 괄호를 쓰고, 콤마로 구분된 0개 이상의 함수 인자 표현식 리스트를 쓰고, 닫는 괄호로 끝남
  • 첫 번째로 함수 표현식을 평가, 그 다음으로 함수 인자 표현식을 평가해 인자 값 리스트 만듦
  • 호출 표현식 맨 앞에 있는 표현식이 프로퍼티 접근 표현식이라면, 이 호출은 메서드 호출이라고도 함
f(0)              // f는 함수 표현식, 0은 인자 표현식
Math.max(x,y,z)   // Math.max는 함수, x, y, z는 인자
a.sort()          // a.sort는 함수, 인자는 없음

 

🔅 조건부 호출

  • ES2020에서는 () 대신 ?.()를 통해 함수 호출 가능
  • ?.() 호출 문법을 사용하면 ?. 왼쪽의 표현식이 null/undefined로 평가될 때 호출 표현식 전체를 undefined로 평가하고, 예외는 일어나지 않음
  • ES2019 이전에는 sort() 와 같이 선택 사항인 함수 인자를 받는 메서드 작성 시 if문을 통해 인자가 전달됐는지 확인해야 했으나 ES2020의 조건부 호출 문법을 쓰면 ?.()를 통해 함수를 호출하기만 하면 됨
  • ?.()는 왼쪽에 있는 것이 null/undefined인지만 체크하며, 값이 실제로 함수인지까지 체크하지는 않음
  • ?.()를 사용한 함수 호출 역시 단축 평가임 ⇒ ?. 왼쪽의 값이 null/undefined이면 괄호 안에 있는 함수 인자 표현식은 평가되지 않음
  • 메서드 호출에는 반드시 프로퍼티 접근이 수반됨
function square(x, log) {    // 두 번째 인자는 선택 사항인 함수
	log?.(x);                  // 함수를 받았으면 호출
	return x*x;
}
let f = null, x = 0;
try {
	f(x++);      // f가 null이면 TypeError 발생
} catch(e) {
	x            // => 1: x는 예외 발생 전에 증가함
}
f?.(x++)       // => undefined: f는 null이지만 예외가 발생하지 않음
x              // => 1: 단축 평가이므로 x는 증가하지 않음
o.m()      // 일반적인 프로퍼티 접근, 일반적인 호출
o?.m()     // 조건부 프로퍼티 접근, 일반적인 호출
o.m?.()    // 일반적인 프로퍼티 접근, 조건부 호출

 

📍 객체 생성 표현식

  • 객체를 생성하고 함수(생성자)를 호출해 객체 프로퍼티를 초기화함
  • 호출 표현식과 같지만, 앞에 new 키워드를 붙인다는 점이 다름
  • 생성자 함수에 전달할 인자가 없다면 빈 괄호 생략 가능
  • 객체 생성 표현식의 값은 새로 생성된 객체
new Object()
new Point(2, 3)

// 전달할 인자 없다면 빈 괄호 생략 가능
new Object
new Date

 

📍 연산자 개요

  • 자바스크립트 연산자는 필요에 따라 피연산자의 타입을 변환함
    • EX) “3” * “5”의 결과는 숫자 15 (자바스크립트가 피연산자를 숫자로 변환하므로)
  • 자바스크립트 값은 모두 true나 false 같은 값이므로 불 타입의 피연산자를 예상하는 연산자는 어떤 타입의 피연산자라도 받을 수 있음
  • 일부 표현식에는 이후의 평과 결과에 영향을 미치는 부수 효과가 있음
    • EX) 할당 연산자, 증감 연산자, delete 연산자
    • 함수나 생성자 바디에 부수 효과가 있는 연산자를 사용하면 해당 함수 호출이나 객체 생성 표현식에도 부수 효과가 있음
  • 어떠한 연산자보다도 프로퍼티 접근과 호출 표현식, 배열 인덱스의 우선순위가 더 높음

 

  • ?? 연산자는 ||나 && 사이의 우선순위는 아직 정의되지 않았으며, ES2020은 ??를 ||나 &&와 함께 사용할 때 명시적으로 괄호를 사용할 것을 요구함
    • ?? 연산자: 첫 번째로 정의된 값을 반환
a ?? b    // a가 null과 undefined가 아니면 결과는 a, 그 외에는 b

// 예시
let firstName = null;
let lastName = null;
let nickName = "바이올렛";

// null이나 undefined가 아닌 첫 번째 피연산자
alert(firstName ?? lastName ?? nickName ?? "익명의 사용자"); // 바이올렛

 

  • **(지수), *(곱셈), /(나눗셈), %(나눗셈), -(뺄셈), 이 다섯 기본 연산자는 피연산자를 평가해서 필요하다면 숫자로 변환 후 연산을 진행함 (숫자가 아니며 숫자로 변환 불가능한 피연산자는 NaN 값으로 변환됨)

 

  • ** 연산자(Math.pow()와 동일)
    • 다른 연산자들과 달리 오른쪽에서 왼쪽으로 동작
2**2**3   // (2**(2**3))

 

  • / 연산(나눗셈)
    • 결과는 항상 부동 소수점
    • 0으로 나누면 양의 무한대 또는 음의 무한대
    • 0을 0으로 나누면 결과는 NaN

 

  • % 연산
    • 결과의 부호는 첫 번째 피연산자를 따름
    • 나머지 연산자는 일반적으로 정수 피연산자를 받지만, 부동 소수점 값도 가능
6.5 % 2.1   // => 0.2

 

  • + 연산
    • 두 피연산자의 타입이 일치하지 않으면 타입 변환 필요 (문자열 병합에 우선순위)

 

  • == 연산자(동등 연산자)
    • 값을 비교할 때 타입 변환을 허용함
    • 두 피연산자가 ‘같다고 볼 수 있는지’ 체크
    • 두 값의 타입이 달라도 값이 같으면 같은 값
    • 하나가 null이고 다른 하나가 undefined이면 같은 값
    "1" == true  // => true : true를 숫자로 변환하고 문자열 1을 숫자 1로 변환 후 비교
    

 

  • === 연산자(일치 연산자)
    • 값을 비교할 때 타입 변환은 수행하지 않음
    • 두 피연산자가 ‘완전히’ 일치하는지 체크
    • 두 값이 다른 타입이면 같은 값이 아님
    • 두 값이 모두 null이거나 모두 undefined이면 같은 값
    • 두 값 중 하나라도 NaN이면 같은 값이 아님

🔅 NaN은 자기 자신을 포함해 어떤 값과도 같지 않음

 

  • in 연산자
    • 왼쪽 피연산자가 문자열, 심벌, 문자열로 변환될 수 있는 값이라고 예상하며, 오른쪽 피연산자는 객체로 예상함
    • 왼쪽 피연산자가 오른쪽 객체의 프로퍼티 이름일 경우 true를 반환함
    let point = { x: 1, y: 2 };
    "x" in point        // => true
    "z" in point        // => false : 객체에는 "z" 프로터피가 없음
    "toString" in point // => true : 객체는 toString 메소드를 상속함
    
    let data = [1, 2, 3];
    "0" in data     // => true : 배열에 요소(인덱스) "0"이 있음
    1 in data       // => true : 숫자는 문자열로 변환되며 요소(인덱스) "1"이 있음
    

 

  • instanceof 연산자
    • 왼쪽 피연산자는 객체, 오른쪽 피연산자는 객체의 클래스라고 예상함
    • 왼쪽의 객체가 오른쪽 클래스의 인스턴스면 true를 반환함
    • 오른쪽 피연산자가 객체의 클래스가 아니면 TypeError 발생
    let d = new Date();
    d instanceof Date;    // => true
    d instanceof Object;  // => true : 객체는 모두 Object의 인스턴스임
    

 

  • && 연산자
    • 왼쪽의 값이 false 같은 값이면 전체 표현식의 값도 반드시 false 같은 값이므로 오른쪽의 표현식은 평가하지도 않음 ⇒ 단축 평가(short circuit)
if(a === b) stop();    // a === b일 때만 stop() 호출
(a === b) && stop();   // 위와 똑같이 동작
Comments