이 글은 Joel Thoms의 "자바스크립트에 대한 애정을 언어의 90%를 쓰레기통에 버리면서 다시 발견했던 과정"
 번역한 글을  보고, 번역이 안된 중요 부분을 추가 번역 한 것입니다.




[번역] 자바스크립트에서 for 루프를 다시 생각해보자 

JavaScript 의 for  루프는 나름 그 역할을 잘 해주었지만, 이제는 쓸모가 없어졌으며 새로운 함수형 프로그래밍 기술에 의해 은퇴 해야 할 때로 보입니다.걱정은 하지 마세요 굳이 함수형 프로그래밍 마스터가 될 필요는 없습니다. 그저 현재의 프로젝트에서 바로 시작 해 볼 수 있는 간단한 (하지만 쿨한) 녀석입니다.

JavaScript for loop의 문제점은 무엇일까요?

for 루프는 상태 변이와 부수효과을 조장합니다. 그 결과  각종 버그와 예측할 수없는 코드의 잠재적인 원인이 되곤 합니다. 우리는 모두 전역 상태가 나쁘다는 말을 들었습니다. (지역상태도 전역상태와 같이 악을 공유하고 있지만, 규모가 작기 때문에 큰 문제가 되진 않았습니다.) 하지만 우리는 실제로 문제를 해결하지 못했으며 단지 그것을 최소화 하는데 주력 하였지요.

가변적인 상태에서 - 알 수 없는 특정 시점에 - 변수가 알려지지 않은 이유로 - "변경" 되어, 그  값이 변경된 이유를 디버깅하고 검색하는 데 몇 시간을 소비하게됩니다. 뭐 머리 좀 쥐어 뜯으면 되겠지요. 하지만 머지않아..머리카락이 그리워질 거에요.

이제 그 부수효과에 대해 재빠르게  이야기해 보죠. 이 단어가 별로 와닿지 않는다면 그냥 끔찍한 부작용으로 생각하는게 좋을거 같습니다. 혹시 당신의 몸에 부작용이 생기길 바랍니까? 아니죠?  마찬가지로 프로그램에 부작용이 있기를 원하십니까? 역시 아니겠죠?  네 저는 제 프로그램에 부작용이 생기는 것을 원하지 않습니다! 따라서 이 문제에 대해서 좀 적극적으로 행동 했습니다.

그럼 부수효과가 무엇일까요?

함수가 함수 범위 밖의 어떤 것을 수정할 때 부수효과가 있는 것으로 간주합니다. 멤버 변수의 값을 변경하거나, 키보드 입력을 읽거나, API 호출을하거나, 디스크에 데이터를 쓰고, 콘솔에 로깅하는 등의 작업등이 그것 들이죠.

부수효과은 강력하고 효율적인것 처럼 보이기도 하지만  그 만큼 큰 책임이 따릅니다.  

자기도 모르게 떠 안을 수 있는 큰 책임을 줄이기 위한 방법은 가능한 경우 부수효과를 제거하거나 캡슐화하여 관리해야 합니다. 부수효과가 있는 기능은 테스트하기가 어렵고 추론하기도 더 어렵습니다. 가능한 한 제거 하십시요. 그렇다면 우리는 부작용에 대해 걱정하지 않게 될  것입니다.

좋습니다.  
썰은 그만 풀고 개발자 답게 코드로 봅시다. 아마 수천 번 이상 본 전형적인 for 루프를 살펴 보겠습니다.

const cats = [
  { name: 'Mojo',    months: 84 },
  { name: 'Mao-Mao', months: 34 },
  { name: 'Waffles', months: 4 },
  { name: 'Pickles', months: 6 }
]
var kittens = []

for (var i = 0; i < cats.length; i++) {
  if (cats[i].months < 7) {
    kittens.push(cats[i].name)
  }
}
console.log(kittens)

앞으로 이 코드를 단계별로 리팩터링하여 좀 더 아름답게 변형하는 것이 얼마나 쉬운지 확인 시켜 드릴 것입니다.

1. 첫 번째로 변경하고자 하는 것은 if 문을 자체 함수로 추출하는 것입니다.

const isKitten = cat => cat.months < 7
var kittens = []
for (var i = 0; i < cats.length; i++) {
  if (isKitten(cats[i])) {
    kittens.push(cats[i].name)
  }
}

일반적으로 if 문을 추출하는 것은 가독성에 매우 좋습니다. 즉 마법수 같은 느낌의 "7 개월 미만" 이라는  표현보다는 "새끼 고양이" 로 필터링을 변경하는 것은 생각보다 매우 중요한 일이죠. 이제 코드를 읽을 때 의도가 명확 해집니다. 왜 태어난지 7개월 미만의 고양이를 구하는거야? 우리의 의도는 새끼 고양이를 찾는 것입니다, 그래서 코드가 그렇게 말하게 만드십시오! 읽는 사람도 매우 편해 할 것입니다.

또 다른 이점은 isKitten이 이제 재사용이 가능하다는 것입니다.

코드를 재사용 가능하게 만드는 것이 항상 우리의 목표 중 하나가되어야합니다.

2. 다음 변경은 cat 형식의 개체에서 이름으로 변환하는 부분을 추출하는 것입니다. 이 변환의 목적은 나중에 자세히 이해할 수있게 될 것이므로 지금 당장은  그냥 믿어 봅시다.

const isKitten = cat => cat.months < 7
const getName = cat => cat.name
var kittens = []
for (var i = 0; i < cats.length; i++) {
  if (isKitten(cats[i])) {
    kittens.push(getName(cats[i]))
  }
}

필자는 filter 와 map의 메커니즘을 설명하기 위해 몇 단락을 작성하는 것을 고려했지만 그것들을 설명하지 않고서도 그냥 이 코드를 읽고 이해할 수 있다는 것을 보여 주기 위해 건너 뛸 것입니다. filter 문은 조건문과 매칭되고, map 은 변환과 매칭된다는것을 알 수 있을 것 입니다. 반복해서 보세요. 
많은 경우 이해 안되는것도 반복해서 보면 이해가 되기 마련입니다. 

const isKitten = cat => cat.months < 7
const getName = cat => cat.name
const kittens = cats.filter(isKitten).map(getName)

우리는 for 문과 kittens.push (...)를 제거했으며 더 이상 var이 사용되지 않게 됩니다. 
즉 상태의 변경이 없어 졌습니다. const 세상이 되었습니다.

const (var와 let보다)를 사용하는 코드는 섹시합니다.

3. 필자가 제안 할 마지막 리팩토링은 필터링과  매핑을 자체 함수로 추출하는 것입니다. 모두 함께 묶어 보시죠. (역주: for 문에서 i 변수를 쫒아가며 남이 짠 혹은 자신이 예전에 짠 코드를 이해할 필요가 없이 자연스럽게 소설 읽듯이 변화 되었습니다.)

const isKitten = cat => cat.months < 7
const getName = cat => cat.name
const getKittenNames = cats =>
  cats.filter(isKitten)
      .map(getName)
const cats = [
  { name: 'Mojo',    months: 84 },
  { name: 'Mao-Mao', months: 34 },
  { name: 'Waffles', months: 4 },
  { name: 'Pickles', months: 6 }
]
const kittens = getKittenNames(cats)
console.log(kittens)




추가 번역 예정 글 ) 

- if 문을 다시 생각해보자.  
switch 문에게 마지막 인사를  
- 악의 축이던 this 없는 자바스크립트 프로그래밍  




TYPESCRIPT + EXPRESS + NODE.JS


Node.js 를 위한 Express 웹 어플리케이션 프레임워크는 비동기 Node.js 엔진상에서 웹앱을 구축하기위한 가장 유명한 
솔루션중의 하나입니다. Express 시작하는 웹앱개발은 매우 쉽습니다. 이 튜토리얼에서는  Express + Node.js 를 npm 으로 세팅하며 

JavaScript 와 TypeScript 를 사용하여 코드를 구축하고 app 을 빌드해보겠습니다.


왜 TypeScript 인가? 

강력한 타입과 함께 ECMAScript 6 (ES6) 를 사용한 차세대 웹어플리케이션을 구축하기 위한 언어로 무난하게 선택되었습니다. 

강력한 타입이  Node.js 서버가 실행되고, JavaScript 가 실행되는 브라우저에서는  JavaScript 를 향상시키는데 필요하진 않지만

개발자가 버그를 줄이며 (버그를 잡기위한 노력,시간 또한)  써드파티 API 를 사용하는데 통찰력을 제공합니다. 


  

시작하기


  1. Node.js 다운로드 및 인스톨 
  2. package.json 파일 만들기 
  3. Express 인스톨 
Bash
$ mkdir myapp     
$ cd myapp        
$ npm init       

npm init 를 하고나서 계속 엔터를 쳐주면 폴더에 package.json 가 생성 되어 있을 겁니다.  

요기다가 몇가지를 추가해보도록 하죠. 


  1. 노드엔진 기술 
  2. 시작 script 생성 
JavaScript
{
  "name": "myapp",
  "description": "Just for fun",
  "version": "1.0.0",
  "private": true,
  "author": "Brian Love",
  "engines": {
    "node": "~5.6"
  },
  "scripts": {
    "start": "node ./bin/www"
  }
}

