자바스크립트 완벽 가이드(JavaScript: The Definitive Guide 7/E) - 4장 : 표현식과 연산자

SangHyun Park·2023년 11월 15일

JavaScript

목록 보기
8/9
  • 표현식과 그 구성 요소인 연산자를 설명.
  • 표현식이란 어떤 값으로 평가(evaluate)되는 구절
  • 프로그램의 포함된 상수도 단순하지만 표현식. 변수 이름 역시 표현식이며 그 변수에 할당된 값으로 평가.
  • 단순한 표현식을 조합해 복잡한 표현식을 만들 때는 대부분 연산자를 사용

4.1 기본 표현식

  • 상수, 리터럴 값, 일부 키웓, 변수 참조
1.23;
("hello");
/pattern/;
true;
falsenull;
this;

i // 변수 i로 평가
undefined // 전역 객체의 'undefined' 프로퍼티 값
  • 식별자를 변수, 상수, 전역 객체의 프로퍼티라고 가정하고 찾지만 없다면 ReferenceError

4.2 객체와 배열 초기화 표현식

  • 값이 새로 생성된 객체나 배열인 표현식.
[] // 빈 배열
[1+2, 3+4]
  • 배열 초기화 표현식 내부의 요소 표현식 역시 배열 초기화 표현식이 될 수 있다.
let matrix = [[1,2,3],[4,5,6],[7,8,9]]
  • 배열 초기화 표현식 내의 요소 표현식은 배열 초기화 표현식을 평가할 때마다 평가된다.
  • 객체 초기화 표현식은 배열과 비슷하지만 중괄호, 각 하위 표현식은 프로퍼티 이름과 콜론으로 시작한다는 점에서 다르다.
let rectangle = {
  upperLeft: { x: 2, y: 2 },
  lowerRight: { x: 4, y: 5 },
};

4.3 함수 정의 표현식

  • 함수를 정의하면 그 값은 함수다.
  • 일반적으로 function 키워드로 시작
let square = function (x) {
  return x * x
}
  • ES6 이후 부터는 ‘화살표 함수’ 문법으로 사용 가능

