- 표현식은 자바스크립트 프로그램의 구절(phrase)과 같다.
- 표현식은 모두 자바스크립트의 '값'으로 평가될 수 있다.
- 표현식에는 값으로 평가되는 것 외에도 변수 할당 같은 부수 효과가 있을 수 있다.
- 리터럴, 변수 참조, 프로퍼티 접근 같은 단순한 표현식을 연산자로 묶어서 더 큰 표현식을 만들 수도 있다.
- 연산자는 크게 산술 연산, 비교, 불, 논리, 할당, 비트 조작 연산자로 나눌 수 있고, 그 외 조건 연산자를 비롯한 기타 연산자가 더 있다.
- + 연산자는 숫자를 더할 때나 문자열을 병합할 때 사용할 수 있다.
- 논리 연산자
&&
와||
에는 때에 따라 피연산자 중 하나만 평가하는 특별한 '단축 평가' 방식이 있다.
단축 평가
getLastElementOfProperty
문제
객체와 키를 입력받아 키에 해당하는 값이 배열인 경우, 마지막 요소를 리턴해야 합니다.
입력
인자 1 : obj
- 임의의 속성을 갖는 객체
인자 2 : property
- string 타입의 키
출력
- 배열의 요소를 리턴해야 합니다.
주의 사항
- 주어진 키에 해당하는 값이 배열이고, 빈 배열이 아닌 경우에만 배열의 요소를 리턴해야 합니다.
- 그 외의 경우, undefined를 리턴해야 합니다.
입출력 예시
const obj = {
abc: [1, 2, 5],
};
let output = getLastElementOfProperty(obj, 'abc');
console.log(output); // --> 5
정리하면, getLastElementOfProperty
는 '객체'와 그 객체에 포함된 '키의 이름'을 입력값으로 받는 함수다.
그리고 객체 안에 그 '키의 이름'을 갖는 key가 있고, 그 key의 value가 배열이라면 그 배열의 마지막 요소를 리턴한다.
나의 코드
function getLastElementOfProperty(obj, property) {
let prop = obj[property];
if (prop.length === 0 || Array.isArray(prop) === false) {
return undefined;
}
return prop[prop.length - 1];
}
1. obj
라는 객체 안에, property
라는 이름을 갖는 키의 value를 prop
이라고 선언한다.
2. prop
이 빈 배열이거나, 아예 배열이 아닐 경우에 함수는 undefined
를 반환한다.
3. 위 두가지 예외가 아닐 경우, 함수는 prop
이라는 배열의 가장 마지막 element 를 반환한다.
꽤나 잘 짰다고 생각했지만 prop.length 를 검사하는 순간 에러가 발생했다.
let prop = obj[property]
에서 obj
에 키가 'arr'
인 프로퍼티가 없으므로prop
에 undefined
가 할당되는데, undefinded
는 length
라는 프로퍼티가 없어서 검사하려 하면 에러가 뜬다.
(p. 73 '프로퍼티를 가질 수 없는 값')
어떤 스타일의 프로퍼티 접근 표현식을 쓰든 .이나 [ 앞에 있는 표현식을 첫 번째로 평가한다. 그 값이 null이나 undefined이면 이 둘은 프로퍼티를 가질 수 없는 값이므로 표현식은 TypeError를 일으킨다.
처음에는 에러를 해석할 생각을 못하고 막 헤매다가 혹시나 하는 마음에 if 문 안에 있는 두가지 조건의 순서를 바꿔보았는데,
오류없이 테스트가 통과됐다.
나는 도무지 이해할 수 없었다. OR 연산자에 순서가 있는 것인가??????
순서가 있었다.
논리 표현식 &&
나 ||
은 왼쪽 것부터 연산한다. 순서 상 오류가 존재하는 값부터 평가했기 때문에 오류가 난 것이다.
(p. 98 'OR 연산자의 단축 평가')
OR 의 왼쪽 부분이 falsy한 값이면 OR 연산자는 오른쪽 부분을 평가하고 그 값을 표현식의 값으로 반환한다.
하지만 OR 의 왼쪽 부분이 truthy한 값이면 단축 평가가 일어나서 오른쪽에 있는 표현식은 평가하지 않고 바로 truthy한 값을 반환한다.
function getLastElementOfProperty(obj, property) {
let prop = obj[property];
if (Array.isArray(prop) === false || prop.length === 0) {
return undefined;
}
return prop[prop.length - 1];
}
여기서는 prop.length
부터 평가하고, 에러가 발생한다.
여기서는 배열인지부터 평가하고, truthy한 값이 나왔기 때문에 단축평가가 일어나서 바로 undefined
를 반환한다.
오류가 발생하는 부분인 prop.length
는 아예 평가하지 않는다.
사실 위 문제에서 prop.length === 0
는 테스트할 필요가 없었다. 아래의 코드를 보자.
function getLastElementOfProperty(obj, property) {
let prop = obj[property]
if(Array.inArray(prop)) {
return prop[prop.length - 1];
}
}
함수는 리턴 명시된 것이 없으면 undefined
를 리턴한다.
(p. 75)
호출 표현식을 평가할 때는 첫 번째로 함수 표현식을 평가하고, 그 다음으로 함수 인자 표현식을 평가해 인자 값 리스트를 만든다. 함수 표현식의 값이 함수가 아니라면 TypeError가 일어난다. 그리고 함수를 정의할 때 지정된 매개변수(parameter)에 인자 값을 순서대로 할당한 다음, 함수 바디를 실행한다. 함수가 return 문을 사용해 값을 반환한다면 그 값이 호출 표현식의 값이다. 그렇지 않다면 호출 표현식의 값은 undefined 이다.
배열이 아닐 경우에는 리턴되는 것이 없어서 undefined
,
배열의 길이가 0일 경우에는 prop
의 인덱스 prop(prop.length - 1)
이 존재하지 않아서 undefined
가 뜬다.
이 부분은 이번 챕터와는 큰 관련이 없지만
예외 사항에 대해 항상 따로 조건문을 만들어서 해결하는 게 아니라,
위의 코드처럼 한 방에 해결될 수 있는 것인지 체크하는 습관을 기르면 좋을 것 같아서 적는다.
비트 연산자
(p. 87)
왜 0과 1을 다뤄야 하는지, 또 이게 실무에서 자주 쓰이는지는 잘 모르겠다.
블로그 몇 개 뒤져본 바로는, 여러개의 불값을 계속 비교해야 할 때 유용하게 쓰일 것 같다.
아래의 내용은 모두 2진수를 기준으로 한다. 다른 진법의 수일 때는 0x1234 to binary 같은 방식으로 검색해서 2진수로 생각.
비트 AND(&): 모두 1일 때만 1, 나머지는 0
비트 OR(|): 하나라도 1이면 1, 둘다 0이면 0
비트 XOR(^): 둘이 다를 때는 1, 둘이 같을 때는 0
왼쪽 시프트(<<): 모든 비트를 왼쪽으로 한 칸 이동. 가장 왼쪽 비트는 버리고 새로 생긴 마지막 자리에는 0을 쓴다.
ex) 00001010 << 2 => 00101000
한 자리 이동하면 2를 곱하는 효과. 두 자리 이동하면 4를 곱하는 효과.
오른쪽 시프트(>>): 모든 비트를 오른쪽으로 한 칸 이동. 밀려난 비트는 그냥 버린다. 비는 자리들은 원래의 부호로 채운다.
ex)
00001100 >> 2 => 00000011
11111111 >> 2 => 11111111
2진수에서 맨 왼쪽 자릿수는 부호를 나타낸다. 0이면 +, 1이면 -
ex)
1000010 은 '-2'
00001010 은 '+10'
0으로 채우는 오른쪽 시프트(>>>): 위와 같지만 비는 자리들을 무조건 0으로 채운다.부호 붙은 32비트 값을 부호 없는 정수처럼 취급할 때 유용하다.
11111111 >>> 2 => 00111111
비트 NOT(~): 1은 0으로, 0은 1로 바꾼다.
근데 이게 뭐 피연산자의 부호를 바꾸고 1을 뺀 것과 동일하다는데 왜 그런지 설명이 없다.
ex) 15 => ~0x0F => ~ 0x0000000F => 0xFFFFFFF0 => -16
NOT 연산을 하면 피연산자의 부호가 바뀌고 거기에 1을 뺀 것과 같다고 한다.
이건 책에서 나온 예시인데 뭔 말인지를 모르겠다.
32비트(4 X 8)라서 8칸인가? 뭐 그런 것도 헷갈리지만,
0xFFFFFFF0 가 10진수 -16 이 되는 과정이.. 아예 이해가 안 됐다.
일단 0x0F 는 2진수로 00001111 이고 NOT 연산을 하면 11110000 이 돼서 0xF0 이 되어야할 것 같지만,
맨 앞 자리수는 부호의 역할을 해야하니 앞에 1이 하나 더 와야하고, 16진수의 규칙을 맞추다보면
결국 1111 1111 1111 1111 1111 1111 1111 0000 까지 가게 된다.
이것저것 찾아보다가 이거 지금 당장 완벽하게 이해하려고 하다가는 개발 때려칠 것 같아서 대충 봤다.
그 과정에서 2진수 양수를 음수로 바꾸는 과정을 알게 됐고, 그 과정은 아래와 같았다.
1. 모든 자릿수의 1과 0을 다 바꾼다. (반전)
2. 반전된 값에 1을 더한다.
3. 맨 앞에 그냥 1을 갖다 붙인다. (부호)
만들어진 2진수 음수를 다시 10진수로 바꾸는 과정은 위의 세 과정을 거꾸로 하면 된다.
1. 맨 앞에 있는 1을 따로 뗀다. (부호)
2. 1을 뺀다.
3. 모든 자릿수의 1과 0을 다 바꾼다.
16진수 음수인 0xFFFFFFF0 을 10진수로 바꿔보자.
16진수 음수 0xFFFFFFF0 을 2진수로 바꾸면 아래와 같다. (F는 15이므로 2진수로 1111)
0b / 1111 / 1111 / 1111 / 1111 / 1111 / 1111 / 1111 0000
(부호로 읽히는 맨 앞 자리와, 맨 뒤 8자리만 신경써도 됨)
1. 맨 앞에 있는 1은 이 수가 음수임을 나타내니 따로 뗀다.
0b '1' / 111 / 1111 / 1111 / 1111 / 1111 / 1111 / 1111 0000
2. 위 숫자에서 1을 빼면
0b '1' / 111 / 1111 / 1111 / 1111 / 1111 / 1111 / 1110 1111
3. 부호 제외 모든 값을 반전시키면
0b '1' / 000 / 0000 / 0000 / 0000 / 0000 / 0000 / 0001 0000
앞에 0 다 떼고 보면 2진수 10000은 10진수로 16이고 맨 앞자리가 1이므로 음수, 즉 -16이 된다.
아래 자료들을 참고했습니다.
코딩교육 티씨피스쿨
4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등
tcpschool.com
[컴퓨터구조] 이진수의 음수 표현
않이... 0과 1로 '-' 를 어떻게 표현해요..? MSB(Most Significant Bit) 가장 중요한 비트 2진수 예시를 하나 들어보겠습니다 10진수 33 33 = 32 + 1 = 2^5 + 2^0 = 2^5 * 1 + 2^4 * 0 + 2^3 * 0 + 2^2 * 0 + 2^1 * 0 + 2^0 * 1 밑줄
kevinkim95-dev.tistory.com
자바스크립트(JavaScript): 비트 연산자 (Bit Operator) - BGSMM
자바스크립트(JavaScript): 비트 연산자 (Bit Operator) 비트 연산자란 2진수(binary)를 연산할 때 사용하는 연산자입니다. 예를 들어 십진법으로 표기한 정수 70을 이진법 표기로 변환하면 1000110B 가 되는
yoonbumtae.com
비트 연산의 개념
조건부 호출
(p. 76)
ES2020에서는 () 대신 ?.()를 통해 함수를 호출할 수 있다. 일반적으로 함수를 호출할 때 괄호 왼쪽의 표현이 null이나 undefined, 기타 함수가 아닌 것으로 평가될 때는 TypeError가 일어난다. ?.() 호출 문법을 사용하면 ?. 왼쪽에 있는 표현식이 null이나 undefined로 평가될 때 호출 표현식 전체를 undefined로 평가하며 예외는 일어나지 않는다.
조건부 프로퍼티 접근과 비슷한 것으로 생각했는데, 예시가 잘 이해되지 않는다.
function square(x, log) { // 두 번째 인자는 선택 사항인 함수
if (log) { // 선택 사항인 함수 인자를 받았음
log(x); // 호출
}
return x * x; // 인자의 제곱을 반환
}
function square(x, log) {
log?.(x);
return x * x;
}
위 코드와 아래 코드는 'log'가 함수면 호출하라는 코드이다. 위는 if 문을 사용했고 아래는 조건부 호출을 사용했다.
정확하게 log 가 square 라는 외부함수 안에서 어떻게 작동하는지 간단한 예시를 만들어보고 싶은데 잘 떠오르지 않는다.
연산자 우선 순위
(p. 81)
typeof my.functions[x](y)
// my라는 객체에는 functions라는 프로퍼티가 있고
// 그 값은 함수의 배열이다. 번호가 x인 함수를 호출하면서 인자 y를 전달하고,
// 반환되는 값의 타입을 구한다.
const array = [
function func1(num) {
return num + 1;
},
function func2(num) {
return num + 2;
}
];
const my = { name: ha jong seung, functions: array }
my.functions[0](3) // Output: 4
my.functions[1](3) // Output: 5
typeof my.functions[0](3) // 'number'
함수 호출을 대괄호로 시작하는 것처럼 보여서 처음에 이해가 좀 어려웠다.
객체의 프로퍼티가 배열이고, 그 배열 안에 함수들이 배치되어 있는 것을 상상하니 이해가 되었다.
연산자 결합성
(p. 82 오른쪽에서부터 결합되는 것들)
y = a ** b ** c // y = {a ** (b ** c)}
x = ~ - y // x = ~ (- y)
w = x = y = z // w = {x = (y = z)}
q = a ? b : c ? d : e ? f : g // q = a ? b : (c ? d : (e ? f : g))
평가 순서
(p. 83)
연산의 순서와 관계 없이, 자바스크립트는 표현식을 왼쪽에서 오른쪽으로 평가한다.
평가 순서는 한 표현식이 다른 표현식에 영향을 미치는 부수 효과가 있을 때에만 의미가 있다.
아래에 그 예시를 들어보겠다.
w를 먼저 평가하고
x를 평가하는데, 이 때 z++ 이므로 x에는 1이 할당된 후 기존 z 값에 1을 더한다. (x === 1, z === 2)
이후 평가 순서에 따라 y는 2, z는 2이므로 계산된 값은 1 + 2 * 2 = 5 이다.
w를 먼저 평가하고
x를 평가하는데, 이 때 ++z 이므로 x에는 기존 z 값에 1을 더한 2가 할당된다. (x === 2, z === 2)
이후 평가 순서에 따라 y는 숫자 2, z는 2이므로 계산된 값은 2 + 2 * 2 = 6 이다.
'부트캠프 > 자바스크립트 완벽 가이드' 카테고리의 다른 글
7장 배열 (0) | 2023.01.26 |
---|---|
6장. 객체 (0) | 2023.01.15 |
5장. 문 (단순 내용 나열) (0) | 2023.01.07 |
4장 표현식과 연산자 - 스터디 정리 및 회고 (0) | 2023.01.03 |
3장 타입, 값, 변수 (0) | 2022.12.27 |