카테고리 글 목록: 자바스크립트 개론(Eloquent JavaScript)

Marijn Haverbeke, 마레인 하버비케

this, call(), apply()

객체에 메소드를 부여하는 방법중 하나는 다음 코드처럼 함수 값을 객체에 첨가하는것이다

var rabbit = {}; // rabbit객체 생성

//speak메소드 부여
rabbit.speak = function(line) {
 console.log("토끼가말합니다: '", line, "'"); 
}

rabbit.speak("저는 살아 있어요."); 
// 토끼가말합니다 : ' 저는 살아 있어요. '

 

this

그런데 토끼 여러마리여서 서로 다른 말을 해야할때 객체안에 메소드인(rabbit.speak();)  speak() 안에 this는 해당 객체를 가르킨다.

function speak(line) { 
 console.log(this.adjective, "토끼가 말합니다:'",line,"'"); 
}

var whiteRabbit = {adjective: "흰색", speak: speak};
var fatRabbit = {adjective: "살찐", speak: speak};

whiteRabbit.speak("하얀토끼가 좋으세요?");
// 흰색 토끼가 말합니다:' 하얀토끼가 좋으세요? '
fatRabbit.speak("당근먹고싶다");
//살찐 토끼가 말합니다:' 당근먹고싶다 '

call(), apply()

이번엔 반대로 callapply을 이용해 함수에서 객체를 불러보자.

speak.call(fatRabbit,"꺼억~");
//살찐 토끼가 말합니다:' 꺼억~ '

speak.apply(fatRabbit,["냠냠"]); // 배열타입으로 인자를 넘김
//살찐 토끼가 말합니다:' 냠냠 '

주의할 점은 apply배열 ([]) 타입으로 인자를 넘겨야한다는 것이다. 기능은 같다.

function run(from, to) {
 console.log(this.adjective + "토끼가"
             , from + "에서" , to + "로 달려갑니다.");
}

var ary =["찬장","냉장고","집","학교"];

run.apply(fatRabbit, ary);
//살찐 토끼가 찬장에서 냉장고로 달려갑니다.

run.call(whiteRabbit, ary[2],ary[3]);
//흰색 토끼가 집에서 학교로 달려갑니다.

indexOf(), match()

indexOf()

일반적인 용도는 스트링에서 원하는 캐릭터의 인덱스값 추출이다.

var str = "산토끼 토끼야 어디를 가느냐 깡총깡총 뛰면서 어디를 가느냐";
str.indexOf("냐"); // 14 (인덱스는 0부터 시작하고 스페이스도 센다.)

분명 저 동요 마지막에도 “냐”가 있지만 처음 “냐”의 인덱스를 반환했다.
한 스트링에 여러개의 같은 캐릭터는 분명 존재한다. 이때는 어떤것을 추출하는지 모른다.
이럴대 시작 점을 지정해준다. 좀전에 14번째에 “냐”가 하나 있었으므로 그 다음으로 지정한다.

str.indexOf("냐",14); // 14 (x)
str.indexOf("냐",15); // 31 (o)

저말은 두번째 “냐”는 첫번째 인덱스에 +1을 한 만큼 부터 찾을 수 있다.

str.indexOf("냐",str.indexOf("냐")+1); // 31

이 str에는 “냐”가 두개뿐인데 이 범위를 넘어서 호출하면 어떻게 될까?

str.indexOf("냐",32); // -1

-1을 반환한다.
이 조건을 이용해 해당 캐릭터가 몇개있는지 세어볼 수 있다.

var char = "냐";
var idx = -1;
var sum = 0;
do {
 idx = str.indexOf(char,idx+1);
 if(idx != -1)
 sum++;
} while (idx != -1);
console.log("\""+char+"\"가 "+sum+"개 있습니다."); 
// "냐"가 2개 있습니다.

참고) lastIndexOf() – 뒤에서부터 검색한다.

match(regexp)

물론 이와같이 특정 문자를 문자열에서 알아보는데는 match()함수를 쓰면 간단하다. (정규표현식을 사용한다)

str.match(/냐/gi); // ["냐"],["냐"]
str.match(/냐/gi).length; // 2

flag

  • g : 완전일치(발생할 모든 pattern에 대한 전역 검색)
  • i : 대/소문자 무시
  • gi : 대/소문자 무시하고 완전 일치

생략할 경우 기본값은 g

클로저(Closure), filter()

function makeAdder(amount) { 
 return function (number) {
   return number + amount;
 };
}
var addTwo = makeAdder(2);
addTwo(3); // 5

클로저를 설명하는 예문이다.
저 예문에서 같은 결과를 갖는 호출은 이러하다

makeAdder(2)(3); // 5

이것을 어떻게 설명해야할까.  이러한 것을 ‘상향 펀아그 문제(upward Funarg problem)’  라고 한다고 책은 설명한다.

클로저 개념의 로컬변수를 참조하고 있는 함수 내의 함수이다.
일반적인 프로그램에서 함수가 리턴하면 그안에있는 local 변수는 소멸되나 클로저함수로부터 리턴된 익명함수의 경우 그 로컬변수가 유지된다 는 것이다.