4.4 프로퍼티 접근 표현식

  • 객체 프로퍼티나 배열 요소의 값으로 평가.
  • expression . identifier
  • expression [ expression ]
  • 위의 두가지 스타일이 있다. 어떤 스타일이든 .이나 [ 앞에 있는 표현식을 먼저 평가. null이나 undefined이면 표현식은 TypeError 일으킨다.
  • 프로퍼티 이름에 스페이스나 구두점, 숫자인 경우 대괄호 표기법 사용.

4.4.1 조건부 프로퍼티 접근

  • expression ?. identifier

  • expression ?. [expression ]

  • 왼쪽의 표션식이 null이나 undefined로 평가 될때의 에러를 방지할 수 있다. 뒤의 프로퍼티의 접근을 막아 null 또는 undefined로 평가.

  • 이런 형태의 프로퍼티 접근 표현식을 “옵션 체인”이라고 부른다.

4.5 호출 표현식

  • 함수나 메서드를 호출하는 문접
f(0); // f는 함수 표현식, 0은 인자 표현식
a.sort() // a.srot는 함수고 인자는 없음
  • 호출 표현식을 평가할 때는 첫 번째는 함수 표현식을 평가하고, 그 다음으로 함수 인자 표현식을 평가해 인자 값 리스트를 만든다 → 함수 표현식 값이 함수가 아니라면 TypeError
  • 인자 값을 함수를 정의할 때 지정된 함수 매개변수(parameter)에 순서대로 할당한 다음 함수 바디 실행.
  • 함수가 return 문을 사용해 값을 반환한다면 그 값이 호출 표현식의 값.
  • 그렇지 않다면 undefined.
  • 호출 표현식 맨 앞에 있는 표현식이 프로퍼티 접근 표현식이라면 이 호출은 메서드 호출이라고 한다.

4.5.1 조건부 호출

  • ES2020에서는 () 대신 ?.()로 함수 호출 가능
  • 왼쪽 표현식이 null또는 undefined로 평가될때 표현식 전체를 null또는 undefined로 평가
function square(x, log) {
  if (log) {
    log(x);
  }
  return x * x;
}
// 동일
function square(x, log) {
  log?.(x);
  return x * x;
}

4.6 객체 생성 표현식

  • 객체를 생성하고 함수(생성자)를 호출해 객체 프로퍼티를 초기화.
  • 객체 생성 표현식은 호출 표현식과 같지만, 그 앞에 new 키워드를 붙인다.
new Object();
new Point(2, 3);

4.7 연산자 개요

  • 연산자는 산술, 비교, 논리, 할당 표현식 등에 사용된다.
  • 대부분의 연산자는 +나 = 처럼 부호로 표현
  • 연산자는 연산자 우선순위가 있다.


4.7.1 피연산자 갯수

  • 연산자는 예상하는 피연산자 개수를 기준을 분류가능
  • 단항, 2항, 3항 연산자

4.7.2 피연산자와 결과 타입

  • 대부분은 피연산자가 특정 타입일 것으로 간주하여 특정 타입의 값을 반환.
  • 는 피연산자가 숫자라고 예상하지만 “3” “5”는 유효하고 15를 반환.
  • +는 숫자끼리 더하면 숫자, 문자가 포함되면 문자열을 반환한다.

4.7.3 연산자와 부수 효과

  • 일부 표현식은 이후의 평가 결과에 영향을 미치는 부수 효과가 있다.
  • 할당 연산자가 그 예

4.7.4 연산자 우선순위

  • 연산자는 표와 같이 우선순위를 같는다.
  • 현실에서 연산자 우선순위가 불확실하다면 괄호를 사용해서 순서를 명시적으로 정하기.

4.7.5 연산자 결합성

  • 지수, 단항, 할당, 조건 연산자는 오른쪽에서 왼쪽으로 수행

4.7.6 평가 순서

  • 표현식을 왼쪽에서 오른쪽으로 평가한다.
  • w = x + y z는 w → x,y,z, 순서로 평가한다음 y z ⇒ x의 값을더한다음 w에 할당

4.8 산술 표현식

  • 산술 연산을 비롯한 피연산자의 숫자를 조작하는 연산자에 관한 설명
  • 지수, 곱셉, 나눗셈, 뺄셈은 피연산자를 평가해 필요하다면 숫자로 변환한 다음 제곱, 곱, 몫, 나머지, 차이를 계산. 숫자가 아니며 숫자로 변환할 수도 없는 값은 NaN 값으로 변환

4.8.1 + 연산자

  • 숫자 피연산자는 더하고, 문자열은 병합
    • 연산자의 변환 규칙은 문자열 병합에 우선 순위가 있다.
  • 두 피연산자 모두 문자열로 판단할 수 없을 때만 덧셈을 수행.

4.8.2 단항 산술 연산자

    • -, ++, +, -

증가 ++

  • 피연산자에 1을 더한다.
let i = 1, j = ++i; // i와 j는 2
let n = 1, m = n++; // n은 2이고 m은 1
  • 표현식 x++가 항상 x=x+1과 같지 않다. ++는 절대 문자열을 병합하지 않는다.
  • for 루프에서 1증가시키는 목적으로 많이 사용됨

4.8.3 비트 연산자

  • 숫자의 이진 표현의 비트를 대상으로 저수준 조작을 수행
  • 비트 연산자는 피연산자에 정수 값을 예상하며, 이 값이 64비트 부동 소수점 값이 아니라 32비트 정수인 것처럼 동작.
  • 이들 연산자는 필요하다면 피연산자를 숫자로 변환하고, 그 숫자 값에서 소수 부분과 32번째 이후의 비트를 모두 버려서 32비트 정수로 강제 변환 한다.
  • 시프트 연산자는 이 피연산자를 부호 없는 32비트 정수로 변환한 다음 다섯 번째를 넘어가는 비트를 모두 버려서 적절한 범위의 숫자로 만든다.

4.9 관계 표현식

  • 관계 연산자는 두 값 사이의 관계를 나타내며 false 또는 true를 반환
  • 버그 방지를 위해 === 일치 연산자 써야 한다.

4.9.2 비교연산자

  • 문자열 비교에서 주의점은 문자열은 16비트 정수의 연속이기에 ASCII 대문자는 ASCII 소문자보다 작다.
  • < 연산자는 “ZOO”는 “arrdsf”보다 작다고 판단함.
  • 문자열 비교 알고리즘이 필요할 때는 String.localeCompare() 메서드를 고려.
  • 또는 toLowerCase(), toUpperCase()

4.9.3 in 연산자

  • in 연산자는 왼쪽 피연산자가 문자열, 심벌, 문자열로 변환될 수 있는 값이라고 예상.
  • 오른쪽은 객체라고 예상
  • 왼쪽 피연산자가 오른쪽 객체의 프로퍼티 이름일 경우 true를 반환
let point = { x: 1, y: 1 };

"x" in point; // true
"Z" in point; // false
"toString" in point; // true : 객체는 toString 메서드를 상속

4.9.4 instanceof 연산자

  • 왼쪽 피연산자가 객체, 오른쪽 피연산자는 객체의 클래스라고 예쌍.
  • 왼쪽에 있는 객체가 오른쪽에 있는 클래스의 인스턴스라면 true, 아니면 false를 반환한다.
let d = new Date()
d instanceof Date // true
d instanceof Object // true
d instanceof Number // false : d는 Number의 객체가 아니다.
  • 객체는 모두 Object의 인스턴스.
  • 오른쪽 피연산자가 객체의 클래스가 아니라면 TypeError가 발생한다.
  • 자바스크립트는 o instanceof f라는 표현식을 평가할 때 먼저 f.prototype을 평가한 후 o의 프로토타입 체인에서 그 값을 찾는다. 찾으면 o는 f의 인스턴스이며 연산자는 true를 반환.

4.10 논리 표현식

  • 논리연산자 &&, ||, !는 불 연산응ㄹ 수행한다.

4.10.1 불 AND(&&)

  • 불 AND연산을 수행
  • 첫 번째 피연산자와 두 번째 피연산자가 모두 true일 때만 true를 반환.
  • 둘 중에 하나라도 false면 false를 반환.
  • 자바스크립트 값은 모두 true 같은 값 또는 false 같은 값으로 해석 가능
  • 이 연산자는 먼저 왼쪽에 있는 첫 번째 피연산자를 평가, 왼쪽 값이 false이면 오른쪽 표현식을 평가하려 하지 않고 왼쪽에 있는 값을 반환한다.
  • 반면 왼쪽에 있는 값이 true면 표현식의 전체적인 값은 오른쪽에 있는 값
  • 오른쪽에 있는 값이 true 값이면 전체적인 값도 반드시 true 같은 값.
let o = { x: 1 };
let p = null;

o && o.x // 1 = o는 true같은 값이므로 o.x으 ㅣ값을 반환
p && p.x // null : p는 false 같은 값이므로 p.x는 평가하지 않는다.

&&는 오른쪽에 있는 피연산자를 평가할 수도, 그렇지 않을 수도 있다는 점을 이해해야한다.

이런 동작 방식은 단축 평가라고 부르며 코드를 조건부로 실행하려 할 때 사용

if (a === b) stop(); // a===b일때만 stop()호출
a === b && stop(); // 똑같이 동작

4.10.2 불 OR(||)

  • OR 연산 수행.
  • 둘다 true 같은 값이면 true 같은 값을 반환.
  • 이 연산자는 먼저 왼쪽에 있는 첫번째 피연산자를 평가, true 같은 값이면 단축 평가가 일어나 오른쪽 표현식을 평가하지 않고 바로 true 같은 값을 반환
  • 반면 첫 번째가 false 같은 값이면 || 연산자는 두 번째 피연산자를 평가하고 그 값을 표현식의 값으로 반환.

이 연산자는 몇 가지 변수 중에서 첫 번째로 등장하는 true 같은 값을 선택하려 할 때 자주 사용.

// 셋 표현식중 true같은 값이 나오면 그것이 변수의 값 (첫번째 부터 시작)
let max = maxWidth || preferences.maxWidth || 500;

4.10.3 불 Not(!)

  • 피연산자의 불값을 부정 할 때 사용.
  • !는 항상 true, false를 반환.
  • !는 단항연산자이므로 우선순위가 높고 피연산자와 단단히 결합되므로 적절한 괄호가 필요.
// 드 모르간의 법칙
!(p && q) === (!p || !q) // true
!(p || q) === (!p && !q) // true

4.11 할당 표현식

  • JS는 = 연산자를 사용해 변수나 프로퍼티에 값을 할당 한다.
i = j = k = 0 // 세 변수 모두 0으로 초기화

4.11.1 할당과 연산

  • JS는 일반적인 = 할당 연산자 외에도 할당 연산자와 다른 연산자를 결합한 단축 표현을 지원
  • 예를 들어 +=

4.12 평가 표현식

  • JS는 문자열을 JS 소스 코드로 해석하고 평가해서 값을 얻을 수 있다.
eval("3+2") // 5

거의 필요하지 않다 (안쓴다.)

4.12.1 eval()

  • 인자 하나 받고 문자열이 아닌 값을 전달하면 그 값을 반환.

4.12.2 전역 eval()

  • eval()은 로컬 변수를 엉망으로 만들어 JS 최적화를 어렵게 만든다.

4.13 기타 연산자.

4.13.1 조건 연산자 (?:)

  • 유일한 3항 연산자.
x > 0 ? x : -x; // x의 절댓 값

첫 번째 피연산자를 불로 평가하고 해석, 첫 번째 피연산자의 값이 true 같은 값이면 두 번째 피연산자를 평가하고 그 값을 반환. 그렇지 않다면 세번째 피연산자를 평가하고 그 값을 반환.

greeting = "hello" + (username ? username : "there");

// 둘 다 동일

greeting == "hello";
if (username) {
  greeting += username;
} else {
  greeting += "there";
}

4.13.2 null 병합 연산자(??)

  • 왼쪽 피연산자가 null이나 undefined로 평가될 때만 두 번째 피연산자를 반환한다.
  • 표현식 a에 부수 효과가 없다면 a ?? b는 다음 코드와 동등하다.
a !== null && a !== undefined ? a : b;

??는 첫 번째 true 같은 값이 아닌 “정의된” 첫 번째 피연산자가 필요할 때 ||를 대체할 수 있는 유용한 연산자

let max = maxWidth || preferences.maxWidth || 500;

이런 관용적 표현에서 문제는 어떤 환경에서는 0이나 빈 문자열, false가 유효한 값임에도 불구하고 모두 false 같은 값으로 취급 한다.

만약 maxWidth가 0이면 그값을 무시된다. 하지만 || 연산자를 ??로 바꾼다면 다음과 같이 0 역시 유효한 값으로 사용될 수 있다.

let max = maxWidth ?? preferences.maxWidth ?? 500;

?? 연산자는 &&나 || 연산자와 비슷하긴 하지만 우선순위 관계가 명확하지 않는다. 그렇기에 괄호를 사용

4.13.3 typeof 연산자

  • 이는 단항 연산자이며 피연산자 타입을 가리지 안흔ㄴ다.

typeof는 피연산자의 값이 null일 때 ‘object’를 반환한다. null과 객체를 구별하고 싶다면 직접 테스트 해야한다.

객체 클래스는 모두 뭉뚱그려 “object”로 평가하므로 다른 방법을 통해 구별 해야한다.

4.13.4 delete 연산자

  • 피연산자로 지정된객체 프로퍼티나 배열 요소를 삭제하는 단항 연산자.
  • 이 연산자는 프로퍼티를 삭제한다는 부수 효과를 목적으로 사용되며 반환되는 값을 기대하지 않는다.

4.13.5 await 연산자.

  • await는 비동기 연산을 나타내는 프라미스 객체를 피연산자로 예상하고, 프로그램이 마치 그 비동기 연산이 끝나길 기다리는 것처럼 동작. 하지만 실제로 기다리지는 않고 넘어가며 다른 비동기 동작도 동시에 일어날 수 있따.

4.13.6 void 연산자.

  • 단항, void는 피연산자를 평가한 후 그 값을 버리고 udefined를 반환.
  • 피연산자의 값이 버려지므로, void 연산자는 피연산자에 부수 효과가 있을 때에만 의미 있다.
profile
마라토너

0개의 댓글