Juni_Dev_log

모던 자바스크립트 입문(MJB) [5장] 표현식과 연산자 - ④ 논리 연산자와 관계 연산자 본문

Theorem (정리)/Javascript

모던 자바스크립트 입문(MJB) [5장] 표현식과 연산자 - ④ 논리 연산자와 관계 연산자

Juni_K 2020. 11. 13. 17:33

논리식(조건식)을 만드는 연산자인 논리 연산자와 관계 연산자를 배워보자.

① 관계 연산자

관계 연산자는 두 개의 피연산자를 비교한 결과를 논리값(true/false)으로 반환한다.

관계 연산자는 주로 제어구조(if/else구문 , while문, do/while문, for문)에서 조건식을 만들 때 사용한다.

연산자 예제 예제의 뜻
== 같이 같음 a == b a 값과 b 값이 같으면 true. 다르면 false.
!= 같이 다름 a != b a값과 b값이 다르면 true.
같으면 false.
=== 값과 타입이 같음 a === b a와 b의 값과 타입이 같으면 true.
그 외에는 false.
!== 값과 타입이 다름 a !== b a와 b의 값과 타입이 다르면 true.
그 외에는 false.
< 작음 a < b a 값이 b 값보다 작으면 true.
그 외에는 false.
> a > b a 값이 b 값보다 크면 true.
그 외에는 false.
<= 작거나 같음 a <= b a 값이 b 값보다 작거나 같으면 true.
그 외에는 false.
>= 크거나 같음 a >= b a 값이  b 값보다 크거나 같으면 true. 그 외에는 false.

이 중에서 동일 (==)연산자와 일치(===) 연산자는 변수가 같은지를 판정하는 연산자지만, 의미가 다르다.

비동일 연산자(!=)는 동일 연산자의 부정이며, 불일치 연산자(!==)는 일치 연산자의 부정이다.

 

▷동일 연산자

좌변과 우변의 피연산자가 같은지를 판별한다.

내부적으로는 좌우 피연산자의 타입을 변환한 다음에 좌변과 우변이 같은지를 느슨하게 비교한다.

① 좌우 피연산자의 타입이 같을 때

값이 같으면 true / 다르면 false 로 판정한다.

이 값은 변수의 데이터 타입에 따라 달라지는데, 원시 타입 변수의 값과 객체 타입 변수의 값은 내용물이 다르기 때문이다.

원시 타입 변수의 값은 데이터 그 자체인데, 객체 타입 변수의 값은 객체의 참조라는 점이 다르다.

따라서, 객체 타입변수의 값이 같은지를 판별하는 행위는 같은 객체를 가리키는지를 판별하는 것과 같다.

var a = [1, 2, 3];
var b = [1, 2, 3];
var c = a;
console.log(a == b); //false
console.log(a == c); //true

a 와 b 모두 배열의 참조가 저장되어 있다. 각 배열에는 같은 데이터가 담겨 있지만 메모리에서 차지하는 위치가 다르다.

따라서 a값과 b값이 다르다고 판정한다.

② 좌우 피연산자의 타입이 다를 때

두 피연산자가 같은 타입이 되도록 타입 변환한 다음에 다음 규칙에 따라 동일한 판별을 한다.

- undefined 와 null은 같은 것으로 친다.
- 한 쪽이 숫자고 다른 한 쪽이 문자열이라면, 문자열을 숫자로 변환해서 비교한다.
- 둘 중에 한쪽이 논리값이면 true는 1 / false는 0 으로 변환해서 비교한다.
- 한쪽이 객체이고 다른 한쪽이 숫자 또는 문자열이면 객체를 toString이나 valueOf 메서드를 사용해서 타입을 변환한 다음에 비교해야한다.
- 앞의 규칙에서 벗어나면 모두 "같지 않음"으로 판정한다.
null == undefined
//true

1 == "1"
//true

"0xff" == 255
//true

true == 1
//true

true == "1"
//true

(new String("a")) == "a"
//true

(new Number(2)) == 2
//true

[2] == 2
//true

▷ 일치 연산자

일치 연산자(===)는 피연산자를 평가한 후에 타입을 변환하지 않은 상태의 두 값을 엄격하게 비교한다.

타입과 값이 모두 같으면 같다고 판정하고 그렇지 않으면 같지 않다고 판정한다.

