함수 생성
함수 리터럴
<script>
function add(x, y) {
return x + y;
}
</script>
1) 자바스크립트 함수 리터럴은 function 키워드로 시작
2) 함수명은 함수 몸체의 내부 코드에서 자신을 재귀적으로 호출하거나 또는 자바스크립트 디버거가 해당 함수를 구분하는 식별자로 사용. 함수명은 선택 사항, 함수명이 없는 함수를 익명 함수라고 한다.
3) 매개변수 리스트에 매개변수 타입을 기술하지 않는다.
함수 선언문(function statement)
<script>
function add(x, y) {
return x + y;
}
console.log(add(3, 4));
</script>
1) 함수 선언문 방식은 함수 리터럴 형태와 같다.
2) 함수 선언문 방식으로 정의된 함수의 경우 반드시 함수명이 정의되어 있어야 한다.
3) 위 함수 선언문의 실제 구조는 자바스크립트 엔진에 의해 함수 변수 add 형태로 변경되어 (let add = function add() {..}) 함수 외부에서 호출이 가능한 형태이다.
4) 일반적으로 함수 선언문 방식으로 선언된 함수의 경우 함수 끝에 세미콜론(;)을 붙이지 않는다.
함수 표현식(function expression)
<script>
let add = function (x, y) {
return x + y;
};
let plus = add;
console.log(add(3,4)); (출력값) 7
console.log(plus(5,6)); (출력값) 11
1) 함수 리터럴로 하나의 함수를 만들고, 여기서 생성된 함수를 변수에 할당
2) 함수 이름이 선택 사항이며, 보통 사용하지 않는다.
3) 위 예시의 변수 add는 함수 이름이 아니다. 함수가 할당된 함수 변수이다.
4) 일반적으로 함수 표현식 방식으로 선언된 함수의 경우 함수 끝에 세미콜론(;)을 붙이는 것을 권장한다.
let add = function sum(x, y) {
return x + y;
};
console.log(add(3,4));
console.log(sum(3,4));
1) 함수 표현식에서 사용된 함수 이름이 외부 코드에서 접근 불가능
let factorialLet = function factorial(n) {
if(n <= 1) {
return 1;
}
return n * factorial(n-1);
};
console.log(factorialLet(3)); (출력값) 6
console.log(factorial(3)); (출력값) Uncaught ReferenceError: factorial is not defined 에러 발생
1) 함수 외부에서는 함수 변수 factorialLet로 함수를 호출, 함수 내부에서 이뤄지는 재귀 호출은 factorial() 함수 이름으로 처리
</script>
Function() 생성자 함수
<script>
let add = new Function('x', 'y', 'return x + y');
console.log(add(3, 4)); (출력값) 7
</script>
-- 자주 사용되지는 않음
함수 호이스팅
<script>
console.log(add(2,3));
function add(x, y) {
return x + y;
}
console.log(add(3, 4));
1) add(2,3) 시점에서 함수가 정의되지 않았음에도 하단에 정의된 add() 함수를 호출하는 것이 가능하다. 이는 함수가 자신이 위치한 코드에 상관없이 함수 선언문 형태로 정의한 함수의 유효 범위는 코드의 맨 처음부터 시작한다는 것을 확인할 수 있다. 이것을 '함수 호이스팅'이라고 부른다.
2) 함수 호이스팅은 함수를 사용하기 전에 반드시 선언해야 한다는 규칙을 무시하므로 코드의 구조를 엉성하게 만들 수도 있다고 지적하며, 함수 표현식 사용을 권장한다.
console.log(add(2,3));
let add = function (x, y) {
return x + y;
};
console.log(add(3, 4));
1) 함수 표현식 형태로 정의되어 있어 호이스팅이 일어나지 않는다.
2) 함수 호이스팅이 발생하는 원인은 자바스크립트의 변수 생성과 초기화 작업이 분리돼서 진행되기 때문이다.
</script>
함수 객체
함수도 객체
<script>
function add(x, y) {
return x + y;
}
add.result = add(3, 2);
add.status = 'OK';
console.log(add.result); (출력값) 5
console.log(add.status); (출력값) 'OK'
1) 함수도 객체다. 즉, 함수의 기본 기능인 코드 실행뿐만 아니라, 함수 자체가 일반 객체처럼 프로퍼티들을 가질 수 있다.
</script>
함수도 값 = 일반 객체처럼 취급
- 리터럴에 의해 생성
- 변수나 배열의 요소, 객체의 프로퍼티 등에 할당 가능
<script>
let bar = function () { return 100; };
console.log(bar());
let obj = {};
obj.baz = function () { return 200; };
console.log(obj.baz());
</script>
<script>
let foo = function (func) {
func();
};
foo(function() {
console.log('Function can be used as the argument.');
}); (출력값) 'Function can be used as the argument.'
</script>
<script>
let foo = function () {
return function () {
console.log('this function is the return value.');
};
};
let bar = foo();
bar(); (출력값) 'this function is the return value.'
</script>
함수 객체의 기본 프로퍼티
function add(x, y) {
return x + y;
}
console.dir(add);