엔진과 스크립트 속성이 추가되었습니다. 엔진프로퍼티가 필요하진 않지만 만약 Google Cloud platform에

디플로이 하려면 필요 할 수 있습니다. 스크립트 속성은 npm 을 사용하여 실행될 수 있는 스크립트들의 맵입니다.

이 예제에서 "start" 라는 이름의 스크립트를 생성하였고 그것은 노드를 실행시키며 JavaScript 파일을 실행시키기위한

초기화를 제공합니다.  여기서는 "www" 라는 이름의 파일(확장자가 없음을 주의) 과 함께 bin 이라는 디렉토리

를 생성하였습니다. 이 www 파일은 JavaScript 파일인데 node.js 에 의해 실행되며 Express 웹 어플리케이션을

시작시킬 것입니다.


Express설치 


 다음으로는 express 패키지를  설치할것인데요. npm 을 사용하면 누워서 떡 먹기에요. 

Bash
$ npm install express --save


설치를 하고나면 myapp 폴더안에 node_modules 폴더가 만들어져 있을겁니다.

이 폴더는 express 가 사용하는 많은 노드 모듈들을 포함하고 있지요. 

만약 Git 을 사용한다면  .gitignore  파일에 저 폴더를 추가하는것을 잊지 마시구요. 


Express + Node.js 세팅 


express 를 시작하기 전에 먼저 bin/www  파일을 만들겠습니다. 

Bash
$ mkdir bin
$ cd bin
$ touch www


좋아하는 에디터 하나 열어서 타이핑 할 준비를 하세요. 일단 코딩을 하고나서 TypeScript 로 바꿀겁니다.

서버 시작을 위해 TypeScript  를 이용하지 않는 이유는 글쎄요..일단 이 코드를 쓰고 나면 끝나기 때문입니다. 좀 낙척적으로 보이나요?

수 많은 시간을 통해 얻은 그냥 제 경험인데요.. 가장 기초적인 코드가 아래에 있습니다.

JavaScript
#!/usr/bin/env node
"use strict";

//module dependencies.
var app = require("../app");
var debug = require("debug")("express:server");
var http = require("http");

//get port from environment and store in Express.
var port = normalizePort(process.env.PORT || 8080);
app.set("port", port);

//create http server
var server = http.createServer(app);

//listen on provided ports
server.listen(port);

//add error handler
server.on("error", onError);

//start listening on port
server.on("listening", onListening);

우리가 한 첫번째 일은 "../app" 모듈을 요구한것인데요. 이것은 app.js 와 연계 되어 있고  우리의 루트 폴더에 있어야 할 겁니다. 

app.js 파일은 우리의 TypeScript 소스파일로 부터 만들어질겁니다.  지금 그것을 만드는걸 걱정하지마세요. 그냥 기억만 해두세요.

app.js


자 아래 코드들을 www 파일에 추가해 봅시다. 

normalizePort() 는 받아드리는 포트를 설정 합니다. process.env.PORT설정에 설정되있지 않다면 디폴트로 8080 을 사용합니다. 

JavaScript
/**
 * Normalize a port into a number, string, or false.
 */
function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

추가적으로 , 잡히지 않은 예외가 던저질때 호출되는 onError 함수 


JavaScript
/**
 * Event listener for HTTP server "error" event.
 */
function onError(error) {
  if (error.syscall !== "listen") {
    throw error;
  }

  var bind = typeof port === "string"
    ? "Pipe " + port
    : "Port " + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case "EACCES":
      console.error(bind + " requires elevated privileges");
      process.exit(1);
      break;
    case "EADDRINUSE":
      console.error(bind + " is already in use");
      process.exit(1);
      break;
    default:
      throw error;
  }
}

onListening 은 서버가 성공적으로 시작되면 한번만 호출됩니다. 

JavaScript
**
 * Event listener for HTTP server "listening" event.
 */
function onListening() {
  var addr = server.address();
  var bind = typeof addr === "string"
    ? "pipe " + addr
    : "port " + addr.port;
  debug("Listening on " + bind);
}


오케이, 우리는 바쁜 일은 다 끝냈습니다.  이제 TS 를 이용하여 프로그램을 작성해 봅시다. 







TypeScript 컴파일러 와 링커 설치 


타입스크립트 환경을 우리 어플리케이션에 설정해 봅시다. 

첫번째로 필요한것은 타입스크립트 컴파일러를 설치하는것 입니다. 간단하게 npm 으로 가능하죠. 


Bash
$ npm install typscript --save

타입스크립트를 컴파일하기위해 우리는 Grunt task runner 를 사용할 것입니다. 

npm 을 사용하여 다시 grunt 관련하여 여러가지를 인스톨 해보죠. 그리고 gruntfile.js 파일을 만들 것 입니다. 

Bash
$ npm install grunt --save
$ npm install grunt-cli --save
$ npm install grunt-ts --save
$ npm install grunt-tslint --save
$ npm install grunt-contrib-watch --save
$ npm install tslint --save
$ touch gruntfile.js



gruntfile_js

우리의 타입스크립트 코드를 컴파일하기 위해 몇가지 추가해봅시다. 

gruntfile.js 을 열어서 아래와 같이 코딩 ~



JavaScript
module.exports = function(grunt) {
  "use strict";

  grunt.initConfig({
    ts: {
      app: {
        files: [{
          src: ["src/**/*.ts", "!src/.baseDir.ts", "!src/_all.d.ts"],
          dest: "."
        }],
        options: {
          module: "commonjs",
          noLib: true,
          target: "es6",
          sourceMap: false
        }
      }
    },
    tslint: {
      options: {
        configuration: "tslint.json"
      },
      files: {
        src: ["src/**/*.ts"]
      }
    },
    watch: {
      ts: {
        files: ["js/src/**/*.ts", "src/**/*.ts"],
        tasks: ["ts", "tslint"]
      }
    }
  });

  grunt.loadNpmTasks("grunt-contrib-watch");
  grunt.loadNpmTasks("grunt-ts");
  grunt.loadNpmTasks("grunt-tslint");

  grunt.registerTask("default", [
    "ts",
    "tslint"
  ]);

};


타입스크립트 컴파일러는 src/ 디렉토리 안에 우리가 만든 모든 .ts 파일을 다룰 것 입니다.

.baseDir 와 _all.d.ts 같은것들은 무시할 것이구요. node_modules/.bon/tsc 바이너리 실행자를 사용하여 이것들을 수동적으로 실행 할수 있습니다.

또한 이런것들을 하기위해 package.json 파일안에 스크립트를 생성할 수 도 있습니다. 다음 스크립트를 package.json 에 간단히 추가해봅시다.  


package.json 파일엔 아래처럼 grunt 가 추가되야 합니다. 

JavaScript
{
  "name": "myapp",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "grunt": "grunt",
    "start": "node ./bin/www"
  },
  ...
}


다음엔 tslinit 가 필요합니다. tslinit.json 파일은 다음과 같습니다. 

JavaScript
{
  "rules": {
    "class-name": true,
    "curly": true,
    "eofline": false,
    "forin": true,
    "indent": false,
    "label-position": true,
    "label-undefined": true,
    "max-line-length": [true, 150],
    "no-arg": true,
    "no-bitwise": true,
    "no-console": false,
    "no-construct": true,
    "no-constructor-vars": false,
    "no-debugger": true,
    "no-duplicate-key": true,
    "no-duplicate-variable": true,
    "no-empty": true,
    "no-eval": true,
    "no-string-literal": true,
    "no-switch-case-fall-through": true,
    "no-trailing-whitespace": true,
    "no-unused-expression": true,
    "no-unused-variable": false,
    "no-unreachable": true,
    "no-use-before-declare": true,
    "no-var-requires": false,
    "one-line": [
      true,
      "check-open-brace",
      "check-catch",
      "check-else",
      "check-whitespace"
    ],
    "quotemark": [true, "double"],
    "semicolon": true,
    "triple-equals": [true, "allow-null-check"],
    "typedef": [true,
      "callSignature",
      "indexSignature",
      "parameter",
      "propertySignature",
      "variableDeclarator",
      "memberVariableDeclarator"],
    "use-strict": false,
    "variable-name": [
        true,
        "allow-leading-underscore"
    ],
    "whitespace": [
      true,
      "check-branch",
      "check-decl",
      "check-operator",
      "check-separator",
      "check-type"
    ]
  }
}

작업이 끝나면 grunt 커맨드를 실행할수 있을겁니다.

Bash

$ npm run grunt

여기서 ERR가 발생하는데 TypeScript compiler 가 es6 빌트인 오브젝트인 Array 등을 알지 못해서인데  아래 같이 인클루드를 해주면 된다. 

/// <reference path="../node_modules/typescript/lib/lib.es6.d.ts"/>

여기 튜토리얼에서는 대신  _all.d.ts 파일을 참조했다. lib.es6.d.ts 디펜던시를 포함하여  모든 프로젝트 디펜던시는 여기에 놓기 때문이다.

그리고 타입스크립트 파일들의 탑레벨에 _all.d.ts 파일을 참조하였다. 

즉 app.ts 파일의 처음에 아래를 추가 

/// <reference path="_all.d.ts"/>

Grunt Executing TypeScript compiler and linter





타입스크립트 Express 웹 어플리케이션 


TypeScript + Express + Node.js 를 사용하여 코딩할 준비가 마침내 되었습니다. 