단, NaN만큼은 NaN을 포함한 모든 값과 같지 않다고 판정한다.

NaN === NaN
//false

NaN은 x !== x 를 만족하는 유일한 값임을 알 수 있다. 변수 값이 NaN인지 isNaN 함수로 확인할 수 있다.

동일연산자에서 true 였던 모든 표현식은 일치 연산자로 했을 때 모두 false가 된다.

null === undefined
//false

1 === "1"
//false

"0xff" === 255
//false

true === 1
//false

true === "1"
//false

(new String("a")) === "a"
//false

(new Number(2)) === 2
//false

[2] === 2
//false

② 논리 연산자

논리 연산자는 관계 연산자를 사용하며 만든 논리식과 결합하여 더욱 복잡한 논리를 정의한다.

연산자 예제 예제의 뜻
&& 논리곱 a && b a와 b가 모두 true면, true.
그 외에는 false.
|| 논리합 a || b a와 b 중 하나라도 true면, true.
모두가 false면 false.
! 부정 !a a가 true면 false. fasle면 true.
x > 0 && y > 0
// x와 y가 모두 참일 때만 true

x > 0 || y > 0
// x와 y 중에서 하나라도 참이면 true. 모두가 false면 false.

!(a && b) 
// a 또는 b가 거짓

관계 연산자(>,<)의 우선순위가 논리 연산자(&& , ||) 보다 높기 때문에 괄호를 쓸 필요가 없다.

▷ 피연산자의 평가

논리연산자의 피연산자는 논리값이 아니여도 된다. 피연산자가 논리값이 아니라면 필요에 따라서 타입을 변환한다.

논리 연산자가 피연산자를 어떤 타입으로 변환하는지는 표를 참고하자.

피연산자 평가되는 값
0, -0, 빈 문자열(""), NaN, null, undefined false
0을 제외한 숫자, 빈 문자열을 제외한 문자열, 모든 객체, 심벌 true

▷ 논리곱 연산자와 논리합 연산자의 단락 평가

논리곱(&&) 연산자와 논리합(||) 연산자는 단락 평가를 한다.

단락 평가란, 첫 번째 피연산자 값이 표현식을 결정하면 두 번째 피연산자를 평가하지 않는 것을 말한다.

또한 논리곱 연산자와 논리합 연산자는 논리값(true/false) 대신에 마지막으로 평가한 피연산자를 반환한다.

논리값을 반환하는 C나 Java와 다르기에 유념하자.

논리곱 연산자를 살펴보자.

a && b

a를 평가하는 값이 false 라면, b에 값에 관계없이 표현식 전체가 false가 된다.

따라서 b의 값을 평가를 하지 않는다.

이 때 && 연산자는 false 대신, a의 값 자체를 반환한다. 또한 a를 평가한 값이 true라면, 전체 표현식의 값이 b의 값으로 결정된다.

이 때 && 연산자는 true 나 false 대신에, b의 값 자체를 반환한다.

이렇게 작성하더라도 논리값이 필요한 장소에서는 논리값으로 타입이 변환되기 때문에 논리식 자체는 정상적으로 동작한다.

var p = null;
p && p.name 
// null : p가 false 로 평가되므로 p를 반환한다. 우변은 평가하지 않는다.

p = {name : "Tom", age : 18};
p && p.name 
// "Tom" : p가 true로 평가되므로, p.name을 반환한다.

이 코드를 단순히 p.name 이라고 고치면 p값이 null일 때 오류가 발생한다.

그러나 첫 번째 줄처럼 작성하면 단락을 평가하므로 p가 null일 때 p.name은 평가하지 않아 오류가 발생하지 않는다.

 

다음으로 논리합 연산자를 살펴보자.

a || b

a를 평가한 값이 true면 표현식의 값이 b의 값과는 상관없이 true 이므로, b의 값을 평가하지 않는다.

이 때 || 연산자는 true 대신 a 값을 반환한다.

또한 a를 평가한 값이 false면 전체 표현식 값이 b의 값으로 결정된다.

이 때 || 연산자는 true나 false 대신에 b의 값을 반환한다.

이 또한 정상적으로 동작한다.

 

|| 연산자는 여러 개의 값 후보 중에서 null 또는 undefined가 아닌 값을 선택하고자 할 때 유용하게 사용된다.

var time = time_interval || animationSetting.time || 33;