function createFunction() {
 var local = 100;
 return function () {return local; };
}
createFunction()(); // 100

filter()

필터를 사용해보자.

먼저 1~10까지의 배열을 만들고

var arr = [1,2,3,4,5,6,7,8,9,10];

그중에 특정요소를 필터링해보자. 예) 3의 배수

arr.filter(function (n) { return n % 3 == 0 }) // [3,6,9]

n에 arr의 값들이 대입되어 3의 배수만 리턴된다.

이제 이것을 응용해 ‘2의 배수‘, ‘3의 배수’를 만드는 클로저를 만들어보자.

var arr = [1,2,3,4,5,6,7,8,9,10];

// 배수필터를 만드는 제너레이터
var generateFilter = function(x){
 return function(n) { return n % x == 0 };
}

var filter2x = generateFilter(2); // 2의 배수 필터
var filter3x = generateFilter(3); // 3의 배수 필터

arr.filter(filter2x); // [2,4,6,8,10]
arr.filter(filter3x); // [3,6,9]

 

 

isNaN(“123”) 은 왜 false를 리턴하는가?

isNaN()

NaN은 ‘Not a Number’란 표현이고 직역하면 ‘숫자가 아니다’가 된다.
isNaN(value)은  value가  ‘숫자가 아닌가? ‘라는 질문에
true 혹은 false를 리턴한다.

간단히 “apple”이란 문자열을 넣어보자.  isNaN(“apple”) 하면 “apple”이 String 이기 때문에 NaN(숫자가 아님)이므로 true를 리턴한다.

그런데 123은 어떨까? 숫자니까 false가 나온다..

여기까진 상상한데로다.

하지만  “123”은 어떻게 될까?

isNaN("123")  // false

음…..문자열인데 false 다. 타입을 확인해도

typeof "123" // "String"

“123”은 스트링이 맞다. 숫자가 아니니까 true를 리턴해야 하는게 아닌가?

isNaN("apple")  // true
isNaN("123")       // false <= ?
isNaN(123)         // false
isNaN("apple123")  // true
isNaN("123apple")  // true

따옴표를 한 “123” 은 문자열 type이지만 (숫자로만 이루어져 있을 경우)  isNaN(“123”)은  먼저 내부적으로 숫자 123으로 변경되기 때문에 false를 리턴한다.

prompt 로 입력받은 숫자

prompt로 숫자를 입력받아서 계산을 수행할 수 있을까?

원하는 정사각형의 가로나 세로 길이를 입력받아 면적을 제는 간단한 프로그램을 만들어보자

var theNumber = prompt("숫자를 입력하세요",""); // 입력 5
console.log(theNumber*theNumber); // 25

잉? 잘 되네..?? ㅋ

자 여기서 확인해 볼 필요가 있다.

theNumber*theNumber;
25
typeof theNumber;
"string"

그렇다 String이다.

그럼 결과값으로 출력한 25는 어떤 type일까?

var total = theNumber*theNumber;
typeof total;
"number"

숫자..라네요.

결국 곱하기 연산을 하면서 두개의 문자를 숫자로 인식해주었다.

그럼 “5”*”5″ 는 어떻게 될까?

"5"*"5"
25

역시나 숫자로 계산된 25이다.

즉 String 5와 String 5를 곱셈 (*) 연산하면 Number 25가 된다.

책에서는  prompt입력값이 String이란것을 알려주기 위해 다음과 같은처리를 해주었지만

//(42p 예제)
var theNumber = Number(prompt("숫자를 입력하세요",""));
alert("입력한 숫자의 제곱은 " + (theNumber * theNumber) + "입니다.");

테스트 결과 수식에서 내부적으로 Number 형식으로 바뀌는 관계로 저자가 든 예가 무색하게 되었다.

어쨌든 저자가 말하려고 했던 의도는 prompt 입력한 숫자의 결과가 String 타입이란 것과 이 문자열 값을 Number(String)를통해 숫자로 강제 변환할 수 있다는 점인듯 하다.

아마도 저 예제를 바로잡으려면 theNumber+theNumber 로 해야했을것이다.
(그래야 Number()를 사용하지 않으면 원치않는 답인 “55” 가 나온다는것을 보여줄 수 있었을 것이다.)

최종결론:
prompt로 숫자(5) 입력받으면 // “5” : String 타입

"5" * "5"//  25: Number
"5" *  5 //  25 : Number
 5  *  5 //  25 : Number
"5" + "5"// "55": String
"5" +  5 // "55": String
 5  +  5 //  10 : Number
 
 5 * "five" // NaN
 5 + "five" // "5five" : String

  • 피연산자 중 하나가 NaN이면 결과도 NaN 입니다.
  • 피연산자 중 하나가 숫자가 아니라면 이면에서 Number()를 적용하고 기타 규칙을 적용해 숫자로 변환합니다.
    니콜라스 자카스 의 프론트엔드 개발자를 위한 자바스크립트 프로그래밍(76- 77p)