시작하기위해 먼저 src 디렉토리를 만들어 봅시다. 그리고 app.ts 파일을 만드세요. 


Bash
$ mkdir src
$ cd src
$ touch app.ts


제일먼저 만들어 볼것은 서버 클래스 입니다. 

JavaScript
"use strict";

import * as bodyParser from "body-parser";
import * as express from "express";
import * as path from "path";

/**
 * The server.
 *
 * @class Server
 */
class Server {

  public app: express.Application;

  /**
   * Bootstrap the application.
   *
   * @class Server
   * @method bootstrap
   * @static
   * @return {ng.auto.IInjectorService} Returns the newly created injector for this app.
   */
  public static bootstrap(): Server {
    return new Server();
  }

  /**
   * Constructor.
   *
   * @class Server
   * @constructor
   */
  constructor() {
    //create expressjs application
    this.app = express();

    //configure application
    this.config();
  }
}

소스를 재빨리 살펴봅시다.

  1.  먼저 자사스크립트를 위해 STRICT MODE 를 사용했구요.
  2. 필요한 모듈을 임포트 하였습니다. body-parser 모듈 과 익스프레스 엔진과 path 모듈을 사용하였습니다.
  3. 서버클래스를 만들었구요.
  4. 서버 클래스는 express.Application 속성을 가지고있습니다. 
  5. 서버 클래스는 새로운 서버클래스인스턴스를 만드는 부트스트랩 정적메소드를 가지고 있습니다.
  6. 서버 클래스는 생성자도 가지고 있네요. 생성자에서는 설정등을 해주고 있습니다.  

다음으로 진행하기 전에 몇가지 알아 봅시다.

  1. body-parser 모듈을 아직 인스톨하지 않았습니다. npm 으로 해야하죠.
  2. 타입스크립트 컴파일러는 Array 같은 빌트인 오브젝트를 알지 못합니다.
  3. 타입스크립트 컴파일러는 익스프레스를 위한 타입 정의를 알지 못합니다. 
해결해 봅시다.

먼저 body-parser 모듈 설치 

Bash
$ npm install body-parser --save

_all.d.ts 파일을 src 폴더 아래에 만듬. 이것은 우리 어플리케이션을 위한 타입 정의를 가르키는 참조를 포함하게 될것입니다.

Bash
$ touch _all.d.ts

 _all.d.ts 파일에 아래 추가 

JavaScript
/// <reference path="../node_modules/typescript/lib/lib.es6.d.ts" />

app.ts 파일에 _all.d.ts 파일 참조 

JavaScript
/// <reference path="_all.d.ts" />


타입스크립트 컴파일러는 이제 ES6 인터페이스에 대하여 알게 되었습니다. 추가적으로 메인 타이핑 파일을 참조할 것입니다. 이제 

익스프레스나 앵귤러같은 오픈소스 프로젝트와 함께 할때 강력한 타입스크립트의 타입의 장점을 취하기위해 커먼모듈에 대한 타입 정의 파일을 얻기위한 Typings 를 사용할것입니다.  


Typings 설치


Bash
$ npm install typings --save

일단 typings 를 인스톨하고나서  타입스크립트 정의 파일들을 인스톨링 하고 서치하기 위한 커맨드라인 인터페이스를 사용할수 있습니다. 

Bash
$ node_modules/.bin/typings install body-parser --ambient --save
$ node_modules/.bin/typings install express --ambient --save
$ node_modules/.bin/typings install node --ambient --save
$ node_modules/.bin/typings install serve-static --ambient --save
$ node_modules/.bin/typings install express-serve-static-core --ambient --save
$ node_modules/.bin/typings install mime --ambient --save


다음 단계는 typings 로 부터 생성된 typings/main.d.ts 파일을 참조하는것이다. 이 파일은 _all.d.ts 파일과 비슷한데 이것은 타입스크립트 정의 파일의 모두의 참조를 포함합니다. 

아래와 같이 _all.d.ts 파일을 열어서 추가하세요. 

JavaScript
/// <reference path="../typings/main.d.ts" />

이제 모든것이 세팅되었으니 다시 어플리케이션을  시작해 봅시다.

$ npm run grunt
$ npm start


만약 브라우저에서 http://localhost:8080  로 연결해보면 에러가 날것인데 그건 루트에 대한 GET 라우트가 정의되지 않아서입니다. 

localhost_8080

Routes 정의 

마지막 단게는 웹 어플리케이션을 위한 경로를 정의하는것 입니다. 라우트 모듈의 분리된 클래스들에서 설정할것인데요.
여기 src/routes/index.ts 파일을 봅시다. 

JavaScript
/// <reference path="../_all.d.ts" />
"use strict";

import * as express from "express";

module Route {

  export class Index {

    public index(req: express.Request, res: express.Response, next: express.NextFunction) {
      //render page
      res.render("index");
    }
  }
}

export = Route;

.

Index.index() 라우트는 간단하게 index 페이지를 랜더링합니다. 이것을 묶어주기위해 우리는 app.ts 에서 routes/index.ts 모듈을 요구해야합니다. 

JavaScript
import * as indexRoute from "./routes/index";


그리고 나서 새로운 private 메소드를 routes() 라는 이름으로 만들것이고 생성자에서 호출될것입니다.

JavaScript
/**
 * Constructor.
 *
 * @class Server
 * @constructor
 */
constructor() {
  //create expressjs application
  this.app = express();

  //configure application
  this.config();

  //configure routes
  this.routes();
}

routes() 메소드는 간단하게 웹 루트 "/" 에 대한 GET 요청과 엮여졌습니다. 

JavaScript
/**
 * Configure routes
 *
 * @class Server
 * @method routes
 * @return void
 */
private routes() {
  //get router
  let router: express.Router;
  router = express.Router();

  //create routes
  var index: indexRoute.Index = new indexRoute.Index();

  //home page
  router.get("/", index.index.bind(index.index));

  //use router middleware
  this.app.use(router);
}

routes() 메소드에서 익스프레스의 Router() 로 처음 접근되고나서 이전에 임포트한 모듈을 사용합니다. index 클래스에 대한 새로운 인스턴스를 만들었고 Index 클래스는 

index 라는 이름의 메소드를 가졌습니다.  (조금 헥깔릴것이라고 생각되네요)


Hello World


마침내 다 만들었습니다.  어플리케이션을 다룰때 저는 jade 템플릿 엔진을 이용합니다. 

npm 을 이용해서 설치해보시죠. 

Bash
$ npm install jade --save

views 폴더에 index.jade 파일을 만듭니다. 

Bash
$ mkdir views
$ cd views
$ touch index.jade

 index.jade 파일을 열고 다음을 타이핑해 넣습니다.

h1 Hello World!
p You did it, congratulations!

재컴파일 하는것을 잊지 마시구요. grunt watch 명령어를 사용해도 됩니다.

Bash
$ npm run grunt watch

TS 가 컴파일되면 , 서버를 재시작하세요.

Bash
$ npm start

이제 다음과 같이 시작됩니다. 

Hello World

Download Source

TypeScript + Express + Node.js Sample App.


안녕하세요 (_._)  

"프로 자바스크립트 테크닉" 과  "인사이드 자바스크립트"라는 책을 참조하여 문제를 만들었습니다.

언어를 공부하는 방법으로는  직접만들거나, 다른언어와의 비교를 통해서 습득하는 방법 말고도

문제를 풀면서 언어를 공부하는것도 지겨운 공부를 재밌게하는 방법중 하나라고 생각합니다 :-D

* 함수라 쓰고 객체라 읽는다.

* 함수안에 함수가 있는것이 모든 혼란의 근원이지만,  정수가 된다."


자 렛츠고~!!

1 번 .  다음 구문은 올바른 것인가?   (DOM)

html ====================================

<input id="input1" type="text" value="값" />  

javascript ================================

var inputTxt = document.getElementById('input1'); 

inputTxt.text("hi");   


2 번. 다음 구문은 올바른 것인가?  (DOM & Jquery)

 html ====================================

<input id="input1" type="text" value="hello" />  

javascript ================================

var t = $("#input1");

alert(t.text());

alert(t.html());

alert(t.val());

t.text("bye");


3번. 다음을 실행하면 결과는 ?    (객체 생성 방법)

 var People1 = { age : 44, name : 'brad' };

 function People2(name, age){

 	this.age = age;

 	this.name = name;

 };

var People3 = function (name, age){

 	this.age = age;

 	this.name = name;

 };

 var brad = new People3('brad', 30);

 console.log(brad.age);     // ??


4번. 다음을 실행하면 결과는 ?   (일급함수)

var foo = function(){
			return function(){

 				console.info("hi");

                    };

           };


var bar = foo();

bar();  // ??


5번. 다음을 실행하면 결과는 ?   (즉시실행함수)

(function(name){

	console.info("hello -> " + name);

})("brad");


6번. 다음을 실행하면 결과는 ?    (애매한 자바스크립트의 this) 

var name = "outside name";

var myObject = {

			name: "foo",

			sayName : function(){

					console.info(this.name);

			}

};


var otherObject = { name : "bar" };

otherObject.sayName = myObject.sayName;

myObject.sayName();  // ??

otherObject.sayName();  // ??


