Toyo의 모든 글

for…in 문 사용시 주의할 점

getElementsByTagName 으로 특정Tag를 선택한후 for/in 문으로 인덱스를 뿌렸을때 이상한 것을 발견했다.

<h1>보라돌이</h1>
<h1>뚜비</h1>
<h1>나나</h1>
<h1>뽀</h1>
var tagH1 = document.getElementsByTagName("h1");

for(var i in tagH1){
  console.log(i);  // 0,1,2,3,length,item,namedItem
}

0,1,2,3 뿐만 아니라 length,item,namedItem 이란 요소가 따라왔다.
다시

document.getElementsByTagName("h1")[0]  // <h1>보라돌이</h1>
document.getElementsByTagName("h1")[1]  // <h1>뚜비</h1>
document.getElementsByTagName("h1")[2]  // <h1>나나</h1>
document.getElementsByTagName("h1")[3]  // <h1>뽀</h1>
document.getElementsByTagName("h1")[4]  // undefined

배열로 접근한다고 잡히지도 않는다.

getElementsByTagName 는 배열이 아니라 nodelist이다.

더군다나 for in 문은 배열의 인덱스를 출력하는것이라기보다 오브젝트의 property를 뱉어낸다.
배열도 오브젝트이다. 인덱스를 뿌려줄수있으나 nodelist라던가 특정 object의 경우 프로퍼티까지 다 나온다.

반대로 배열에서

var a = [ , , 3];

의 경우  일반 for문을 돌리면

for(var i=0; i < a.length ; i++) 
 console.log(a[i]); // undefined, undefined, 3

이렇게 나오지만  for/in문을 쓰면

for(var i in a)
console.log(i); // 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]

 

 

마트 유정란 집에서 부화시키기

2015년 5월 16일 토요일 저녁(0~1일차)

TV에서 스타킹을 보는데 닭이 하나 나와서 재주를 부린다.
놀라운건 이 닭이 마트에서 파는 유정란을 부화한것이라고..

스타킹에 출연한  마트 유정란으로 키운 천재닭 마트의 재주
스타킹에 출연한 마트 유정란으로 키운 ‘천재닭 마트’

어렸을때 학교앞에서 병아리를 많이 사서 닭으로 길렀던 기억이 나서..흥분
정말 되는지 테스트해보고도 싶고..
원래 부화기를   DIY해서 만들려면 전구 혹은 히터 그리고 박스나 온도조절기등 어느정도 준비를 해야하는데 마음이 급해서 마침 몇 주 전에 산 요구르트 제조기가 생각났다.

수은온도계와 롯데슈퍼에서 산 유정란 (사진을 못찍었다.)함께 사서 집에 있던 요구르트 제조기에서 부화를 시도하기로 작정

IMG_1687

(당일날 찍은사진은 아니지지만)요구르트 제조기가 공간이 좁아서 한알만 시도해야하는 상황..
유정란 10개중 맘에드는 하나만 골랐다.(유정란은 5월 14자 포장된걸 잘 골라 샀다. 너무 오래된건 부화률이 떨어진다고 하여)

제조기는 일정온도를 유지하는 단순한 기계이다. 하지만 40도는 도달하는듯하다.
인터넷에 찾아본 바 부화 적정온도37.5 ~ 37.7도 
요구르트 제조기 밑쪽이 좀 뜨거운듯 하여 아래 박스로 구조물을 조금 만든뒤 위로 띄워서
못쓰는 수면양말속에 달걀을 넣었다.

수은 온도계가 크기땜에 들어갈 곳이 없어서 ..ㅜ.ㅜ 그냥 방에 온도재는용도로 쓰고
온도를 제대로 재지 못해 난감해 하다 결국 온습도계를 인터넷으로 주문했다.

IMG_0602
 이 모델은 외부 온도를 같이 잴수 있는 센서와 선이 있는모델이다.
나와같은 상황에선 잘산거같다. 선만 부화기 요구르트 제조기안에 넣으면되니까..IMG_1686
너무 꽉 닫으면 환기가 안되기땜에 항상 살짝씩은 열어두었다.