- name 프로퍼티 : 함수의 이름
- caller 프로퍼티 : 자신을 호출한 함수
- arguments 프로퍼티 : 함수를 호출할 때 전달된 인자값
- length 프로퍼티 : 함수가 정상적으로 실행될 때 기대되는 인자의 개수
<script>
function func0() {
}
function func1(x) {
return x;
}
function func2(x, y) {
return x + y;
}
function func3(x, y, z) {
return x + y + z;
}
console.log(func0.length); (출력값) 0
console.log(func1.length); (출력값) 1
console.log(func2.length); (출력값) 2
console.log(func3.length); (출력값) 3
</script>
- prototype 프로퍼티 : 모든 함수는 객체로서 prototype 프로퍼티를 가지고 있다. 여기서 주의할 것은 함수 객체의 prototype 프로퍼티는 모든 객체의 부모를 나타내는 내부 프로퍼티인 [[Prototype]]과 혼동되어서는 안 된다.
-> 두 프로퍼티 모두 프로토타입 객체를 가리킨다는 점에서는 공통점이 있지만, 관점에 차이가 있다. 모든 객체에 있는 내부 프로퍼티인 [[Prototype]]는 객체 입장에서 자신의 부모 역할을 하는 프로토타입 객체를 가리키는 반면에, 함수 객체가 가지는 prototype 프로퍼티는 이 함수가 생성자로 사용될 때 이 함수를 통해 새엇ㅇ된 객체의 부모 역할을 하는 프로토타입 객체를 가리킨다.
-> prototype 프로퍼티는 함수가 생성될 때 만들어지며, constructor 프로퍼티 하나만 있는 객체를 가리킨다. 그리고 prototype 프로퍼티가 가리키는 프로토타입 객체의 유일한 constructor 프로퍼티는 자신과 연결된 함수를 가리킨다. 즉, 자바스크립트에서는 함수를 생성할 때, 함수 자신과 연결된 프로토타입 객체를 동시에 생성하며, 이 둘은 prototype과 constructor라는 프로퍼티로 서로를 참조하게 된다.
<script>
function myFunction() {
return true;
}
console.dir(myFunction.prototype);
console.dir(myFunction.prototype.constructor);
</script>

다양한 함수 형태
콜백 함수
- 콜백 함수는 코드를 통해 명시적으로 호출하는 함수가 아니라, 개발자는 단지 함수를 등록하기만 하고, 어떤 이벤트가 발생했거나 특정 시점에 도달했을때 시스템에서 호출되는 함수를 말한다. 또한, 특정 함수의 인자로 넘겨서, 코드 내부에서 호출되는 함수 또한 콜백 함수가 될 수 있다.
- 대표적인 콜백 함수의 사용 예는 자바스크립트에서의 이벤트 핸들러 처리이다. 웹 페이지가 로드되거나 키보드가 입력되는 등의 DOM 이벤트가 발생할 경우, 브라우저는 정의된 DOM 이벤트에 해당하는 이벤트 핸들러를 실행시킨다. 만약 이러한 이벤트 핸들러에 콜백 함수가 등록했다면, 콜백 함수는 이벤트가 발생할 때마다 브라우저에 의해 실행되게 된다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type="text/javascript" src="hello_javascript.js"></script>
<script>
window.onload = function () {
alert('This is the callback function.');
};
</script>
</body>
</html>

즉시 실행 함수
- 함수를 정의함과 동시에 바로 실행하는 함수
- 최초 한 번의 실행만을 필요로 하는 초기화 코드 부분 등에 사용
- jQuery와 같은 자바스크립트 라이브러니나 프레임워크 소스들에서 사용
+ 함수 내부에서 정의된 매개변수와 변수들은 함수 코드 내부에서만 유효하기 때문에 라이브러리 코드를 즉시 실행 함수 내부에 정의해두게 되면, 라이브러리 내의 변수들은 함수 외부에서 접근할 수 없다. 따라서 이렇게 즉시 실행 함수 내에 라이브러리 코드를 추가하면 전역 네임스페이스를 더럽히지 않으므로, 이후 다른 자바스크립트 라이브러리들이 동시에 로드가 되더라도 라이브러리 간 변수 이름 충돌 같은 문제를 방지할 수 있다.
<script>
(function (name) {
console.log('This is the immediate function -> ' + name);
})('foo');
</script>

