2.값_3

악음·2021년 7월 19일
0

i will know javascript

목록 보기
6/12
post-thumbnail

무한대

C와 같은 전통적인 컴파일 언어를 쓰는 개발자들은 '0으로 나누기(Divide By Zero) 비슷한 컴파일/런타일 에러를 숱하게 보았을것이다.

var a=1/0 ;

그러나 자바스크립트에서는 0으로 나누기 연산이 잘 정의되어 있어서 에러 없이 Infinity(Number.POSITIVE_INFINITY)라는 결과값이 나온다.
분자가 -이면 -Infinity(Number.NEGATIVE_INFINITY)가 나온다
자바스크립트는 유한 숫자 표현식(앞에서 언급한 IEEE 754 부동 소수정)을 사용하므로 수학 교과서와는 다르게 덧셈, 뺄셈 같은 연산 결과가 +무한대/-무한대가 될 수 있아 예를 들면

let a =Number.MAX_VALUE // 1.7976931348623157e+308
a+a //무한대
a+Math.pow(2,970) //무한대
a+Math.pow(2,969) //무한대

IEEE 754 명세에 따르면 덧셈 등의 연산 결과가 너무 커서 표현하기 곤란할 때 가장 가까운수로 반올림 모드가 결과값을 정한다. 대략적으로 이야기하면 Number.MAX_VALUE +Math.pow(2,969)는 무한대 보다는 Number.MAX_VALUE에 가깝기 때문에 버림 처리하고
Number.MAX_VALUE+Math.pow(2,970)는 무한대에 더 가깝기 때문에 올림 처리한다

여기까지 만 생각하자 더이상 생각하단간 두뇌에 상처나서 뇌 손실이 올수도 있으니깐.

그래도 부연 설명을 하자면

  • 일단 무한으로 들어서면 다시 유한으로 들어올수없다 (무한 자채가 유한하게 될수 없기에)
  • 무한에 무한을 나누면 1이나 무한대가 될것 같지만 결과는 NAN이다
    (이건 수학책과 동일하다)
  • 유한한 양수를 무한대로 나누면 당연히 0 유한한 음수를 무한대로 나누면?
0/-infinity // -0???

0(Zero)

자바스크립트에는 0은 두가지가 있다 -0과 +0,,, -0??

일단 -0을 이해하기 위해선 자바스크립트가 0을 다루는 방식을 먼저 짚어보자
음의 영은 표기만 -0으로 하는 것이 아니다. 특정 수식의 연산 결과 또한 -0으로 떨어진다.
예를 들면


let a =0/-3 //-0
let b =0*-3 //-0
  • 신기 한점은 -0을 "문자열"화 하면 항상 "0"이다.(toString())
  • 반대로 "-0"을 숫자화 하면 -0이 나온다.(Number("-0"))

여기서 그렇다면 -0을 다른 값들과 비교해보자

let a =0
let b=0/-3

a==b //true
-0==0 //true

a===b //true
-0===0 //true

0>-0 //false
a>b //false

위에 식을보면 0는 -0과 같다 때문에 0과 -0을 구분 짓기 위해선
다음과같은 함수를 사용해보자

const isNegZero=(n)=>{
  n=Number(n)// 여기서 무조건 -0이 되도록바꿈
  // 만약 n이 0과 같고 1과 나눴을때 -인피니티가 나올경우
  //(분모가 0이면 무한 -0이면 네거티브 무한)true 둘중 하나라도
  //안맞으면 false(그냥 0이다)
  return (n===0)&&(1/n ===-Infinity)
}
isNegZero(-0) //true
isNegZero(0/-3) //true
isNegZero(0) //false

그렇다면 -0은 도대체 왜 만든 것일까?

만약 크기로 어떤 정보를 표현을 한다고 해보면 (예를들면 x좌표 y좌표를 왔다갔다하는 에니메이션)
해당값이 어떤 값으로부터 왔느냐? 를 알기위함이다
+방향에서 온것인지 -방향에서 온것인지 분기시켜주려면 -0/0으로 표현 해주면 되기 때문이다.

javascript는 무엇을 표현하기 위해 만들어졌는지 알아야한다.

js는 원래 프론트앤드를 동적으로 표현하기위한 언어이고

프론트엔드는 에러가 나도 무방하다면 그대로 표현을 해주어야 하기때문에

나사가 하나 빠져보이기도하고 이치에 안맞아보이기도한다.

하지만 그렇다고해서 나쁜 언어는 아닌 것이다.

모두 필요에 의해서 만들어진 것 뿐 이것을 잘 파악하고 잘사용하도록 하면 되겠다.

2.4.4. 특이한 동등 비교

앞서 설명했듯이 NaN과 -0의 동등 비교는 독특하다 NaNa는 자기 자신과도 동등하지 않아(특정 관점으로 봤을때-이전 포스트를 참고)Number.isNaN()을 사용해야하고(또는 폴리필)마찬가지로 -0도 거짓말 쟁이라서 보통 0과 동등한척함으로(엄격 동등 연산자에서도)isNegZero함수를 꼼수로 써야한다.

때문에! 이를 해결하기 위한 Object.is()유틸리티를 지원한다.

let a = 2/"foo" //NaN
let b = -3*0 //-0

object.is(a,NaN) // true
object.is(b,-0) // true
object.is(b,0) //false 정확히 0과 -0을 비교한다.

Object.is()는 특이한 동등비교를 위해 사용하도록하자.

2.5 값 vs 레퍼런스