참고로
요구르트 제조기는 1.5만원.
유정란 10개 4000원
온습도계 1만원 총 3만원이 들었다. (나머지 유정란은 먹었다~~ㅠㅠ)

5월 22일 금요일 (6~7일차)

일주일 정도 지난뒤 Candling(검란 : 불빛에 비춰서 알을 관찰하는것)을 해보았다.IMG_1672

IMG_1671

 

와!! 달걀 껍질이 좀 고르지않고 어두워서인지 잘 보이진 않았는데…사진을찍고보니
선명하진 않지만 실핏줄(vessel)이 보인다.(스마트폰 Flash light로)
진행되고 있구나… 안도의 한숨~ ^__^;

온도는 정확히 맞추기가 힘들었다.
어쩔때 보면 38도를 넘기도 하고 39도가 되어있기도 해서 열어서 식혀주기도하고..
온도계 센서를 알 위쪽에대면 35도대고 아래쪽에 대면 38도가 넘고 그래서…
그냥 감으로 이정도면 되지않을까 의구심가득…진행한 면이 있다.

온도는 28도정도인가 최소 발육이 진행되는 온도만 넘으면 발육이 멈추지는 않는것같다.
하지만 고온에 오래 노출되는것도 위험한.. 그래서 높은온도를 더 무서워했다.

적정온도인 37.7보다 약간높으면 부화가 하루정도 빠르고 약간 적으면 부화가 하루이틀 느리다고 한다.

중간중간 인터넷 검색을 하면서 많은 자료를 보았다.

 

전란(알을 굴려서 온도나 혈액순환을 돕는거..)을 해주어야 하는데
기계가 없으니 수동으로 해야했다. 거의 매일 해주긴했다.

중간중간 검란을 더 해봤는데 심장이 뛰는건지 발을 움직이는건지 검은게 꿀렁 꿀렁 하는것이
자라고 있는듯 했다. 사실  부화가 첨이고 일반적인 부화방식이 아니라서..
이게 썩고있는건지 의심이 갈때가 한두번이 아니었다는..

6월 4일 목요일 (18~19일차)

저녁에 찍은사진이다.
IMG_1683

Air Sac(사진에 보이는 공기층)이 크게 보인다.
18일이 되면 저 에어섹이 커지고 안이 검어진다고

보통 21일이 되면 나온다는데 6월 6일 토요일 전후로 나오지 않을까 기대를 하고있다.

6월 6일 토요일 (21~22일차)

5월 16일에 입란했지만 거의 10시넘은 밤에 한지라 사실상 오늘이 21일차라고 봐도 무방..하지만 아직 파각소식이 없다…머가 잘못된걸까? 온도가 낮아서일 가능성이 있다.. 그리고 전란을 18일째부터는 멈추라고 하는데 이건 기계를 써서 굴릴때 파각하는 애들이 있늘까봐 그런걸로 아는데…. 아닐지도… 어쨋든 난 몇번 좀 뒹굴뒹굴 해줬다..

오늘중에 나왔으면 좋으련만…23일째 나오는 애들도 있다고 하니 기다려봐야겠다..

 

현재시간  15:30분 – 병아리 소리가 들림 !!!!!!!

껍질 두두리는 소리 (볼륨업)..”똑 똑 똑 똑”…

“삐약 삐약” 소리

6월 7일 일요일 (22~23일차)

새벽2시 드디어 첫 파각 육안으로 확인!!
귀로는 몇시간동안 이어폰마이크를 껍질에 닿게해서 계속 듣고있었다. 녹음도하고..
너무 여리게 톡톡 쪼아서 그렇게 밖에 들리지 않았다.
구두발자국소리 처럼 일정한 비트.. 96비트 Moderato로
“콕-콕-콕-콕….”
거의 쉬질 않더란…어떻게 이럴 수 있는지 신기하다.
그래서 이게 심장박동인가?? 계속 의심했으나 중간중간 쉬는것을 봐서
부리로 쪼는 소리라고 생각…