내부 함수
- 함수 내부에서 정의된 함수
- 클로저 생성, 부모 함수 코드에서 외부에서의 접근을 막고 독립적인 헬퍼 함수를 구현하는 용도 등으로 사용
- 내부 함수에서는 자신을 둘러싼 부모 함수의 변수에 접근이 가능하다.
- 내부 함수는 일반적으로 자신이 정의된 부모 함수 내부에서만 호출이 가능하다.
<script>
function parent() {
var a = 100;
var b = 200;
function child() {
var b = 300;
console.log(a);
console.log(b);
}
child();
}
parent();
child();
</script>

- 특정 함수 스코프 안에 선언된 내부 함수를 호출할 수 있다. 부모 함수에서 내부 함수를 외부로 리턴하면, 부모 함수 밖에서도 내부 함수를 호출하는 것이 가능하다.
<script>
function parent() {
var a = 100;
var child = function () {
console.log(a);
}
return child;
}
var inner = parent();
inner(); -> 클로저
</script>

<script>
var self = function () {
console.log('a');
return function () {
console.log('b');
}
}
self = self();
self();
</script>
함수 호출과 this
arguments 객체
- 함수 형식에 맞춰 인자를 넘기지 않더라도 함수 호출이 가능하다.
<script>
function func(arg1, arg2) {
console.log(arg1, arg2);
}
func();
func(1);
func(1,2);
func(1,2,3);
</script>
- 자바스크립트의 함수 호출 특성 때문에 함수 코드를 작성할 때, 런타임 시에 호출된 인자의 개수를 확인하고 이에 따라 동작을 다르게 해줘야 할 경우가 있다. 이를 가능케 하는게 바로 'arguments 객체'다.
- 'arguments 객체'는 함수를 호출할 때 넘긴 인자들이 배열 형태로 저장된 객체를 의미한다. -> 이 객체는 실제 배열이 아닌 '유사 배열 객체'(length 프로퍼티를 가진 객체)
<script>
function add(a, b) {
console.dir(arguments);
return a + b;
}
console.log(add(1));
console.log(add(1,2));
console.log(add(1,2,3));
</script>

<script>
function sum() {
var result = 0;
for(var i = 0; i < arguments.length; i++) {
result += arguments[i];
}
return result;
}
console.log(sum(1,2,3));
console.log(sum(1,2,3,4,5,6,7,8,9));
</script>
- arguments 객체는 매개변수 개수가 정확하게 정해지지 않은 함수를 구현하거나, 전달된 인자의 개수에 따라 서로 다른 처리를 해줘야 하는 함수를 개발하는 데 유용하게 사용할 수 있다.
호출 패턴과 this 바인딩
- 함수 호출 시 기존 인자와 함께 arguments 객체 및 this 인자가 함수 내부로 암묵적으로 전달된다.
- 함수가 호출되는 방식(호출 패턴)에 따라 this가 다른 객체를 참조(this 바인딩)할 수 있어서 주의해야 한다.
- 객체의 프로퍼티가 함수일 경우, 이 함수를 '메서드'라고 부른다. 이러한 메서드를 호출할 때, 메서드 내부 코드에서 사용된 this는 '해당 메서드를 호출한 객체로 바인딩'된다.
<script>
var myObject = {
name: 'foo',
sayName: function () {
console.log(this.name);
}
};
var otherObject = {
name: 'bar'
};
otherObject.sayName = myObject.sayName;
myObject.sayName();
otherObject.sayName();
</script>
- 자바스크립트에서 '함수를 호출'하면, 해당 함수 내부 코드에서 사용된 'this는 전역 객체에 바인딩'된다. 브라우저에서 자바스크립트를 실행하는 경우 전역 객체는 window 객체가 된다. '내부 함수를 호출'했을 경우에도 그대로 적용된다.
- 내부 함수 호출 패턴을 정의해 놓지 않으면 예상 결과와 다르게 내부 함수의 this는 전역 객체(window)에 바인딩된다.
<script>
var myObject = {
value: 1,
func1: function () {
this.value += 1;
console.log('func1() called. this.value : ' + this.value);
func2 = function () {
this.value += 1;
console.log('func2() called. this.value : ' + this.value);
func3 = function () {
this.value += 1;
console.log('func3() called. this.value : ' + this.value);
}
func3();
}
func2();
}
};
myObject.func1();
</script>