다른 형태의 언어는 사용하는 구분에 따라 값-복사(copy), 레퍼런스 복사의 형태로 할당/전달한다.
자바스크립트에서는 어떤 방식으로 값을 전달/할당하는지 알아보자

자바스크립트에서는 레퍼런스(참조)된 값을 가리키므로 서로 다른 10개의 래퍼런스가 있따면 이들을 저마다 항상 공유된 단일 값을 개별적으로 참조한다.
대신 값의 타입만으로 값-복사(변수에 직접적으로 값을 할당) 혹은 레퍼런스-복사(변수에 변수를 할당)
둘 중 한쪽이 결정된다
예를 들면

let a =2
let b =a //b는 언제나 a에서 값을 !!복사!! 한다.
b++
a // 2
b // 3

let c =[1,2,3]
let d =c //d는 항상 c레퍼런스 하며 d가 바뀌면 c도 바뀐다.(d를 바꾸는건 c를 바꾸는 것과 같다)

d.push(4)
c //[1,2,3,4]
d //[1,2,3,4]

null, undefined,string,number,boolean 그리고 ES6의 symbol 같은 단순값(스칼라 원시값)은 언제나 값-복사 방식으로 할당/전달된다.
객채(배열과 박싱된 객체 래퍼)나 함수 등 합성값 은 할당/전달시 반드시 래퍼런스 사본을 생성한다.

여기서 c와 d는 단지 [1,2,3]이라는 값을 동등하게 참조(공유)만 한다는 사실이다, 따라서 d를 통해 [1,2,3]을 변경하면 [1,2,3]이라는 값을 동등하게 참조하고있는 c의 값도 같이 바뀐다.

중요!
레퍼런스는 변수가아닌 값 자체를 가리킨다

let a = [1,2,3]
let b = a
a // [1,2,3]
b // [1,2,3] a변수를 레퍼런스하는것이아닌 a변수에 들어있는 "값인 [1,2,3]을 참조"
//이후
b=[4,5,6] //이럴경우 [1,2,3]이 영향을 받지 않고 [4,5,6]으로 새로은 값이 할당된다.

여기서 함수의 경우를 살펴보면
만약 파라미터로 받은 배열에 4를 푸쉬한뒤
파라미터에 다시 [4,5,6]을 할당한뒤 7을 푸쉬한다면
어떤 일이 일어날까?

let a =[1,2,3]

function foo(x){
  x.push(4)
  x //[1,2,3,4]
  //그후
  x=[4,5,6]
  x.push(7)
  x //[4,5,6,7]
  
}
foo(a)

a// [1,2,3,4]

이런경우 a의 레퍼런스 사본이 x에 할당된다, x와 a는 모두 동일한 [1,2,3]값을 가리키는 별도의 레퍼런스다.
그런뒤 새로운 값을 할당 할경우([4,5,6])
초기 인자로 넘겨주었던 a에값을 더이상 바라보지 않게된다.
때문에 더이상 a가 가지고있던 [1,2,3]이라는 값이 변하지 않게된다.
때문에 a에값에 간단히 [4,5,6]이라는 배열을 넣으려면

let a =[1,2,3]

function foo(x){
  x.push(4)
  x //[1,2,3,4]
  //그후
  x.length=0 //모든 배열을 지움 즉 a가 가지고있던 배열을 모두 비움(새로운값을 할당하는것이아니다)
  x.push(4,5,6,7) //비워진 a가 가지고있던 배열에 push
  x //[4,5,6,7]
  
}
foo(a)

a// [4,5,6,7]

값-복사(값만 복사)/ 레퍼런스-복사(어떤 값을 참조하는걸 복사)이냐는 사용자 마음대로 결정 할 수 없음을 기억하자

합성값(배열같은)을 복사하여 사용하고 싶다면
순수한 값만을 복사하여 할당하는 함수를 사용하면된다.

let a =[1,2,3,4]
let b =a.slice() //얕은 복사에 의한 사본만들기
let a=[1,2,3,4]
b.push(1)
a // [1, 2, 3, 4]
b // [1, 2, 3, 4, 1]

반대로 반대로 스칼라 원시 값을 래퍼런스처럼 바뀐 값이 바로바로 원본에 반영되도록 넘기려면 원시 값을 다른 함성값(객체, 배열)으로 감싸야한다.

function foo(wraped){
 	wraped.a=42 
}
let wrap={a:2}
let ref=wrap
foo(wrap)

wrap.a //42
ref.a //42

Number()를 사용하는 방법이 있다고는 하지만 일반적이지도 않고 좋은 습관도 아니라고한다. 이방법은 과감히 스킵하도록하자

2.6 정리

  • 자바스크립트 배열은 모든 타입의 값들을 숫자로 인덱싱한 집합니다
  • 문자열은 일종의 '유사배열'이지만 나름 특성이 있기에 배열로 다루고자 하면 조심히 다루자
  • 자바스크립트의 숫자는 '정수'와 '부종 소수점 숫자' 모두 포함한다.
  • null타입은 null이란 값 하나뿐이고 마찬가지로 undefined타입또한 undefined뿐이다.
    void연산자는 모든 값을 undefined으로 바꾼다.
  • 숫자에는 NaN, +Infinity, -Infinity, -0같은 특수 값이 있다.
  • 단순 스칼라 원시값(문자열, 숫자 등등)은 값-복사에 의해 합성값(객체 등)은 레퍼런스-복사에 의해 값이 할당/전달된다
profile
RN/react.js개발자이며 배운것들을 제가 보기위해서 정리하기 때문에 비속어 오타가 있을수있습니다.

0개의 댓글