Javascript Pattern 함수

이 글은 JavaScript Patterns Build Better Applications with Coding and Design Patterns 책을 학습 후 중요 부분을 요약한 것입니다.

Background

  • 자바스크립트의 함수는 일급 객체다.
  • 함수는 유효범위를 갖는다.
  • 함수는 다음과 같은 특징을 갖는 객체다.
    • 프로그램 실행(런타임) 중에 동적으로 생성한다
    • 변수에 할당할 수 있고, 다른 변수에 참조를 복사할 수 있고, 확장가능하고, 삭제할 수 있다.
    • 다른 함수의 인자로 전달할 수 있고, 다른 함수의 반환 값이 될 수 있다.
    • 프로퍼티와 메서드를 가질 수 있다.
  • 자바스크립트에서 함수는 하나의 객체
  • 자바스크립트는 중괄호 {} 의 유효범위가 함수 내부를 제외하곤 없다.

용어

  • 기명 함수 (named function expression)
    var add = function add (a, b) {
    return a + b;
    };
    
  • 무기명 함수 (unnamed function expression) & 함수 표현식 (function expression)
    var add = function (a, b) {
    return a + b;
    };
    
  • 함수 선언문 (function declaration)
    function foo() {
    // body
    }
    

콜백 패턴

  • 함수는 객체이기 때문에 아래처럼 함수를 다른 함수에 콜백 형태로 전달할 수 있다.
    function writeCode (callback) {
    callback();
    }
    
    function introduceBugs () {
    // body...
    }
    writeCode(introduceBugs);
    // ! introduceBugs() 를 넘기게 되면 함수가 호출이 되므로 함수의 참조값만 넘긴다.
    
  • 대부분의 클라이언트 브라우저 프로그래밍은 event-driven 방식 : Don't call us, we'll call you

  • 아래는 타임아웃에 관한 안티패턴이다.

    var thePlotThickens = function () {
    console.log('500ms later...');
    }
    setTimeout(thePlotThickens(), 500); // 안티패턴
    // setTimeout(thePlotThickens, 500) 처럼 함수 포인터만 넘겨줘야 한다.
    

함수 반환하기

  • 함수 반환의 간단한 예
    var setup = function () {
    alert(1);
    return function() {
      alert(2);
    };
    };
    
    var my = setup(); // alert(1)
    my();             // alert(2)
    
  • 함수 반환에서 사용되는 클로저의 예
    var setup = function() {
    var count = 0;
    return function () {
      return (count += 1);
    };
    };
    
    var next = setup();
    next(); // 1
    next(); // 2
    next(); // 3
    

자기 자신을 정의하는 함수

  • 새로운 함수로 자기 자신을 덮어쓰는 경우 : 어플리케이션 성능에 많은 도움이 된다
    // Lazy Function Definition : 사용 시점 전까지 함수를 정의하지 않고 있다가, 호출된 이후에는 더 적게 일한다.
    var scareMe = function () {
    alert("Boo!");
    scareMe = function () {
      alert("Double boo!");
    };
    };
    
    scareMe();  // Boo!
    scareMe();  // Double Boo!
    
  • scareMe() 함수를 일급객체로 사용하는 예
    scareMe.property = "properly";
    var prank = scareMe;v
    var spooky = {
    boo: scareMe;
    };
    
    prank();  // Boo!
    prank();  // Boo!
    console.log(prank.property); // "properly"
    
    spooky.boo(); // Boo!
    spooky.boo(); // Boo!
    console.log(spooky.boo.property); // "properly"
    
    scareMe();  // Double Boo!
    scareMe();  // Double Boo!
    console.log(scareMe.property);  // undefined
    

