[자바스크립트/Javascript] 타입, 값

cool_kim·2021년 7월 6일
1

Javascript

목록 보기
1/7
post-thumbnail

Chapter1 타입

동적언어인 자바스크립트는 타입의 개념이 없다고 생각할 수 있지만 존재한다.

1.2 내장 타입

✔ 자바스크립트의 내장 타입(7가지)

  • null
  • undefined
  • boolean
  • number
  • string
  • object
  • symbol

📍 typeof 연산자를 통해 값 타입 확인 가능

typeof undefined === "undefined"; // true
typeof true === "boolean"; // true
typeof 42 === "number"; // true
typeof "42" === "string"; // true
typeof { life : 42 } === "object"; // true
typeof Symbol() === "symbol"; // true
typeof function a(){ /* ... */ } === "function"; // true
typeof [1, 2, 3] === "object"; //true
typeof null === "object"; // true

✔ null은 typeof로 반환했을 때 "null"을 반환하지 않는다.

📍 null값을 정확히 확인하기 위한 코드

var a = null;
(!a && typeof a === "object"); // true



1.3 값은 타입을 가진다

✔ 값은 타입이 있지만, 변수에는 따로 타입이 없다.
42는 내장된 숫자 타입, "42"는 문자열 타입이지만 숫자 42에서 생성 가능

var a = 42;
typeof a; // "number"

a = true;
typeof a; // "boolean"

typeof typeof 42; // "string"

✔ 값이 없는 변수의 값은 undefined, typeof의 결과는 "undefined"
자바스크립트에서 undefined와 undeclared는 완전히 다른 개념이다.

  • undefined : 접근 가능한 범위에 변수가 서언되어 있으나 아무 값도 할당되지 않은 상태
  • undeclared : 접근 가능한 범위에 변수 자체가 선언조차 되지 않은 상태

하지만, typeof의 결과는 두개 모두 "undefined"이다.
--> typeof의 안전가드(safety guard) 때문

✔ typeof의 안전 가드 없이 전역 변수를 체크하는 방법
DEBUG 변수가 최상위 전역 스코프에 true로 선언되어있다.

1. if( typeof DEBUG !== "undefined" ) 조건문 사용
2. if( window.DEBUG ) 조건문 사용

두 가지가 있지만 window 객체를 통한 전역 변수 참조는 삼가는 것이 좋음.


✔ 오픈소스 코드를 자신의 코드에 붙여야 할때 오픈소스 코트에 특정 변숫값이 정의되어 있는지 확인해야할 때

첫번째 방법
function doSomethingCool() {
	var helper = 
   	(typeof FeatureXYZ !== "undefined") ?
       FeatureXYZ :
       function() { /* 기본 XYZ 기능 */ };
       
   var val = helper();
}

두번째 방법

//즉시 호출 함수 표현식
(function() {
	function FeatureXYZ() { /* 나의 XYZ 기능 */ }
    
    function doSomethindCool() {
    	var helper = 
    		(typeof FeatureXYZ !== "undefined") ?
        	FeatureXYZ :
        	function() { /* 기본 XYZ 기능 */ };
    	var val = helper();
   	}
    doSomethingCool();
})();

세번째 방법 (의존성 주입(Dependency Injection))

