코딩/Javascript

자바스크립트의 덧셈 연산 완벽 분석

드리프트 2020. 12. 9. 19:42
728x170



NodeJS, DenoJS, TypeScript 모두 최근 가장 핫한 프로그래밍 언어인데요!

모두 자바스크립트입니다. 자바스크립트가 브라우저에서만 머물다가 NodeJS가 나오면서

본격적으로 유명해지기 시작했는데요.

이제은 웹페이지를 HTML, CSS, Javascript로 만들면 약간 촌스럽다고 해야 하나요?

다들 React, Vue, Angular, Svelte 등등 Javascript Framework을 많이 사용합니다.

그래도 이 모든 Framework의 기본은 자바스크립트인데요.

이번 블로그 내용은 자바스크립트에서의 덧셈 (Addition) (더하기) 에 대해 알아 보겠습니다.

맛보기로 다음 결과를 예상해 보시겠어요?

true + [] = ?


어려우시죠?

이제 본격적으로 자바스크립트 덧셈에 대해 알아 보겠습니다.

자바스크립트로 프로그래밍 할 때 "+" 연산자를 많이 사용하는데 예상과 다르게 출력되는 경우를 많이 겪었습니다.

자바스크립트는 (거의) 모든 데이터 유형간에 "+" 연산자 사용을 지원하는데 그 결과가 혼란스러울 수 있습니다.

자바스크립트에는 컴파일러가 없기 때문에 "+" 연산에 대해 컴파일 에러를 뿜을 수 없습니다.

더하기("+") 연산자는 문자열 연결 또는 숫자 덧셈을 담당합니다.

문자열 연결 및 숫자 덧셈 등 간단한 원리를 이해할 수 있으면 이 블로그가 필요없겠죠?

이제 이상한 자바스크립트 더하기("+") 예제와 설명을 살펴 보겠습니다.

우선, JavaScript는 왼쪽에서 오른쪽으로 덧셈을 수행합니다.

그리고 중학교 수학에서 배운데로 괄호를 먼저 계산합니다.

// 순차적으로 덧셈을 해볼까요?
1 + 2 + (3 + 4) 1 + 2 + 7 3 + 7 10


위 예제는 연산 우선순위에 따라 괄호를 먼저 계산하고 나머지를 계산하는 간단한 예제입니다.

이제 본격적으로 알아봅시다.

1. 자바스크립트 규칙에 따르면 두 개의 숫자 항목이 있는 경우 출력은 숫자 값입니다.

1 + 1 = 2


2. 문자열 연결은 다른 프로그래밍 언어에도 있으며 "+" 연산자를 문자열 연결에 사용하는 것은 당연합니다.

"Santa" + " " + "Claus" = "Santa Claus"


여기까지는 우리의 예상되로 출력되어 아무 문제 없습니다.


자바스크립트에서 덧셈 연산자에 있어 가장 중요한 룰

1. 숫자 값에 숫자가 아닌 값을 덧셈 연산하면 가능한 경우 숫자가 아닌 값을 숫자 값으로 변환합니다.

그렇지 않으면 둘 다 문자열로 변환됩니다.

2. 숫자가 아닌 값을 어떤 값에 덧셈 연산하면 그 전에 두 값을 문자열로 변환합니다.


위 2개의 룰에 대해 예제를 들어볼까요?

1 + "2" = "12"

1 + 1 + "2" = "22"

1 + "1" + "2" = "112"


혼란스러우시죠? 저도 그렇네요! ^^

첫 번째 줄에는 숫자 1과 숫자가 아닌 값("2")이 덧셈 연산되는데요.

둘 다 덧셈 연산되기 전에 문자열로 변환되었습니다.

"1" + "2" = "12"


두 번째 줄에는 숫자 더하기와 문자열 연결이 모두 있습니다.

물론 왼쪽에서 오른쪽으로 진행됩니다.

1 + 1 = 2

2 + "2" = "2"+ "2" = "22"


세 번째 줄에는 숫자 1이 문자열로 변환되어서 문자열 연결만 연산됩니다.

"1" + "1" + "2" = "112"

이제 좀 이해 되시나요?

안타깝게도 여기서 끝나지 않습니다.

JavaScript의 다른 데이터 유형은 덧셈에 사용될 때 다소 비정상적?인 방식으로 작동합니다.



Boolean

boolean 값으로 무언가를 더하기하면 어떻게 될까요?

1 + true = 2

1 + false = 1

true + true = 2

false + false = 0

"1" + true = "1true"

"1" + false = "1false"


boolean 값은 JavaScript에서 숫자 값으로 변환 할 수 있으며 변환 결과는 1 또는 0입니다.

true => 1

false => 0


그리고 boolean 값과 문자열 값을 결합하면 true가 "true"로, false가 "false"로 변환됩니다.



객체, 배열, 함수

덧셈의 값 중 하나가 객체, 배열 또는 함수이면 이를 원시 값으로 변환하려고 합니다.

1. valueOf() 함수를 실행하고 만약 그 결과가 원시 값인 경우 해당 값을 사용합니다.

2. toString() 함수를 실행해서 문자열로 변환시킵니다.

