2. JS는 다음의 값을 거짓으로 처리(따라서 거짓이 아니면 참)
"", 0, false, undefined, null, NaN
Boolean
문제점
1. C스타일(false가 없고 0을 사용하는)을 뛰어넘어 다양한 편법으로 상태를 검사하는 코드가 난무하게 됨
function(){
if(arguments.length) ...
function(v){
if(v.trim()) ...
2. 가부 판정을 위한 메소드의 부재로 숫자나 문자열을 false대용으로 사용
if(str.indexOf(token) > -1) ...
if(i = str.indexOf(token), i > -1) v = str.substring(0, i);
3. true, false의 용도를 정확히 하고 자주 쓰는 판정에 대한 Boolean 메소드 제공
Boolean
if(v === parseInt(v, 10)) ...
if(Number.isInteger(v)) ...
if(v === v + 1 || v === v - 1) ...
if(Number.isFinite(v))...
str = 'abcd';
if(str.indexOf('c') > -1) ...
if(str.include('c')) ...
if(str.substr(0, 3) === 'abc') ...
if(str.startsWith('abc')) ...
if(str.substr(str.length - 3) === 'bcd') ...
if(str.endsWith('bcd')) ...
Array.prototype.include = function(v){
return this.indexOf(v) > -1;
};
4. 애매한 부정값으로 사용되므로 값이 없음을 나타내도록 언어의 여러 요소에서 강제하여 제한된 용도로 사용하게 유도함
undefined
let [a, b = 3] = [1];
console.log(b); //3
let [a, b = 3] = [1, undefined];
console.log(b); //3
const test = function(a, b = 3){
return a + b;
};
console.log(test(1)); //4
console.log(test(1, undefined)); //4
6. 어떠한 코드 상의 힌트도 없고, 단지 언어스펙을 외운 사람만 의미가 파악됨.
또한 언어에 정의된 키워드가 아니므로 오용할 수 있음.
arguments
const test = function(){
let arguments = 3;
console.log(arguments);
}
test(5,6,7) //3
const test = function(...arg){
console.log(arg);
};
코드에서 인식할 수 있는 명확한 표현으로 변경
7. 자동 형변환 등 엔진의 내재된 작동에 사용자가 작성한 객체가 반응하게 하려면
어떠한 코드 상의 힌트도 없고, 단지 언어스펙을 외운 사람만 의미가 파악됨.
Symbol
let a = {
toString:_=>'a',
valueOf:_=>1,
toJSON:=>'{a:1}'
};
let a = {
[Symbol.toPrimitive]:hint=>hint == 'string' ? 'a' : 1
};
코드에서 인식할 수 있는 명확한 표현으로 변경
toString, valueOf, toJSON 등은 일반문자열이므로
이것이 엔진 작동에 반응하는 트리거 메소드라는 것을
코드로 알 방법은 없고
언어스펙을 외운 사람만 의미가 파악됨.
코드 상으로 시스템요구사항에 맞는 트리거 메소드임을 이해할 수 있음.
9. for, while 등 문으로 구성된 반복은 실행될 수 있으나 값으로 존재할 수 없음.
Iterable & Iterator
1. 사전에 정의가 끝난 정적 값에 대한 반복만 처리가능
2. 반드시 반복전에 반복할 대상에 대한 사전처리가 완료되어있어야 함.
3. 반복처리될 대상에 대한 지연처리가 불가
인자로 전달하거나 상태를 기억해 둘 수 없음
표준적인 값형태의 반복처리기가 필요.
1. 수식적이거나 알고리즘을 통해서 계산될 값은 미리 정의해둘 이유가 없음
2. 메모리에 정의한적이 없어도 반복시점에 자원을 확보하여 반환할 수도 있음
지연실행의 필요성
10. 반복이란 매 반복시 마다 실행될 부분과 계속 반복할지 여부로 구성됨
Iterable & Iterator
while(계속 반복할지 여부){
반복시마다 실행될 부분
}
1. 반복시마다 실행될 부분 → next()메소드를 호출함
2. 계속 반복할지 여부 → next로 반환된 객체의 done키에 있는 boolean값
Iterator Protocol
let [a, b, c] = 'abc';
let a = [...'abc'];
const test = (...arg)=>arg.join('-');
test(...'abcd'); //'a-b-c-d'
문자열은 Iterable(Iterator를 반환함)
12. Iterable을 직접 제작하는 것은 상당한 반복작업을 유발하므로 언어차원에서 간략한 문법을 제공함.
generator
const gene = function*(max){
let cursor = -1;
while(cursor++ < max) yield cursor * cursor;
};
((...a)=>console.log(a))(...gene(10)); //[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
1. 객체 컨텍스트를 변수 컨텍스트로
2. 제어문을 중지시킬 수 있는 yield
실행중인 EC에 대해 suspend를 걸 수 있는 기능을 엔진차원에서 추가
14. 결국 OOP는 여전히 가장 주요한 아키텍쳐방법론임. 프로토타입을 통해 대체가능성과 내적동질성을 확보할 수는 있음.
Class
var Dog = function(){};
Dog.prototype.bark = function(){
return 'woof';
};
var Spitz = function(){};
Spitz.prototype = new Dog();
Spitz.prototype.bark = function(){
return 'woof woof'; //더 시끄러움.
};
var dog = new Spitz();
console.log(dog instanceof Dog); //true 대체가능성
console.log(dog.bark()); //woof woof 내적동질성
15. 중복키의 문제
1. 프로토타입은 단일 체이닝 구조로 대상객체가 상위객체의 키를 가리도록 되어있음.
2. 따라서 반드시 하위클래스는 상위의 이름을 피해서 제작해야만 상위구조와 상호작용할 수 있음.
Class
var Parent = function(){};
Parent.prototype.test = function(){return 'parent';};
var Child = fuction(){};
Child.prototype = new Parent();
Child.prototype.test = function(){return 'child';};
var child = new Child();
child.test(); //child
생성자에서도 제네릭화된 로직을 짤 수 없고 반드시 하드코딩된 클래스 이름을 알아야만 생성자 체인을 시킬 수 있음.
var Parent = function(a){
this.a = a
};
var Child = fuction(a, b){
Parent.call(this, a);
this.b = b;
};
16. 새로운 컨텍스트 객체 도입
1. 기존 this가 현재 주체가 되는 객체의 참조로서 동작하는 컨텍스트를 제공했다면,
2. super는 상위클래스에 대한 상대경로를 제공하는 새로운 컨텍스트를 제공함.
단 사용되는 곳에 따라 의미가 달라짐.
Class
const Parent = class{
constructor(a){
this.a = a;
}
}
const Child = class extends Parent{
constructor(a, b){
super(a); //Parent.call(this, a); 에 해당됨
this.b = b;
}
}
생성자에서 사용되는 경우
17. Class
const Parent = class{
test(){
return 'parent';
}
};
const Child = class extends Parent{
test(){
const v = super.test(); //Parent.prototype.test.call(this) 에 해당
return v + ':child'
}
};
메소드에서 사용되는 경우
그 외에도
1. 생성자를 일반 함수로 호출하려하거나
2. 메소드를 생성자로 사용하려 하는 등의 오용되는 문제에 대해
예외를 발생시켜 방어하도록 설계됨.