동적언어인 자바스크립트는 타입의 개념이 없다고 생각할 수 있지만 존재한다.
✔ 자바스크립트의 내장 타입(7가지)
📍 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
✔ 값은 타입이 있지만, 변수에는 따로 타입이 없다.
42는 내장된 숫자 타입, "42"는 문자열 타입이지만 숫자 42에서 생성 가능
var a = 42;
typeof a; // "number"
a = true;
typeof a; // "boolean"
typeof typeof 42; // "string"
✔ 값이 없는 변수의 값은 undefined, typeof의 결과는 "undefined"
자바스크립트에서 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();
자바스크립트의 배열은 타입이 다른 문자열, 숫자, 객체, 심지어 다른 배열이나 어떤 타입의 값을 함께 담을 수 있다.
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로 세팅한것과 똑같지는 않다.
a["foobar"] = 2;
a["13"] = 42; a.length; // 14
🔸 배열 원소의 인덱스는 확실히 숫자만 사용하는 것이 좋다.
✔ 유사 배열 값을 진짜 배열로 바꾸고 싶을 때
function foo() {
var arr = Array.prototype.slice.call( arguments );
arr.push( "bam" );
console.log( arr );
}
foo( "bar", "baz" ); // ["bar", "baz", "bam"]
문자의 배열이라고 생각하지만 실제로는 생김새만 비슷할 뿐 문자 배열과 같지 않다.(유사배열)
📍 문자열은 불변 값, 배열은 가변 값이다
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("");
✔ 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;
};
}
✔ 값 아닌 값
✔ undefined
✔ void
✔ NaN(Not a Number)
var a = 2 / "foo"; //NaN
typeof a === "number" //true
???????숫자 아님의 typeof는 숫자다???????
-> NaN은 경계 값(Sentinel Value)의 일종으로 숫자 집합 내에서 특별한 종류의 에러 상황을 나타낸다.
NaN은 다른 어떤 NaN과도 동등하지 않다. ∴ NaN !== NaN
📍 NaN여부 확인하기
var a = 2 / "foo";
isNaN(a); //true
isNaN()함수는 인자 값이 숫자인지 여부를 평가하는 기능이 전부
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;
};
}
✔ 무한대
var a= 1 / 0;
연산을 해도 에러 없이 Infinity값이 나옴✔ 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;
};
}
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]
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의 내용을 건드릴 수 없다.
정리왕 아니신가여??👍