해당 포스팅은 링크된 웹사이트를 보고 이해한 내용을 정리합니다.
객체
: `키(key)`와 `값(value)`로 구성된 `프로퍼티(property)`들의 집합
: `프로퍼티(property) + 메소드(method)`로 구성된 집합
* 프로퍼티 값: JS에서 사용할 수 있는 모든 값 사용 가능 = 함수 사용 가능 => `메소드`
프로퍼티
: 프로퍼티를 식별하기 위한 식별자인 프로퍼티 `키`
- 프로퍼티 키: 빈 문자열을 포함하는 모든 문자열 or symbol 값
- 프로퍼티 값: 모든 값
* 이미 존재하는 프로퍼티 키를 중복 선언하면 나중에 선언한 프로퍼티가 먼저 선언한 프로퍼티를 덮어씀
* 배열과 달리 객체는 순서 보장 X
객체 생성 방법
- `클래스 기반 객체 지향 언어` : 클래스 사전 정의, 필요한 시점에 `new`연산자 사용해 인스턴스 생성
- `Prototype 기반 객체 지향 언어` 자바스크립트 : 클래스라는 개념 X, 별도의 객체 생성 방법 존재
* ECMAScript 6에는 클래스 도입 - 기존 프로토타입 기반 패턴의 Syntactic sugar
객체 리터럴
가장 일반적인 JS의 객체 생성 방식
`{ }`를 이용해 객체 생성: `{ }` 내에 1개 이상의 프로퍼티 기술 / 아무것도 기술하지 않으면 빈 객체
Object 생성자 함수
`new 연산자 + Object 생성자 함수` 호출해 빈 객체 생성
//생성자 함수
var person = new Object();
// 프로퍼티 추가
person.name = 'Lee';
person.gender = 'male';
* 일반 함수와 생성자 함수 구분 : 생성자 함수 이름 PascalCase 사용
💡 용어 정리
PascalCase :
- 모든 단어 첫 글자 `대문자`
- 클래스 이름에 사용, 클래스가 프로그래밍에서 가장 주요하고 높은 위치
- 고유 명사처럼 특정됨
- `명사`
- 클래스명에 명사를 여러 개를 붙여 쓰는 경우, 각 명사의 첫 글자 모두 대문자로 표기(인터페이스에도 적용)
CamelCase :
- 첫 단어를 `제외`하고 나머지 단어의 첫 번째 글자만 대문자
- 함수나 변수에 사용, 함수는 동작 시키는 명령어 개념
- 첫 단어가 주로 `동사`
- 변수는 형용사로 시작하는 경우도 많음 ex) fastCarId, largeTreeNm
생성자 함수
객체를 생성하기 위한 `템플릿(클래스)`처럼 사용
프로퍼티가 동일한 객체 여러 개 간편 생성 가능
// 생성자 함수
function Person(name, gender) {
var married = true; // private
this.name = name; // public
this.gender = gender;
this.sayHello = function(){
console.log('Hi! My name is ' + this.name);
};
}
// 인스턴스의 생성
var person1 = new Person('Lee', 'male');
var person2 = new Person('Kim', 'female');
* `this` = 생성자 함수가 생성할 `instance`
* `public` : 외부에서 참조 가능
* `private` : 외부에서 참조 불가능, 내부 접근 자유
객체 프로퍼티 접근
프로퍼티 키
문자열(빈 문자열 포함)을 지정
문자열, symbol 값 외의 값 지정 시, `암묵적 타입 변환`으로 문자열이 됨
JS에서 사용 가능한 유효한 이름인 경우, 따옴표 생략 가능
`표현식`을 프로퍼티 키로 사용하려면, `대괄호`로 묶기
프로퍼티 값 읽기
- `마침표(.) 표기법`
: 프로퍼티 키가 유효한 JS 이름 + 예약어 X
- `대괄호([ ]) 표기법`
: 프로포티 키가 유효한 JS 이름 X || 예약어 O
* 객체에 존재하지 않는 프로퍼티 참조 = `undefined` 반환
프로퍼티 값 갱신
프로퍼티에 새로운 값 할당
프로퍼티 동적 생성
객체 X인 프로퍼티 키에 값 넣으면 주어진 키와 값으로 프로퍼티 생성해 객체에 추가
프로퍼티 삭제
`delete` 연산자 사용하면 객체의 프로퍼티 삭제 가능
for-in문
객체(배열 포함)에 포함된 모든 프로퍼티에 대한 루프 수행
객체의 문자열 키(key) 순회하기 위한 문법
* `배열`에는 사용하지 않는 것이 좋음
- 프로퍼티 순서 보장 X
- 배열 요소들만 순회하는 것 X
for-of문
위의 `for-in문`의 단점을 보완
배열의 요소를 순회하기 위해 사용
Pass-by-reference
변수 `bar`에 변수 `foo`의 값을 할당할 경우
* 변수 `foo`는 생성된 객체의 참조값을 저장
변수 `foo`와 `bar` 모두 동일한 객체 참조
= 참조하고 있는 객체의 `val`값이 변경되면 두 변수 모두 변경된 객체의 프로퍼티 값 참조(복사 X)
Pass-by-value
원시 타입(한 번 정해지면 변경 불가)은 값(value)으로 전달 = 값이 복사되어 전달
참조 타입으로 저장되는 것이 아니라 값 자체의 저장
변수 b에 a를 할당할 경우, 변수 a의 값 1은 복사되어 변수 b에 저장
객체의 분류
1) `Built-in Object`는 웹페이지 등을 표현하기 위한 공통 기능 제공
- `Standard Built-in Objects (or Global Objects)`
- `BOM (Browser Object Model)`
- `DOM (Document Object Model)`
* `Standard Built-in Objects`를 제외한 `BOM`과 `DOM`을 `Native Object`로 분류 하기도 함
2) `Host Object`는 사용자가 생성한 객체
- `constructor` 혹은 `객체리터럴`을 통해 사용자가 정의하고 확장시킨 것
* 따라서 위의 객체들 구성된 후, 구성됨
객체와 변경불가성
`Immutability`: 객체 생성 후, 상태 변경 X 디자인 패턴
객체가 참조를 통해 공유되어 있다면 언제든지 변경될 수 있기 때문에 큰일남, 그래서 변경 불가로 짜는 것
의도하지 않은 객체의 변경이 발생하는 원인 : 주로 "레퍼런스를 참조한 다른 객체에서 객체를 변경"하기 때문
해결 방법
- 객체를 불변객체로 만들어 프로퍼티 변경 방지
- 변경이 필요한 경우, 참조가 아닌 객체의 `방어적 복사`를 통해 새로운 객체 생성 후 변경
- `Observer 패턴`으로 객체의 변경에 대처
`불변 객체` 사용하면 복제||비교를 위한 조작 단순화 + 성능 개선 도움
객체가 변경 가능한 데이터를 많이 가지고 있는 경우에는 오히려 부적절
immutable value vs. mutable value
JS의 원시타입 = 변경 불가능한 값`(immutable value)`
이외의 모든 값은 객체 타입 = 변경 가능한 값`(mutable value)`
=> 객체: 새로운 값을 다시 만들 필요 X `직접 변경 가능`
var str = 'Hello';
str = 'world';
메모리에 "Hello" 생성 - 식별자 str은 메모리 주소
새로운 문자열 "world" 생성 - 식별자 str은 메모리 주소
따라서, "Hello"와 "world" 모두 메모리에 존재
다만, 변수 str은 "Hello" -> "world"를 가리키도록 변경된 것
var arr = [];
console.log(arr.length); // 0
var v2 = arr.push(2); // arr.push()는 메소드 실행 후 arr의 length를 반환
console.log(arr.length); // 1
배열(객체)의 메소드 push(): `직접 대상 배열 변경`
배열은 객체, 객체는 mutable value
같은 원리로,
user안의 name을 user.name으로 myName 변수에 넣은 후, user.name의 값을 변경할 때, myName이 참조하는 값의 변화는 없지만,
user2 객체의 name 프로퍼티에 새로운 값을 할당하면 객체는 변경 가능 값이므로 user1과 user2가 같은 address를 참조하고 있기에 변경됨
불변 데이터 패턴(immutable data pattern)
객체의 변경이 필요한 경우, 참조가 아닌 객체의 방어적 복자를 통해 새로운 객체를 생성한 후 변경
- 객체의 방어적 복사(defensive copy)
Object.assign - 불변객체화를 통한 객체 변경 방지
Object.freeze
Object.assign
// Syntax
Object.assign(target, ...sources)
target 객체로 소스 객체의 프로퍼티 복사
`소스 객체의 프로퍼티 == target 객체 프로퍼티` 이면, 소스 객체의 프로퍼티로 덮어쓰기 + 리턴값: target 객체
1) 기존 객체를 변경하지 않고 객체를 복사해 사용
2) deep copy 지원 X
3) 객체 내부의 객체는 Shallow copy
Object.freeze
1) 불변 객체로 만들기 가능
2) 객체 내부의 객체는 변경 가능
3) `Deep freeze` 할 경우 내부 객체 변경 불가능
Immutable.js
`Object.assign`과 `Object.freeze` 사용해 불변 객체를 만드는 방법은 번거로움+성능 이슈로 큰 객체에는 사용하지 X
그래서 또 다른 대안으로 `immutable.js`를 사용
List, Stack, Map, OrderedMap, Set, OrderedSet, Record와 같은 `영구 불변(Permit Immutable) 데이터 구조` 제공
[사용 방법]
$ npm install immutable
const { Map } = require('immutable')
const map1 = Map({ a: 1, b: 2, c: 3 })
const map2 = map1.set('b', 50)
map1.get('b') // 2
map2.get('b') // 50
함수
어떤 작업을 수행하기 위해 필요한 문(statement)의 집합을 정의한 코드 블록
1) 함수: 이름 + 매개변수
2) 필요한 때 호출해 문들을 일괄 실행
3) 여러번 호출 가능
4) 코드의 재사용 용이
5) 위의 기능 외에 객체 생성, 객체의 행위 정의(메소드), 정보 은닉, 클로저, 모듈화 등의 기능 수행
+ JS 함수는 객체(일급 객체, First-class object) but, 다른 객체와 달리 호출 가능
변수, 객체, 배열 등 함수에 저장 가능 && 다른 함수에 전달되는 인수로 사용 가능 && 함수의 반환값이 될 수 있음
함수 정의
- 함수 선언문
- 함수 표현식
- Function 생성자 함수
함수 선언문
`함수 선언문(Function declaration) 방식`으로 정의한 함수는 `function` 키워드와 이하 내용으로 구성
// 함수 선언문
function square(number) {
return number * number;
}
`함수명`
생략 불가
함수 몸체에서 자신을 재귀적(recursive) 호출 || JS 디버거가 해당 함수를 구분할 수 있는 식별자
`매개변수 목록`
0개 이상의 목록, 괄호로 감싸고, 콤마로 분리
타 언어와의 차이점: 매개변수 타입 기술 X
-> 함수 몸체 내에서 매개변수의 타입 체크 필요할 수 있음
`함수 몸체`
함수가 호출됐을 때 실행되는 문들의 집합
중괄호{ }로 문들을 감싸고, return문으로 결과값 반환
-> return value
함수 표현식
First-class object
생성, 대입, 연산, 인자 또는 반환값 전달
프로그래밍 언어의 기본적 조작을 제한없이 사용할 수 있는 대상
`일급 객체 특성`
1) 무명의 리터럴로 표현이 가능
2) 변수나 자료 구조(객체, 배열…)에 저장 가능
3) 함수의 파라미터로 전달 가능
4) 반환값(return value)으로 사용 가능
함수 리터럴 방식으로 함수 정의하고 변수 할당 가능 = `함수 표현식(Function expression)` 방식
함수 표현식 방식으로 정의한 함수는 함수명 생략 가능 = `익명 함수(anonymous function)`
일급객체 = 변수에 할당 가능
이때의 `변수`는 함수명이 아닌 할당된 함수를 가리키는 참조값 저장
=> 함수 호출시 함수명이 아닌 변수명 사용
Function 생성자 함수
함수 선언문과 함수 표현식 모두 함수 리터럴 방식으로 함수 정의
=> 내장 함수 `Function 생성자 함수`로 생성하는 것을 단순화시킨 short-hand
new Function(arg1, arg2, ... argN, functionBody)
Function.prototype.constructor 프로퍼티로 접근
함수 호이스팅
함수 선언의 위치와 상관없이 코드 내 어느 곳에서든지 호출 가능
자바스크립트는 ES6의 let, const를 포함한 모든 선언(var, let, const, function, function*, class) Hoisting
var 선언문이나 function 선언문 등 모든 선언문이 해당 Scope의 선두로 옮겨진 것처럼 동작하는 특성
= 모든 선언문 선언 이전에 참조 가능
# 함수 선언문으로 정의된 함수
`함수 선언`, `초기화`, `할당` 동시 실행
JS 엔진이 스크립트가 로딩되는 시점에 바로 초기화 - variable object에 저장
#함수 표현식의 경우 `변수 호이스팅` 발생
`변수 생성`, `초기화`, `할당` 분리 진행
호이스팅된 변수는 `undefined`로 초기화 - 실제값의 할당은 할당문에서 진행
* 로딩 시점에 VO에 함수 할당 X, runtime에 해석되고 실행됨
⭐함수 표현식 사용 권고
1. 함수 호이스팅이 함수 호출 전, 반드시 함수 선언해야 한다는 규칙을 무시
= 코드 구조 엉성
2. 대규모 애플리케이션 개발 시, 인터프리터가 너무 많은 코드를 VO에 저장
= 애플리케이션 응답 속도 저하
매개변수(Parameter, 인자)
함수 작업 실행을 위해 추가적 정보가 필요한 경우 지정
함수 내에서 변수와 동일하게 동작
매개변수 vs 인수(argument)
매개변수: 변수와 동일하게 메모리 공간 확보하며 함수에 전달한 인수는 매개변수에 할당됨
만약 인수를 전달하지 않으면 매개변수는 `undefined`로 초기화
var foo = function (p1, p2) {
console.log(p1, p2);
};
foo(1); // 1 undefined
Call-by-value
원시 타입 인수는 `Call-by-value`(값에 의한 호출)로 동작
매개변수에 값을 복사해 함수로 전달하는 방식
함수 내에서 매개변수를 통해 값 변경되어도 전달이 완료된 원시 타입 값은 변경 X
Call-by-reference
객체형(참조형)인수는 `Call-by-reference`(참조에 의한 호출)로 동작
매개변수 값이 복사되지 않고 객체의 참조값이 매개변수로 저장되어 함수로 전달되는 방식
매개변수 참조값 이용해 객체의 값 변경했을 때 전달된 참조형의 인수값도 같이 변경
chageVal() function
원시 타입과 객체 타입 인수를 전달 받아 함수 몸체에서 매개변수 값 변경
원시 타입 인수는 값 복사해 매개변수에 전달하니 함수 몸체에서 값을 변경해도 side-effect 발생 X = 순수 함수(Pure function)
객체형 인수는 값 변경시, 원본 객체가 변경되는 side-effect 발생 = 비순수 함수(Impure function)
- 복잡성 증가 = 비순수 함수를 줄이면 디버깅 용이
반환값
자신을 호출한 코드에게 수행한 결과를 반환 시 나온 값
- `return` 키워드 : 함수를 호출한 코드(caller)에게 값을 반환할 때 사용
- 배열 등을 이용해 한 번에 여러 개 값 반환 가능
- 반환 생략 가능, 암묵적 undefined 반환
- JS 해석기는 `return` 키워드 만나면 함수 실행 중단 - 함수 호출 코드로 복귀
If, `return` 키워드 이후에 다른 구문 존재, 해당 구문 실행 X
함수 객체의 프로퍼티
함수 = 객체 : 프로퍼티를 가질 수 있음
일반 객체와 달리, 함수만의 프로퍼티
arguments 프로퍼티
함수 호출 시 전달된 argument들의 정보를 담아 순회 가능한 `유사 배열 객체`이며 함수 내부에서 지역변수처럼 사용됨 = 함수 외부 사용 X
매개변수는 인수로 초기화
매개변수의 수보다 인수를 적게 전달(multiply(), multiply(1)), 인수가 전달되지 않은 매개변수 `undefined`로 초기화
매개변수의 수보다 인수를 많이 절달, 초과된 인수 무시
* arguments 객체는 매개변수 수가 확정되지 않은 `가변 인자 함수` 구현시 유용
💡 용어 정리
- `전역 변수(Global variable)`: 어느 위치에서든 호출하면 사용 가능
함수 밖에 선언해 클래스 전체에 사용 가능
- `지역 변수(Local variable)`: 특정 구역 { } 내에서 생성되어, 해당 구역에서만 사용 가능
함수 속에 선언되어 해당 함수 속에서만 사용 가능
- `클래스 변수(static)`: 객체를 따로 생성하지 않아도 사용 가능
남발시 프로그램 실행 속도 저하
- `유사 배열 객체(array-like object)`: length 프로퍼티를 가진 객체
배열이 아님, 배열 메소드를 사용하는 경우 에러 발생
`Function.prototype.call`, `Function.prototype.apply` 사용해야 함
caller 프로퍼티
자신을 호출한 함수
function bar() {
return 'caller : ' + bar.caller;
}
length 프로퍼티
함수 정의 시 작성된 매개변수의 수
function foo() {}
console.log(foo.length); // 0
function bar(x) {
return x;
}
console.log(bar.length); // 1
* arguments.length(함수 호출시 인자의 수)의 값과 다를 수 있음
name 프로퍼티
함수명
기명함수의 경우 함수명 = 값
익명함수의 경우 빈문자열 = 값
__proto__ 접근자 프로퍼티
모든 객체는 내부 슬롯 [[Prototype]] O = 프로포타입 객체를 가리킴
프로포타입 객체
= 다른 객체에 공유 프로퍼티를 제공하는 객체
`__proto__` 프로퍼티는 `[[Prototype]]` 내부 슬롯이 가리키는 프로토타입 객체에 접근하기 위해 사용하는 접근자 프로퍼티
내부 슬롯에는 직접 접근 X, 간접적 접근 방법을 제공하는 경우에 한해 접근할 수 있음
= `__proto__` 프로퍼티를 통해야 함
prototype 프로퍼티
함수 객체만이 소유하는 프로퍼티 = 일반 객체에는 없음
함수가 객체를 생성하는 생성자 함수로 사용될 때, 생성자 함수가 생성한 인스턴스의 프로토타입 객체를 가르킴
함수의 다양한 형태
즉시 실행 함수
함수 정의와 `동시에` 실행되는 함수(IIFE, Immediately Invoke Function Expression)
최초 1번만 호출 = 다시 호출 불가
//즉시 실행 함수(named immediately-invoked function expression)
(function myFunction() {
var a = 3;
var b = 5;
return a * b;
}());
* 익명일 경우 myFunction이 없으면 됨
`JS의 문제점`
파일이 분리되어 있어도 global scope가 하나 && 선언된 변수/함수는 코드 내, `어디서든지 접근 가능`
= 다른 스크립트 파일 내, 동일한 이름으로 명명된 변수/함수가 같은 스코프 내에 존재할 경우 문제가 됨
# `해결 방법`: 즉시 실행 함수 내에 처리 로직을 모아 변수/함수명의 충돌 방지
`jQuery` 같은 라이브러리의 경우, 즉시 실행 함수 내에 정의해두면 `독립된 영역` 내에 있게 되므로 충돌 문제 방지 가능
내부 함수
함수 내부에 정의된 함수
내부 함수는 자신을 포함하고 있는 부모 함수의 변수에 접근 가능하지만, + 부모 함수의 외부에서 접근 불가하고, 부모 함수는 자식(내부) 함수의 변수에 접근 불가
재귀 함수
자기 자신을 호출하는 함수 e.g) `피보나치 수열`, `팩토리얼`
무한히 연쇄 호출하므로 멈출 수 있는 조건(`탈출 조건`) 필요
탈출 조건이 없는 경우, 함수 무한 호출 => `stackoverflow` 에러 발생
`for문`, `while문`으로 구현 가능
반복문보다 직관적으로 이해 용이한 경우에만 `한정적` 적용 추천
콜백 함수
특정 이벤트가 발생했을 때, 시스템에 의해 호출되는 함수 e.g) `이벤트 핸들러 처리`
매개 변수를 통해 전달되고 전달받은 함수의 내부에서 `어느 특정 시점`에 실행
`setTimeout()`의 콜백 함수
setTimeout(function () {
console.log('1초 후 출력된다.');
}, 1000);
* 2번째 매개변수에 전달된 시간이 지나면 1번째 매개변수에 전달한 콜백 함수 호출됨
`비동기식 처리 모델`에 사용 = 처리가 종료하면 콜백 함수를 미리 매개변수에 전달하고 `처리 종료`하면 `콜백 함수 호출`
= 콜백 함수는 콜백 큐에 들어 있다가 이벤트 발생 시, 호출 + 콜백 함수는 클로저, 콜백 큐에 단독 존재하다가 호출되어도 전달받은 함수의 변수에 접근 가능
타입 체크
JS는 `Dynamic Typed` 언어로 변수에 할당될 값 예측 곤란
* 문법 상 문제가 없기 때문에 컴파일러 에러가 뜨지 않고 코드가 실행되는 경우가 많음
= 따라서 JS는 타입 체크가 필요!
typeof
typeof ''; // string
typeof 1; // number
typeof NaN; // number
typeof true; // boolean
typeof []; // object
typeof {}; // object
typeof new String(); // object
typeof new Date(); // object
typeof /test/gi; // object
typeof function () {}; // function
typeof undefined; // undefined
typeof null; // object (설계적 결함)
typeof undeclared; // undefined (설계적 결함)
* `null` 제외한 `원시타입` 체크 시에는 문제 X but, 객체의 종류 구분 체크까지는 곤란
Object.prototype.toString
여러 종류의 객체(일반 객체, 배열, Date, RegExp, Function, DOM 요소 등)를 구분할 수 있는 타입 체크 기능
객체를 나타내는 문자열 반환
`Function.prototype.call` 메소드: 모든 타입의 값의 타입
`String.prototype.slice` 메소드: `[object ]`를 제외하고 타입을 나타내는 문자열만 추출
* 타입이 같지 않으면 `TypeError` 발생
instanceof
객체의 상속 관계 체크
피연산자인 객체가 우항에 명시한 타입의 instance 여부를 알려줌
이때의 타입은 `constructor`이고 일치하는 것이 있다면 `true` 반환
css 함수의 1번째 매개변수에는 HTMLElement를 상속받은 DOM 요소(HTMLHeadElement 등)를 전달해야 함
= 이를 하나씩 다 체크하기엔 품이 많이 들기 때문에 HTMLElement를 상속받은 객체(: DOM 요소)인지 확인해야 함
유사 배열 객체
배열인지 체크하기 위해 `Array.isArray` 메소드 사용
`length` 프로퍼티를 갖는 객체 : 문자열, arguments, HTMLCollection, NodeList 등
+ 순회 가능, `call`, `apply` 함수 사용해 배열 메소드 사용 가능
console.log(Array.isArray([])); // true
* 유사 배열인지 체크하기 위해 우선 `length` 프로퍼티를 갖는지, `length` 프로퍼티 값이 정상적인 값인지 체크
프로토타입 객체
클래스 기반: 객체 생성 이전에 클래스 정의 - 객체(instance) 생성
프로토타입 기반: 클래스 없이(class-less) - 객체 생성 => JS
❗프로토타입 기반 객체 지향 프로그래밍
JS의 모든 객체는 자신의 `부모 역할`을 담당하는 객체와 연결
객체 지향의 `상속 개념`과 같이 부모 객체의 프로퍼티/메소드를 `상속` 받아 사용 가능
이때, 부모 객체 = `Prototype 객체`(: Prototype)
* Prototype 객체는 생성자 함수에 의해 생성된 각각의 객체에 공유 프로퍼티 제공 목적으로 사용
자바스크립트의 모든 객체는 [[Prototype]]이라는 인터널 슬롯(internal slot) 소유
[[Prototype]]의 값
null 또는 객체, 상속 구현에 사용
`__proto__` accessor property로 접근 가능
= 내부적으로 Object.getPrototypeOf가 호출되어 프로토타입 객체 반환
[[Prototype]] 객체의 데이터 프로퍼티
get 액세스를 위해 상속되어 자식 객체의 프로퍼티처럼 사용 가능
but, set 액세스는 허용 X
# 객체를 생성할 때 프로토타입 결정, 결정된 프로토타입 객체는 다른 임의의 객체로 변경 가능
= 부모 객체인 프로토타입 동적 변경 가능
[[Prototype]] vs prototype 프로퍼티
함수도 객체, [[Prototype]] 인터널 슬롯 소유 but, 함수 객체는 일반 객체와는 달리 `prototype 프로퍼티`도 소유
[[Prototype]] | `함수를 포함한` 모든 객체가 가지고 있는 인터널 슬롯 객체 입장) 자신의 `부모 역할을 하는 프로토타입 객체`를 가리키며, 함수 객체의 경우 `Function.prototype`를 가리킴 |
prototype 프로퍼티 | `함수 객체만` 가지고 있는 프로퍼티 함수 객체가 생성자로 사용될 때, 이 함수를 통해 `생성될 객체의 부모 역할을 하는 객체(프로토타입 객체)`를 가리킴 |
constructor 프로퍼티
객체의 입장에서 자신을 생성한 객체를 가리킴
Animal() 생성자 함수에 의해 생성된 객체를 cat이라고 하면, cat 객체를 생성한 객체는 Animal() 생성자 함수
이때 cat 객체 입장에서 자신을 생성한 객체는 Animal() 생성자 함수
cat 객체의 프로토타입 객체는 Animal.prototype
Prototype chain
특정 객체의 프로퍼티나 메소드에 접근하려고 하는데 접근할 것이 없다면, [[Prototype]]이 가리키는 링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티나 메소드를 차례로 검색하는 것
var tomato = {
name: 'tomatomato',
day: 15
}
// Object.prototype.hasOwnProperty()
console.log(tomato.hasOwnProperty('name')); // true
* tomato 객체의 `hasOwnProperty` 메소드를 가지고 있지 않아 에러가 발생해야 하는 것처럼 보임에도 정상적으로 작동하는 이유
객체 리터럴 방식으로 생성된 객체의 프로토타입 체인
객체 리터럴로 객체를 생성하면 JS 내부적으로 Object() 생성자 함수를 사용해 객체 생성
* 객체 리터럴을 사용하여 객체를 생성한 경우, 그 객체의 프로토타입 객체는 Object.prototype
생성자 함수로 생성된 객체의 프로토타입 체인
객체 생성 방식 | 엔진의 객체 생성 | 인스턴스의 prototype 객체 |
객체 리터럴 | Object() 생성자 함수 | Object.prototype |
Object() 생성자 함수 | Object() 생성자 함수 | Object.prototype |
생성자 함수 | 생성자 함수 | 생성자 함수 이름.prototype |
+ 이 역시도 객체이므로 일반 객체와 같이 `프로퍼티 추가/삭제` 가능 + 즉시 `프로토타입 체인에 반영`
+ 원시 타입은 객체가 아니므로 프로퍼티/메소드 소유 불가 but, 원시 타입으로 프로퍼티/메소드 호출 시, 원시 타입과 연관된 객체로 `일시적 변환`되어 프로토타입 객체 공유 = `직접 추가 불가`
프로토타입 객체 변경 시, 주의할 점
1. 프로토타입 객체 `변경 시점 전`에 생성된 객체: `기존` 프로토타입 객체를 [[Prototype]]에 바인딩
2. 프로토타입 객체 `변경 시점 후`에 생성된 객체: `변경된` 프로토타입 객체를 [[Prototype]]에 바인딩
프로토타입 체인 동작 조건
객체의 프로퍼티 참조
- 해당 객체에 프로퍼티 없는 경우, 프로토타입 체인 동작
객체의 프로퍼티 값을 할당
- 프로토타입 체인 동작 X
- 객체에 해당 프로퍼티가 있는 경우 값 재할당
- 없는 경우 해당 객체에 프로퍼티 동적으로 추가
유효범위(Scope)
* 전역변수와 지역변수 이야기를 잘 참조해서 이해하면 될 것 같다.
참조 대상 식별자를 찾아내기 위한 규칙
= 스코프가 없다면, 같은 식별자 이름끼리 충돌을 일으킬 수 있고, 그렇기 때문에 식별자 이름도 전체에서 1개만 사용 가능하게 될 것
Scope 구분
전역 스코프(Global scope)
: 코드어디에서든지 참조 가능
`var` 키워드로 선언한 전역 변수는 전역 객체 `window`의 프로퍼티
* Entry point가 없어 함수 영역 밖의 전역에서 선언 가능
= 시작점이 없어 코드가 나타나는 즉시 해석/실행 = 전역 변수 남발하게 되는 문제 발생 = 변수 이름 중복, 의도치 않은 재할당에 의한 상태 변화로 코드 예측 곤란
지역 스코프(Local scope || Function-level scope)
: 함수 코드 블록이 만든 스코프, 함수 자신과 하위 함수에서만 참조 가능
JS에서 Scope의 특징
함수 레벨 스코프 사용
💡용어 정리
블록 레벨 스코프
: 코드 블록 { } 내에서 유효(참조/접근)한 스코프
비블록 레벨 스코프
: JS는 블록 레벨 스코프를 사용하지 않기 때문에 코드 블록 밖에서 선언된 변수는 코드 블록 내에서 선언되어도 모두 전역 스코프 소유
함수 레벨 스코프
: 함수 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효 = 함수 외부에서는 유효 X
* 전역 변수와 지역 변수가 중복 선언된 경우
- 전역 영역에서는 전역변수만 참조 가능, 함수 내 지역 영역에서는 전역과 지역 변수 모두 참조 가능
- 변수명이 중복된 경우는 지역 변수 우선 참조
* ECMAScript6에서 도입된 `let` 키워드 사용 시, 블록 레벨 스코프 사용 가능
렉시컬 스코프
var x = 1;
function foo() {
var x = 10;
bar();
}
function bar() {
console.log(x);
}
foo(); // ?
bar(); // ?
1. Dynamic scope(동적 스코프)
함수를 어디서 호출하였는지에 따라 상위 스코프 결정
= 함수 `bar`의 상위 스코프는 함수 `foo`와 전역
2. Lexical scope(렉시컬 스코프) *대부분 이 방식을 따름
함수를 어디서 선언하였는지에 따라 상위 스코프 결정
= 함수 `bar`의 스코프는 전역
암묵적 전역(implicit global)
선언하지 않은 식별자에 값을 할당하면 전역 객체의 프로퍼티가 되기 때문에 마치 선언된 변수처럼 동작
실제로 변수는 아니기 때문에 변수 호이스팅을 발생하지 않음
변수가 아니라 단지 프로퍼티인 경우 `delete` 연산자로 삭제 가능
전역 변수는 프로퍼티지만 `delete` 연산자로 삭제 불가능
'JS' 카테고리의 다른 글
JAVASCRIPT의 배열은 배열이 아니다? (0) | 2024.08.15 |
---|---|
정규표현식(Regular Expression) (0) | 2024.08.13 |
JAVASCRIPT04 (0) | 2024.08.13 |
JAVASCRIPT02 (0) | 2024.08.02 |
JAVASCRIPT01 (0) | 2024.07.30 |