7번. 다음을 실행하면 결과는 ?    (애매한 자바스크립트의 this) 

var name = "outside name";

var myObject = {
		name: "foo",
		sayName : function(){	
				var name = "inside name";
				console.info(this.name);
		}
};

var otherObject = { name : "bar" };

otherObject.sayName = myObject.sayName;

myObject.sayName(); 
otherObject.sayName();


8번. 다음을 실행하면 결과는 ? (애매한 자바스크립트의 this) 

var value = 100;

var myObject={

	value : 1,

	func1 : function(){

			this.value +=1;

			console.log(" func1 this.value : "+ this.value);	

			func2 = function(){

				this.value += 1;

				console.log(" func2 this.value : " + this.value);

			}

			func2();

		}

}; 

myObject.func1();  // ??


9번. 다음을 실행하면 결과는 ? (애매한 자바스크립트의 this) 

name = "john";

var myObject = {

	name  : "foo",

    func1 : function(){

		 sayName =  function() {

				console.log("My name is " + this.name);     

				};

      	         sayName();  

            } 
};

myObject.func1();  // ??   


10번. 다음을 실행하면 결과는 ?   (클로저)

function outerFunc(){

	var x = 10;

	var innerFunc=function(){ console.log(x)};

	return innerFunc;

}

var inner = outerFunc();

inner();  // ? 


11번. 다음을 실행하면 결과는 ?     (모듈패턴)

var module = (function () {

    var privateKey = 0;

    function privateMethod() {

        return ++privateKey;

    }

    return {

        publickey : privateKey,

        publicMethod : function () {

            return privateMethod();

        }

    }


})();


console.log(module.publicMethod()); // ?

console.log(module.publicMethod()); // ?

console.log(module.publickey);          // ?


12번. 다음을 실행하면 결과는  참인가 거짓인가 ?    (객체 참조)

var items = new Array('one','two');

var itemsRef = items;

items.push('three');


alert (items.length == itemsRef.length);  // ??


13번. 다음을 실행하면 결과는  참인가 거짓인가 ?   (객체 참조) 

var items = new Array('one','two');

var itemsRef = items;

items = new Array("new");


alert (items == itemsRef); // ??


14번. 다음을 실행하면 결과는  참인가 거짓인가 ?   (문자열 과  참조) 

var item = "test";

var itemRef = item;

item += "ing";

alert(item == itemRef);  // ??


15번. 다음을 실행하면 결과는  참인가 거짓인가 ?   (스코프) 

var foo = "test"

if(true){

	var foo = "new test";

}

alert (foo == "new test");  // ??

function test(){

	var foo = "old test";

}

test();

alert( foo == "new test");  // ??


16번. 다음을 실행하면 결과는  참인가 거짓인가 ?     (커링) 

function addGenerator( num ) {

	return function ( toAdd) {

		return num + toAdd

	};

}

var addFive = addGenerator ( 5 );

alert (addFive ( 4 ) == 9 );


17번. 다음을 실행하면  thanks for your  다음에 어떤 단어가 출력되겠는가?     (클로저, 유효범위 )

var obj = document.getElementById("input1");

var items = ["click", "keypress"];

for(var i = 0; i < items.length; i++){

	(function(){

		obj[ "on" + items[i] ] = function(){

		  console.info("thanks for your " + items[i]);   // ??

		};

	})();

}


18번. 다음을 실행하면 결과는  참인가 거짓인가 ?   (컨텍스트)

var obj = {

	yes : function(){

		this.val = true;

	},

	no: function(){

		this.val = false;

	}

};

alert(obj.val != null);   // ??

obj.yes();

alert(obj.val == true);  // ??


window.no = obj.no;

window.no();

alert(obj.val == false);  // ??


19번. 다음을 실행하면 결과는  참인가 거짓인가 ?  (객체, Privileged 메소드)

function User ( name, age ) {

	var year = (new Date()).getFullYear() - age;

	this.getYearBorn = function(){

		return year;

	}

}


var user = new User('bob', 42);

alert(user.year == 42);  // ??


20번. 다음을 실행하면 결과는  참인가 거짓인가 ?  (프로토타입)

function Person(name) {

    this.name = name || "brad"; 

}


Person.prototype.getName = function(){

    return this.name;

};


function Korean(name){}

Korean.prototype = new Person();


var kor1 = new Korean();

console.log(kor1.getName() == "brad");  


var kor2 = new Korean("john");

console.log(kor2.getName() == "john");  



프로 자바스크립트 테크닉 (http://www.yes24.com/24/goods/3044388) 

인사이드 자바스크립트(http://www.yes24.com/24/Goods/11781589?Acode=101) 


1. 실행 컨텍스트

 - 실행 가능한 자바스크립트 코드블록이 실행되는 환경
 - 실행 컨텍스트가 형성되는 경우
  • 전역코드
  • eval() 함수로 실행되는 경우
  • 함수 안의 코드를 실행한 경우 <- 대부분의 경우임
- 현재 실행되는 컨텍스트에서 이 컨텍스트와 관련 없는 실행코드가 실행되면, 새로운 컨텍스트가 생성되어 
  스택에 들어가고 제어권이 그 컨텍스트로 이동한다.

* 실행 컨텍스트 생성과정

     

  1) 활성객체 생성
     - 해당 컨텍스트에서 실행에 필요한 정보(매개변수, 사용자정의 객체 등)들을 저장.

  2) arguments 객체 생성
    - 활성객체는 arguments 프로퍼티로 arguments 객체 참조

  3) 스코프 정보 생성
    - 현재 컨텍스트의 유효범위를 나타내는 스코프 정보를 생성. Linked List 구조로 상위 컨텍스트
      접근 
가능.
    - 현재 생성된 객체가 스코프 체인의 제일 앞에 추가되며, execute()함수의 인자나 지역변수 등에
      접근할 
수 있다. 

  4) 변수 생성
    - 컨텍스트 내부에서 사용되는 지역변수 생성.
    - 변수나 내부함수를 메모리에 생성만 함. 변수값에 undefined 할당.
      (초기화는 각 변수나 함수식 실행 
시 수행됨)

 
 5) this 바인딩
    - this 키워드를 사용하는 값이 할당됨. 참조객체가 없으면 전역객체 참조. 

  6) 코드 실행
    - 코드 내 표현식 실행. 변수 초기화 ,연산, 또라는 하뭇 실행 등 수행.
    (* 전역 실행 컨텍스트는 arguments 객체가 없으며 전역객체 하나만을 포함하는 스코프 체인이 있음)


2. 스코프 체인
- Javascript 에서는 함수내의 {} 블록 구문은 유효범위가 없음. 오직 함수만이 유효범위의 한 단위가 됨. 
- 이 유효범위를 나타내는 스코프가 [[scope]] 프로퍼티로 각 함수 객체 내에서 Linked List  형식으로 관리되며 이를 스코프 체인이라 함. 
- 각 실행컨텍스트의 변수객체가 구성요소인 리스트와 같다. 
- 각각의 함수는 [[scope]] 프로퍼티로 자신이 생성된 실행 컨텍스트의 스코프 체인을 참조한다. 
- 이 실행 컨텍스트는 실행된 함수의 [[scope]] 프로퍼티를 기반으로 새로운 체인을 만든다. 






1) 전역실행 컨텍스트의 스코프 체인




2) 함수를 호출한 경우 생성되는 실행 컨텍스트의 스코프 체인




2. 클로저

- 이미 생명 주기가 끝난 외부함수의 변수를 참조하는 함수

- 클로저로 참조되는 외부변수를 자유변수(Free Variable) 라 부른다.



1) 클로저의 활용
  • 특정 함수 사용자가 정의한 객체의 메서드 연결하기



  • 함수의 캡슐화

    : 클로저는 사용하여 buffAr 변수를 외부접근으로 부터 차단.

2) 클로저를 활용할 때 주의사항

  • 클로저의 프로퍼티 값이 쓰기 가능하므로 그 값이 여러 번 호출로 항상 변할 수 있음에 유의한다.



  • 하나의 클로저가 여러 함수 객체의 스코프 체인에 들어가 있는 경우도 있다.



  • 루프 안에서 클로저를 활용할 때는 주의하자.



http://juheejin.tistory.com/48  펌 

■ Module Pattern



다음은 클로저를 이용해 모듈 패턴을 구현한 코드입니다.  



var module = (function () {

/**
* --------------------------------
* 모듈 패턴을 구현한 클로저 코드
* --------------------------------
*/

// 은닉될 멤버 정의
var privateKey = 0;

function privateMethod() {
return ++privateKey;
}

// 공개될 멤버 (특권 메소드) 정의
return {
publickey : privateKey,
publicMethod : function () {
return privateMethod();
}
}

})();

console.log(module.publicMethod()); // 1

console.log(module.publicMethod())// 2 (클로저로 인한 결과)


                                                                           (코드 - 1)


모듈 패턴은 반환값이 함수가 아니라 객체이며, 자동 호출된다는 점만 제외하고 클로저와 유사합니다.
(잘못된 설명으로 생각함. 함수를 반환하는게 클로저는 아니기 때문)