const me = { name: "Charlie", age: 34 }

me + 1 = "[object Object]1"


기본적으로 valueOf() 함수 실행은 빈 객체 {} 를 리턴합니다.

이 객체는 원시 값이 아니며 toString() 함수를 사용하여 변환을 시도할 겁니다.

기본적으로 객체의 toString() 함수 리턴 값은 "[object Object]"이며 덧셈 연산에 그다지 유용하지 않습니다.

불행히도 개발할때 이런 출력을 많이 보았습니다.

때로는 valueOf() 또는 toString() 함수를 재정의하고 우리가 원하는 값을 반환하는 것이 편리 할 수 있습니다.

const me = { name: "Charlie", age: 34 }

me.valueOf = function () {
    return this.age
}

me + 1 = 35


위 코드를 실행해 보면 "me" 객체의 valueOf() 함수를 재정의 해서 이제 숫자 값을 리턴하며 마지막 코드에서 1과 함께 34를 더합니다 (35).

함수(function)나 클래스(class)에 대해서도 valueOf() 및 toString() 함수는 객체(Object)와 동일하게 작동합니다.

function myEmployee(name, age) {
    this.name = name;
    this.age = age;
    this.toString = function () {
        return `${this.name} is ${this.age} years old`;
    }
}

new myEmployee("Charlie", 34) + 100 = "Charlie is 34 years old100"


배열(Array)의 경우 valueOf() 함수는 원시 값이 아닌 배열을 반환합니다.

toString()의 출력은 쉼표로 결합 된 내용입니다.

빈 배열은 빈 문자열과 동일합니다.

[] + 1 = "1"

[1, 2, 3] + [4, 5, 6] = "1,2,3" + "4,5,6" = "1,2,34,5,6"


여기까지 계속 읽으셨으면 아래 퀴즈를 풀어볼까요?

const me = { name: "Charlie", age: 34 }
me.toString = function () {
    return this.age
}

true + me + 65 + [2] + false + me + "merry christmas"


답은 맨 끝에 있습니다.




Date

모든 규칙에는 예외가 있는데요,

 

Date 객체를 제외한 모든 네이티브 ECMAScript 객체는 힌트 번호가 제공된 것처럼 힌트 부재를 처리합니다.
Date 객체는 힌트 String이 제공된 것처럼 힌트 부재를 처리합니다.


Date.valueOf() 함수는 숫자 타입(type)을 반환하지만 "+" 연산자를 사용하면 날짜가 문자열 유형으로 변환됩니다.

new Date(2020, 11, 9) + 12345 = "Wed Dec 09 2020 00:00:00 GMT+0900 (대한민국 표준시)12345"

new Date(2020, 11, 9) + "12345" = Wed Dec 09 2020 00:00:00 GMT+0900 (대한민국 표준시)12345"

new Date(2020, 11, 9).toString() = "Wed Dec 09 2020 00:00:00 GMT+0900 (대한민국 표준시)"

new Date(2020, 11, 9).valueOf() = 1607439600000



기타 이상한 계산 예제

undefined + 1 = NaN // undefined는 NaN 이라는 숫자 타입(type)으로 변환 => NaN

undefined + "1" = "undefined1"

null + 1 = 1 // null은 숫자 0으로 변환

null + "1" = "null1"

NaN + 1 = NaN

NaN + "1" = "NaN1"

Symbol(1) + 1 // TypeError: Cannot convert a Symbol value to a number

BigInt(1) + 1 // TypeError: Cannot mix BigInt and other types, use explicit conversions

BigInt(1) + "2" = "12"


읽은 것처럼 자바스크립트 덧셈은 우리가 수학에서 배운 것만큼 쉽지 않습니다.

그러나 위의 변환 규칙 중 일부를 기억하면 나중에 헷갈리실 일은 없으실 겁니다.





퀴즈 답

const me = { name: "Charlie", age: 34 }

me.toString = function () {
    return this.age
}

true + me + 65 + [2] + false + me + "merry christmas"


연산 우선 순위는 왼쪽에서 오른쪽으로 계산되기 때문에 일단 true + me 가 계산됩니다.

true는 1이 될지 "true"가 될지는 me 객체를 보고 결정되는데 me 객체는 toString() 함수에서 숫자 타입(type)으로 변환됩니다.

그래서 true도 1로 변화되어 일단 true + me = 1 + 34 가 됩니다.

그 다음 1 + 34 + 65 는 당연하게 숫자 100으로 계산됩니다.

그 다음 100 + [2] 인데 배열은 기본적으로 문자열로 변환되므로 "2"로 변환됩니다.

그러면 앞에 100은 문자열 "100"으로 변환되어

"100" + "2" 가 됩니다. 결과는 "1002" 가 됩니다.

그 다음 "1002" + false 인데요. "1002"가 문자열이라 false는 "false" 문자열이 되어 답은 "1002false"가 됩니다.

그 다음 "1002false" + me 인데요. me는 34라서 "1002false" + 34 가 되어 이 역시 "1002false34" 가 됩니다.

마지막으로는 당연히 문자열 연산이 되어 답은 "1002false34merry christmas" 가 됩니다.

그리드형