즉시 실행 함수

  • 즉시 실행함수는 선언됨과 동시에 실행된다. 자기 실행함수 라고도 한다.
  • 즉시 실행함수의 패턴은 다음과 같다.
    1. 함수를 함수 표현식으로 선언한다 (var a = function(){}; 타입으로는 동작하지 않는다.)
    2. 함수 마지막에 괄호쌍을 추가한다.
    3. 전체 함수를 괄호로 감싼다.
  • 즉시 실행함수는 코드 안의 모든 코드를 지역 유효범위로 감싸고, 어떤 변수도 전역 유효범위로 새어나가지 않게 한다.
    (function () {
    var days = ["Sun", "Mon"];
        today = new Date();
        msg = "Today is" + days[today.getDay()];
    
    alert(msg);
    }());
    
    // days, today, msg 변수들의 범위는 모두 전역이 아니다.
    
  • 즉시 실행함수의 매개변수는 다음과 같이 전달한다.
    (function (who, when) {
    
    console.log("I met " + who + " on " + when);
    
    }("Joe Black", new Date()));
    
  • 일반적으로 즉시 실행함수에는 매개변수를 많이 전달하지 않는게 코드 가독성에 도웅이 된다.

즉시 실행함수의 반환 값

  • 다른 함수와 마찬가지로 즉시 실행함수도 값을 반환할 수 있고, 변수에 할당할 수 있다.

    var result = (function() {
    return 2 + 2;
    }());
    
    var getResult = (function () {
    var res = 2 + 2;
    return function () {
      return res;
    };
    }());
    
  • 장점과 사용방법 : 선언된 모든 변수는 스스로를 호출하는 함수의 지역변수가 되기 때문에, 임시 변수가 전역 공간을 어지럽힐까 걱정하지 않아아도 된다.

즉시 객체 초기화

  • 아래 패턴은 객체가 생성된 즉시 객체를 초기화 한다.

    ({
    maxWidth: 600.
    maxHeight: 400,
    
    gimmeMax : function () {
      return this.maxWidth + "x" + this.maxHeight;
    },
    init : function () {
      console.log(this.gimmeMax());
    }
    }).init();
    
  • 위 패턴의 장점은 초기화 하는 동안, 전역 네임스페이스를 보호할 수 있다.

  • 위 패턴의 단점은 자바스크립트 compression 시에 즉시 실행 함수 패턴에 비해 효과적으로 압축하지 못할 수 있다. 왜냐하면 비공개 프로퍼티와 이름이 더 짧게 변경되지 않는 것이 compression 도구 관점에서는 안전하기 때문.

초기화 시점의 분기

  • 초기화 시점의 분기(로드타임 분기)는 각각 브라우저의 기능을 확인하는 최적화 패턴이다.
  • 아래 코드처럼 각 브라우저의 기능 지원범위를 확인할 수 있다.

    var utils = {
    addListener: function (el, type, fn) {
      if (typeof window.addEventListener === 'function') {
        el.addEventListener(type, fn, false);
      } else if (typeof document.attachEvent === 'function') {
        el.attachEvent('on' + type, fn);
      } else {
        el['on' + type] = fn;
      }
    },
    removeListener: function (el, type, fn) {
      // ...
    }
    };
    
  • 브라우저의 기능은 독립적으로 변하기 때문에, 상기 코드로 초기화 시점 분기를 사용해 기능 지원여부를 판단한다.

설정 객체 패턴

  • 설정 객체 패턴은 좀 더 깨끗한 API 를 제공하는 방법. 라이브러리나 reuse 컴포넌트를 만들 때 유용하다.
  • addPerson() 이라는 함수를 만들 때, 인자수가 많아지면 다음과 같이 addPerson("First Name", "Last Name", ...) 함수 선언이 길어지므로 아래와 같은 패턴을 사용한다.

    addPerson(conf);
    var conf = {
    userName : "batman",
    first: "Bruce",
    last: "Wayne"
    };
    
  • 위 패턴의 장점은
    1. 매개변수와 순서를 기억할 필요가 없다.
    2. 선택적인 매개변수를 안전하게 생략이 가능하다.
    3. 매개변수 추가 제거가 편하다.
  • 위 패턴의 단점은
    1. 매개변수의 이름을 기억해야한다.
    2. 프로퍼티의 이름은 압축되지 않는다.

커리 (Curry)

