프로토타입 1 2 실행결과 및 방법 윈도우10

  • 자바스크립트 타입
  • NaN (Not a Number)
  • delete 연산자
  • 객체의 모든 연산은 참조 값을 처리
  • Array 랑 Object 구분 방법
  • delete & splice 연산자 in 배열
  • typeof 연산자
  • == 연산자와 === 연산자
  • 함수 호이스팅
  • 함수의 length 속성
  • 내부 함수
  • 생성자 함수
  • instaceof 를 활용한 생성자 함수 구분법
  • prototype & constructor
  • 프로토타입 체이닝
  • Object, String, Number 프로토타입 객체 메서드 재정의
  • 즉시 실행 함수
  • 클로져
  • map() 구현
  • 실행 컨텍스트를 이해하기 위한 자바스크립트 동작과정
  • 변수 초기화 과정
  • 실행 컨텍스트를 이해하기 위한 문제
  • arguments 객체
  • apply() & call()
  • this 바인딩
  • 스코프 체인을 이해하기 위한 예제
  • 클로져 정의 및 코드 예시
  • 클로져 활용
  • 참고자료

자바스크립트 타입

기본 타입

  1. Number - 실수, 부동소수점 64비트(double)
  2. String - 문자열
  3. Boolean - True, False
  4. undefined - 변수에 값이 할당되지 않을 때 인터프리터가 undefined 로 할당. 값이자 타입
  5. null - 개발자가 의도적으로 할당하는 값. typeof 값이 Object 로 반환. 따라서 === 로 확인
var nullCheck = null; console.log(typeof nullCheck === null); // false console.log(nullCheck === null); // true

참조 타입(객체 타입)

  1. Object
  2. Array - 배열도 객체로 취급
  3. Function - 함수도 객체로 취급

NaN (Not a Number)

수치 연산을 해서 정상적인 값을 얻지 못할 때 발생하는 에러

console.log(1 - 'hello'); // NaN var foo = { name: 'foo', major: 'cs' }; foo['full-name'] = 'ffoo'; console.log(foo['full-name']); // 'ffoo' console.log(foo.full-name); // NaN, 프로퍼티명이 연산자를 포함할 경우

delete 연산자

객체 프로퍼티를 삭제하는 기능. 객체 삭제는 불가능

// 1. 객체 프로퍼티를 삭제 var foo = { name: 'foo', nickname: 'pangyo' }; delete foo.nickname; console.log(foo.nickname); console.log(foo); // {name: "foo"} // 2. delete 로 객체를 삭제할 경우 (변화 없음) var foo = { name: 'foo', nickname: 'pangyo' }; delete foo; console.log(foo); // {name: "foo", nickname: "pangyo"}

객체의 모든 연산은 참조 값을 처리

값 비교시에 사용하는 == 를 적용한 예제를 보자.

var a = 10; var b = 10; var objA = { value: 100 }; var objB = { value: 100 }; var objC = objB; console.log(a == b); // true console.log(objA == objB); // false console.log(objB == objC); // true

Array 랑 Object 구분 방법

var arr = []; var obj = {}; arr.constructor.name; // "Array" obj.constructor.name; // "Object"

delete & splice 연산자 in 배열

배열에서 delete 를 사용하면 요소의 값만 undefined 로 변경하고, 해당 요소 index 를 지우지는 않는다.

var arr = [1, 2, 3]; delete arr[1]; console.log(arr); // [1, undefined × 1, 3]

반대로 splice 는 해당 요소 전체를 아예 잘라내서 없앤다.

var arr = [1, 2, 3]; arr.splice(1, 1); console.log(arr); // [1, 3]

typeof 연산자

각 데이터 타입에 대한 typeof 수행결과는 다음과 같다.

var num = 10; var str = "a"; var boolean = true; var obj = {}; var undefined; var nullValue = null; var arr = []; function func() {}; console.log(typeof num); // number console.log(typeof str); // string console.log(typeof boolean); // boolean console.log(typeof obj); // object console.log(typeof undefined); // undefined console.log(typeof nullValue); // object (null 은 object) console.log(typeof arr); // object (배열도 object) console.log(typeof func); // function

== 연산자와 === 연산자

== 와 === 의 가장 큰 차이점은 값 뿐만 아니라 타입까지 체크하느냐이다. 또한 == 는 수행시에 타입이 다를 경우 타입을 일치시켜 값을 비교하는 특징이 있다.

console.log(1 == '1'); // true console.log(1 === '1'); // false