그리고 인스턴스를 여러 개 만들어 낼 수 있는 구조라는 점에서 싱글톤 패턴과 차이가 있습니다. 위의 코드를 실행하면 익명함수가 자동으로 호출되어 익명함수가 반환하는 객체가 Module 변수에 할당되게 됩니다. 따라서 위와 같이 module.publicMethod() 호출할 수 있습니다. 위의 코드는 하나의 인스턴스 객체만을 생성하고 있어서 싱글톤과 유사합니다.
하지만 아래와 같이 자동으로 호출(self-invoking) 되는  구조를 없애면 여러 개의 인스턴스를 생성하여 사용할 수 있습니다.



자동 호출 구조를 없앤 코드입니다.


var Module function () {


/**
* --------------------------------
* 모듈 패턴을 구현한 클로저 코드
* --------------------------------
*/

// 은닉될 멤버 정의
var privateKey = 0;

function privateMethod() {
return ++privateKey;
}

// 공개될 멤버 (특권 메소드) 정의
return {
publickey : privateKey,
publicMethod : function () {
return privateMethod();
}
}

};

// 두 개의 인스턴스 생성
var obj1 = Module();
obj1.publicMethod(); // 1 출력
obj1.publicMethod(); // 2 출력

var obj2 = Module();
obj2.publicMethod(); // 1 출력
obj2.publicMethod(); // 2 출력

 (코드 -2)


위와 같이 Module 함수를 정의(즉시실행 X) 하여 함수를 호출하면 여러 개의 인스턴스인 객체를 생성하여 사용할 수 있습니다. 클로저 인스턴스와 유사하지만 한가지 차이점은 내부의 익명함수에서 반환값이 함수가 아니라 객체를 반환한다는 점입니다.




위에까지의 내용은 http://e-rooms.tistory.com/5 에서 퍼왔구요.



그럼 코드 - 1 에서 



console.log(module.publicMethod()); // 1

console.log(module.publicMethod()); // 2 


이거 아래에 


console.log(module.publickey);  를 넣으면  몇이 나올까요? 



답은 0 입니다. 




.



어떻게 이렇게 어려운(직관적이지않은) 언어가 세상을 지배하게 됬는지 정말 불가사의하다.


함수 

함수생성법

객체생성법

함수 호이스팅

함수 프로퍼티 

함수 형태  

함수호출과 This 바인딩 

함수 리턴 

프로토타입 체이닝

실행 컨텍스트

스코프 체인 

클로저 

객체지향프로그래밍

함수형 프로그래밍




자바스크립트에서 함수


C/JAVA/C# 등에서 보통 함수는 특정기능을 제공하고 결과값을 얻는 행위 only     

                                                  VS

자바스크립트에서의  함수는 위에것 + 모듈화 처리 + 클로저 + 객체생성등등 JS의 근간이 되는 많은 기능이 추가되어있다. 


“자바스크립트 공부에서 가장 중요한것은  함수의 기능에 대한 기존 프레임에서 알을 깨고 나오는것” 그리고 this 에 대한 감각.


1. 함수안에 함수가 들어가서 c++/java 의  멤버함수 (js에서는 메소드) 역할을 할 뿐 아니라 

2.변수에 할당이 가능하고 매개변수로도 넘길수있고,리턴값 및 배열의 값으로 사용 

3.결정적으로 클래스 역할도 한다. 




함수 정의의 3가지 방법 

 

함수 선언문 

function add(x,y){

return x+y;

}

“반드시 함수명이 정의되어야 한다” 

“function 이라는 키워드를 명시적으로 사용한다”

“ 리턴값과 매개변수로 넘기는값에 변수 타입을 기술하지 않는다”


함수 표현식 (1)

var  add = function(x,y){

return x+y;};


var plus = add;

함수 선언문 방식과 거의 유사하다.

유일한 차이점은 함수이름이 선택사항, 보통 사용하지 않음. 

위의 add와 plus 는 동일한 값(익명함수) 을 참조한다. 

관례적으로 함수뒤에 ; 를 넣는다. 


함수 표현식 (2) 

var  add = function sum(x,y){

return x+y;};


console.log(sum(3,4));  

어떻게 될까? 



console.log(sum(3,4));   // 에러!!!


함수 표현식에서 사용된 함수이름은 외부코드에서 접근 불가능!! 
(내부에서는 가능)



Function() 생성자 함수를 통한 생성  

new Function(arg1,arg2,...argN, func body);

var add = new Function(‘x’,’y’, ‘return x+y’);
console.log(add(3,4)) // 7



함수선언문,함수표현식 둘다 내부적으로 이것을 이용한다.
일반적으로 자주 사용되지는 않는다.
이런게 있다는것만 알아두자.



함수 호이스팅 


add(2,3); // 5 <------- (1)

function add(x,y){  <-------- (2)
return x+y;}

add(3,4); // 7  <--------(3) 


(1) 시점에는 함수 (2) 가 정의되지도 않았는데 호출이 가능하다 . 
함수 선언문 형태로 정의한 함수의 유효범위는 코드의 맨 처음으로  끌어올려 Hoisting 진다..
따라서 함수 선언문보다는 함수 표현식을 사용하기를 강력히 권고한다.  (코드구조가 엉성해짐) 
 


함수도 객체다.   

function add(x,y){
return x+y;}

add.result = add(3,2);
add.status = ‘OK’; 


보통 객체처럼 result / status 같은 프로퍼티를 가질수있다.
근데 놀라운것은 나중에 필요할때마다 추가할수있다는점이다. 



객체 생성 in Javascript 

// (생성자)  함수를 통함. 
function Person(name,age){
            this.name = name;
            this.age    = age
 
           // 메서드도 추가 가능 
};
 
var foo = new Person('foo',30);  // 이미 선언되있던 함수를 가지구  new 를 사용하여 만든다. 


//객체 리터럴을 통해서 (JSON 처럼) 
var foo = { name : ‘foo’,  
           age : 30
};



함수는 일급객체이다. 


//변수나 프로퍼티의 값으로 할당 

var foo = 100;
var bar = function(){return 100; };
console.log(bar()); // 100 

var obj = {};
obj.baz = function(){return 200;}
console.log(obj.baz()); // 200 


//함수 인자로 전달

    var foo = function(func){
func(); // 인자로 받은 함수 호출 
    };

    foo(function(){
                 console.log(“hello”);
          }
    );



//리턴 값으로 활용  

var foo = function(){
return function(){
console.log(“hello”);
                         };
                };

var bar = foo();
bar();

> hello


함수 객체의 기본 프로퍼티   


function add(x,y){return x+y;}

console.log(add);

argument,
caller,
length,
name,
prototype,
__proto__   

등과 같은 다양한 프로퍼티가 기본적으로 생성되어 있다. 
(ECMA표준도 있고, 브라우저별 프로퍼티도 있음) 


[[Prototype]] 


모든 자바스크립트 객체는 자신의 프로토타입을 가리키는 
[[Prototype]] 내부 프로퍼티를 가진다. 크롬브라우저에서는
__proto__ 라고 표현.


Function.prototype 
Object.prototype 
Array.prototype
Person.prototype
Number.prototype / String.prototype

constructor 프로퍼티
toString() 메서드
apply(thisArg,argArray) 메서드
call(thisArg,[,arg1[,arg2,]]) 메서드 
bind(thisArg,[,arg1[,arg2,]]) 메서드



함수 형태 


콜백 함수

비동기 처리에서 어떤 신호가 왔을때
웹페이지에서 특정 DOM이벤트에 대응하는 함수 


즉시 실행 함수(1) 
(function(name){
console.log(‘hello -> ‘ + name);
})(‘foo’);

다시 호출안됨. 초기화코드에서 사용하면 좋음.


즉시 실행 함수(2)  
(funtion(window,undefined){

..
})(window);

프레임워크에서 사용됨
jquery 에서 전체를 묶음(바운더리만듬)


내부 함수

자신을 둘러싼 부모함수의 변수에 접근 가능

        (클로저) 


함수를 리턴하는 함수

var self = function(){
         console.log(‘a’);
      return function(){
                  console.log(‘b’);
                  }
}

self = self(); // result  >>  a
self();          // result  >>  b





호출패턴 과 This바인딩 

자바스크립트에서 함수를 호출할때 기존 매개변수로 전달되는 인자값에 더해, arguments 객체 및  This인자가 함수 내부로 

암묵적으로 전달됨. 자바스크립트에서 This 는 매우 중요하다. 함수가 호출되는 방식(호출패턴)에 따라 This 가 다른 객체를 

참조(This바인딩) 하기 때문이다.


This바인딩(객체의 메소드호출시) 

객체의 메서드를 호출할때, 메서드 내부에서 사용하는 This 는 해당메서드를 호출한 객체로 바인딩된다. 

var myObject = {
name: ‘foo’,
sayName : function(){
console.log(this.name);
}
};

var otherObject = { name : ‘bar’ };

otherObject.sayName = myObject.sayName;

myObject.sayName(); // ‘foo’
otherObject.sayName(); // ‘bar’



This바인딩(함수 호출시1) 

This는 전역 객체에 바인딩된다. 

-브라우저라면 window 객체
- Node 라면 globlal 객체 

“자바스크립트의 전역변수는 실제로는 전역객체의 프로퍼티들이다.”
var foo = “hello world”;
console.log(foo);   // hello world
console.log(window.foo); // hello world

var test = ‘This is test’;
console.log(window.test);

var sayFoo = function(){
console.log(this.test);
}