함수 적용

  • 순수한 함수형 프로그래밍에서 함수는 호출된다고 표현하기 보다 적용된다고 표현한다.
  • 자바스크립트에서도 Function.prototype.apply() 를 이용하여 함수를 적용할 수 있다.
    var sayHi = function (who) {
    return "Hello" + (who ? ", "+ who : "") + "!";
    };
    
    sayHi();        // "Hello"
    sayHi("World"); // "Hello, World"
    sayHi.apply(null, ["hello"]); // "Hello, hello!"
    
  • apply 의 첫 번째 매개변수가 null 이면 this 는 전역 객체를 가리킨다.
    var alien = {
    sayHi: function(who) {
      return "Hello" + (who ? ", " + who : "") + "!";
    }
    };
    
    alien.sayHi("world"); // "Hello, world!"
    sayHi.apply(alien, ["humans"]); // "Hello, humans!"
    
  • 이 코드에서 sayHi() 내부의 this 는 alien 을 가리킨다. 앞선 예제의 this 는 전역 객체를 가리킨다.

  • call() 메서드 역시 apply() 메서드와 비슷하며, 함수의 매개변수가 하나일 때는 굳이 배열을 만들지 않고 아래와 같이 call() 을 이용한다.

    sayHi.apply(alien, ["humans"]); // "Hello, humans!"
    sayHi.call(alien, "humans");    // "Hello, humans!"
    
Advertisements

Javascript Pattern 기초

이 글은
JavaScript Patterns Build Better Applications with Coding and Design Patterns 책을 학습 후 주요 부분을 요약한 것입니다.

Basics

  • 전역변수 사용 최소화
  • var 선언 1회
  • 루프내 length 캐쉬 사용
  • 코딩 규칙 준수

전역변수 사용 최소화

  • 네임스페이스 패턴 & 즉시 실행함수 사용
  • 아래와 같은 안티패턴은 피한다.
    function foo () {
    var a = b = 0;
    }
    

    위는 이렇게 바꿔야 한다.

    function foo () {
    var a, b;
    a = b = 0;
    }
    
  • 전역객체 접근방법
    var global = (function() {
    return this; // new 와 생성자를 이용하지 않고 호출하면 this는 항상 전역객체를 가리킨다.
    }());
    
  • 단일 var 패턴
    function func () {
    var a = 1,
        b = 2,
        sum = a + b,
        myobject = {},
        i,
    
        // 변수를 선언할 때는 항상 초기값을 같이 지정하는 습관을 들인다.
    }
    

    위와 같은 코드 기법은 코드 작성량과 전송량이 모두 줄어드는 이점이 있다.

For 루프

  • 아래의 코드는 루프 순회시마다 배열의 length에 접근하는 문제점이 있다 (일반적으로 length 접근은 비용이 크다)

    for (var i = 0; i < array.length; i++) {
    //
    }
    

    위 코드를 아래와 같이 바꾼다.

    for (var i = 0, var max = array.length; i < max; i++) {
    //
    }
    

    또한 이를 단일 var 패턴과 조합하면,

    function looper () {
    var i = 0,
        max,
        array = [];
    
    for (i = 0, max = array.length; i < max; i++) {
    
    }
    }
    

    가 될 수 있다.

For-in 루프

  • 객체를 순회할 때 사용하는 함수

    var man = {
    hands: 2,
    legs: 2,
    heads: 1
    };
    
    if (typeof Object.prototype.clone === "undefined") {
    Object.prototype.clone = function () {};
    }
    
  • 위 코드의 경우 for-in 루프를 사용할 때 주의할 점은
    // 안티패턴
    for (var variable in object) {
    console.log(i, ":", man[i]);
    }
    // 콘솔 출력 내용
    // hands : 2
    // legs : 2
    // heads : 1
    // clone function()
    
  • 프로토타입 체인에 따라 상속받은 메서드를 의도치 않게 출력하였다. 따라서 이는 아래와 같이
    // 올바른 패턴
    for (var i in man) {
    if (man.hasOwnProperty(i)) {
      console.log(i, ":", man[i]);
    }
    }
    // 콘솔 출력 내용
    // hands : 2
    // legs : 2
    // heads : 1
    