function doSomethingCool(FeatureXYZ) {
	var helper = FeatureXYZ ||
        	function() { /* 기본 XYZ 기능 */ };
    var val = helper();




Chapter2 값

2.1 배열

자바스크립트의 배열은 타입이 다른 문자열, 숫자, 객체, 심지어 다른 배열이나 어떤 타입의 값을 함께 담을 수 있다.

var a = [ 1, "2", [3]]

✔ 배열의 크기는 미리 정하지 않고 선언 가능, 원하는 값 추가 가능

📍 구멍난 배열

var a = [];

a[0] = 1;
a[2] = [3];

a[1]; // undefined

a.length; // 3

위 코드에서 a[1]은 undefined이긴 하지만 a[1] = undefined로 세팅한것과 똑같지는 않다.

  1. 배열 인텍스는 기본적으로 숫자이지만, 키/프로퍼티 문자열을 추가 할 수 있다.
    a["foobar"] = 2;
  2. 하지만 배열의 길이는 증가하지 않는다.
  3. 키로 넣은 문자열 값이 표준 10진수 숫자로 들어가게 되면 숫자 키를 사용한 것과 같이 된다.
    a["13"] = 42; a.length; // 14

🔸 배열 원소의 인덱스는 확실히 숫자만 사용하는 것이 좋다.

✔ 유사 배열 값을 진짜 배열로 바꾸고 싶을 때

  • 배열 유틸리티 함수(indexOf(), concat(), forEach())를 사용하여 해결
  • slice() 함수의 기능 차용 -> ES6부터는 Array.from() 사용
function foo() {
	var arr = Array.prototype.slice.call( arguments );
    arr.push( "bam" );
    console.log( arr );
}

foo( "bar", "baz" ); // ["bar", "baz", "bam"]



2.2 문자열

문자의 배열이라고 생각하지만 실제로는 생김새만 비슷할 뿐 문자 배열과 같지 않다.(유사배열)

📍 문자열은 불변 값, 배열은 가변 값이다

var a = "foo";
var b = ["f", "o", "o"];

a.length; //3
b.length; //3

a.indexOf("o"); //1
b.indexOf("o"); //1

var c = a.concat("bar"); //"foobar"
var d = b.concat(["b", "a", "r"]); //["f", "o", "o", "b", "a", "r"]

a === c; //false
b === d; //false

a; //"foo"
b; //["f", "o", "o"]

배열에서는 b[1]처럼 접근하는 것이 맞지만, 문자열은 a.charAt(1);로 접근해야 맞다.

📍 문자열은 불변 값이기 떄문에 내용을 바로 변경하지 않고 새로운 문자열을 생성한 후 반환하한다.

c = a.toUpperCase();
a === c; //false
a; // "foo"
c; // "FOO"

✔ 대부분의 배열 메소드는 문자열에 쓸 수 없지만 문자열에 대해 불변 배열 메소드를 빌려 쓸 수 있다.
var c = Array.prototype.join.call(a, '-');

✔ reverse()함수는 배열의 가변 메소드이기 때문에 문자열에서 빌려 쓸 수 없다. 해결책은 다음과 같다.
var c = a.split("").reverse().join("");



2.3 숫자

✔ 0.1 + 0.2 === 0.3; //false
-> 이러한 미세한 오차를 '머신 입실론'이라고 한다.
ES6에는 Number.EPSILON이 미리 정의 되어 있다.
이전 브라우저 폴리필은 다음과 같다

if(!Number.EPSILON) {
	Number.EPSILON = Math.pow(2,-52);
}

function numbersCloseEnoughToEqual( n1, n2 ) {
	return Math.abs( n1 - n2 ) < Number.EPISLON;
}

var a = 0.1 + 0.2;
var b = 0.3;

numbersCloseEnoughToEqual( a, b ); //true

✔ 정수인지 확인
ES6부터는 Number.isInteger()로 확인 가능
이전 브라우저 폴리필

if(!Number.isInteger) {
	Number.isInteger = function(num) {
    	return typeof num == "number" && num % 1 == 0;
    };
}



2.4 특수 값

✔ 값 아닌 값

  • undefined타입의 값은 undefined 뿐
  • null타입의 값은 null 뿐

  • null은 식별자가 아닌 특별한 키워드로 null변수에 뭔가 할당 할 수 없음
  • undefined는 식별자로 쓸 수 있음

✔ undefined

  • 느슨한 모드에서는 전역 스코프에서 undefined란 식별자에 값 할당 가능
  • 모드에 상관없이 undefined란 이름을 가진 지역 변수 생성 가능

✔ void

  • 어떤 값이든 무효로 만들어 항상 결괏값을 undefined로 만듬
  • 기존 값은 건드리지 않고 연산 후 값은 복구할 수 없다.



2.5 특수 숫자

✔ NaN(Not a Number)

  • 숫자 아님 보다는 유효하지 않은 숫자, 실패한 숫자, 몹쓸 숫자라고 하는게 정확
var a = 2 / "foo"; //NaN
typeof a === "number" //true

???????숫자 아님의 typeof는 숫자다???????
-> NaN은 경계 값(Sentinel Value)의 일종으로 숫자 집합 내에서 특별한 종류의 에러 상황을 나타낸다.
NaN은 다른 어떤 NaN과도 동등하지 않다. ∴ NaN !== NaN

📍 NaN여부 확인하기

  • isNaN()함수
var a = 2 / "foo";
isNaN(a); //true

isNaN()함수는 인자 값이 숫자인지 여부를 평가하는 기능이 전부

  • Number.isNaN()함수
    -> ES6이전 폴리필(1)
if(!Number.isNaN) {
	Number.isNaN = function(n) {
    	return (
        	typeof n === "number" &&
            window.isNaN(n)
        );
    };
}

var a = 2 / "foo";
var b = "foo";

Number.isNaN(a); //true
Number.isNaN(b); //false
        

-> ES6이전 폴리필(2)

if(!Number.isNaN) {
	Number.isNaN = function(n) {
    	return n !== n;
    };
}

✔ 무한대

  • 자바스크립트는 0으로 나누기 연산이 잘되어 있어 var a= 1 / 0;연산을 해도 에러 없이 Infinity값이 나옴
  • 무한을 무한으로 나누면 NaN값.

✔ 0(영)

  • 음의 영(-0)
  • 양의 영(+0)

ES6부터는 두 값이 절대적으로 동등한지 확인하는 새로운 유틸리티 Object.is()를 지원
-> 특이한 동등비교에 사용

var a = 2 / "foo";
var b = -3 * 0;

Object.is( a, NaN ); //true
Object.is( b, -0 ); //true
Object.is( b, 0 ); //false

폴리필

if(!Object.is) {
	Object.if = function(v1, v2) {
    	//'-0' 테스트
        if( v1 === 0 && v2 === 0 ) {
        	return 1 / v1 === 1 / v2;
        }
        //'NaN' 테스트
        if( v1 !== v1 ) {
        	return v2 !== v2;
        }
        //기타
        return v1 === v2;
    };
}



2.6 값 vs 레퍼런스

  • null, undefined, string, number, boolean, symbol과 같은 단순 값은 항상 값-복사 방식
  • 객체, 함수 등 합성 값은 항상 레퍼런스-복사 방식
  1. 배열
var a = [1,2,3];
var b = a;

a; //[1,2,3]
b; //[1,2,3]

b = [4,5,6];

a; //[1,2,3]
b; //[4,5,6]
  1. 함수 인자
function foo(x) {
	x.push( 4 );
    x; //[1,2,3,4]
    
    x = [4,5,6];
    x.push( 7 );
    x; //[4,5,6,7]
}

var a = [1,2,3];
foo(a);

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

하지만, x = [4,5,6]; x.push( 7 ); 대신 x.length = 0; x.push(4,5,6,7);으로 코드를 코치면 a는 [4,5,6,7]이 된다.
-> x.length = 0; x.push(4,5,6,7);은 새배열을 생성하는 코드가 아니라 이미 두 변수가 공유한 배열을 변겅하는 코드기 때문.

✔ 인자없이 slice()를 호출하면 전혀 새로운 배열의 사본을 만든다. 그렇게 복사한 사본만을 가리키는 레퍼런스를 전달하니 foo()는 a의 내용을 건드릴 수 없다.

  • 스칼라 원시 값을 레퍼런스처럼 바뀐 값이 바로바로 반영되도록 넘기기 위해서는 원시 값을 다른 합성 값(객체, 배열)으로 감싸야한다.
  • 스칼라 원시 값을 Number 객체 래퍼로 감싸면 Number 객체의 레퍼런스 사본이 함수에 전달되는 것은 맞지만 자동으로 공유된 원시 값을 변경하지 못한다.
    -> Number객체에서 자동 언박싱되므로 바깥의 원시값은 변경되지 않은 불변의 원본 Number 객체를 참조한다.
profile
FE developer

1개의 댓글

comment-user-thumbnail
2021년 7월 7일

정리왕 아니신가여??👍

답글 달기