sayFoo();   // 'This is test' 



This바인딩(함수 호출시2) 

이런 특성은 내부 함수를 호출할때도 마찬가지이다.  즉 외부에서 객체를 호출한게 아니라면 this 는 전역임(아 어렵다 ..OTL) 
var value = 100;
var myObject={
value : 1,
func1 : function(){
this.value +=1;
console.log(‘ func1 this.value : ‘ + this.value);
func2 = function(){
this.value += 1;
console.log(‘ func2 this.value : ‘ + this.value);
}
func2();
}
}; 

myObject.func1();

  • " func1 this.value : 2"
  • " func2 this.value : 101"


This바인딩(함수 호출시3) 

다음과 같이  해결한다. 
var value = 100;
var myObject={
value : 1,
func1 : function(){
var that = this; 
this.value +=1;
console.log(‘ func1 this.value : ‘ + this.value);
func2 = function(){
this.value += 1;
console.log(‘ func2 this.value : ‘ + that.value);
}
func2();
}
}; 

myObject.func1();

  • " func1 this.value : 2"
  • " func2 this.value : 2"



This바인딩(생성자 함수 호출) 

그냥 기존함수에 new연산자를 붙여서 호출하면 해당 함수는 생성자 함수. 

new연산자로 함수를 생성자로 호출했을때 일어나는 일  (굉장히 중요함!!!) 

1.  빈 객체 생성 및 this 바인딩 
2.   this  를 통한 프로퍼티 생성 
3.  생성된 객체 리턴 

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

var foo = new Person("foo");
console.log(foo.name);  

  • "foo"
1. Person() 함수가 생성자로 호출되면, 일단 빈객체가 생성된다. this 를 빈객체와 바인딩 
2.빈객체는 Person() 의 prototype 프로퍼티가 가르키는 객체를 연결해서 자신의 프로토타입으로 설정
3. this 가 가르키는 빈 객체에  name 이라는 동적 프로퍼티 생성 
4. 리턴값이 없으므로  this  로 바인딩한  객체가 리턴값으로 반환되어 foo변수에 저장 





객체 생성 


객체 리터럴 vs 생성자 함수


리터럴은 한번만 가능 /   생성자함수를 사용하면 같은 형태를 재 생성 가능   




생성자 함수 호출시 

new없이   vs  new 를 붙여서 

this바인딩이 다르기때문에 오류발생가능 큼 
개발할때 큰 주의 요망. 

많은 라이브러리에서 내부코드에 저것을 방지하기위한 코드패턴이들어가있다. 

function A(arg){
                            if(!(this instanceof A))
                                         return new A(arg);





This바인딩(call, apply ) 

둘다 기능은 같으니 apply로 설명.
지금까지는 this 가  강제로 바인딩되었지만 apply를 사용하면 명시적으로 바인딩 

apply는 Function.property 객체의 메서드이다.
다음과 같이 호출가능.
function.apply(thisArg, argArray) 

function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}

var foo = {};

Person.apply(foo, [‘foo’, 30, ‘man’]);      // foo 객체가 Person 객체의 this 로 바인딩됨. 
console.dir(foo);

  • [object Object] {
      age
    : 30,
      gender
    : "man",
      name
    : "foo"
    }



함수 리턴  (1)

일반 함수나 메서드는 리턴값을 지정하지 않을 경우, undefined값이 리턴 

var noReturnFunc = function(){
console.log(‘This function has no return statement.’);

};


var result = noReturnFunc();
console.log(result);    // undefined




함수 리턴  (2)

생성자 함수에서 리턴값을 객체로 지정할경우 그 객체가 리턴된다. 

function Person(name,age,gender){
this.name = name;
this.age    = age;
        this.gender = gender;

return {name: "bar", age:20, gender : "woman"};
};

var foo = new Person("foo",30,"man");
console.log(foo);

  • [object Object] {
      age
    : 20,
      gender
    : "woman",
      name
    : "bar"
    }

   

생성자 함수의 리턴값으로 넘긴 값이 객체가 아니라 불린,숫자,문자열일경우는 무시. 



prototype 프로퍼티 vs [[Prototype]] 







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

var jane = new Person(‘Jane’);    // Person 객체는 상위에 프로토타입을 가지고있고, jane 이라는 새 객체가 생성되면 
                                                // jane 에 [[Prototype]] 이 생기고 그것은 Person 의 프로토타입을 가르키게 된다. 
                                                // 공통 상위단이 생긴다고 생각하면 된다.



프로토타입 체이닝

자바스크립트에서 객체는 자기 자신의 프로퍼티뿐만 아니라 ,
자신의 부모역할을 하는 프로토타입객체의 프로퍼티 또한 마치 자신의 것 처럼 접근하는게 가능하다. 이걸 가능케하는게 바로 프로토타입 체이닝!!




프로토타입 체이닝(1)

객체 리터럴 방식으로 생성된 객체의 프로토타입 체이닝 
Object.prototype 객체 

name = "john";
var myObject = {
name : ‘foo’,
sayName: function(){
console.log(‘My name is ‘ + this.name);    // 여기서  this 는  myObject  이다.
}
};

myObject.sayName();   
myObject.hasOwnProperty(‘name’);

  • "My name is foo"
  • true


name = "john";
var myObject = {
name : "foo",
  func1 : function(){
  sayName =  function() {
console.log("My name is " + this.name);     
};
        sayName();  // 이거 호출시  this 는 전역이다.  이런거 너무 헥깔린다;;
}
};

myObject.func1();   
myObject.hasOwnProperty("name");

  • "My name is john"
  • true



프로토타입 체이닝(2)

생성자 함수로 생성된 객체의 프로토타입 체이닝  
Person.prototype (함수에 연결된 프로토타입 객체는 디폴트로 constructor프로퍼티만 가진 객체이다) 

Person() -> Person.prototype->Object.prototype 

function Person(name,age,hobby){
this.name = name;
this.age    = age;
        this.hobby = hobby;
}

var foo = new Person(‘foo’,30,’tennis’);

foo.hasOwnProperty(‘name’); // true




프로토타입 체이닝(3)

프로토 타입 체이닝의 종점 Object.prototype 

객체 리터럴 방식이나 생성자 함수 방식에 상관없이 모든 자바스크립트 객체는 프로토타입체이닝으로 
Object.prototype 객체가 가진 프로퍼티와 메서드에 접근하고,서로 공유가 가능하다. 


그리고 표준 빌트인 프로토타입 객체에도 사용자가 직접 정의한 메서드를 넣을수 있다. 

String.prototype.testMethod = function(){
console.log(‘hello’);
};

var str = ‘this is test’;
str.testMethod();



프로토타입 체이닝(4)

프로토타입 메서드와 this 바인딩 

메서드 호출 패턴에서의 this 는 그 메서드를 호출한 객체에 바인딩!!

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

Person.prototype.getName= function(){
return this.name;};

var foo = new Person(‘foo’);
foo.getName();  //  foo 

Person.prototype.name = ‘person’; // 동적 추가 
Person.prototype.getName(); // person 



프로토타입 체이닝(5)

디폴트 프로토타입(Person.prototype같은) 은 다른 객체로 변경이 가능 하다.

디폴트 프로토타입 객체는 함수가 생성될때 같이 생성되며, 함수의 prototype 프로퍼티에 연결 된다. 

자바스크립트에서는 이렇게 함수를 생성할때 해당 함수와 연결되는 디폴트 프로토타입 객체를 다른 일반 객체로 변경하는것이 

가능하다. 이러한 특징을 이용해서 객체지향의 상속을 구현한다.   


프로토타입 체이닝

객체의 프로퍼티 읽기나 메서드를 실행할때만 프로토타입 체이닝이 동작한다.




실행 컨텍스트

실행 가능한 코드를 형상화하고 구분하는 추상적인 개념 

실행컨텍스트가 형성되는 경우는 전역코드함수로 실행되는 코드, eval() 함수로 실행되는 코드 3가지 

현재 실행되는 컨텍스트에서 이 컨텍스트와관련 없는 실행 코드가 실행되면, 새로운 컨텍스트가 생성되어 스택에 들어가고 

제어권이 그 컨텍스트로 이동한다. 



실행 컨텍스트 생성과정 

1. 활성 객체 생성 

(해당 컨텍스트에서 실행에 필요한 여러가지 정보를 담을 객체)

- 매개변수
- 사용자가 정의한 변수 및 객체
- 스코프 정보

등이 앞으로 담겨짐..  

변수객체로 사용됨. 
 

2.  arguments 객체 생성
  - 매개변수를 담는다. 


3. 스코프 정보 생성  
 -리스트형식으로 만들어짐 이것은 상위 실행컨텍스트의 변수도 접근가능
 -이 리스트를 스코프 체인이라고 함

4. 변수 생성 
   - 지역변수의 생성이 이루어진다.
          (생성은 변수객체에 이루어짐,활성객체가 변수객체로 사용됨) 
   - 생성이 이루어지는것이지 초기화가 이루어지지는 않는다.
   - 초기화는 표현식이 실행될때 이루어진다. 
           (변수객체 생성이 다 이루어진후 표현식 실행됨)

5. this 바인딩 



스코프 체인 

void A()
{
for(.....)
{
int a = 10;
}

printf(“ a: %d”, a);   // 컴파일 에러  c 언어는 for, if문도 하나의 스코프
}  