Switch

  • 아래의 패턴으로 가독성을 향상시킬 수 있다.
    var inspect_me = 0,
      result = '';
    
    switch (inspect_me) {
    case 0:
      result = "zero";
      break; // 각 case문에 break 반드시 포함
    case 1:
      result = "one";
      break;
    default: // switch 문 안에 default 는 반드시 포함
      result = "unknown";
    }
    

들여쓰기

  • 중괄호 { } 의 안에 있으면 들여쓴다.
  • 중괄호 { 시작의 위치는 개발자마다 아래와 같이 2가지로 분류된다.
    // (1)
    if (true) {
    // body
    }
    // (2)
    if (true)
    {
    // body
    }
    
  • 위의 (2) 경우가 아래와 같은 문제를 만들 수 있다.
    function func () {
    return
    {
        name : "Bat"
    };
    } // 결과값 : undefined
    
  • 자바스크립트는 행 종료시 자동으로 세미콜론을 추가하기 때문에 위의 코드는 결국 아래와 같다.
    function func () {
    return undefined;
    {
        name : "Bat"
    };
    } // 결과값 : undefined
    
  • 결론 : 여는 중괄호 { 는 항상 선행하는 명령문과 동일한 라인에 두어야 한다.
    function func () {
    return {
        name : "Bat"
    };
    } // 결과값 : {name : "Bat"}
    

공백

  • 문어체 영어는 쉼표마침표 뒤에 공백을 둔다.
    // (1)
    for (var i = 0, max = 10; i < max; i += 1) {
    
    }
    // (2)
    var a = [1, 2, 3];
    // (3)
    var o = {a: 1, b: 2};
    // (4)
    myFunc (a ,b ,c)
    // (5)
    function myFunc() {}
    // (6)
    var myFunc = function () {};
    
  • 공백을 많이 사용하여 코드의 가독성을 높이면, 코드의 Byte 양이 늘어나는 부작용이 있다.

  • 이는 compression을 이용하여 해결한다. (빌드용 & 배포용 나눌 것)

명명규칙

  • 생성자 함수의 첫 글자는 대문자로
  • 카멜 표기법 (camel case) : 각 단어의 첫 글자만 대문자 firstName
  • 언더스코어 표기법 (underscore) : 단어를 _로 잇는다. first_name

주석작성

  • 코드내 주석으로 API 문서를 자동화 해주는 툴은 jsdocyuidoc 이 있다.

코드압축

  • 압축도구는 아래와 같은 작업으로 코드 Byte 양을 줄인다.
    1. 공백, 줄바꿈, 주석 등을 제거한다.
    • 매개변수 길이 를 줄인다.
    • 전역변수 를 바꾸는 경우 코드를 망가뜨릴 수 있으므로, 보통 지역변수 를 바꾼다.
  • 미리부터 압축된 코드를 작성하려는 것은 잘못된 생각!!

요약

  • 전역변수 사용 최소화
  • 함수 내 var 선언은 1회
  • 내장 생성자의 프로토타입은 확장하지 X
  • 공백, 중괄호 규칙 준수
  • 명명 규칙 준수

Service Worker Introduction II

Service Worker 실행하기 위한 환경

  • 지원되는 브라우저 : Chrome 46 ↑, Firefox, Opera, Safari (지원예정)
  • 브라우저 지원에 대한 상세한 내용은 여기 참고
  • HTTPS 통신이 가능한 서버에서만 동작한다 (테스트를 위한 localhost 제외)

Service Worker 등록하기

  • 아래 코드처럼 javascript로 서비스워커를 등록한다.
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js').then(function(registration) {
    // Registration was successful
    console.log('ServiceWorker registration successful with scope: ', registration.scope);
  }).catch(function(err) {
    // registration failed 😦
    console.log('ServiceWorker registration failed: ', err);
  });
}
  • 위 코드를 살펴보면, 먼저 서비스워커 API 존재 유무를 파악한다.
  • 존재할 시에 /sw.js 파일을 등록한다.
  • 한가지 알아둘 것은, 페이지가 로딩될 때 마다 register() API를 호출해도 된다. 브라우저가 서비스워커 실행 유무를 판단하여 알아서 처리하기 떄문이다.
  • 서비스워커 파일은 도메인 루트에 위치한다. 예를 들어, 도메인이 /example/sw.js 인 경우, /example/로 시작하는 모든 도메인에 대하여 서비스워커가 실행된다.
  • 크롬브라우저 주소창에 chrome://inspect /#service-workers 입력하면 서비스워커 콘솔을 사용할 수 있다.
  • 등록된 서비스워커는 chrome://serviceworker-internals 을 통해 확인 및 관리 할 수 있다.

