==
와 ===
자바스크립트에서 일치하는지(같은지) 비교하는 연산자(equality operator)에는 대표적으로 두 가지가 있다. ==
와 ===
이다.
흔히 ==
를 느슨한 동등 비교 연산자(loose equality comparison), ===
를 엄격한 동등 비교 연산자(strict equality comparison)라고 한다.
==
와 ===
의 차이점==
(loose equality comparison, double equals)NaN
, +0
, -0
에 대한 특별한 처리를 한다.+0 == -0; // true
NaN == NaN; // false
===
(strict equality comparison, triple equals)false
를 리턴한다.true
를 반환한다. 비교하려는 값이 숫자일 경우 둘 다 NaN
이 아니어야하고, 값이 같아야 같은 것으로 본다. +0
과 -0
을 비교하는 경우도 같다고 보고 true
를 리턴한다.NaN
, +0
, -0
에 대한 특별한 처리를 한다.+0 === -0; // true
NaN === NaN; // false
Object.is
NaN
, +0
, -0
을 다루지 않는다. 이 점만 빼면 ===
와 동작방식이 똑같다.Object.is(NaN, NaN); // true
Object.is(+0, -0); // false
🤔 지금까지 설명한 것은 비교하려는 두 변수가 원시 타입(Primitive Type)일 때 얘기이다. 원시 타입이 아닌 두 객체를 비교할 경우 구조가 똑같다하더라도 같지 않으므로
false
를 리턴한다. 참조타입에서는==
를 쓰던===
를 쓰던 동일하게 동작한다는 것이다.
객체를 비교할 때는 다음과 같은 연산이 수행된다.
valueOf()
메서드가 있다면 호출된다.valueOf()
의 결과가 원시 타입이 아니라면 toString()
을 호출한 후 비교 연산을 수행한다.let a = {};
a.toString = function () {
return 'hello';
};
let b = 'hello';
a == b; // true;
위 예제를 보면 ==
연산에서 toString()
이 호출되었음을 알 수 있다. 과정을 자세히 보면 다음과 같다.
[Step 1] -> 배열 즉 객체인 a와 문자열 b를 비교하려고 한다. a는 객체이므로 a에 valueOf()연산이 수행된다.
[Step 2] -> a에 valueOf() 연산을 한 결과가 원시타입이 아니므로 toString()이 호출된다.
[Step 3] -> a의 toString()을 호출했을 때 'hello'가 반환되도록 했으므로 a와 b는 같은 것으로 판별된다.
[Result] = true
💡
Object.prototype.valueOf()
valueOf()
메서드는 특정 객체의 원시값을 반환하는 함수이다.
let a = {};
let b = {};
a == b; // false
a === b; // false
let c = [];
let d = [];
c == d; // false
c === d; // false
자바스크립트에서 문자열은 원시타입이지만 객체로도 만들 수 있다.
let a = 'string';
let b = new String('string');
a == b; // true
a === b; // false, 타입이 다르기 때문에 false가 리턴된다.
typeof a; // "string"
typeof b; // "object"
출처: 동치 비교 모델 - MDN
==
에서 일어나는 자동 형변환==
로 비교하려는 값의 타입이 서로 다른 경우, 자바스크립트는 이 값들을 숫자형으로 바꿔서 비교한다.
'01' == 1; // true, 문자열 '01'이 숫자 1로 변환된 후 비교가 진행된다.
// Boolean값의 경우 true는 1로, false는 0으로 변환된 후 비교가 이루어진다.
true == 1; // true
false == 0; // true
이렇게 숫자형으로 자동 변환되기 때문에 ==
는 0
과 false
를 구별하지 못한다.
0 == false; //true, false는 숫자로 변환되어 0이 된다.
'' == false; //true, ''(빈문자열)과 false는 숫자로 변환되어 0이 된다.
타입까지 같은지 확인하는 ===
을 사용하면 0
과 false
를 구별할 수 있다.
0 === false; // false, 피연산자의 타입이 다르기 때문이다.
null
이나 undefined
와 비교하기null
vs undefined
===
를 사용하여 null
과 undefined
를 비교두 값의 자료형이 다르기 때문에 false
가 반환된다.
null === undefined; // false
==
를 사용하여 null
과 undefined
를 비교==
를 사용하여 null
과 undefined
를 비교하면 특별한 규칙이 적용돼 true
가 반환된다. null
과 undefined
이 두 값은 자기들끼리는 잘 어울리지만 다른 값들과는 잘 어울리지 못한다.(이는 아래서 살펴볼 것이다.)
null == undefined; // true
>
,<
,>=
,<=
)를 사용하여 null
과 undefined
를 비교null
과 undefined
는 숫자형으로 변환된다. null
은 0
, undefined
는 NaN
으로 변환된다.null
vs 0
null > 0; // (1) false
null == 0; // (2) false
null >= 0; // (3) true
위 코드의 비교 결과가 이상하지 않은가? (3)에서는 null
이 0
보다 크거나 같다고 했기 때문에 (1), (2) 둘 중 하나는 참이어야 하는데 둘 다 거짓을 반환하고 있다. 그 이유는 ==
와 기타 비교 연산자(>
,<
,>=
,<=
)의 동작 방식이 다르기 때문이다.
==
로 비교할 때, 피연산자가 null
이나 undefined
인 경우에는 형변환을 하지 않는다.>
,<
,>=
,<=
)를 사용할 경우 null
과 undefined
는 숫자형으로 반환된다. 그렇기 때문에 (1), (3)에서 null
은 0
으로 변환되는 것이다.undefined
undefined > 0; // (1) false
undefined < 0; // (2) false
undefined == 0; // (3) false
undefined
가 NaN
으로 변환되는데(숫자형으로 변환) NaN
이 피연산자인 경우 비교 연산자는 항상 false
를 반환한다.undefined
는 ==
로 비교했을 때 null
이나 undefined
와 같고, 그 이외의 값과는 같지 않기 때문에 false
를 반환한다.지금까지 살펴본 undefined
와 null
과 관련해 일어날 수 있는 예외 상황을 아래와 같은 방법을 통해 예방하자.
===
를 제외한 비교 연산자의 피연산자에 undefined
나 null
이 오지 않도록 주의하자.undefined
나 null
이 될 가능성이 있는 변수가 <
, >
, <=
, >=
의 피연산자가 되지 않도록 주의하자. 만약 변수가 undefined
나 null
이 될 가능성이 있다고 판단되면, 이를 따로 처리하는 코드를 추가하자.흥미로운 상황
let a = 0;
console.log(Boolean(a)); // false
let b = '0';
console.log(Boolean(b)); // true
console.log(a == b); // true
a와 b 두 값을 비교하면 true
가 반환되는데, 값을 각각 논리 평가하면 하나는 true
, 하나는 false
을 반환한다는 점에 의아할 수 있다. 자바스크립트에선 자연스러운 결과이다. 동등 비교 연산자 ==
는 피연산자를 숫자형으로 바꾸지만(위에서 문자열 "0"
을 숫자 0
으로 변환시킨 것처럼), Boolean을 이용한 명시적 변환에서는 다른 규칙이 적용되기 때문이다.
👉 자바스크립트에서의 형변환(명시적 형변환, 암묵적 형변환) 자세히 보러가기
// 여러가지 값을 숫자로 변환해보자.
Number(null); // 0 // null은 숫자로 변환하면 0이다.
Number(undefined); // NaN
Number(''); // 0
Number(' '); // 0
Number(false); // 0
Number([]); // 0
Number({}); // NaN
왜 0 == '0'
과 0 == []
이 true
인데, '0' == []
은 false
일까? 차근차근 살펴보자.
0
과 '0'
비교하기위에서 봤던 것 처럼 ==
연산의 경우 두 대상의 타입이 다르면 형변환한 후에 값을 비교한다. 문자열인 '0'
이 숫자형인 0
으로 변환되고 값의 비교가 진행된다.
[Step 1] -> 0 == "0"
[Step 2] -> 0 == (Number) "0"
[Result] = true
0
과 []
비교하기[]
은 배열이고 배열은 객체에 속한다. 자바스크립트에서 원시타입과 객체를 비교할 때는 객체에 ToPrimitive라는 추상 연산을 가한 후 얻은 값을 통해 비교하게 되어 있다고 한다. ToPrimitive 추상 연산에서는 먼저 객체의 DefaultValue가 될 요소를 찾아야 하는데 그 과정은 다음과 같다.
valueOf()
를 적용한 뒤, valueOf()
의 결과가 원시타입인지 확인한다.toString()
을 적용한다.빈 배열에 위 과정을 적용하면 다음과 같다.
let a = [];
a.valueOf();
// []
a.toString();
// ""
빈 배열([]
)에 valueOf()
메서드를 적용하게 되면 빈 배열을 얻게 되고 이는 원시타입이 아닌 참조 타입이므로 toString()
연산을 적용하게 된다. toString()
을 적용하면 빈 배열은 빈 문자열이 된다. 마지막에는 이 빈 문자열을 숫자형으로 변환한 결과인 0과 좌측 피연산자인 0을 비교하게 되므로 이 비교의 결과도 true
가 된다.
[Step 1] -> 0 == [] // 직접 비교 불가, []에 toPrimitive 연산 적용
[Step 2] -> 0 == ""
[Step 3] -> 0 == (number) ""
[Result] = true
'0'
과 []
비교하기1), 2)에서 다룬 내용들을 동원하면 마지막 수수께끼도 풀 수 있다.
원시 타입과 객체 타입을 비교하면 객체 타입에서 ToPrimitive라는 연산이 진행되는 것을 위에서 보았다. 그리고 빈 배열에 ToPrimitive 연산을 적용하면 결과는 빈 문자열이었다.
따라서 '0' == []
는 '0' == ''
가 되고, '0'
와 ''
는 타입은 문자열로 같지만 값은 다르므로 비교 결과는 false
가 된다.
[Step 1] -> "0" == [] // 직접 비교 불가, []에 toPrimitive 연산 적용
[Step 2] -> "0" == ""
[Step 3] -> "0" == "" // 비교 수행
[Result] = false
위에서 봤던 뚱이와 광선맨 문제
0 == '0';
0 == [];
'0' == [];
정답
0 == '0'; // true
0 == []; // true
'0' == []; // false
문제
0 == '\n0\n';
'' == '0';
0 == '';
false == 'false';
false == '0';
false == undefined;
false == null;
null == undefined;
' \t\r\n' == 0;
정답
0 == '\n0\n'; // true
'' == '0'; // false, 문자열은 사전순으로 비교한다.
0 == ''; // true
false == 'false'; // false
false == '0'; // true
false == undefined; // false
false == null; // false
null == undefined; // true
' \t\r\n' == 0; // true
💡 자바스크립트 이스케이프 시퀀스
\
(백슬래시)를 이용하면 자바스크립트에서 특수문자를 나타낼 수 있다.ex)
\t
: 탭(Tab),\n
: 줄 바꿈(엔터),\r
: 캐리지 리턴(CR),\b
: 백스페이스
문제
5 > 4;
'apple' > 'pineapple';
'2' > '12';
undefined == null;
undefined === null;
null == '\n0\n';
null === +'\n0\n';
정답
5 > 4; // true
'apple' > 'pineapple'; // false, 문자열 비교는 사전순 기준. "a"는 "p"보다 작다.
'2' > '12'; // (1) false, 문자열 비교는 사전순 기준, 왼쪽 피연산자의 첫번째 글자 "2"는 오른쪽 피연산자의 첫번째 글자 "1"보다 크다.
undefined == null; // true, ==로 비교할 때 null과 undefined는 같다.(위에서 배웠다.)
undefined === null; // false, 둘의 자료형이 다르므로 false이다.
null == '\n0\n'; // false, ==로 비교할 때 null은 오직 null과 undefined와 같다.
null === +'\n0\n'; // false, 자료형이 다르므로 false가 반환된다. // 단항 더하기 연산자(+)는 valueOf() 연산이다.
💡 문자열을 비교할 때는 사전순, 정확히 말하면 유니코드순으로 한 문자, 한 문자씩 비교를 진행한다. + (1)은 문자열과 문자열의 비교이므로 어떤 것도 숫자형으로 형변환되지 않는다.
👉 자바스크립트에서의 형변환(명시적 형변환, 암묵적 형변환) 자세히 보러가기
(예정)