함수 호이스팅

add(2, 3); // add is not a function var add = function (a, b) { return a + b; }; add(4, 5);

위 코드의 실행 결과는 add is not a function 이다. 위 코드를 실행할 때 자바스크립트 엔진 관점에서 호이스팅을 적용하여 코드 순서를 변경해보면 아래의 결과가 된다.

var add; add(2, 3); add = function (a, b) { return a + b; }; add(4, 5);

함수의 length 속성

function func1(a) { return a; } function func2(a, b) { return a + b; } function func3(a, b, c) { return a + b + c; } console.log('func1 length : ' + func1.length); // func1 length : 1 console.log('func2 length : ' + func2.length); // func2 length : 2 console.log('func3 length : ' + func3.length); // func3 length : 3

내부 함수

함수의 내부에 정의한 함수

function parent() { var a = 10; var b = 20; function child() { var b = 30; console.log(a); console.log(b); } child(); } parent(); // 10, 30 child(); // child is not defined

생성자 함수

일반 객체 선언과 다르게 여러 개의 객체를 찍어낼 수 있는 함수. 함수명 맨 앞 글자는 대문자, 호출 시에 new 사용.

function Developer(name, stack, city) { this.name = name; this.stack = stack; this.city = city; } var dev = new Developer('captain', 'web', 'pangyo'); var devops = new Developer('hulk', 'devops', 'seoul'); console.log(dev); // Developer {name: "captain", stack: "web", city: "pangyo"} console.log(devops); // Developer {name: "hulk", stack: "devops", city: "seoul"}

위에서 생성한 dev 객체는 아래와 같이 constructor(생성자)가 Developer이다.

instaceof 를 활용한 생성자 함수 구분법

자바스크립트는 생성자 함수 형식이 별도로 없기에 기존 함수에 new만 붙여주면 생성자 함수 생성이 가능하다. 따라서, 생성자 함수가 아닌데 new 를 붙이는 경우를 대비해서 아래와 같은 기법을 적용할 수 있다. 대부분의 오픈소스 라이브러리에서 사용하는 패턴