Service Worker 설치하기

  • 위 등록 절차를 거쳤다면, 이젠 서비스워커에서 사용할 자원들을 설치할 차례다.
  • 아래 코드를 이용하여 어떤 파일들을 캐싱할 것인지 결정한다.
self.addEventListener('install', function(event) {
  // Perform install steps
});
  • install 콜백 함수 안에 다음 3가지 순서를 추가한다.
    1. [열기] cache 열기
    2. [캐싱] 사용할 파일들 캐싱하기
    3. [확인] 해당 파일들이 모두 캐싱 되었는지 확인
var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
  '/',
  '/styles/main.css',
  '/script/main.js'
];

self.addEventListener('install', function(event) {
  // Perform install steps
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
});
  • caches.open()에 정의한 캐쉬명 변수를 이용하여 캐쉬 사용을 시작한다.
  • 다음 cache.addAll()를 이용하여 array 안에 선언한 필요파일들을 모두 캐싱한다.
  • event.waitUntil() 메서드로 설치가 얼마나 오래 걸리던 간에, 설치 후에 해당 이벤트를 수행할 수 있도록 한다. (Javascript Promise 사용됨)
  • 결론적으로, 모든 파일들이 성공적으로 캐싱되면 서비스워커가 정상적으로 설치된다.
  • 주의할 점은, 여기서 한 개의 파일이라도 캐싱에 실패할 경우 인스톨 전체의 프로세스가 종료된다는 것이다. 따라서 신중하게 캐싱할 파일 리스트를 정한다.

Cache 와 Return 요청

  • 서비스워커 설치까지 완료했다면, 이제 캐쉬된 결과를 받아볼 차례다
  • 설치 완료후에 페이지 이동이나 갱신을 하게 되면, 서비스워커는 fetch라는 이벤트를 수행하게 된다.
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }
        return fetch(event.request);
      }
    )
  );
});
  • caches.match()는 들어오는 request에 대해서 서비스워커가 생성한 캐쉬가 있는지 확인한다
  • 만약 생성된 캐쉬가 있다면, 캐쉬 값을 리턴한다. 그렇지 않은 경우에는 fetch() 를 콜한다.
  • fetch() 네트워크로부터 받을 데이터가 있다면, 네트워크 요청을 보내 해당 데이터를 받는다.
  • 네트워크 요청을 각각 캐쉬로 저장하고 싶다면, 아래와 같은 형식으로 구현하면 된다.
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }

        // IMPORTANT: Clone the request. A request is a stream and
        // can only be consumed once. Since we are consuming this
        // once by cache and once by the browser for fetch, we need
        // to clone the response.
        var fetchRequest = event.request.clone();

        return fetch(fetchRequest).then(
          function(response) {
            // Check if we received a valid response
            if(!response || response.status !== 200 || response.type !== 'basic') {
              return response;
            }

            // IMPORTANT: Clone the response. A response is a stream
            // and because we want the browser to consume the response
            // as well as the cache consuming the response, we need
            // to clone it so we have two streams.
            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(event.request, responseToCache);
              });

            return response;
          }
        );
      })
    );
});

위 코드의 로직은 다음과 같다.

  1. fetch reqeust에 대해 promise (then) 콜백 호출
  2. 받은 response에 대하여 아래와 같은 절차를 수행
    • response 유효성 검사
    • status 값이 200인지 확인
    • type 값이 basic인지 확인 (our origin 인지 확인 / 3rd party 자원은 캐쉬가 되지 않는다는 의미)
  3. 위 절차들을 통과하면, response를 복제한다. 이유는 response는 Stream 방식이기 떄문에, body는 오직 한번만 실행 가능하다. 캐쉬 후에 브라우저에도 response를 던져야 하기 때문에, 복제를 해서 한개는 캐쉬를 한개는 브라우저에 각각 사용한다.