자바스크립트는 오직 함수만이 유효범위의 한 단위가 된다.
유효범위는 스코프체인을 따라서 위로 계속 올라가면서 넓어짐.. 

- 각 함수객체는 [[scope]] 프로퍼틸 현재 컨텍스트의 스코프체인을 참조한다. 
- 한 함수가 실행되면 새로운 실행 컨텍스트가 만들어지는데, 이 새로운 실행 컨텍스트는 현재 실행되는 함수객체의 
  스코프 프로퍼티를 복사하고 새롭게 생성된 변수객체를 해당 체인의 제일 앞에 추가한다. 



스코프 체인 예제(1)

var value = “value1”

function printFunc(){
var value = “value2”;
function printValue(){
return value;
}

console.log(printValue());
}

printFunc();

  • "value2"


스코프 체인 예제(2)

var value = “value1”

function printValue(){
return value;
}

function printFunc(func){
var value = “value2”;

console.log(func());
}

printFunc(printValue);

  • "value1"
각 함수객체가 처음 생성될 당시 실행 컨텍스트가 무엇인지를 생각해야한다. 




클로저

function outerFunc(arg1, arg2){
var local = 8;
function innerFunc(innerArg){
console.log((arg1 + arg2) / (innerArg + local));
}
return innerFunc;
}

var exam1 = outerFunc(2,4);
exam1(2);   
  • 0.6



클로저(1)

function outerFunc(){
var x = 10;
var innerFunc=function(){ console.log(x)};
return innerFunc;
}

var inner = outerFunc();
inner(); 
  • 10

outerFunc 실행컨텍스트는 사라졌지만 outerFunc 변수객체는 여전히 남아있다.



클로저(2)
함수의 캡슐화
( I am brad. I live in seoul. I’am 28 years old 문장 출력하기)

var buffAr = [
‘I am’,
‘’,
‘.I live in ‘,
‘’,
‘. I \’ am’,
‘’,
‘years old.’,
]; 

function getCompletedStr (name, city, age) 
{
buffAr[1] = name;
buffAr[2] = city;
buffAr[5] = age;

return buffAr.join(‘’);
};

var str = getCompletedStr(‘brad’, ‘seoul’, 28);
console.log(str);



객체지향 프로그래밍 

var person = { 
name = “brad lee”,
getName : function(){
return this.name;
},
setName : function(arg){
this.name = arg;
}
};

function create_object(o){
function F(){};
F.prototype = o;
return new F();
}

var student = create_object(person);

student.setName(“ john “);
console.log(student.getName());  // john 


student 객체는 person 객체를  상속받은후에 다른 기능을  추가도  할수있게 된다. 

student.setAge = function (arg) { .....}  



하지만 이렇게 되면 코드가 지져분해지는데 , 그래서  라이브러리들은  extend() 함수를 제공

jQuery 1.0 의 extend() 를 살펴보자

jQuery.extend = jQuery.fn.extend = function(obj, prop) {
if( ! prop ){ prop = obj ;  obj = this; }
for(var i in prop ) {
obj[i] = prop[i];   // 얕은 복사 
}
return obj;
}; 


jQuery 1.7 에서는 깊은복사/얕은복사 선택을 해준다. 

jQuery 에서 이런것은 extend 는 

내가 만든 기능을 jQuery 에 플러그인으로 집어넣기 위해 사용할수 있겠다.


(function ($) {
$.extend( $.fn , {
my_func : function() {
...
}
} );
}) (  jQuery );






함수형  프로그래밍 

function sum( arr ){
var len = arr.length;
var i = 0 , sum = 0;

for ( ; i < len ; i++) {
sum += arr[i];
}

return sum;
}

var  arr = [ 1, 2, 3, 4, 5];
console.log(sum (arr));
  • 15

//////////////////////////////////////////////////////////////////////////////////

// 함수를 인자로 매개변수로 넘겨서 계산하게함. 
var sumFunc = function (x,y){
return x + y;
};

function reduce ( func, arr ){
var len = arr.length;
var i = 0 , sum = 0;
for( ; i < len ; i++){
sum  = func(sum, arr[i]);
}
return sum;
}

var  arr = [ 1, 2, 3, 4, 5];
console.log(reduce(sumFunc, arr));

  • 15






참고 

http://www.yes24.com/24/goods/11781589?scode=032&OzSrank=1
http://www.yes24.com/24/goods/3071412?scode=032&OzSrank=10



https://developer.mozilla.org/ko/docs/Web/JavaScript/Memory_Management  펌


개요

C 언어같은 저급 언어는 메모리 관리를 위해 malloc() 과 free()를 사용한다. 반면, 자바스크립트는 무언가가 생성되었을 때(오브젝트나 문자열 등) 메모리를 할당하고 쓸모 없어졌을 때 '자동으로' free 한다. '자동으로' 라는 말에는 혼란의 여지가 있다. 이는 자바스크립트를 포함한 여러 고급 언어 개발자들에게 메모리 관리가 불가능하다는 인상을 준다. 하지만 실상은 그렇지 않다.  

메모리 생존주기

메모리 생존주기는 프로그래밍 언어와 관계없이 비슷하다.

  1. 필요할때 할당한다.
  2. 사용한다. (읽기, 쓰기)
  3. 필요없어지면 해제한다. 

첫 번째 부분과 두 번째 부분은 모든 언어에서 분명하게 기술되지만 마지막 부분은 조금 다르다. 저급 언어에서는 분명히 기술되지만 자바스크립트 같은 고급 언어에서는 분명하게 기술되지 않는다(역자: 명시적으로 free를 하지 않는다는 의미). 

자바스크립트에서 메모리 할당

값 초기화

자바스크립트에서는 프로그래머들이 일일히 메모리 할당을 하는 수고를 덜어주기위해 값을 선언할 때 메모리를 할당한다. 

var n = 123; // 정수를 담기 위한 메모리 할당
var s = "azerty"; // 문자열을 담기 위한 메모리 할당

var o = {
  a: 1,
  b: null
}; // 오브젝트와 그 오브젝트에 포함된 값들을 담기 위한 메모리 할당

var a = [1, null, "abra"]; // (오브젝트 처럼) 배열과 배열에 담긴 값들을 위한 메모리 할당

function f(a){
  return a + 2;
} // 함수를 위한 할당(함수는 '호출가능한' 오브젝트이다)

// 함수식 또한 오브젝트를 담기위한 메모리를 할당한다. 
someElement.addEventListener('click', function(){
  someElement.style.backgroundColor = 'blue';
}, false);

함수 호출을 통한 할당

몇 가지 함수에서도 메모리 할당이 일어난다. 

var d = new Date();
var e = document.createElement('div'); // DOM 엘리먼트를 위해 메모리를 할당한다.

몇 가지 메쏘드도 새로운 값이나 오브젝트를 담기 위해 메모리 할당이 일어난다.

var s = "azerty";
var s2 = s.substr(0, 3); // s2는 새로운 문자열
// 자바스크립트에서 문자열은 immutable 값이기 때문에 메모리를 새로 할당하지 않고 단순히 [0, 3] 이라는 범위만 저장한다. 

var a = ["ouais ouais", "nan nan"];
var a2 = ["generation", "nan nan"];
var a3 = a.concat(a2); // 4개의 원소를 가진 새로운 배열

값 사용

값 사용이란 기본적으로는 할당된 메모리를 읽고 쓰는 것을 의미한다. 변수나 오브젝트 속성 값을 읽고 쓸때 값 사용이 일어난다. 또 함수 호출시 함수에 인수를 넘길때도 일어난다. 

할당된 메모리가 더 이상 필요없을 때 해제하기

이 단계에서 대부분의 문제가 발생한다. "할당된 메모리가 더 이상 필요없을 때"를 알아내기가 힘들기 때문이다. 이제까지는 개발자들이 메모리가 필요없어질 때를 정하고 free하곤 했다. 

고급 언어 인터프리터는 "가비지 콜렉터"라는 소프트웨어를 가지고 있다. 가비지 콜렉터란 메모리 할당을 추적하고 할당된 메모리가 더 이상 필요 없어졌을 때 해제하는 작업을 한다. 이 작업은 근사적인 작업이다. 왜냐하면 일반적인 경우에 어떤 메모리가 필요없는지 알아내는 것은 알고리즘으로 풀 수 없는 비결정적인 문제이기 때문이다. (역자: 세상에 존재하는 모든 가비지 콜렉터는 안전하지만 완전하지 않다. 가비지 콜렉터는 항상 필요없어진 메모리만을 해제하지만 모든 필요없어진 메모리를 해제하는건 아니다)

가비지 콜렉션

위에서 언급한 것처럼 "더 이상 필요없는" 모든 메모리를 찾는건 비결정적이다. 따라서 몇 가지 제한을 두어 "더 이상 필요없는 모든 메모리"가 아니라 "더 이상 필요없는 몇몇 메모리"를 찾아보자. 몇 개의 가비지 콜렉션 알고리즘을 소개하고 한계점을 알아볼 것이다.

참조

가비지 콜렉션 알고리즘의 핵심 개념은 참조이다. A라는 메모리를 통해 (명시적이든 암시적이든) B라는 메모리에 접근할 수 있다면 "B는 A에 참조된다" 라고 한다. 예를 들어 모든 자바스크립트 오브젝트는 prototype 을 암시적으로 참조하고 그 오브젝트의 속성을 명시적으로 참조한다.

