레이블이 javascript인 게시물을 표시합니다. 모든 게시물 표시
레이블이 javascript인 게시물을 표시합니다. 모든 게시물 표시

2021년 12월 3일 금요일

Clojure를 하다가 Node.js/Express 만져보면서 느낀점.

Javascript는 참으로 특이한 언어이다. 문서를 보지 않아도 대충 보고 쓸 수 있을 정도로 직관적이다. 당연히 공식문서를 우선적으로 읽고 차근차근 문법을 익히고 개발을 진행하겠지만, 이상하게도 자바스크립트를 만질 때는 공식문서를 보지 않고, 기존 코드를 보더니 '음... 이렇게 돌아가는 거군?' 하면서 간단한 코드를 짤 수 있게 되었다. '물론 null이 Object라던가 하는 문제는 그 이후에 하나씩 알아가는 거고'

Node.js/Express를 현업에서 쓰진 않았지만 어떻게 사용하는지 궁금했다.

해서 Udacity강의를 들어보고 아래 유튜브를 보았다.


고백하자면 Clojure의 웹개발과 아주 비슷하다는 느낌을 받았다. 유튜브 강의에서 나오는 몇몇 기능은 개선을 하면 좀 더 멋지게(생산성있게) 개발할 수 있겠다는 생각도 했다. 

2019년 11월 22일 금요일

[javascript] jQuery는 어떻게 new가 없이 생성되는 것일까?

언젠가 개발을 처음 시작할 때 나프다의 [정개발]이라는 분과 슬랙 대화를 한 적이 있다. 당시 나는 자바만을 배우고 있었음에도, 그분은 언젠가 jQuery소스를 공부해야 한다고 했었다.
그때는 자바 개발하기에도 바쁜데 자바스크립트에 제이쿼리를 사용하는 것도 모자라 소스를 공부?
라고 생각했었다.

지금은 자바스크립트를 배우는 것이 자바에도 도움되고 더 나아가 내가 어떤 언어를 짜던 잘나가는 오픈소스를 읽는 것이 중요하다고 생각한다. (하지만 제대로 소스를 판 적은...)
오늘은 제이쿼리에게서 한 수 배워보고자 한다.

오늘 주제는 jQuery는 어떻게 new를 없앴는가? 이다.
대부분 new를 없앤다는 것은 생성자를 사용하지 않는 다는 말과 같다. 매개변수가 필요하면 그 매개변수를 클로저로 덮어서 함수로 던지는 것이다. 그러면 그 함수의 스코프는 자기 함수 밖에 있는 변수(매개변수 포함)를 덮은(closure) 채로 세상 밖에 내던져진다.

하지만 jQuery는 그런 것 같지 않다. 어떻게 그렇지 않은지 아주 쉬운 방법으로 알 수 있다.


이처럼 __proto__안에 수 많은 함수들이 있는 것을 알 수 있다. 어쩌면 당연한 것이다. 수 많은 기능들을 사용할 때마다 각 Execution Context에 포함해서 던진다는 말은 사용할 때마다 동일한 기능들이 메모리에 올라간다는 말이니까. 한 두개면 괜찮겠지만 저게 몇 개인가!

new를 사용하는 것이 분명하다.

그렇다면 jQuery소스를 한번 구경해보자.

jQuery($)의 정체