- 부모 함수의 this를 내부 함수가 접근 가능한 다른 변수에 저장하는 방법을 사용해서 해결
<script>
var myObject = {
value: 1,
func1: function () {
var that = this;
this.value += 1;
console.log('func1() called. this.value : ' + this.value);
func2 = function () {
that.value += 1;
console.log('func2() called. this.value : ' + that.value);
func3 = function () {
that.value += 1;
console.log('func3() called. this.value : ' + that.value);
}
func3();
}
func2();
}
};
myObject.func1();
</script>

생성자 함수를 호출할 때 this 바인딩
- 기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다.
<script>
var Person = function (name) {
this.name = name;
};
var foo = new Person('foo');
console.log(foo.name);
</script>
- 객체 생성 두가지 방법(객체 리터럴, 생성자 함수)
<script>
var foo = {
name: 'foo',
age: 35,
gender: 'man'
};
console.dir(foo);
function Person(name, age, gender, position) {
this.name = name;
this.age = age;
this.gender = gender;
}
var bar = new Person('bar', 33, 'woman');
console.dir(bar);
var baz = new Person('baz', 25, 'man');
console.dir(baz);
</script>

- 일반 함수 호출의 경우 this가 window 전역 객체에 바인딩되는 반면에, 생성자 함수 호출의 경우 this는 새로 생성되는 빈 객체에 바인딩된다.
<script>
function Person(name, age, gender, position) {
this.name = name;
this.age = age;
this.gender = gender;
}
var qux = Person('qux', 20, 'man');
console.log(qux);
console.log(window.name);
console.log(window.age);
console.log(window.gender);
</script>
- call과 apply 메서드를 이용한 명시적인 this 바인딩
- apply() 메서드에서 첫 번째 인자 thisArg는 apply() 메서드를 호출한 함수 내부에서 사용한 this에 바인딩할 객체를 가리킨다. 즉, 첫 번째 인자로 넘긴 객체가 this로 명시적으로 바인딩되는 것이다. 두 번째 argArray 인자는 함수를 호출할 때 넘길 인자들의 배열을 가리킨다.
- call() 메서드는 apply() 메서드의 배열 형태의 인자를 각각 하나의 인자로 넘기는 방식만 다르다. ex) Person.apply(foo, ['foo', 30, 'man']); -> Person.call(foo, 'foo', 30, 'man');
<script>
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
var foo = {};
Person.apply(foo, ['foo', 30, 'man']);
console.dir(foo);
</script>
-> Person('foo', 30, 'man') 함수를 호출하면서, this를 foo 객체에 명시적으로 바인딩한 것

- apply() 메서드를 활용한 arguments 객체의 배열 표준 메서드 slice() 활용
<script>
function myFunction() {
console.dir(arguments);
var args = Array.prototype.slice.apply(arguments);
console.dir(args);
}
myFunction(1,2,3);
</script>
1) arguments 객체는 length 프로퍼티만을 가진 유사 객체 배열이므로, shift()와 같은 표준 배열 메서드를 호출하면 에러가 발생한다.
2) apply() 메서드로 arguments 객체에서 마치 배열 메서드가 있는 것처럼 처리할 수 있다. Array.prototype.slice.apply(arguments)를 'Array.prototype.slice() 메서드를 호출하고, 이때 this는 arguments 객체로 바인딩한다.'로 해석하게 되면 arguments 객체가 Array.prototype.slice() 메서드를 마치 자신의 메서드인 양 arguments.slice()와 같은 형태로 메서드를 호출하라는 것이다.

함수 리턴
- 자바 스크립트 함수는 항상 리턴값을 반환한다. return 문을 사용하지 않았더라도 규칙에 따라 항상 리턴값을 전달
<script>
var noReturnFunc = function () {
console.log('This function has no return statement.');
};
var result = noReturnFunc();
console.log(result);
</script>
-- 리턴문이 없는 함수의 경우, 함수를 호출할 때 undefined 값이 리턴

- 생성자 함수에서 리턴값을 지정하지 않을 경우 this로 바인딩 된 새로 생성된 객체가 리턴된다. 때문에 생성자 함수에서는 일반적으로 리턴값을 지정하지 않는다.
- 생성자 함수에서 명시적으로 객체를 리턴할 경우 명시적으로 return된 객체가 출력된다.
<script>
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.dir(foo);
</script>