이렇게 작성하면 time_interval이 정의되어 있을 때는 time_interval 값을 사용한다.

정의되지않았을 때는 animationSetting 객체의 time 프로퍼티를 사용한다.

이 프로퍼티도 정의되지않았을 때 정수 (33) 을 사용한다.

 

또한 || 연산자는 함수에서 인수의 초깃값을 설정할 때 유용하게 사용할 수 있다.

function f(x) {
	x = x || 100;
	...
}

함수 f를 인수 없이 f() 라고 호출하면 인수 x에 undefined 가 들어간다.

undefined는 false로 평가되므로 다음처럼 된다.

x = x || 100; 
// 100

인수로 지정해서 f(2)라고 호출하면 인수 x에는 2가 들어간다. 2는 true로 평가되기 때문에 2를 반환한다.

x = x || 100;
// 2

 그러나 이 방법은 x에 false로 평가되는 값(0 , "") 등을 넘기면 그 값이 사용되지 않고 초깃값이 그대로 사용된다는 단점이 있다.

 

(연습)

1. 

alert( null || 2 || undefined );

-> 2 를 띄운다.

 

2.

alert( alert(1) || 2 || alert(3) );

-> 1과 2를 연속해서 띄운다. (처음 인수인 alert(1) 로 1을 뛰우고, alert은 undefined로 정의되기때문에 true한 값을 찾기 위해 패스. 다음 2를 띄우고, 멈춘다.)

 

3.

alert( 1 && null && 2 );

 -> 처음으로 false 값인 null을 뛰운다.

 

4,

alert( alert(1) && alert(2) );

-> 1을 뛰우고, undefined 값이 나온다. (1을 alert 하지만, alert는 undefined 값을 띄운다. 이후 alert(2)까지 진행되지 않는다. -> 앞에서 걸렸기 떄문에  )

 

5. 

alert( null || 2 && 3 || 4 );

->  && 연산자의 우선순위가 || 보다 높기 때문에, 2 && 3 을 먼저 진행한다. 결과 3을 반환하고, alert(null || 3 || 4) 가 된다. 그 결과로 첫번째 true 한 값인 3을 띄운다.

 

6. 어떤 alert 이 실행될까?

if (-1 || 0) alert( 'first' );
if (-1 && 0) alert( 'second' );
if (null || -1 && 1) alert( 'third' );

-> 첫 번째 if구문은 -1 || 0 => true 이기 때문에 first를 호출한다.

    두 번째 if 구문 -1 && 0 => false 이기 때문에 alert을 뛰우지않는다.

    세 번째 if 구문은 true 이기때문에 alert을 띄운다. 

-> first, third 를 띄운다.

 

7. 로그인 구현하기

 

프롬프트(prompt) 대화상자를 이용해 간이 로그인 창을 구현해보세요.

사용자가 "Admin"를 입력하면 비밀번호를 물어보는 프롬프트 대화상자를 띄워주세요. 이때 아무런 입력도 하지 않거나 Esc를 누르면 "취소되었습니다."라는 메시지를 보여주세요. 틀린 비밀번호를 입력했다면 "인증에 실패하였습니다."라는 메시지를 보여주세요.

비밀번호 확인 절차는 다음과 같습니다.

  • 맞는 비밀번호 "TheMaster"를 입력했다면 "환영합니다!"라는 메시지를 보여주세요.
  • 틀린 비밀번호를 입력했다면 "인증에 실패하였습니다."라는 메시지를 보여주세요.
  • 빈 문자열을 입력하거나 입력을 취소했다면 "취소되었습니다."라는 메시지를 보여주세요.

중첩 if 블록을 사용하고, 코드 전체의 가독성을 고려해 답안을 작성하세요.

힌트: 프롬프트 창에 아무것도 입력하지 않으면 빈 문자열인 ''가, ESC를 누르면 null이 반환됩니다.

 

let username = prompt("사용자를 입력해주세요.");

if (username == "Admin") {
	let password = prompt("비밀번호를 입력해주세요.");
    
    if(password == "TheMaster"){
    	alert("환영합니다!");
    }else if(password == '' || password == null){
    	alert("취소되었습니다.");
    }else{
    	alert("인증에 실패했습니다.");
    }
}
else if(username == '' || username == null){
	alert("취소되었습니다.");
}
else{
	alert("로그인에 실패했습니다.");
}
Comments