제이쿼리 사이트에서 일반 개발용소스를 참고했다.(https://code.jquery.com/jquery-3.4.1.js)

일단 jQuery를 실행하면 무엇이 나오는가? 그것이 시작일 것이다. 일단 jQuery는 함수일 것이다. 그러니까 new가 없이 실행되는 것이다. jQuery라는 이름의 함수를 찾아보자.

var version = "3.4.1",
  // Define a local copy of jQuery
  jQuery = function( selector, context ) {
  // The jQuery object is actually just the init constructor 'enhanced'
  // Need init if jQuery is called (just allow error to be thrown if not included)
      return new jQuery.fn.init( selector, context );
  },
...


그렇다. jQuery를 실행하면 내부적으로 jQuery.fn.init이라는 생성자를 실행한다!

jQuery.fn.init의 정체

이제부터는 jQuery의 소스를 이용하면, 말이 많아지기 때문에, 제이쿼리의 간단한 구조를 간단히 설명하고자 한다. 조그만한 자바스크립트 라이브러리를 만들었다고 해보자.

/*
제이쿼리 구조를 설명하기 위해 만든 간단한 함수
*/
(function (global) {
  var mini = function(name) {
    return new mini.fn.init(name);
  }

  mini.fn = mini.prototype = {};

  mini.fn.sayHi = function() {
    console.log("hi " + this.name);
    return this;
  }

  mini.fn.sayBye = function() {
      console.log("bye " + this.name);
      return this;
  }

  mini.fn.init = function(name) {
    this.name = name;
  }

  mini.fn.init.prototype = mini.fn;
  global.mini = mini;
})(window);

var M = mini("Nam");
M.sayHi().sayBye();

녀석의 이름은 mini다. 즉시실행함수를 만들어서 스코프가 밖으로 나가지 못하도록 하였으며 글로벌에 mini라는 변수를 넣어야 하기 때문에 window를 매개변수로 넣었다.

jQuery에서는 이 global이라는 녀석을 받아서 jQuery에서 사용할 수 있는 녀석인지 유효성 체크도 한다.
그리고 mini를 실행하면 mini.fn.init 이라는 객체를 뱉을 것이다.
한번보자.

실행해보자.
var M = mini("nam");
M.sayHi().sayBye();

잘 보면 두 함수를 연속적으로 실행할 수 있는데, 이건 다른 라이브러리에서도 많은 보았을 것이다. 이렇게 함수를 연결하는 방법은 우리가 사용하는 객체를 계속 리턴하는 것이다.
그런데 왜 prototype이 아니라 fn이라는 녀석에 넣은 것일까?

아마 계속 쓰일 녀석이라서 fn으로 간편화 한게 아닌가 추측을 하지만 정확한 이유는 모른다.
이렇게 new가 없이 객체를 생성하는 방법을 알아보았다.

2019년 4월 18일 목요일

[SICP][scheme][javascript] 아무것도 없는 것에서 무언가를 추상화할 수 있을까?

출처 : https://youtu.be/ymsbTVLbyN4

SICP의 강의는 손이 잘 가지 않는 영상이다.
난 항상 SICP를 읽어보려고 하지만 일주일이 지나면 역시나 다른책을 꺼내 읽고 있다. 이 못난 학생은 그럼에도 황새를 쫓아가보겠다고 공부를 하다. 그러다 나는 아래의 코드를 보게 되었다.

(define (cons a b)
  (lambda (pick)
    (cond ((= pick 1) a)
          ((= pick 2) b))))

(define (car x) (x 1))
(define (cdr x) (x 2))

;; 설명을 위해 더해진 것.
(define A (cons 1 2))
(print (car A))
(print (cdr A))
이 비디오를 보는 사람들 중 몇몇은 이미 이 내용을 설명할 때, 경악을 금치 못했다.
이 코드가 대체 무엇이길래 그러는가. 난 이 코드에서 모든 추상화가 시작되진 않았을까 상상해본다.
데이터 추상화란 무엇인가.

1. "부를 수 있게 된다"는 것이다. (그것은 이름업슨 데이터에 이름을 부여함으로써 시작된다. 하여 머릿속으로도 다룰 수 있게 된다.) 2. 그것은 마치 마술사가 주문에 이름을 부여하는 것처럼 마법과 같다.
3. 우리는 이 개념의 정확한 정의를 나중에 내릴 수 있게 된다. (일단 이름을 부름으로써 나중에 해당 개념에 대한 정의가 바뀌면 그 정의만 바꾸면 되는 것이다.)
4. (추가적인 특징) 우리는 이 추상화된 데이터를 강제할 수 있다.

rational number

이 책에서 설명하는 rational number에 대해 알아보자.
우리는 일단 데이터를 추상화 해야 한다.
분자 n, 분모 d가 있다.
이것들을 make-rat에 넣어서 하나의 패키지를 생성하는 것이다.
(make-rat n d) ; rational-number (n,d의 저장형태는 알 필요없음)
(numer rational-number) ; numerator 분자
(denom rational-number) ; denominator 분모
여기서 make-rat는 constructor, numer/denom은 selector라고 불린다.
하나의 데이터는 이렇게 constructor와 selector로 추상화될 수 있다.

이렇게 유리수를 make-rat, numer, denom으로 추상화 하였다.
한번 더하기를 해보자.
(define (+rat x y)
  (make-rat
    (+ (* (numer x) (denom y))
       (* (numer y) (denom x)))
    (* (denom x) (denom y))))
여기서 x,y는 rational-number이다. 우리는 x,y가 어떻게 구현되어 있는지는 모르지만, make-rat으로 유리수를 만들 수 있고, numer/denom으로 꺼낼 수 있는 것은 알고 있다.
곱하기도 만들자.
(define (*rat x y)
  (make-rat)
  (* (number x) (number y))
  (* (denom x) (denom y)))
자 이제 유리수를 구현해보자. 어떻게 담아야 할까?
(define (make-rat n d) (cons n d))
(define (numer x ) (car x))
(define (denom x) (cdr x))
이렇게 cons를 이용하여 pair(리스트)를 만든다.
그러니 rational-number란 무엇인가?
이것은 사실 pair일 뿐이다. 첫번째와 두번째라는 박스가 있는 자료구조일 뿐이다.
그렇다면 pair라는 것은 무엇인가? 이것도 또한 두개의 값이 쌍으로 있는 추상적인 개념이다.
쌍의 개념은 cons라는 constructor와 car, cdr이라는 selector로 표현할 수 있다.
여기서 car는 첫번째 값을 말하며, cdr는 두번째 값을 지칭한다.

아무것도 없는 곳에서 추상화하기

그렇다면 pair라는 추상적인 개념은 무엇을 기반으로 만들어진 개념일까?(무엇 위에서 쌓여 올라왔는가)
여기서 저자는 아무것도 없는 곳에서 생성된다고 한다.

(define (cons a b)
  (lambda (pick)
    (cond ((= pick 1) a)
          ((= pick 2) b))))

(define (car x) (x 1))
(define (cdr x) (x 2))
여기에 데이터가 보이는가? 어떤 자료구조가 보이는가?
쌩뚱맞게 lambda가 보인다. cons는 일단 procedure를 다시 리턴한다.
그리고 pick이라는 변수를 받는데 그것이 1이면 a를 리턴하고, 2이면 b를 리턴하는 함수를 리턴한다.

car를 보자. (x 1)라는 함수가 있는데, 여기서 1이 매개변수 pick에 매핑될 것이다. 첫번째 값(즉 a)이 리턴
cdr에서는 (x 2)로써, pick=2가 되고 두번째 값(b)가 리턴된다.

대체 어디에 a,b는 어떤 자료구조로 저장되는 건가? 아무것도 아닌 것에 저장되는 느낌이다.
closure를 이해한다고 생각했는데 이 코드를 보자마자 '이해한다는 것이 무엇인지조차 모르고 있었구나' 라는 생각이 들었다.

공부를 하면 할 수록, 항상 겸손해야 한다는 생각을 할 수 밖에 없다.
겸손하지 않으면, 배울 수가 없으니...

강의 교수님이 한 말씀을 여기 기록하지 않을 수가 없다.
So in some sense, we don't need data at all to build these data abstractions.
We can do everything in terms of procedures.

procedures are not just the act of doing something.
procedures are conceptual entities, objects.

강의가 끝나고 한 지적인 학생이 엄청난 질문은 하는데 그것은 바로
(cons 1 2)의 값과 1초 후 동일한 프로시저호출인(cons 1 2)는 동일한 녀석인가? 이다.

이 말은 (+ 2 2)와 4는 동일한 녀석인가?

를 고민하는 문장인데, 한시간의 수업에서 대체 이런 질문이 어떻게 나올 수 있었을까? 라는 생각이 든다.
이 질문에 대해 교수님은 당장은 대답할 수 없지만, 곧 하게 될 것이라는 말을 하며 끝낸다.

언제 저 답을 알 수 있을까? 빨리 다음을 알고 싶다.

여담으로 아래 scheme코드를 js로 변경해보자.
function cons (a , b) {
  return function(pick) {
    switch(pick) {
    case 1 : 
      return a;
    case 2 :
      return b;
    default:
      return;
    }
  }
}

function car(x) { return x(1); }
function cdr(x) { return x(2); }

var procedure = cons(10,20);

car(procedure);
cdr(procedure);

2019년 3월 5일 화요일

[javascript] 그래서 커리를 어떻게 쓰려고?

내가 처음 커리를 써야겠다고 생각했던 것은 로직이 계속 겹치고 있었기 때문이다.

예를들어보자. 인풋별로 유효성을 체크하는데 유효성을 체크할 때마다 하는일이 조금씩 다르다.

// 핸드폰 인풋 묶음 3개 유효성검사
var phone_1 = trim($("#phone_1").val());
var phone_2 = trim($("#phone_2").val());
var phone_3 = trim($("#phone_3").val());

if (phone_1 < 3 && phone_1 ...) { // 여러 and문과 유효성검사
  $("#phone_1").focus();
  alert("휴대폰 유효성검사실패");
  return;
}
... phone_2, phone_3

$("#hidden_phone_").val(phone_1 + "-" phone_2 + "-" + phone_3);
이런 코드가 있다. 익숙하다. 일단 커리를 쓰기 전에 여기서 if문에 있는 모든 유효성검사는 하나의 함수로 추상화 해놓자.
if (validate(phone_1) { // 여러 and문과 유효성검사
  $("#phone_1").focus();
  alert("휴대폰 유효성검사실패");
  return;
}
if (validate(phone_2) {
  $("#phone_2").focus();
  alert("휴대폰 유효성검사실패");
  return;
}
if (validate(phone_3) {
  $("#phone_3").focus();
  alert("휴대폰 유효성검사실패");
  return;
}
이런 코드를 본 적 없는가? 여기서 분명 우리는 뭔가 더 할 수 있을 것 같다. 어떻게 해야 할까? 맞다 객체를 넘기는 것이다.
function validateAndFocus(phone, $phone) {
  if (!validatePhone(phone) {
    alert("휴대폰 유효성검사 실패");  
    $phone.focus();
    return false;
  }
  return true;
}
이제 이걸 사용해보자
if(!validateAndFocus(phone_1, $("#phone_1")) {
  return;
}
if(!validateAndFocus(phone_2, $("#phone_2")) {
  return;
}
if(!validateAndFocus(phone_3, $("#phone_3")) {
  return;
}
...
뭔가 여기도 겹치는 것 같다.
if (!(validateAndFocus(phone_1, $("#phone_1")) &&
      validateAndFocus(phone_2, $("#phone_2")) &&
      validateAndFocus(phone_3, $("#phone_3"))) {
  return;
}
undersocre.js의 and가 있다면 더 좋을 것 같다. 끝이 없을 것 같으니 여기서 넘어가자. underscore를 쓸 수 없어서 curry만 임시 사용하는 것이다. 이정도만 깔끔해보인다. 그런데 여기 주민번호와 카드번드까지 추가되었다.
var card1 = $("#card1");
var card2 = $("#card2");
var card3 = $("#card3");
var card4 = $("#card4");

var national_id_1 = $("#national_id");
var national_id_2 = $("#national_id");

우리는 validateAndFocus를 사용할 수 있는가? 사용할 수 없다. 1. 휴대폰번호의 유효성과 아이디의 유효성은 다르다. (predicate의 분리 필요) 2. 유효성 검사 이후 하는 일이 각각 다를 것이다. 하지만 형태는 동일하다. 1. 주어진 값의 유효성을 검사한다. 2. 통과하면 true 실패하면 false를 리턴한다. 3. 유효성검사에 실패하여 false를 리턴하기 전에 각각 해야하는 일이 있다.
// 동일한 형태의 일을 하는 함수 템플릿 정의
function validateTemplate(pred, func, obj1, obj2, obj3) {
  if (pred(obj1, obj2, obj3)) {
    func(obj1, obj2, obj3);
    return false;
  }
  return true;
}
자 이 형태를 만들었다. 이제 어떻게 사용할지 보자. 유효성 검사를 분리해서 넣을 것이다. 그렇다면 이렇게 될 것이다.
var curried = curry(validateTemplate);
var validatePhoneCurried = curried(function(n) {
  return validatePhone(n);
});
var validateCardCurried = curreid(function(n) {
  return validatePhone(n);
});
var validateNationalidCurreid = curried(function(n) {
  return validateNationalid(n);
});
뭐 대충 이렇게 코딩했다. 현재까지는 pred를 커리하고 이제는 각 pred마다 할일을 다르게 넣을 수 있겠다. 첫번째 인자를 _로 한 이유는 focus나 val("") 등 DOM조작을 위한 객체를 따로 넘긴다고 하자.
var validatePhoneFinal = validatePhoneCurried(function(_, $dom) {
  alert("헨드폰문제!");
  $dom.focus();
});

var validateCardFinal = validateCardCurried(function(_, $dom) {
  alert("카드문제");
  $dom.focus();
});

var validateNationalidFinal = validateNationalidCurreid (function(_, $dom) {
  alert("주민번호문제");
  $dom.focus();
});
이렇게 만들었다 치자. 이제 위에 코드를 저것들로 바꾸면 된다.
if (!(validatePhoneFinal(phone_1, $("#phone_1")) &&
      validatePhoneFinal(phone_2, $("#phone_2")) &&
      validatePhoneFinal(phone_3, $("#phone_3"))) {
  return;
}

....
if (!(validateCardFinal(card_1, $("#card_1")) &&
      validateCardFinal(card_2, $("#card_2")) &&
      validateCardFinal(card_3, $("#card_3")) &&
      validateCardFinal(card_4, $("#card_4"))) {
  return;
}
...
if (!(validateNationalidFinal(national_id_1 , $("#national_id_1")) &&
      validateNationalidFinal(national_id_2 , $("#national_id_2"))) {
  return;
}
여기서 map이나 every 같은 것을 이용하는 것이 큰 도움이 될 것 같지만 그러진 않겠다. 오늘의 주제는 커리니까.
curry라는 함수 한번 가지고 놀아봤다.

[javascript]자바스크립트 curry 구현소스를 파악해보자.

출처 :
https://edykim.com/ko/post/writing-a-curling-currying-function-in-javascript/
https://medium.com/@kevincennis/currying-in-javascript-c66080543528

커링이라는 개념은 하스켈을 공부할 당시 알게 되었다.
clojure의 partial과 비슷한 개념이다. (동일한가?)

여튼 자바스크립트로 curry를 쓰고 싶은 욕구가 강했지만, underscore.js같은 라이브러리를 사용할 수 없는 제약이 있어,
다른 누군가가 어떻게 curry만을 구현했는지 확인하고 복붙을 하기로 하였다.

그 중에 위의 링크를 확인했고 하나하나 파고들어갔다. 아래 내용은 위 블로그를 읽고 나만의 부연설명을 추가한 것이다.


function curry(fn) {
 var arity = fn.length; // 함수의 필요 인자
 // 매번 curry된 함수를 호출할 때마다 새로운 인자를 배열에 넣어 클로저 내에 저장한다.
 // 배열의 길이는 fn.length와 동일해야 한다. (실행될 때)
 // 혹여 인자의 수가 동일하지 않으면 새로운 함수로 반환한다.
 
 // 인자 목록을 가지는 클로저가 필요하다 (함수로 둘러쌓아야 한다 
 // 또 여기서 개별의 클로저가 생성되야 하니까 즉시 실행함수로 만든다.)
 // 전체인자(배열)과 fn.length를 확인
 // 인자의 수가 부족하면 부분적으로 적용된 함수를 반환 
 // 인자의 수가 충족하면 fn에 모든 인자를 적용,호출하여 리턴
 return (function resolver() {
  // 지금까지 받은 인자를 복사한다.  // 이전 클로저가 오염되지 말게
  var memory = Array.prototype.slice.call(arguments);
  // resolver는 익명함수를 반환한다. 
  // resolver는 인자가 부족할 때 반환한다.
  // resolver가 새로 반환되는 이유는 클로저를 위한 것같다.
  
  // resolver는 바로 실행되고 익명함수를 하나 리턴한다.
  // resolver가 이전까지 모은 인자를 가지고 있다. (memory)
  // 이걸 변수에 담았다가 나중에 실행시킬 것이다.
  // 실행시키면 arguments에서 인자를 memory와 합체한다.
  // 그리고 원래 실행되어야할 함수(fn)이 필요로 하는 인자의 갯수와 비교
  // 지금까지 모은 인자(local)과 arity의 길이가 맞다면
  // 원래함수를 호출(fn), 그렇지 않으면 resolver를 다시 반환 (인자를 더 받는다)
  return function() {  
   var local = memory.slice();
   Array.prototype.push.apply(local, arguments);
   next = local.length >= arity ? fn : resolver;
   return next.apply(null, local);
  };
 }());
}

한번 생성한 커리에 함수를 넣어보자. 지금 함수를 넣으면 인자 fn에 들어가는 것이다.
====
function volume(l, w, h) {
  return l * w * h;
}

var curried = curry(volume);

이제 아래 코드를 보면서 어떻게 되는지 보자.
function curry(fn) {
 var arity = fn.length; // 1. 숫자 3이 저장된다.
 return (function resolver() {
  var memory = Array.prototype.slice.call(arguments); // 2. resolver를 인자없이 실행하였다.(현재는 커리만 되는 상태)
  
  return function() {
   var local = memory.slice();
   Array.prototype.push.apply(local, arguments);
   next = local.length >= arity ? fn : resolver;  // 3. arity가 부족하므로 함수를 리턴.
   return next.apply(null, local);
  };
 }());
}

함수를 리턴받아서 curried에 넣었다. 여기에 다른 인자들을 넣어보자.
일단 2를 넣을 것인데 l, w, h 총 3개가 필요한 함수에게는 부족한 인자 갯수이다.
var length = curried(2);

어떤 일이 일어나는지 아래를 보자.
function curry(fn) {
 var arity = fn.length; 
 return (function resolver() {
  var memory = Array.prototype.slice.call(arguments);
  
  return function() {  // 1. resolver로 반환된 익명함수가 실행됨. 
   var local = memory.slice();  // 2. memory는 현재 0개의 인자를 가지고 있다. 
   Array.prototype.push.apply(local, arguments);  // 3.이번에 추가된 2가 local에 push된다.
   next = local.length >= arity ? fn : resolver;  // 4.아직 인자가 1개이기 때문에 다시 resolver를 반환한다.
   return next.apply(null, local);  // 4. 알다시피 이번에 던져지는 resolver는 또한 새로운 클로저와 함께하는 새로운 함수다.
  };
 }());
}

한번 더
var lengthAndWidth = length( 3 );

function curry(fn) {
 var arity = fn.length; 
 return (function resolver() {
  var memory = Array.prototype.slice.call(arguments);
  
  return function() {  
   var local = memory.slice();  // 1. 새로운 클로저에 들어있는 memory는 [2] 이다. local에 복사한다. (이전 클로저에 해를 끼치지 않게)
   Array.prototype.push.apply(local, arguments);  // 2. 새로운 인자 3을 추가한다. [2,3]
   next = local.length >= arity ? fn : resolver;  // 3.아직 인자가 2개이기 때문에 다시 resolver를 반환한다.
   return next.apply(null, local);  // 4. 알다시피 이번에 던져지는 resolver는 또한 새로운 클로저와 함께하는 새로운 함수다.
  };
 }());
}

이제 마지막으로 하나의 인자만 더 넣으면 실행될 것이다.
console.log( lengthAndWidth( 4 ) ); // 24

왜 그렇게 되는지 아래를 보자.
function curry(fn) {
 var arity = fn.length; 
 return (function resolver() {
  var memory = Array.prototype.slice.call(arguments);
  
  return function() {  
   var local = memory.slice();  // 1. 새로운 클로저에 들어있는 memory는 [2,3] 이다. local에 복사한다. (이전 클로저에 해를 끼치지 않게)
   Array.prototype.push.apply(local, arguments);  // 2. 새로운 인자 4을 추가한다. [2,3,4]
   next = local.length >= arity ? fn : resolver;  // 3.아직 인자가 3개이기 때문에 fn을 넣는다. (fn은 지금까지 변경된 적도 사용된 적도 있다. 하지만 이날을 위해 기다렸다)
   return next.apply(null, local);  // 4. 이번에는 함수를 던지지 않고 fn의 리턴값이 실행될 것이다. (하지만 fn이 리턴하는게 함수라면... 음...)
  };
 }());
}

2018년 12월 11일 화요일

[javascript] Object.create 함수로 크로스 브라우저 호환성 보완 - 05

/*
Object.create 함수로 크로스 브라우저 호환성 보완
*/
(function () {
  if(!Object.create) {
    Object.create = (function () {
      function F() {}
      return function (o) {
        F.prototype = o;
        return new F();
      }
    })();
  }
});

[javascript][prototype] ECMA의 class와 extends를 통한 상속 - 04

/* 
class와 extends를 통한 상속
자바스크립트의 new기능의 모호함을 해소하기 위해
ECMA6에서는 class와 extends를 정의한다.
*/

class Person {
  constructor() {
    this.name = "anonymous";
  }
}

class User extends Person {
  constructor() {
    super();
    this.name = "User";
  }
}

var user1 = new User();
console.log(user1 instanceof Person);
console.log(user1 instanceof User);
console.log(user1.constructor); // [Class: user]

[javascript][prototype] Object.create과 new 키워드로 생성자의 연결이 깨지지 않도록 - 03

/* Object.create와 new 키워드 조합 
Object.create과 new 키워드를 조합하여 생성자의 연결이 깨지지 않도록 해보자.
*/

function Person() {
  this.name = "unknown";
}

function User() {
  this.name = "user";
}

// 두 번째 매개변수는 키는 속성명으로 
// 새로 만든 객체에 추가될 속성 설명자(descriptor)를 지정합니다.
// writable option defaults to false.
// https://stackoverflow.com/questions/7757337/defining-read-only-properties-in-javascript
User.prototype = Object.create(Person.prototype, {
  constructor: { // Person.prototype.constructor 는 이제 읽기전용이다. 
    value: User  // 왜냐하면 기본이 writable = false 이다.
  }
})

var myuser = new User();
console.log(myuser instanceof User);
console.log(myuser instanceof Person);
console.log(myuser.constructor);
이 녀석은 이전

[javascript][prototype] 자바스크립트 프로토타입 맛보기 -02

function Car() {
  this.wheel = 4;
  this.beep = "BEEP";
}

Car.prototype.go = function () {
alert(this.beep);
  };

function Truck() {
  this.wheel = 6;
  this.beep = "TRUCK!!";
}

Truck.prototype = new Car();

function SUV() {
  this.beep = "SUV!!";
}
SUV.prototype = new Car();

var truck = new Truck(),
suv = new SUV();

console.log(truck.wheel);  // 6
console.log(suv.wheel);  // 4
console.log(truck.beep);  // TRUCK!!
console.log(suv.beep);  // SUV!!
Car라는 객체를 프로토타입에 넣어보니
Truck과 Suv 는 go라는 함수를 공통으로 사용할 수 있으며, 특히 SUV의 경우 wheel을 정의하지 않았지만
Car에 정의되어 있는 wheel = 4로 바퀴갯수 프로퍼티를 가질 수 있게 되었다.

[js] Object.create을 사용한 자바스크립트 상속 - 01

// 실제로는 Object.create 이라는 함수가 있다. 더글라스 크락포드가 주장했던 함수형태

Object.my_create = function(o) {
  function F() {};
  F.prototype = o;  // O는 처음부터 prototype역할을 하는 녀석일 것이다.
  return new F(); 
}

function Person(name) {
  this.name = name;
}

Person.prototype = {
  yell: function() {
    alert("My name is " + this.name);
  }
};

var u = Object.my_create(Person.prototype); // prototype을 넘기는 것을 주의하자.

u.name = "U";
u.yell();  // alert
/*
이렇게 Object.create 함수를 통해 객체를 생성하면 
개발자가 new 키워드를 사용하지 않고 함수 호출로 객체가 생성된다.

new 키워드를 사용할 때와는 달리 전체적으로 소스에 생성자의 개념이 사라지고 (new가 없으니까)
객체의 인스턴스와 인스턴스 간의 상속을 강조하는 것이 Object.create의 특징 (???)
*/

[js] 자바스크립트 상속기존의 자바스크립트 상속과 그 이면 - 00

function Person() {
  this.name = "anomymous";
  this.sayHello = function() {
    console.log(this.name + "!!");
  };
}

function Nam() {
  this.name = "Nam";
}
Nam.prototype = new Person();

var nam = new Nam();
nam.sayHello();
console.log(nam instanceof Nam);  // true
console.log(nam instanceof Person);  // true

console.log(nam.constructor);  // ?? Nam이 아니라 Person!!
/*
생성자 속성은 객체가 가지고 있는 것이 아니라. 프로토타입이 가지고 있다.
객체는 내부 링크로 프로토타엡에 있는 생성자 속성을 참조한다.

지금 Nam프로토타입은 
그런데 new Person()으로 원래 객체(여기선 Nam)의 생성자를 가지고 있는 프로토타입을 덮어씌우면
Nam의 생성자는 연결고리를 잃는다. (원래 Nam의 생성자는 function Nam() {...} 을 가리키고 있어야 한다.)

따라서 nam.constructor를 실행하면 [Function: Person] 이 나온다.
뭐 잘 작동하기는 하지만 연결이 끊어진다는 것은 좋은 것이 아니다.
*/


괜찮은 링크.
https://medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67

2018년 10월 29일 월요일

[javascript] 접근자 defineProperties를 이용한 생성자 설정.

function Person(name) {
this.name = name;
}
Person.prototype = {
  yell: function() {
    console.log("my name is " + this.name);
  }
}
/* 수정불가 */
var user = Object.create(Person.prototype, {
  name: {
    value: 'new name'
  }
})

/* 수정가능 */
user = Object.create(Person.prototype, {
  name : {
    value: 'new name2',
    configurable: true,
    enumerable: true,
    writable: true
  }
})

/* 접근자 활용의 예 */

Object.defineProperties(user, {
  firstName: {
    value: "Younghwan",
    writable: true
  },
  lastName: {
    value: "Yang",
    writable: true
  },
  fullName: {
    get: function() {
      return this.firstName + " " + this.lastName;
  },
  set: function(value) {
    var res = value.split(' ');
    if (res.length > 1) {
      this.firstName = res[0];
      this.lastName = res[1];
    } else {
      console.log('wrong format');
    }
  }
});

console.log(user.fullName);
user.fullName = "Hello world!";
console.log(user.firstName); // Hello
console.log(user.lastName); // World!

2018년 8월 17일 금요일

[javascript] 함수를 이용한 개발-02

출처 : 하스켈로 배우는 함수형 프로그래밍

함수를 이용한 개발

- 함수의 부품화, 그리고 조합(합성)


함수를 좀 더 부품화 할 필요성이 있다고 저자는 생각했었나 보다.
사실 첫번째 소스코드에서 보면 알 듯이 기본적으로 기존의 좌표를 새로운 좌표로 변형해서 리턴하는 것이 목표이다.

config값과 coord(좌표)값을 받으면 new coord 가 리턴되어서 나오는 것이다.
이제 필자는 config와 coord를 분리해야 한다고 한다.

왜냐하면 "무언가(config)와 좌표를 받아 좌표를 반환하는" 함수들을 결합할 경우,
"무언가" 부분을 부여해야 한다는 것이 조합할 때의 표현을 복잡하게 만들기 때문이라고 한다.

즉, 이제 함수를 부품화해서 레고처럼 조합할 껀데, 받아야 하는 것이 2개보다는 1개가 낫다.
뭐 이런 말이려나.

여튼 "좌표(coord)만 받아서 좌표를 반환하는" 함수를 서로 조합한다면 서로 아무리 조합해도
"좌표를 받아서 좌표를 내뱉는 "함수들 이기 때문에 조합이 더 편해질 것이다.

아래 내용은 "무언가(config)와 좌표를 받아 좌표를 반환했던" 함수였던 것에서
"무언가를 받고나서 '좌표를 받아 좌표를 반환하는 함수'를 반환하는 함수"로 변경했다.

일종의 커링을 사용한 것인데 무언가(config)를 좌표(coord)와 분리시키기 위해서
이전에 내가 임의로 붙여봤던 커링을 사용해서 config를 미리 함수 안에 넣어버렸다. 그리고 조합을 할 때는

좌표만을 다루는 함수끼리 조합이 되도록!

// 보다 나은 부품화

function clone(obj) {
  var f = function(){};
  f.prototype = obj;
  return new f;
}


// 함수 합성
function compose (f,g) { return function(x) { return f(g(x)); } };


// 병행 이동의 프리미티브
//var trans = function (dx, dy, coord) {
// var result = clone(coord);
// result.x += dx;
// result.y += dy;
// return result;
//}
var trans = function (dx, dy, coord) {
 return function(coord) {
  var result = clone(coord);
  result.x += dx;
  result.y += dy;
  return result;
 }
}

// 회전 이동의 프리미티브
//var rotate = function (theta, coord) {
// var result = clone(coord);
// result.x = Math.cos(theta) * coord.x - Math.sin(theta) * coord.y;
// result.y = Math.sin(theta) * coord.x + Math.cos(theta) * coord.y;
// return result;
//}
var rotate = function(theta) {
 return function(coord) {
  var result = clone(coord);
  result.x = Math.cos(theta) * coord.x - Math.sin(theta) * coord.y;
  result.y = Math.sin(theta) * coord.x + Math.cos(theta) * coord.y;
  return result;
 }
}

// 설정을 베이스로 한 병행 이동
//var transByConfig = function (config, coord) {
// return trans(config.ofsX, config.ofsY, coord);
//}
var transByConfig = function(config) {
 return trans(config.ofsX, config.ofsY);
}

// 설정을 베이스로 한 회전
//var rotateByConfig = function (config,coord) {
// var preTrans = trans(-config.rotAt.x, -config.rotAt.y, coord);
// var rotated = rotate(config.theta, preTrans);
// var postTrans = trans(config.rotAt.x, config.rotAt.y, rotated);
// return postTrans;
//}
var rotateByConfig = function(config) {
 return compose(trans(config.rotAt.x, config.rotAt.y),
      compose(rotate(config.theta),
        trans(-config.rotAt.x, -config.rotAt.y)));
}

// 설정을 베이스로 한 좌표 변환
//var convertByConfig = function(config, coord) {
// return transByConfig(config, rotateByConfig(config, coord));
//}
var convertByConfig = function(config) {
 return compose(transByConfig(config), rotateByConfig(config));
}


var config = {
 rotAt : { x : 0.5, y : 0.5 }
 , theta : Math.PI / 4
 , ofsX : -0.5
 , ofsY : -0.5
}

var unit_rect = [
{x:0,y:0}, {x:0,y:1}, {x:1,y:1}, {x:1,y:0}
]

// 변환 후의 좌표
//var converted_rect = unit_rect.map(function(coord) { 
// return convertByConfig(config, coord);
//}
// 매개변수가 하나로 변하면서 map에서 바로 만들어진 함수를 지정할 수 있다. 하지만 curry를 이용해서 사용할 수도 있겠다.
var converted_rect = unit_rect.map(convertByConfig(config));

converted_rect.map(function(coord) {
 console.log('(' + coord.x.toFixed(6) + ',' + coord.y.toFixed(6)+')');
}
함수 안에 있는 알고리즘을 보는 것이 아니라. return을 하는 방식이 변경되었고 compose를 이용한 함수합성을 유심히 봐야한다.

[javascript] 함수를 이용한 개발-01

출처 : 하스켈로 배우는 함수형 프로그래밍

함수를 이용한 개발

- 맞을지 안 맞을지도 모르는 부품을 만들거나 맞추는 능력


이 코드의 내용은 전적으로 "하스켈로 배우는 함수형 프로그래밍"의 내용을 가져온 것이다. 그곳에서 내가 더한 내용은 소스 맨밑에 currying을 이용한 부분밖에 없다.
해당 내용은 서적을 읽어야 왜 이러한 기술이 중요한 지 알 수 있다.
서적에서는 C언어로는
  1. 실행시간에 함수를 생성할 수 없어서 결국 for-loop만을 고집해야 하는 점.
  2. 그 작은 차이가 조금씩 코드를 변화하고
  3. 결국 개발자의 생각하는 방식마저 변화시킬 것이며
  4. 자유를 얻게 될 것이라고 말하는 것 같았다.
해당 소스는 꽤나 단순하다.(??) 그리고 내용을 이해하지 않고 함수들의 구조들이 어떻게 변화되는지만 봐도 흥미롭다.
좌표에 한 점이 있는데 이 점을 dx,dy로 이동시키느냐, 혹은 각도(theta)를 이용해서 회전함으로 좌표를 옮기느냐 하는 것이다. (혹은 둘다)
// 객체 복사용
function clone(obj) {
  var f = function(){};
  f.prototype = obj;
  return new f;
}

// 병행 이동의 프리미티브
var trans = function (dx, dy, coord) {
 var result = clone(coord);
 result.x += dx;
 result.y += dy;
 return result;
}

var rotate = function (theta, coord) {
 var result = clone(coord);
 result.x = Math.cos(theta) * coord.x - Math.sin(theta) * coord.y;
 result.y = Math.sin(theta) * coord.x + Math.cos(theta) * coord.y;
 return result;
}

// 설정을 베이스로 한 병행 이동
var transByConfig = function (config, coord) {
 return trans(config.ofsX, config.ofsY, coord);
}

// 설정을 베이스로 한 회전
var rotateByConfig = function (config,coord) {
 var preTrans = trans(-config.rotAt.x, -config.rotAt.y, coord);
 var rotated = rotate(config.theta, preTrans);
 var postTrans = trans(config.rotAt.x, config.rotAt.y, rotated);
 return postTrans;
}

// 설정을 베이스로 한 좌표 변환
var convertByConfig = function(config, coord) {
 return transByConfig(config, rotateByConfig(config, coord));
}
var config = {
 rotAt : { x : 0.5, y : 0.5 }
 , theta : Math.PI / 4
 , ofsX : -0.5
 , ofsY : -0.5
}

var unit_rect = [
{x:0,y:0}, {x:0,y:1}, {x:1,y:1}, {x:1,y:0}
]

// 여기서 coord가 map으로 연결되서서 계속바뀌고
// config는 그대로 계속 공통으로 쓰게 되는 함수를 콜해서 리턴하는 익명함수를 각각 만들어서 실행한다.
var converted_rect = unit_rect.map(function(coord) { 
 return convertByConfig(config, coord);
});

converted_rect.map(function(coord) {
 console.log('(' + coord.x.toFixed(6) + ',' + coord.y.toFixed(6)+')');
});

/// curry를 사용하면 좀 더 이뻐 보일 수 있겠다.
console.log('====');
var curriedConvertedByConfig = function(coord) {
 return convertByConfig(config, coord);
}
var converted_rect2 = unit_rect.map(curriedConvertedByConfig);
converted_rect2.map(function(coord) {
 console.log('(' + coord.x.toFixed(6) + ',' + coord.y.toFixed(6)+')');
});
위 내용을 html을 하나 생성해서 로드해보자.

다음페이지
http://www.whynam.com/2018/08/javascript-02.html

2018년 4월 9일 월요일

[javascript] ie 버전체크

var ie = (function(){
           if (window.ActiveXObject === undefined) return null;
           if (!document.querySelector) return 7;
           if (!document.addEventListener) return 8;
           if (!window.atob) return 9;
           if (!document.__proto__) return 10;
           return 11;
  })(); //ie버전체크

2018년 3월 5일 월요일

자바스크립트 정규식을 이용한 DOM 객체 클래스 삭제 방법

$('.evtPop').find('.img').removeClass(function (idx, className) {
  console.log('클래스삭제', idx, className);
  return className.match(/win\d/).join(' ');
})

2017년 8월 17일 목요일

[서평] Node를 사용한다면 어디로 가야 하는지 가르쳐주는 [Node.js 6.x 블루프린트]


나는 Node.js에 대해 잘 알지 못한다. Node.js 자바스크립트만 알면 된다는 장점을 내새워 치고 올라오고 있는 기술 중 하나이다.
Node.js는 커지는 신생 기술인만큼 변화의 물결이 크다.(지금도 초기 만큼 변화가 큰지는 모르겠지만) 그만큼 개발자체와 새로운 변화를 사랑하는 사람들에게는 행복할 것이다.

해도해도 너무하다는 말도 많을 것이다.

내가 Node.js에 대해 잘 알지도 못하면서 이런 말을 하는 이유는 Node관련 세미나를 한 번 견학한 적이 있기 때문인데 그때도 1년에 다르게 새로운 기술이 나오고, 이전 기술이 옛것이 되면서 크게 발전하고 있다는 것이다.

그러므로 기존에 사용하던 모듈이 금방금방 deprecated 되는 현상을 맞볼 것이라고 한다.

예를 들자면,
이 책에서 promise라는 기술을 콜백 지옥에서 벗어나기 위한 기술로 소개한다. 하지만 내가 견학했던 세미나에선 이렇게 말했다.

"Promise도 이젠 옛말이죠. 이런 다른 걸 씁니다."

이 격한 파도의 물결을 온몸으로 받아치고 싶다면, Node.js를 그래도 알아야만 한다면

당신은 이 책을 읽어야 한다.

이 책은 말그대로 청사진이다. Node.js는 너무 많은 변화가 자칫 어디로 가야할지 헷갈리게 할 수 있다. 여기저기서 소위 약파는(?) 기술들이 많기 때문이다.

이 책을 한 번 읽으면 Node.js라는 기술이 어디로 가고 있는지 알 수 있을 것이다.

2017년 6월 12일 월요일

[JavaScript] 스코프 제대로 알기 - 3

스코프 제대로 알기 - 3

부제: 함수를 이용한 스코프

function myAlert(index) {
   $("#js-scope3-number"+index).click(function() {
      alert("js-scope3-number" + index + " 번을 클릭했습니다.");
    });
}
$(document).ready(function(){
  var len = 3,
       i;
  for (i = 0; i < len; i++) {
   myAlert(i);
  }
});
보면 알 수 있겠지만 함수 안에서는 새로운 스코프가 연결된다. 다르게 해보자
$(document).ready(function(){
  var len = 3,
       i;
  for (i = 0; i < len; i++) {
    (function (index) {
      $("#js-scope3-number"+index).click(function() {
       alert("js-scope3-number" + index + " 번을 클릭했습니다.");
     });
    }(i));
  }
});
관련페이지 http://www.whynam.com/2017/06/javascript.html http://www.whynam.com/2017/06/javascript-2.html

[JavaScript] 스코프 제대로 알기 - 2

스코프 제대로 알기 - 2

부제: with내용 대충하고 넘어가기

var yhnam = {
  name="yhnam"
  blog="www.iamfrom.kr"
  git="ssisksl77"

with (yhnam) {
    console.log(name);
    console.log(blog);
    console.log(git);
}
실행안해봐도 대충 뭔지 알았을 것이다. 블록 안에 해당 객체가 가지고 있는 변수들을 추가한다. 이걸로 이용해서 문제를 해결하자.
var len = 3,
     i;
for (i = 0; i < len; i++) {
  with ({idx: i}){
    $("js-scope2-number"+idx).click(function() {
      alert("js-scope2-number" + idx + " 번을 클릭했습니다.");
    }
  }
}
     
하지만 이렇게 쓰지 말길.. ECMAScript에서 with쓰지말라고 아예 없애버린듯 하다. 관련페이지 http://www.whynam.com/search/label/javascript http://www.whynam.com/2017/06/javascript-3.html