- 생성자 함수에서 명시적으로 기본 타입(불린, 숫자, 문자열) 값을 리턴했을 경우 리턴값을 무시하고 this로 바인딩된 객체가 리턴된다.
<script>
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
return 100;
}
var foo = new Person('foo', 30, 'man');
console.dir(foo);
</script>

프로토타입과 프로토타입 체이닝
prototype 프로퍼티와 [[Prototype]] 링크 구분
- prototype 프로퍼티는 함수의 입장에서 자신과 링크된 프로토타입 객체를 가리키고 있으며, 이에 반해 [[Prototype]] 링크는 객체의 입장에서 자신의 부모 객체인 프로토타입 객체를 내부의 숨겨진 링크로 가리키고 있다.
<script>
function Person(name) {
this.name = name;
}
var foo = new Person('foo');
console.dir(Person);
console.dir(foo);
</script>

객체 리터럴 방식으로 생성된 객체의 프로토타입 체이닝
- 프로토타입 체이닝은 자바스크립트에서 객체가 자기 자신의 프로퍼티뿐만이 아니라, 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티 또한 마치 자신의 것처럼 접근하는 게 가능하게 만든다.
<script>
var myObject = {
name: 'foo',
sayName: function () {
console.log('My Name is ' + this.name);
}
};
myObject.sayName();
console.log(myObject.hasOwnProperty('name'));
console.log(myObject.hasOwnProperty('nickName'));
myObject.sayNickName();
</script>
1) 객체 리터럴로 생성한 객체는 Object()라는 내장 생성자 함수로 생성된 것이다. Object() 생성자 함수도 함수 객체이므로 prototype이라는 프로퍼티 속성이 있다. 따라서 자바스크립트의 규칙으로 생성한 객체 리터럴 형태의 myObject는 Object() 함수의 prototype 프로퍼티가 가리키는 Object.prototype 객체를 자신의 프로토타입 객체로 연결한다.([[Prototype]] 링크)
2) 자바스크립트에서 특정 객체의 프로퍼티나 메서드에 접근하려고 할 때, 해당 객체에 접근하려는 프로퍼티 또는 메서드가 없다면 [[Prototype]] 링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티를 차례대로 검색하는 것을 프로토타입 체이닝이라고 한다.
3) myObject 객체에서 sayName() 메서드를 호출할 때는 해당 객체 내에 메서드가 있어 바로 수행된다. 반면에 hasOwnProperty() 메서드를 호출하는 동작 과정은 다르다. myObject.hasOwnProperty() 메서드를 호출했지만, myObject 객체는 hasOwnProperty() 메서드가 없다. 그렇다면 이후 myObject 객체의 [[Prototype]] 링크를 따라 그것의 부모 역할을 하는 Object.prototype 프로토타입 객체 내에 hasOwnProperty() 메서드가 있는지 검색하게 되고, Object.prototype 객체에 포함되어 있으면 정상적으로 코드가 수행되게 된다.

<script>
var myObject = {};
Object.prototype.sayBabo = function () {
console.log('babo!');
};
console.dir(myObject);
console.log(myObject.sayBabo());
</script>

생성자 함수로 생성된 객체의 프로토타입 체이닝
- 자바스크립트에서 모든 객체는 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체(부모 객체)로 취급한다.
<script>
function Person(name, age, hobby) {
this.name = name;
this.ag = age;
this.hobby = hobby;
}
var foo = new Person('foo','30','tennis');
console.log(foo.hasOwnProperty('name'));
console.log(Person.prototype);
</script>
1) foo 객체의 생성자는 Person() 함수이다. 따라서 자바스크립트의 룰에 따라 foo 객체의 프로토타입 객체는 자신을 생성한 Person 생성자 함수 객체의 prototype 프로퍼티가 가리키는 객체(Person.prototype)가 된다. 즉, foo 객체의 프로토타입 객체는 Person.prototype이 된다.
2) foo.hasOwnProperty() 메서드를 호출했지만, foo 객체는 hasOwnProperty() 메서드가 없어서 프로토타입 체이닝으로 foo의 부모 객체인 Person.prototype 객체에서 hasOwnProperty() 메서드를 찾는다.
그러나 함수에 연결된 프로토타입 객체는 디폴트로 constructor 프로퍼티만을 가진 객체이므로 hasOwnProperty() 메서드는 없다. Person.prototype 역시 자바스크립트 객체이므로 Object.prototype을 프로토타입 객체로 가진다. 따라서 프로토타입 체이닝은 Object.prototype 객체로 계속 이어진다. 그리고 Object.prototype 객체의 hasOwnProperty() 메서드가 실행되므로 에러가 발생하지 않고 true가 출력된다.

