-
Notifications
You must be signed in to change notification settings - Fork 13
== vs === JavaScript: Double Equals and Coercion — AJ Meyghani
== (double equals 또는 느슨한 비교) 연산자는 흥미로운 연산자 입니다. 어떻게 동작하는지 모르기 때문에 사용을 꺼려합니다. 그러나 그것은 피할만한 좋은 이유는 아닙니다. == 의 동작원리를 이해하고 피해야하는 이유를 아는 것이 더 좋습니다. 이유를 알게되면 왜 === 를 사용해야하는지 알 수 있습니다.
이 글을 읽고 난 후 강제 변환과 double equals 의 모든 것을 알게 될 것 입니다.
double equals 연산자는 타입이 같지 않다면 피연산자를 강제로 변환시킵니다. 타입이 같다면 triple equals 연산자를 이용합니다. 그래서 2 == 3 이랑 2 === 3 은 같습니다. 2 와 3 모두 number 타입이기 때문입니다. 그러나 타입이 같지 않다면 피연사들중 하나 또는 모두를 강제 변환하려합니다. 예를 들어 2 == "3" 을 비교한다면 숫자 2와 3 인 문자열을 비교합니다. 이 떄 JavaScript 는 문자열 3을 숫자 3으로 강제 변환 한 다음 2를 호출합니다. 2 === 3 을 비교하고 결과는 false 가됩니다.
우리는 double equals 연산자의 기초를 알고 더 깊이 연산자가 어떻게 결정하는지에 대한 방법을 학습합니다.
(참고: ECMAScript@2015 언어 스펙에서 추상적인 비교에 대한 정보를 읽을 수 있습니다) x 와 y 를 비교할 때 x 와 y 는 모든 타입이 될 수 있습니다.
시작: 만약 타입의 유형이 같다면 Javascript 는 === 연산자를 호출하여 완료처리 할 것 입니다. 그러나 다른 타입일 경우 Javscript 는 다른 방법을 따릅니다. Javascript 무엇이 필요한지 알아내기 위한 시도를 합니다.
다음 실행 단계입니다.
a. 먼저 null 그리고 undefined 를 비교합니다.
b. 문자열과 숫자를 비교합니다. 문자열 값을 number 로 형변환하고 == 를 호출하고 처음 단계로 돌아갑니다.
2 == "3"
↓ coerce "3" to number 3.
2 == 3 calling double equals again, and start over
c. boolean 값을 숫자로 형변환하고 == 로 비교합니다.
true == "3"
↓ coerce true to number 1.
1 == "3" start over again.
d. 객체를 숫자, 문자열, 심볼과 비교. 객체를 원시 타입으로 변경 한 후 비교합니다.
{ a: 'hello'} == "5"
↓ coerce the object to a primitive
"[object Object]" == "5" and start over again
다른 예제
[1,2,3] == "5"
↓ coerce object to a primitive
"1,2,3" == "5" and start over again
위의 단계중 일치하는 것이 없다면 마지막에 false 를 반환합니다.
객체가 어떻게 원시 타입으로 형변환 되는지 설명합니다.
먼저, 객체의 valueOf 메소드를 호출합니다. valueOf 통해 원시 타입이 리턴되면 그 값을 사용합니다.
그렇지 않은 경우 Object 의 toString 메서드를 호출합니다. toString 이 원시 타입을 반환하면 그것을 사용합니다.
그렇지 않으면 오류가 발생됩니다.
Note1: 기본적으로 발생하는 단계입니다. 문자열 선언을 암시하면 Javscript 는 valueOf 보다 toString 을 먼저 호출합니다. Javascript 는 기본적으로 숫자를 선호하기 때문에 valueOf 를 먼저호출하고 toString 을 호출합니다.
Note2: Date 객체를 원시 객체로 강제 변환할 때 Javascript 는 문자열을 선호합니다. 그렇기 때문에 toString 을 먼저 호출 한 다음 valueOf 를 호출합니다.
예시1
var x = {};
x.valueOf(); // -> {} : not a primitive, call toString now.
x.toString(); // -> "[object Object]": string is a primitive, I like it!
위 예제에서 x 가 원시 타입으로 변환되면 ㄹ과는 문자열이 됩니다.
예제2
var x = {name: 'Tom'};
x.valueOf = function () {return 5;};
x.valueOf(); // -> 5 : number primitive, I liked it!
위의 예제에서 x 가 원시타입으로 강제 변환되면 숫자 5 가 결과가 됩니다.
예제3
// coercing an array of numbers to a primitive
var x = [1,2,3];
x.valueOf(); // -> [1,2,3] : not a primitive, call toString now.
x.toString(); // -> "1,2,3" : string is a primitive, I like it!
예제에서 x 가 원시타입으로 강제 변환되면 결과는 문자 123 이 됩니다.
예제 4
// coercing an array of elements with different types to a primitive
var x = [undefined, null, true, 1, "5", new Date(), {a: 2}, [1,2,3]]
x.valueOf(); // -> Array itself : not a primitive, call toString now.
x.toString(); // -> ",,true,1,5,Sat Mar 05 2016 10:59:47 GMT-0500 (EST),[object Object],1,2,3" : string is a primitive, I like it!
예제에서 x 가 원시타입으로 변환 될 때 결과는 다음과 같은 문자열이 됩니다.
", true, 1,5, Sat Mar 05 2016 10:59:47 GMT-0500 (EST), [object Object], 1,2,3 "
number 의 강제변환: toNumber 가 다른 유형에 대해 수행하는 작업입니다.
- undefined → NaN
- null → 0
- number → number 자체
- boolean → false 0, true 1
- string → 숫자 반환이면 반환, parse string, 그렇지 않으면 NaN, 빈문자열은 0
Eg: "5" → 5, "" → 0, "29xY" → NaN - object:
먼저 ToPrimitive 를 호출 한 다음 결과에 대해 ToNumber 를 호출합니다. 객체가 어떻게 원시 타입으로 변경되는지 알아보려면 toPrimitive 섹션을 참고하세요
다음은 ToNumber 가 다른 타입에 대해 수행하는 작업입니다.
- undefined → "undefined"
- null → "null"
- number → “number”
- boolean → “true”, “false”
- string → 숫자 반환이면 반환, parse string, 그렇지 않으면 NaN, 빈문자열은 0
- object → ToPrimitive 를 먼저 호출한 후 ToPrimitive 결과에 대해 ToString 를 호출합니다. 객체가 어떻게 원시 타입으로 변환되는지 궁금하다면 toPrimitive 섹션을 참고하세요
JavaScript 의 falsy 한 값은 아래와 같습니다.
-
undefined
-
null
-
0
-
""
-
NaN
-
undefined → false
-
null → false
-
number → false if 0, otherwise, true
-
boolean → input iteself
-
string → false if empty string "", otherwise true
-
object → true. This follows that array → true
-
Date → true
이제 Double Equals 가 어떻게 동작하는지 알게되었다. 몇가지 예제를 살펴 볼 것이다.
타입이 다릅니다. 우리는 긴 경로를 가야합니다.
null 과 undefined 의 비교 ? no 숫자와 문자열의 비교 ? no boolean 과 다른 타입을 비교 ? no object 를 문자나 숫자와 비교 ? yes
배열을 원시타입으로 변경하고 다시 Double Equals 를 호출합니다. 배열에서의 valueOf 호출의 출력은 [] 입니다. 원시 값이 아니기 때문에 사용할 수 없습니다. toString 을 호출합니다. 결과값은 "" 입니다.
"" == 0
같은 타입이기 때문에 0 === 0 이고 결과는 true 입니다.
[] == 0
↓
"" == 0
↓
0 == 0
↓
0 === 0
→ true
[] == "0"
↓
"" == "0"
↓
"" === "0"
→ false
false == 1
↓
0 == 1
↓
0 === 1
→ false
{} == false
↓
{} == 0
↓
"[object Object]" == 0
↓
NaN == 0
↓
NaN === 0
→ false
undefined == false
↓
undefined == 0
→ false
[1,2,3] == 123
↓
"1,2,3" == 123
↓
NaN == 123
↓
NaN === 123
↓
→ false
[123] == 123
↓
"123" == 123
↓
123 == 123
↓
123 === 123
↓
→ true
[123] == "123"
↓
"123" == "123"
↓
"123" === "123"
↓
→ true
var x = {
a: '...',
toString: function () {
return false;
},
valueOf: function () {
return new Boolean(true);
}
};
x == "0"
↓
false == "0"
↓
0 == "0"
↓
0 == 0
↓
0 === 0
↓
→ true
x == 5
↓
false == 5
↓
0 == 5
↓
0 === 5
→ false
x == [1,2,3] // both are objects
↓
x === [1,2,3] // they are stored at different addresses
→ false // the address is not the same
일부 의역이 들어간 경우도 있으므로 해당 원문의 내용과 조금 다를 수 있습니다.
문제가 될 소지가 있다거나 혹은 수정이 필요한 사항이 있다면 있다면 issues 보내주세요.
기술문서
- 호출스택
- 원시자료형
- 값타입과 참조타입
- 명시적 변환, 암시적 변환, Nominal, 구조화, 덕 타이핑
- == vs === vs typeof
- 함수 범위, 블록 범위, 렉시컬(lexical) 범위
- 식(expression) vs 문(statement)
- IIFF, Modules, Namespaces
- 메세지큐와 이벤트루프
- setTimeout, setInterval, requestAnimationFrame
- 자바스크립트 엔진
- 비트 연산자, 형식화 배열, 버퍼(배열)
- DOM과 Layout Trees
- 팩토리와 클래스
- this, call, apply, bind
- new, 생성자, instanceof, 인스턴스
- 프로토타입의 상속과 체인
- Object.create와 Object.assign
- map, reduce, filter
- 순수함수, 부수효과, 상태변이
- Closure
- 고차함수
- 재귀
- 컬렉션과 생성기
- Promise
- async, await
- 자료구조
- 함수 성능과 빅 오 표기법
- 알고리즘
- 상속, 다형성, 코드의 재사용성
- 설계패턴
- 부분 어플리케이션, 커링, Compose, Pipe
- 클린코드