IMG_1694
2초에 3번정도? 계산해본결과
20초에 30번..1분엔 거의 90번
한시간엔 약 500번..10시간엔 5000번..

어제 오후 3시부터라고 쳐도 10시간은 족히 넘었다.
그러니 저게 적어도 오천번은 쪼은거란거다..
난 그것도 모르고 열었다 놨다 뒤집었다..ㅡ.,ㅜ
부화직전에 전란을 하지말란 이유를 이제 알거같다.
아주 작은 힘으로 수없이 한곳을 쪼아서 겨우겨우 파각을 하는것인데..말이다!

나오는 동안 털이 말라 껍질에붙지 않도록 습도조절을 잘해야겠다.
이제 자고 일어나면 ..짜잔?~

오전 10시

IMG_1698

IMG_0612

 

죽었다..

내가 너무 마지막에 스트레스를 준것같다..  부리 위치가 아래쪽인거같은데
굳이 원인을 찾자면 마르지 않게한다고 습도를 너무 높인거같기도하고
알을 깨는데 기력을 다 소진한것같기도 하도..거의 다 깼는데..
결국 죽었다.
RIP..

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)

프로토타입(prototype)1 – 속성상속

create()

var stooge = {
	"first-name" : "Jerome",
	"last-name" : "Howard"
};
var another_stooge = Object.create(stooge); 

Object.create(stooge); 를 통해
stooge 오브젝트와 another_stooge는 프로토타입으로 연결되었다.
좀 더 정확히 말하면 another_stooge가 stooge의 요소를 상속받고 있다.

stooge와 another_stooge 모두 “first-name”, “last-name” 요소를 갖고있다.

먼저 stoogenickname을 추가해보자.

stooge.nickname ="Curly";
//stooge 
 Object {first-name: "Jerome", last-name: "Howard", nickname: "Curly"}

와 같은 결과를 갖는다.

이제 두 객체에서 nickname을 접근해보면

stooge.nickname         // "Curly"
another_stooge.nickname // "Curly"

로 같게 나온다.

another_stoogenickname을 추가해보다

another_stooge.nickname ="Moe";

그리고 두 객체에서 nickname을 접근해보면

stooge.nickname         // "Curly"
another_stooge.nickname // "Moe"

stooge의 nickname은 변경이 안되었다.
사실 another_stooge 도 변경됬다기 보다는
nickname 요소를 another_stooge객체 자신이 갖고있는 것과 stooge객체에서 상속받은것 두개인것이다.
대신 자신의 요소를 먼저 바라본다. 이를 확인해보기위해 nickname요소를 삭제해본다.

delete

객체의 요소를 삭제할때는 delete를 사용한다.

delete another_stooge.nickname;

이 때 자신의 것만 삭제되고 상속받은 것은 삭제되지 않아 계속 nickname접근이 가능하다.

stooge.nickname         // "Curly"
another_stooge.nickname // "Curly"

프로토타입의 요소로 접근되는것이라 stooge 와 같은 결과를 얻는다.

javascript에서 Object(객체)내의 element(요소) 접근

다음은 flight  객체이다.

var flight = {
	airline : "Oceanic",
	number : 815,
	departure : {
		IATA : "SYD",
		time : "2015-09-22 15:55",
		city : "Sydney" 

	},
	arrival: {
		IATA : "LAX",
		time : "2015-09-23 15:55",
		city : "Los Angeles"
	}
}

flight 객체안에 airline 요소를 접근하는 방법

  1. flight.airline      // “Oceanic”
  2. filght[“ariline”] // “Oceanic”

결과는 같다.

var stooge = {
	"first-name" : "Jerome",
	"last-name" : "Howard"
};

하지만 위  first-name 처럼 하이픈(“-“)이 들어가면 연산작업이 이루어지므로 따옴표(“) 고 감싸주어야 한다. 예약어를 굳이 사용하고자 할때도  마찬가지이다.
이때는 ‘.’으로 접근 할 수 가 없다.

  1. stooge.first-name      //NaN (x)
  2. stooge[“first-name”] // “Jerome” (o)