Service Worker 업데이트 하기

  • 서비스워커를 업데이트 해야하는 시점에서의 작업 절차는 다음과 같다.
    1. 서비스워커를 업데이트 하라. 사용자가 사이트를 네비게이팅 할 떄, 서비스워커 파일이 1byte라도 다를 경우 새로운 서비스워커로 간주한다.
    2. 새로운 서비스 워커가 시작되고, install 이벤트가 발생한다.
    3. 이 시점에서, 예전에 등록된 서비스워커가 현재 페이지를 제어하고 있기 때문에 새로운 서비스 워커는 waiting 상태로 진입한다.
    4. 사이트에서 열려있었던 페이지가 닫히면, 이전 서비스워커는 종료되고 새로운 서비스워커가 제어를 넘겨받는다
    5. 새로운 서비스워커로 제어가 넘어오면, activate 이벤트가 발생된다.
  • activate 콜백에서 발생하는 가장 흔한 작업은 cache management이다.
  • 이유는 바로 install 단계에서 이전 캐쉬를 다 지우게 된다면, 현재 페이지의 제어를 담당하는 old 서비스워커(현재 페이지에서 사용되고 있는 서비스워커 : 새로운 서비스워커에 비교해 old로 간주)의 경우 캐쉬에서 파일을 제공할 수가 없기 때문이다.
  • 예를 들어, my-site-cache-v1 캐쉬라는 파일이 있다고 가정하자. 그리고 이 캐쉬를 한개는 페이지에 한개는 블로그 포스트에 사용한다고 하자.
  • 이 의미는 install 단계에서 pages-cache-v1blog-posts-cache-v1 라는 두개의 캐쉬를 생성하고, 기존의 my-site-cache-v1 캐쉬는 지운다 는 것이다.
  • 아래의 코드를 확인해보면, cacheWhitelist에 존재하지 않는 캐쉬는 모두 서비스워커에서 삭제한다.
