성능 덕후를 위한 자바스크립트 코딩 패턴 (중급 이상)

이 글은 CodeSchool 의 Javascript Best Practices 를 듣고, 주요 내용을 정리한 글입니다.

Ternary Conditional (삼항 연산자)

  • 다음과 같은 예문이 있다.
var isArthur = true;
var weapon;

if(isArthur) {
  weapon = "Excalibur";
} else {
  weapon = "Longsword";
}
  • 삼항연산자를 이용하면 위의 if else 문을 아래와 같이 바꿀수 있다.
var weapon = isArthur ? "Excalibur" : "Longsword";
  • 또한 삼항 연산자를 다음과 같은 형태로도 사용이 가능하다.
// 두개 이상의 변수를 이용하여 값을 받는 경우
isArthur && isKing ? (weapon = "Ex", helmet = "Goose") : (weapon = "ln", helmet = "Iron")

// 즉시 실행함수로 값을 받는 경우
isArthur && isKing ? function () {
                                   // ...
                     }();
                     :
                     function () {
                       // ...
                     }();

Logical Assignment 1 (OR)

  • OR 연산자 : “falsy” 하지 않은 가장 첫번째 마주친 값을 갖는다.
  • 아래의 삼항 연산자를 OR 연산자를 이용하여 다음과 같이 줄일 수 있다.
// 삼항연산자 사용
function addSword(sword) {
  this.swords = this.swords ? this.swords : [ ];
  this.swords = push.(sword);
}

// OR 연산자 사용
function addSword(sword) {
  this.swords = this.swords || [ ];
  this.swords = push.(sword);
}

// OR 연산자 잘못 사용한 예
function addSword(sword) {
  this.swords = [ ] || this.swords;
  this.swords = push.(sword);
}
// 위의 경우 계속 new array 를 할당함.
  • OR 연산자의 잘못된 사용 예를 더 본다.
// 잘못된 OR 연산자 사용 예
var result1 = 42 || undefined; // undefined 를 절대로 마주치지 않는다.
var result2 = ["Sweet", "array"] || 0; // 0을 절대로 마주치지 않는다.
var result3 = {type: "ring", stone: "diamond"} || ""; // "" 를 절대로 맞추지지 않는다.

// 위를 고쳐보면,
var result1 = undefined || 42;
var result2 = 0 || ["Sweet", "array"]; // 0을 절대로 마주치지 않는다.
var result3 = "" || {type: "ring", stone: "diamond"}; // "" 를 절대로 맞추지지 않는다.

Logical Assignment 2 (And)

  • OR 연산자와는 다르게 두개의 “truthy” 값이 있으면, 마지막으로 확인한 truthy 값이 리턴된다.
  • “falsy” 값의 경우에는 OR 연산자와 동일하게 동작한다.
var result1 = "King" && "Arthur";
console.log(result1); //Arthur
var result2 = "Arthur" && "King";
console.log(result2); // King

Switch Blocks

  • 반복되는 if else 문과 switch 문의 차이점은, 순차적으로 모든 if 문을 도느냐. 아니면 해당하는 case 로 바로 가서 불필요한 연산을 줄이느냐의 차이이다.
var regimnet = 3;

if (regiment == 1) {
  ...
} else if (regiment == 2) {
  ...
} else if (regiment == 3) { // 앞 1,2 를 거쳐 3으로 온다.
  ...
}

switch (regiment) {
  case 1:
    ...
  case 2:
    ...
  case 3: // 3으로 바로 온다.
    ...
}
  • break 문을 사용하지 않고, 공통된 property 를 상위 case 에서 부터 순차적으로 접근하여 추가하는 방법도 있다.

Loop Optimization

  • 컴퓨터 메모리 관점에서 일반적인 for 문의 연산 순서를 보자.
treasureChest = {
  necklaces: ["A", "B", "C", "D"];
};

for (var i = 0; i < treasureChest.necklaces.length; i++) {
  console.log(treasureChest.necklaces[i]); 
} 
  • 위 for 문에서 메모리 연산이 필요한 부분은 다음과 같다.
    1. i 값 탐색
    2. treasureChest 객체 탐색
    3. necklaces 속성 탐색
    4. necklaces 속성의 배열 인덱스 탐색
    5. length 프로퍼티 검색 > 위의 연산을 최적화 해보자 : Cache the necessary values in the local variables