앞으로 "오브젝트"라는 어휘의 의미를 넓혀서 기존의 자바스크립트 오브젝트뿐만 아니라 함수 스코프도 포괄하자.

참조-세기(Reference-counting) 가비지 콜렉션

참조-세기 알고리즘은 가장 무난한 알고리즘이다. 이 알고리즘은 "더 이상 필요없는 오브젝트"를 "어떤 다른 오브젝트도 참조하지 않는 오브젝트"라고 정의한다. 어떤 오브젝트를 참조하는 다른 오브젝트가 하나도 없다면 그 오브젝트에 대해 가비지 콜렉션을 수행한다.

예제

var o = { 
  a: {
    b:2
  }
}; // 2개의 오브젝트가 생성되었다. 하나의 오브젝트는 다른 오브젝트의 속성으로 참조된다.
// 나머지 하나는 'o' 변수에 할당되었다.
// 명백하게 가비지 콜렉션 수행될 메모리는 하나도 없다.


var o2 = o; // 'o2' 변수는 위의 오브젝트를 참조하는 두 번째 변수이다.
o = 1; // 이제 'o2' 변수가 위의 오브젝트를 참조하는 유일한 변수가 되었다.

var oa = o2.a; // 위의 오브젝트의 'a' 속성을 참조했다.
// 이제 'o2.a'는 두 개의 참조를 가진다. 'o2'가 속성으로 참조하고 'oa'라는 변수가 참조한다.

o2 = "yo"; // 이제 맨 처음 'o' 변수가 참조했던 오브젝트를 참조하는 오브젝트는 없다(역자: 참조하는 유일한 변수였던 o2에 다른 값을 대입했다)
// 이제 오브젝트에 가비지 콜렉션이 수행될 수 있을까?
// 아니다. 오브젝트의 'a' 속성이 여전히 'oa' 변수에 의해 참조되므로 메모리를 해제할 수 없다.

oa = null; // 'oa' 변수에 다른 값을 할당했다. 이제 맨 처음 'o' 변수가 참조했던 오브젝트를 참조하는 다른 변수는 없으므로 가비지 콜렉션이 수행된다.

한계: 순환

이 알고리즘은 두 오브젝트가 서로를 참조하면 문제가 발생한다. 두 오브젝트 모두 필요 없어졌더라도 가비지 콜렉션을 수행할 수 없다.

function f(){
  var o = {};
  var o2 = {};
  o.a = o2; // o는 o2를 참조한다.
  o2.a = o; // o2는 o를 참조한다.

  return "azerty";
}

f();
// 두 오브젝트가 만들어지고 서로를 참조해서 순환이 일어났다.
// 함수가 종료되고 나면 사실상 두 오브젝트는 의미가 없어지므로 가비지 콜렉션이 수행되어야 한다.
// 그러나 위의 참조-세기 알고리즘에서는 두 오브젝트 모두 참조를 가지고 있기 때문에 둘 다 가비지 콜렉션이 일어나지 않는다.

실제 예제

인터넷 익스플로러 6, 7 은 DOM 오브젝트에 대해 참조-세기 알고리즘으로 가비지 콜렉션을 수행한다. 흔히, 이 두 브라우저에서는 다음과 같은 패턴의 메모리 누수가 발생한다. 

var div = document.createElement("div");
div.onclick = function(){
  doSomething();
}; // div 오브젝트는 이벤트 핸들러를 'onclick' 속성을 통해 참조한다.
// 이벤트 핸들러의 스코프에도 div 오브젝트가 있으므로 div 오브젝트에 접근할 수 있다. 따라서 이벤트 핸들러도 div 오브젝트를 참조한다.
// 순환이 발생했고 메모리 누수가 일어난다.

표시하고-쓸기(Mark-and-sweep) 알고리즘

이 알고리즘은 "더 이상 필요없는 오브젝트"를 "닿을 수 없는 오브젝트"로 정의한다.

이 알고리즘은 roots 라는 오브젝트의 집합을 가지고 있다(자바스크립트에서는 전역 변수들을 의미한다). 주기적으로 가비지 콜렉터는 roots로 부터 시작하여 roots가 참조하는 오브젝트들, roots가 참조하는 오브젝트가 참조하는 오브젝트들... 을 닿을 수 있는 오브젝트라고 표시한다. 그리고 닿을 수 있는 오브젝트가 아닌 닿을 수 없는 오브젝트에 대해 가비지 콜렉션을 수행한다.

이 알고리즘은 위에서 설명한 참조-세기 알고리즘보다 효율적이다. 왜냐하면 "참조되지 않는 오브젝트"는 모두 "닿을 수 없는 오브젝트" 이지만 역은 성립하지 않기 때문이다. 위에서 반례인 순환 참조하는 오브젝트들을 설명했다.

2012년 기준으로 모든 최신 브라우저들은 가비지 콜렉션에서 표시하고-쓸기 알고리즘을 사용한다. 지난 몇 년간 연구된 자바스크립트 가비지 콜렉션 알고리즘의 개선들은 모두 이 알고리즘에 대한 것이다. 개선된 알고리즘도 여전히 "더 이상 필요없는 오브젝트"를 "닿을 수 없는 오브젝트"로 정의하고 있다.

순환 참조는 이제 문제가 되지 않는다.

첫 번째 예제에서 함수가 리턴되고 나서 두 오브젝트는 닿을 수 없다. 따라서 가비지 콜렉션이 일어난다.

두 번째 예제에서도 마찬가지다. div 변수와 이벤트 핸들러가 roots로 부터 닿을 수 없어지면 순환 참조가 일어났음에도 불구하고 가비지 콜렉션이 일어난다.

한계: 오브젝트들은 명시적으로 닿을 수 없어져야 한다.

이 한계가 지적되었지만 실제로는 사람들은 이 문제를 비롯한 가비지 콜렉션에 별 관심이 없다.

더 보기


  1. $(셀렉).each(function(){
  2.     //루프돌기
  3. });

 

이런 구문을 많이 쓴다. 특히나 플러그인 제작시

 

  1. var public = {
  2.     fn : function(){
  3.         return this.each(function(){
  4.             //메소드
  5.         });
  6.     }//END fn
  7. };

 

이런식으로 오브젝트 포인터에 대해서 return을 해주는데, 여기서 this와 $(this)의 사용이 매번 헷갈린다.

더욱이! public.fn.call(this); 라고 콜을 하는 순간! this에 대한 포인터는 안드로메다로 빠져버린다~~~ 쓩!~

 

애매한건 딱 정리해서 적어둬야겠다.

 

결론은!

each 루프 내에서 this의 포인터는 jQuery 오브젝트가 아닌 DOM 오브젝트이다.

때문에 each내에서 jQuery의 메소드를 사용하고 싶다면,

$(this)로 jQuery오브젝드로 콜을 해야 한다.

 

간단한 예로,

 

  1. (function($){
  2.     var public = {
  3.         init : function(options){
  4.             var opt = $.extend({set:1}, options);
  5.             return this.each(function(){
  6.                 // each 루프
  7.                 // this는 DOM 오브젝트
  8.                 // $(this)가 jQuery 오브젝트
  9.                 this.hide() // ERROR!!
  10.                 $(this).hide() // GOOD~
  11.             };
  12.         } //END init()
  13.     }; //END public
  14.     $.fn.plugin = function(method){
  15.         return public.init.apply(this, arguments);
  16.     };//END plugin
  17. })(jQuery);

 

이렇게 플러그인을 만들때,

최초 $.fn.plugin에서의 this는 jQuery객체 즉, $(this)를 의미한다. 그래서 jQuery의 메소드들을 사용할 수 있다.

플러그인에서 public.init.apply를 이용해 this로 콜을 했으니 this를 받은 public.init() 에서 this는 다시금 $(this)가 된다.

그래서 this.each() 와 같은 jQuery메서드를 쓸 수 있는데,

이제 each안으로 들어오면 this는 DOM오브젝트가 된다. 그래서 여기서는 this가 jQuery의 메서드를 못쓴다.

 

 

한가지 팁은 DOM객체, 여기선 HTMLElement 객체의 기본 속성값은 jQuery 객체로 변환해 사용하는 것보단, 그냥 그 자체로 사용하는게 더 빠르다. 때문에 id값이나 offset, title등의 기본 속성값은 $(this).attr(‘id’)가 아닌 this.id 등으로 사용 해 주자.

 

출처:http://asrahi.me/20194512596




jQuery .each() Syntax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//DOM ELEMENTS
$("div").each(function(index, value) {
    console.log('div' + index + ':' + $(this).attr('id'));
});
//outputs the ids of every div on the web page
//ie - div1:header, div2:body, div3:footer
 
//ARRAYS
var arr = [ "one", "two", "three", "four", "five" ];
jQuery.each(arr, function(index, value) {
       console.log(this);
       return (this != "three"); // will stop running after "three"
   });
//outputs: one two three
 
//OBJECTS
var obj = { one:1, two:2, three:3, four:4, five:5 };
    jQuery.each(obj, function(i, val) {
       console.log(val);
    });
//outputs: 1 2 3 4 5


+ Recent posts