self.addEventListener('activate', function(event) {

  var cacheWhitelist = ['pages-cache-v1', 'blog-posts-cache-v1'];

  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          if (cacheWhitelist.indexOf(cacheName) === -1) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

Rough Edges and Gotchas (현재 떠안고 있는 문제들)

서비스워커에는 현재 다음 두가지 이슈가 있다. 설치 실패 여부 확인 어려움, fetch() 디폴트 값

  1. 설치 실패 여부 확인 어려움
    • 서비스워커가 등록이 되더라도, chrome://inspect/#service-workerschrome://serviceworker-internals 로 확인하기 어렵다.
    • 따라서, chrome://serviceworker-internals 에서 Open DevTools window and pause JavaScript execution on service worker startup for debugging.를 체크하여 install event에 디버깅 문구를 넣어 확인한다.
  2. fetch() 디폴트 값
    • No Credentials by Default : fetch() 이벤트를 default로 사용시에는 쿠키 같은 credential들을 포함하지 않는다. 만약 credential을 포함하고 싶다면 아래와 같이
    fetch(url, {
    credentials: 'include'
    })
    
    • Non-CORS Fail by Default : 3rd party URL을 통한 자원 획득은 허용되지 않는다(CORS 지원하지 않는다면). 만약 CORS를 지원하려면 no-CORS 옵션을 추가한다. 하지만 이 방법은 opaque 응답을 야기하는데, 받은 응답이 성공인지 실패인지 확인할 수 가 없는 단점이 있다.
    cache.addAll(urlsToPrefetch.map(function(urlToPrefetch) {
    return new Request(urlToPrefetch, { mode: 'no-cors' });
    })).then(function() {
    console.log('All resources have been fetched and cached.');
    });
    

Service Worker Introduction I

Service Worker 란 무엇인가

  • Rich offline experiences, periodic background syncs, push notifications 등 네이티브 앱에서만 가능했던 기능들을 웹에서 사용할 수 있도록 지원하는 스크립트
  • 웹 페이지와는 별개로 브라우저의 백그라운드에서 수행되는 스크립트
  • 오늘 기준으로 push notificationsbackground sync 를 지원한다.
  • 오프라인 사용에 대한 완벽한 지원을 한다
  • Service Worker 이전에는 SPA의 AppCache 와 같은 기능들이 존재 했었지만, multiple page에 대한 지원이 되지 않았다.

Service Worker 를 통해 할 수 없는 것들은?

  • Javascript Worker 이기에 DOM 에 직접 접근이 불가. (하지만 원하면 postMessage 와의 인터페이스를 통해 접근 가능)
  • 프로그래밍 가능한 네트워크 프록시이기 떄문에, 페이지 핸들링에 관련된 네트워크 요청을 제어할 수 있다.
  • 사용하지 않을 때는 종료된다. 따라서, onfetch & onmessage 핸들러를 통한 global state에 접근이 불가능 하지만, 원한다면 IndexedDB API를 이용하여 상태를 보존할 수 있다.

Service Worker Lifecycle

  • 웹 페이지와 완전 별개의 라이프싸이클을 갖고 있다.
  • [등록] 서비스워커 사용을 위해서는 먼저 페이지의 자바스크립트를 사용하여 등록해야 한다.
  • [설치] 설치하는 과정에서 static한 자원들을 캐싱하고, 캐싱이 완료되면 서비스 워커가 설치가 된다. 한 개의 파일일라도 캐싱에 실패하면, 설치가 종료되고, 서비스워커는 다시 활성화되지 않는다.
  • [활성] 설치가 되고 나면, 활성 스텝으로 넘어오고, 이 떄 이전(오래된) 캐쉬들을 다룰 수 있는 상태가 된다.
  • [제어] 활성화 스텝 이후에는 서비스 워커가 본격적으로 모든 페이지를 제어하기 시작한다. 서비스워커에게 제어권이 돌아가면, 보통 아래 2가지 상태(Fetch, Terminated)로 나뉘게 된다.
  • [페치/메시지] 네트워크 요청을 받거나 메시지를 페이지로부터 전달받았을 때 데이터를 fetch하거나 메시지 이벤트를 처리한다
  • [종료] 메모리 효율을 위해 서비스워커를 종료한다

Service Worker Overview Imagesw-lifecycle

Service Worker Reference

Function Expression

Function Expression

First Video

function declaredFunction(str) {
    // since its declared, this is actually loaded right when the program is run. And its loaded into memory and held there until you want to use it
},
var functionExpression = function declaredFunction(str) {
    // this will be only loaded when the program reach the line of this code.
}; // since its assigning to a var, it needs to have semicolon at the end.

어차피 functionExpression("hello"); 를 사용할 것이기 때문에, declaredFunction 는 필요가 없다. 따라서 anonymous function을 사용한다. 다시 정리하면,

var functionExpression = function (str) {
    // what a cushy job
};

확인창을 띄우려면?
return confirm("questions") 를 이용

Second Video (Passing Function Expressions As Parameters)

  • map() method will always take in a funtion as a parameter, and return a new array with the results.
var numbers = [12,4,3];
var results = numbers.map("input a function here");
  • map() works like a loop that applies a function to each array index
    map()을 사용하게 되면 위의 코드가 아래 코드의 효과를 갖게 된다.
var results = [];
for (var i = 0; i < numbers.length ; i++) {
    results[i] = coolFunction(numbers[i]);
}
  • 결론적으로 map() 으로 인해 코딩량을 줄일 수 있다.
    > pass in : deliver or submit sth

Third Video (How to return a function from a function)

shift() array의 가장 앞을 자른다.
‘push()’ array의 가장 맨 뒤에 데이터를 밀어 넣는다.

var puzzlers = [
  function(a) { return 8 * a - 10; },
  function(a) { return (a - 3) * (a - 3) * (a - 3); },
  function(a) { return a * a + 4; },
  function(a) { return a % 5; }
];
var start = 2;

// build applyAndEmpty function expression here
var applyAndEmpty = function(input, queue) {
  var length = queue.length;
  for (var i = 0; i < length ; i ++) {
    input = queue.shift()(input);
  }
  return input;
};

alert(applyAndEmpty(start, puzzlers));