function Func(arg) { // instanceof 로 생성자 함수임을 확인 if (!(this instanceof arguments.callee)) // 'this instanceof 함수명' 도 가능 return new Func(arg); this.value = arg || 0; } var a = new Func(100); var b = Func(200); console.log(a.value); console.log(b.value);

prototype & constructor

function func() { return true; } console.log(func.prototype); console.log(func.prototype.constructor);

프로토타입 체이닝

해당 함수에 존재하지 않는 속성, 메서드를 부모 객체(프로토타입 객체)를 찾음

var obj = { name: 'captain', printName: function () { console.log(this.name); } }; obj.printName(); // 'captain' obj.hasOwnProperty('name'); // true obj.hasOwnProperty('city'); // false

obj에서 사용한 printName() 메서드는 obj에 선언되었기 때문에 사용이 가능하다. 하지만 hasOwnProperty() 메서드는 선언되지도 않았는데 사용할 수 있다. 왜냐면 obj의 프로토타입 객체가 Object이고, Object에 내장된 메서드가 hasOwnProperty() 이기 때문에, obj에서 프로토타입 객체의 hasOwnProperty() 를 호출한다.

참고로, 자바스크립트 모든 객체의 최상위 부모 객체는 Object 객체다.

Object, String, Number 프로토타입 객체 메서드 재정의

자바스크립트에서 기본으로 제공하는 Object, String, Number 등의 표준 객체에 사용자가 원하는 기능을 재정의하여 사용할 수 있다.

String.prototype.printText = function (text) { console.log("Print this text out " + text); }; var name = "captain"; name.printText('pangyo'); // 'Print this text out pangyo'

즉시 실행 함수

함수를 정의함과 동시에 바로 실행하는 함수. 함수를 다시 호출할 수 없다는 특징이 있다. 따라서, 최초 한 번의 실행만 요구되는 초기화 코드에 적합하다. jQuery 와 같은 오픈소스 라이브러리들의 구조.

(function (name) { console.log('This is the immediate function : ' + name); })('foo');

클로져

실행이 끝난 함수의 스코프를 참조할 수 있는 함수

function parent() { var a = 'Parent is done'; function child() { console.log(a); } return child; } var closure = parent(); closure();

위 내부함수의 정의대로라면 parent 의 내부함수인 child() 는 외부에서 접근이 불가능하다. 하지만 return 값에 child 를 넘김으로써 외부에서도 child 를 호출할 수 있게 된다. 따라서, child() 에서 parent 의 값을 참고하고 있다면, child() 를 밖에서 호출함으로써 parent() 의 변수에 접근이 가능하게 된다. 이것이 클로져

map() 구현

// definition Array.prototype.myMap = function(callback) { arr = []; for (var i = 0; i < this.length; i++) arr.push(callback(this[i], i, this)); return arr; }; //tests var arrs = ['dic tanin', 'boo radley', 'hans gruber']; var numbers2 = [1, 4, 9]; var goodT = arrs.myMap(function(n) { return n; }); var squareRoot = numbers2.myMap(function(num) { return Math.sqrt(num); }); console.log(goodT); // [ 'dic tanin', 'boo radley', 'hans gruber' ] console.log(squareRoot); // [ 1, 2, 3 ]

실행 컨텍스트를 이해하기 위한 자바스크립트 동작과정

  1. 변수, 함수 선언, arguments 을 가진 활성 객체(Variable Object) 생성
  2. Scope Chain 생성 및 초기화
    • 변수 초기화 : 변수 값에 undefined 할당
  3. this 바인딩
  4. 코드 해석 및 실행
    • 변수 값 할당 : 변수에 실제 값 할당

변수 초기화 과정

  1. 변수 선언 - 변수를 활성 객체에 할당
  2. 변수 초기화 - 변수 값에 undefined 할당
  3. 변수 실제 값 할당 - 변수에 실제 값을 할당

실행 컨텍스트를 이해하기 위한 문제

비동기 실행 방식인 setTimeout 를 이용한 예제이다.

console.log("1"); function exec() { setTimeout(function() { console.log("2"); }, 3000); setTimeout(function() { console.log("3"); }, 0); console.log("4"); setTimeout(function() { console.log(5); }, 1000); } console.log(exec()); // 위 코드 실행 결과 : 1, 4, 3, 5, 2

setTimeout 이 지연시간이 0 이라고 할지라도 실행 컨텍스트가 다르기 때문에 1,4 가 먼저 출력된다.

이번엔 for 문과 setTimeout 이다.

var i; for (i = 0; i < 5; i++) { setTimeout(function() { console.log(i); // 5, 5, 5, 5, 5 }, 1000); }

위 코드를 실행시켰을 때, 이 코드가 실행되는 메인 컨텍스트와 setTimeout 이 실행되는 컨텍스트가 다르기 때문에 일반 프로그래밍 지식 관점에서는 0,1,2,3,4 이라고 추측하겠지만, 실제로는 for 문의 실행이 모두 끝난 후에 setTimeout 의 콜백 함수가 실행되기 때문에 숫자 5가 다섯 번 출력된다.

arguments 객체

함수 호출시에 넘겨진 실제 인자 값을 가진 배열

// 아래 함수 정의에 포함된 인자 값은 2개 function add(a, b) { console.dir(arguments); } console.log(add(1)); // Arguments(1), 0: 1 console.log(add(1, 2)); // Arguments(2), 0: 1, 1: 2 console.log(add(1, 2, 3)); // Arguments(3), 0: 1, 1: 2, 2: 3

arguments 의 활용 : 메서드에 넘겨 받을 인자의 개수를 모를 때 유용

function sum() { for (var i = 0, result = 0; i < arguments.length; i++) { result += arguments[i]; } return result; } console.log(sum(1,2,3)); // 6 console.log(sum(1,2,3,4,5,6)); // 21

참고: arguments는 length 속성과 `arguments[i]`와 같은 index를 지니지만 배열은 아니다. 이러한 객체를 배열과 비슷한 객체(array-like object)라고 한다.

apply() & call()

위에서 배운 arguments에 apply(), call()을 이용하여 실제 배열 메서드를 사용할 수 있다.

// apply() 적용 전 function sum() { console.log("arguments length : " + arguments.length); arguments.push(100); // Uncaught TypeError: arguments.push is not a function console.dir(arguments); // Arguments(3) } sum(1,2,3); // apply() 적용 후 function sum() { var args1 = Array.apply(arguments); args1.push(100); // 0: 100 console.dir(args1); // Array(1) var args2 = Array.prototype.slice.apply(arguments); args2.push(100); // 3: 100 console.dir(args2); // Array(4) } sum(1,2,3); 함수명.apply(대상, 인자 배열);

apply(), call() 메서드는 결국 .apply()를 호출하는 함수를 실행하는 것 이다. 그리고 호출하는 함수의 인자 값에 apply() 로 넘긴 인자 배열을 넣어서 마지막 실행 결과만 대상에 연결한다라고 보면 되겠다.

function user(firstName, lastName, age) { this.firstName = firstName; this.lastName = lastName; } user.apply(window, ['pangyo', 'captain']); // user.call(window, 'John', 'Doe'); 와 같음 console.log(window.firstName); // 'pangyo' console.log(window.lastName); // 'captain'

this 바인딩

일반적으로 함수 내부에서 this를 사용하면 전역 스코프(window)에 접근한다.

// 함수 선언식 var text = 'global'; function binding() { var text = 'local'; console.log(this.text); // 'global' console.log(this); // Window {stop: ƒ, open: ƒ, alert: ƒ, confirm: ƒ, prompt: ƒ, …} } binding(); // 함수 표현식 var text = 'global'; var binding = function() { var text = 'local'; console.log(this.text); // 'global' console.log(this); // Window {stop: ƒ, open: ƒ, alert: ƒ, confirm: ƒ, prompt: ƒ, …} } binding();

객체의 속성에서 함수를 선언하고 this를 사용하면 해당 객체를 접근한다.

var text = 'global'; var binding = { text: 'local', printText: function () { console.log(this.text); // 'local' console.log(this); // {text: "local", printText: ƒ} } }; binding.printText();

함수의 내부함수에서 this를 사용하면 전역 객체(window)에 접근한다.

var text = 'global'; var binding = { text: 'local', printText: function () { console.log(this.text); // local var innerFunc = function () { console.log(this.text); // global }; innerFunc(); } }; binding.printText();

스코프 체인을 이해하기 위한 예제

아래는 전역 스코프와 함수 스코프를 구분하면 된다.

// ex.1 var a = 1; var b = 2; function func() { var a = 10; var b = 20; console.log(a); // 10 console.log(b); // 20 } func(); console.log(a); // 1 console.log(b); // 2

아래는 내부함수 innerfunc 에서 외부함수인 func 의 변수에 접근하고 있다.

// ex.2 var a = 1; function func() { var a = 2; function innerfunc() { return a; } console.log(innerfunc()); // 2 } func();

아래 예제는 func1 의 실행 컨텍스트가 전역이라는 것에 주목한다.

// ex.3 var a = 1; function func1() { return a; } function func2(func1) { var a = 2; console.log(func1()); // 1 } func2(func1);

클로져 정의 및 코드 예시

  • 외부 함수의 실행이 종료되어 컨텍스트가 반환되더라도, 내부 함수로 종료된 외부 함수의 스코프(변수)에 접근이 가능한 기법:스코프 체이닝
  • 이미 생명주기가 끝난 외부 함수의 변수를 참조하는 함수
function func() { var a = 1; var cl = function () { console.log(a); }; return cl } var result = func(); console.dir(result); // [[Scope]] 에서 Closure 함수임을 확인 가능 result();

일정한 형식을 가진 템플릿에서 입력된 값에 따라 다른 결과물을 내는 코드

var str = [ 'hello ', '', ' world' ]; function completeSentence(name) { str[1] = name; return str.join(''); } completeSentence('js');

위 코드에 클로져를 적용하면

function completeSentence(name) { var str = [ 'hello ', '', ' world' ]; return function () { str[1] = name; return str.join(''); }; } var result = completeSentence('js'); result();

위 함수를 좀 더 기능 단위로 분할해보면

function completeSentence(name) { var str = [ 'hello ', '', ' world' ]; // 입력된 문자열로 문장을 완성하는 기능 var complete = function () { str[1] = name; return str.join(''); }; // 문장 완성 기능을 클로져로 빼는 역할 var closure = function () { return complete(); }; return closure; } var result = completeSentence('js'); result();

클로져 활용

클로져를 활용하여 Java나 기타 언어처럼 속성 및 메서드의 범위를 정할 수 있다.

// 클로져로 Java 클래스와 유사하게 모듈화한 예제 var Module = (function() { var privateProperty = 'foo'; function privateMethod(args) { console.log('private method'); } return { publicProperty: '', publicMethod: function(args) { console.log("public method"); }, privilegedMethod: function(args) { return privateMethod(args); } }; })(); Module.privilegedMethod();

참고자료

  • 인사이드 자바스크립트, 한빛미디어
  • Understanding Scope and Context in Javascript
  • alex gist - native js implementation
  • Demystifying JavaScript Closures, Callbacks and IIFEs

Toplist

최신 우편물

태그