javascript 로또 자동번호 생성

우리나라 로또추첨방식은 6/45 즉, 45개의 공중에 6개의 공을 선택한다.
45개의 숫자중 6개의 숫자를 무작위로 선택 후 작은번호부터 재배열 하는것이다.

기존의  알고리즘이 난수를 발생하여 45개중 공을 선택하여 기존의 공과 중복이 되는지를 검출했다면
좀더 효율적인 방법이 없을까 하여. 한번 뽑아낸 공은 제외시키고 그 다음공은 나머지 공에서 추출하는 방법을 고민해보았다.
(이 방법이 현실과 닮아 있긴하다)

  • 일단 반복문을 통해 push()로 1 ~ 45번까지 aryLotto 배열에 담는다.
    그리고 바로  여섯 번에 걸쳐 공을 꺼내게 되는데
  • Math.random() 난수발생 후  Math.floor()를 통해 0~44 까지의 임의 숫자를 생성한다.
    보통 1~45의 숫자를 선택하기 위해 난수발생후 +1을 해주지만 이번방식은 배열의 위치만을 사용할것이므로 그대로 쓴다.
  • 만약 첫번째 난수로 33가 나왔다면 이는 ’34’이란 숫자가 선택된것이다. (idx 변수에 담았다)
    이 34이란 숫자는 나의 로또 번호중 첫번째가 되었다. 이를 배열 myLotto에 push()해서담는다.
  • 담고나서 기존 45개 배열aryLotto에서 선택된 숫자 ’34’ (배열index 로는 33)를 splice()를 통해 삭제한다. splice(idx,1) 에서 첫번째 인자는 배열 index이고 두번째 인자는 갯수이다.(한개의 번호만 제외하는것이므로 1을 사용했다.)
var myLotto = [];
var aryLotto = [];
var idx = 0;

for (var i = 1; i < 46; ++i) aryLotto.push(i)  

while (myLotto.length < 6){
    idx = Math.floor(Math.random()*aryLotto.length);
    myLotto.push(aryLotto[idx]);
    aryLotto.splice(idx,1);
}
  
myLotto.sort(sortNumber);
  
function sortNumber(a, b) {
  return a - b;
}
  • 다시 정리하면 45개의 숫자가 담긴 배열에서 33번째숫자(34)를 선택해서 새배열에 담고
    그 33번째 숫자(34)를 삭제한것이다.
  • 이 과정이 5번반복되면 되는데 중요한것은 빠진 숫자만큼 전체갯수가 줄었기때문에 난수발생시 그만큼 숫자를 줄여야 한다. (첨에 45개중에 골랐다면 그담은 44개중에 골라야하므로)  이는 aryLotto의 배열의 길이(aryLotto.length)와 같으므로 난수발생시 이 길이를 곱해주면된다.
  • 5개의 숫자가 담겼다. 하지만 무작위로 선택된 5개의 숫자를  작은 수 부터 정렬하면 좋겠다. javascript에서 sort()라는 정렬함수를 제공하지만 이는 수를 크기별로 정렬하지 못하고 알파벳순으로 정렬한다.(예를들어 5, 11,7,1,3,10,8 를 sort로 정렬하면 1,10,11,3,5,8 로 정렬된다  우리가 기대한 1,3,5,7,8,10,11이 아니다) 그러므로 펑션을 하나 써서 sort를 해주어야한다.
    function sortNumber(a, b) {
      return a - b;
    }
    aryMyLott.sort(sortNumber);
  • 이제 정렬이 제대로 된다.

이로서 javascript를 이용한  중복검사를 하지 않는 로또자동생성 프로그램을 만들어 보았다.이 프로그램에 사용된 js함수를 정리하면 다음과 같다.

  • push()
  • Math.random()
  • Math.floor()
  • splice()
  • sort()