프로토타입 체이닝의 종점
- Object.prototype 객체는 프로토타입 체이닝의 종점이다. 객체 리터럴 방식이나 생성자 함수를 이용한 방식이나 결국엔 Object.prototype에서 프로토타입 체이닝이 끝난다.
기본 데이터 타입 확장
- 자바스크립트의 숫자, 문자열, 배열 등에서 사용되는 표준 메서드들의 경우, 이들의 프로토타입인 Number.prototype, String.prototype, Array.prototype 등에 정의되어 있다. 물론 이러한 기본 내장 프로토타입 객체 또한 Object.prototype을 자신의 프로토타입으로 가지고 있어서 프로토타입 체이닝으로 연결된다.
- Obejct.prototype, String.prototype 등과 같이 표준 빌트인 프로토타입 객체에도 사용자가 직접 정의한 메서드들을 추가하는 것을 허용한다.
<script>
String.prototype.testMethod = function () {
console.log('This is the String.prototype.testMethod()');
}
var str = "this is test";
str.testMethod();
console.dir(String.prototype);
</script>

프로토타입도 자바스크립트 객체다
- 함수가 생성될 때, 자신의 prototype 프로퍼티에 연결되는 프로토타입 객체는 디폴트로 constructor 프로퍼티만을 가진 객체다. 당연히 프로토타입 객체 역시 자바스크립트 객체이므로 일반 객체처럼 동적으로 프로퍼티를 추가/삭제하는 것이 가능하다. 이렇게 변경된 프로퍼티는 실시간으로 프로토타입 체이닝에 반영된다.
<script>
function Person(name) {
this.name = name;
}
var foo = new Person('foo');
Person.prototype.sayHello = function () {
console.log('Hello');
}
foo.sayHello();
</script>
프로토타입 메서드와 this 바인딩
- 메서드 호출 패턴에서의 this는 그 메서드를 호출한 객체에 바인딩된다.
<script>
function Person(name) {
this.name = name;
}
Person.prototype.getName = function () {
return this.name;
};
var foo = new Person('foo');
console.log(foo.getName());
Person.prototype.name = 'person';
console.log(Person.prototype.getName());
</script>
디폴트 프로토타입은 다른 객체로 변경이 가능하다.
- 디폴트 프로토타입 객체는 함수가 생성될 때 같이 생성되며, 함수의 prototype 프로퍼티에 연결된다. 자바스크립트에서는 이렇게 함수를 생성할 때 해당 함수와 연결되는 디폴트 프로토타입 객체를 다른 일반 객체로 변경하는 것이 가능하다.
- 생성자 함수의 프로토타입 객체가 변경되면, 변경된 시점 이후에 생성된 객체들은 변경된 프로토타입 객체로 [[Prototype]] 링크를 연결한다. 이에 반해 생성자 함수의 프로토타입이 변경되기 이전에 생성된 객체들은 기존 프로토타입 객체로의 [[Prototype]] 링크를 그대로 유지한다.
<script>
function Person(name) {
this.name = name;
}
console.log(Person.prototype.constructor);
var foo = new Person('foo');
console.log(foo.country);
Person.prototype = {
country: 'korea'
};
console.log(Person.prototype.constructor);
var bar = new Person('bar');
console.log(foo.country);
console.log(bar.country);
console.log(foo.constructor);
console.log(bar.constructor);
</script>
1) foo 객체는 디폴트 프로토타입 객체를, bar 객체는 새로 변경된 프로토타입 객체를 각각 [[Prototype]] 링크로 연결한다. 프로토타입이 달라서 foo 객체와 bar 객체는 프로토타입 체이닝이 서로 다른 결과값을 만들어 버린다.

객체의 프로퍼티 읽기나 메서드를 실행할 때만 프로토타입 체이닝이 동작한다.
- 객체의 특정 프로퍼티를 읽으려고 할 때, 프로퍼티가 해당 객체에 없는 경우 프로토타입 체이닝이 발생한다. 반대로 객체에 있는 특정 프로퍼티에 값을 쓰려고 한다면 이때는 프로토타입 체이닝이 일어나지 않는다.