// 1. length property 를 한번만 접근 (기존 for 문은 반복시 마다 접근)
var x = treasureChest.necklaces.length;
for (var i = 0; i < x; i++) {
  console.log(treasureChest.necklaces[i]);
}
  • 위의 리팩토링으로 연산 수가 다음과 같이 줄어들었다.
  • 위 코드는 더 개선할 수 있다.

// 2. for 문의 초기 선언문 쪽에서 x 값을 선언하면, 전역 변수로 var x 를 선언하지 않아도 되어 메모리가 더 효율적이게 된다.
for (var i = 0, x = treasureChest.necklaces.length; i < x; i++) {
  console.log(treasureChest.necklaces[i]);
}
  • 주의할 점 : 자바스크립트는 {} 로 스코핑이 되어 있지 않기 때문에, 위의 for 반복문이 끝나면 x 값은 최종 값으로 할당되어 있다는 사실

  • 또 다른 개선 포인트

// 3. 각 반복 싸이클마다 treasureChest 객체의 속성에 접근을 할 필요가 없다.
var list = treasureChest.necklaces;
for (var i = 0, x = treasureChest.necklaces.length; i < x; i++) {
  console.log(list[i]); } 

모든 인덱스를 접근할 때에는 for loop 문이 좋고, 때로는 for in 보다 성능이 나은 경우가 있다. for in 은 prototype 에 접근하여 기존의 기 정의된 메서드까지 포함하여 출력하므로 비효율적

Performance (Script Loading)

  • Work Intensive javascript 는 body 마지막 태그 맨 위나 async 속성 이용하여 페이지 첫 로딩을 빠르게 한다.

Performance (Inheritance)

  • 자바스크립트에서 상속은 prototype 을 이용
  • 공통으로 쓰는 메서드는 모두 prototype 에 집어 넣는다.
function SignalFire(id, logs) {
  this.id = id;
  this.logs = logs;
  this.functionality1: function() {

  },
  this.functionality2: function() {

  },
  this.functionality3: function() {

  },
}
  • 위 함수의 경우 매번 객체를 생성할 때 마다 사용하지 않는 메서드들을 메모리에서 사용하는 낭비가 발생한다.
  • 따라서, 매번 객체 생성시 필요한 속성이나 메서드만 가져가도록 하고, 공통 메서드는 다음과 같이 prototype 으로 뺀다.
  SignalFire.prototype = {
  functionality1: function() {

  },
  functionality2: function() {

  },
  functionality3: function() {

  },
}

Performance (Indivdual DOM)

  • list 를 배열로 갖는 DOM 요소에 append 메서드를 이용하여 DOM 을 추가하면 전체 리스트가 reflow 된다. 이는 성능에 악영향을 준다.
  • 성능 향상을 위한 해결법은 Fragment 를 사용한다.
var fragment = document.createDocumentFragment();
fragment.appendChild(element);
list.appendChild(fragment);

Performance (Get rid of var redundancy)

  • var 지정어를 사용할 떄, 다음과 같이 코드량을 줄일 수 있다.
var a = 1;
var b = "hello";
var c = ["a","b","c"];

var a = 1,
b = "hello",
c = ["a","b","c"];

// 코드의 가독성이 높아지고, 간결하다.

Performance (String Concatenation)

  • 문자열의 길이에 따라 += 연산자와 join() 메서드의 성능차이가 발생한다.
  • 문자열이 짧을 떄 : += 연산자가 성능이 더 빠르다.
  • 문자열이 길고, 문자열이 배열안에 리스트 형태로 저장되어 있을 때 : join("\n") 메서드가 성능이 우월하다.
var page = "";
for (var i = 0, x = newPageBuild.length ; i < x ; i++) {
  page += newPageBuild[i];
}

// join() 메서드 활용
page = newPageBuild.join("\n");

Namespacing

  • 팀 프로젝트 시 많은 양의 자바스크립트 코드를 작성할 때, 타 팀원이 작성한 전역변수가 overwrite 되는 경우가 발생한다.
  • 이를 막기 위해 namespacing 을 활용한다.
var a = ["Apple", "Banana", "Coil"];
var c = function () {
  console.log("this is not what I want.");
};

var nameSpace = {
a : "1",
b : 23,
c : function() {
  // ...
}
};

// HTML Element click event
onClick=nameSpace.c();
Advertisements

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(&quot;Boo!&quot;);
    scareMe = function () {
      alert(&quot;Double boo!&quot;);
    };
    };
    
    scareMe();  // Boo!
    scareMe();  // Double Boo!
    
  • scareMe() 함수를 일급객체로 사용하는 예
    scareMe.property = &quot;properly&quot;;
    var prank = scareMe;v
    var spooky = {
    boo: scareMe;
    };
    
    prank();  // Boo!
    prank();  // Boo!
    console.log(prank.property); // &quot;properly&quot;
    
    spooky.boo(); // Boo!
    spooky.boo(); // Boo!
    console.log(spooky.boo.property); // &quot;properly&quot;
    
    scareMe();  // Double Boo!
    scareMe();  // Double Boo!
    console.log(scareMe.property);  // undefined
    

즉시 실행 함수

  • 즉시 실행함수는 선언됨과 동시에 실행된다. 자기 실행함수 라고도 한다.
  • 즉시 실행함수의 패턴은 다음과 같다.
    1. 함수를 함수 표현식으로 선언한다 (var a = function(){}; 타입으로는 동작하지 않는다.)
    2. 함수 마지막에 괄호쌍을 추가한다.
    3. 전체 함수를 괄호로 감싼다.
  • 즉시 실행함수는 코드 안의 모든 코드를 지역 유효범위로 감싸고, 어떤 변수도 전역 유효범위로 새어나가지 않게 한다.
    (function () {
    var days = [&quot;Sun&quot;, &quot;Mon&quot;];
        today = new Date();
        msg = &quot;Today is&quot; + days[today.getDay()];
    
    alert(msg);
    }());
    
    // days, today, msg 변수들의 범위는 모두 전역이 아니다.
    
  • 즉시 실행함수의 매개변수는 다음과 같이 전달한다.
    (function (who, when) {
    
    console.log(&quot;I met &quot; + who + &quot; on &quot; + when);
    
    }(&quot;Joe Black&quot;, 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 + &quot;x&quot; + 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 : &quot;batman&quot;,
    first: &quot;Bruce&quot;,
    last: &quot;Wayne&quot;
    };
    
  • 위 패턴의 장점은
    1. 매개변수와 순서를 기억할 필요가 없다.
    2. 선택적인 매개변수를 안전하게 생략이 가능하다.
    3. 매개변수 추가 제거가 편하다.
  • 위 패턴의 단점은
    1. 매개변수의 이름을 기억해야한다.
    2. 프로퍼티의 이름은 압축되지 않는다.

커리 (Curry)

함수 적용

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

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

    sayHi.apply(alien, [&quot;humans&quot;]); // &quot;Hello, humans!&quot;
    sayHi.call(alien, &quot;humans&quot;);    // &quot;Hello, humans!&quot;
    

Javascript Pattern 리터럴 & 생성자

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

객체 리터럴

  • new Object() 보다는 객체 리터럴 var obj = {}; 을 활용한다.
  • 아래와 같이 생성자에 어떤 인자를 받느냐에 따라 다른 객체가 생성된다.
    // 일반 객체
    var o = new Object();
    console.log(o.constructor === Object); // true
    
    // 숫자 객체
    var o = new Object(1);
    console.log(o.constructor === Number); // true
    console.log(o.toFixed(2)); // &amp;quot;1.00&amp;quot;
    
    // 스트링 객체
    var o = new Object(&amp;quot;I am a strong&amp;quot;);
    console.log(o.constructor === String); // true
    console.log(typeof o.substring); // &amp;quot;function&amp;quot;
    
    // boolean 객체
    var o = new Object(true);
    console.log(o.constructor === Boolean); // true
    
  • 위와 같은 이유로, Object() 생성자는 런타임시 결정되는 동적인 값이 생성자에 인자로 전달된 경우 예기치 않은 결과가 반환될 수 있다.

사용자 정의 생성자 함수

  • 내장 생성자가 아니라 직접 사용자가 정의한 생성자 함수 호출은 다음과 같다.

    var Person =  function (name) {
    this.name = name;
    this.say = function () {
      return &amp;quot;I am &amp;quot; + this.name;
    };
    };
    
  • 위의 함수는 사실 아래와 같다.
    var Person =  function (name) {
    // var this = {};
    
    this.name = name;
    this.say = function () {
      return &amp;quot;I am &amp;quot; + this.name;
    };
    
    // return this;
    };
    
  • 따라서, 생성자 함수 내부의 마지막에 다른 객체가 명시적으로 반환되지 않으면, 내부적으로 this 로 참조된 객체가 반환된다.

  • 한가지 더 유의할 점은, 이 Person 함수의 say() 메서드는 객체 생성시에 항상 default로 따라 붙기 때문에 메모리 관점에서 비효율적이다. 그렇기 때문에 아래와 같이

    Person.prototype.say = function() {
    return &amp;quot;I am &amp;quot; + this.name;
    }
    
  • 처럼 필요한 경우에 메서드 멤버는 prototype에 추가하여 사용하는 것이 더 효율적이다.

생성자의 new 강제 패턴

  • 생성자의 첫 글자를 대문자로 쓰는 명명규칙을 사용하여 해당 함수는 늘 생성자로 생성한다.
  • 아래와 같은 that 사용 으로 해당 함수를 생성자로만 사용할 수 있다.

    function Waffle () {
    var that = {};
    that.tastes = &amp;quot;yummy&amp;quot;;
    return that;
    }
    
  • 위처럼 that 을 사용하게 되면 생성자 함수를 new 로 호출하지 않더라도, 항상 생성자 함수로서 역할을 한다.
    var first = new Waffle(),
      second = Waffle();
    console.log(first.tastes); // &amp;quot;yummy&amp;quot;
    console.log(second.tastes); // &amp;quot;yummy&amp;quot;
    
  • 주의 : 위와 같은 that 패턴은 프로토타입에 추가한 멤버를 사용할 수 없게 된다

해결방법

  • 위와 같은 생성자 강제 new 패턴 문제점을 해결하려면 아래와 같은 방식을 사용한다.

    function Waffle () {
    
    if (!(this instanceof Waffle)) {
      return new Waffle();
    }
    
    this.tastes = &amp;quot;yummy&amp;quot;;
    }
    Waffle.prototype.wantAnother = true;
    

배열 리터럴

  • 배열 또한 var a = new Array("1", "2"); 와 같은 형식보다는 var a = [1,2]; 배열 리터럴을 이용한다.
  • 해당 객체가 배열인지 판별하려면 .isArray([]);Object.prototpe.toString()를 사용한다.

JSON

  • JSON : JavaScript Object Notation 의 줄임말이다.
    var json = {
    &amp;quot;name&amp;quot; : &amp;quot;value&amp;quot;,
    &amp;quot;some&amp;quot; : [1, 2, 3]
    };
    

정규표현식 리터럴

  • 정규표현식 생성방법은 다음과 같다.
    • var re = /\\/gm;
    • var re = new RegExp("\\\\", "gm");
  • 문법
    • g : 전역 매칭
    • m : 여러줄 매칭
    • i : 대소문자 구분없이 매칭
    • 여러개 패턴 동시 사용 가능 var re = /pattern/gmi;

원시 데이터 타입 래퍼

  • 자바스크립트는 Number, String, Boolean, null, undefiend 5개의 원시 데이터 타입이 존재한다.

요약

  • 객체 리터럴 표기법 : 이름 – 값 쌍을 쉼표로 분리하고, 괄호로 감싸 객체를 생성한다.
  • 생성자 함수 : 내장 생성자 함수와 사용자 정의 생성자, 내장 생성자는 리터럴 표기가 낫다.
  • 생성자 new 강제 패턴 : 생성자 함수가 항상 new 로 호출한 것처럼 보장하는 패턴
  • 배열 리터럴 표기법 : var a = ["1", "2"];
  • 정규표현식 리터럴 : var a = new RegExp() 보다는 var a = /\\gm/gim; 리터럴 방식을 사용한다.

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 &lt; array.length; i++) {
    //
    }
    

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

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

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

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

    가 될 수 있다.

For-in 루프

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

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

Switch

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

들여쓰기

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

공백

  • 문어체 영어는 쉼표마침표 뒤에 공백을 둔다.
    // (1)
    for (var i = 0, max = 10; i &lt; 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
  • 공백, 중괄호 규칙 준수
  • 명명 규칙 준수

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));