JavaScript -> 객체 지향 언어, 객체 기반 언어
javascript를 구성하는 대부분은 객체이다.
mutable한 특징을 가진다.
: 0개 이상의 property의 집합이다. property는 key와 value의 쌍으로 이루어진다. property는 property attribute를 가진다.
var person = {
name : '홍길동', // 이 하나를 property라고 부른다. (key:value)
age : 20
}
Json
: JavaScript 객체 형태인 데이터를 전달하기 위한 표현법
JavaScript 객체와는 다르다.
: 만드는 방식에 따라 객체 구조가 달라진다.
: 가장 간단한 방법이지만 똑같은 형태의 객체를 여러개 만들 때 비효율적이다.
key를 쓸 때 Namig Rule에 부합하면 따옴표 생략 가능하다.
var obj = {
name: '홍길동',
printName: function myPrint() {
console.log(`내 이름은 ${this.name}`);
},
"!myPhone": "010-1234-5678",
10: 300
};
var age = 20;
var obj = {
userName: '홍길동',
getName: function() { // property인데 통상적으로 method라고 부른다.
console.log(this.userName);
},
setName() { // 얘가 JavaScript(ES6)에서 말하는 method이다.
...
},
age // age: age를 축약한 표현이다.
}
let x = 1;
let y = 2;
const obj = {x, y} // 확장 표현 방식
console.log(obj);
실행 결과 : { x: 1, y: 2 }
let myObj = {
name: '홍길동',
printName() {
console.log(this.name);
}
}
myObj.printName()
실행 결과 : 홍길동
: "new" keyword를 이용해서 생성자 함수를 호출한다.
-> 그 결과 instance가 생성된다.
const person1 = {};
// 객체 literal을 이용한 객체 생성
// -> instance라고 부르지 않는다.
const person2 = new Object();
// 생성자 함수를 이용한 객체 생성
// -> instance라고 부른다.
일반적으로 첫 글자를 대문자로 쓰면 생성자 함수이고, 아니면 일반 함수로 쓰인다.
JavaScript의 대표적인 built-in 생성자 함수
: JavaScript engine이 기동하면 생성된다. 전역 객체(window)에 property로 등록된다.
- Object()
- Array()
- String()
- Number()
- Boolean()
- Function()
- Math
- Json
-> 여기에서 Math와 Json은 생성자 함수가 아니다. 이 둘을 가지고는 객체를 만들지 못한다. 나름대로의 정적 메소드를 사용하여 만든다.
var str = new String('홍길동');
: 생성자 함수를 "new" keyword로 호출한다. 그러면 instance가 만들어지고, 일반 객체와는 다르다. 생성자 함수의 이름 식별자는 PascalCase를 사용한다.
return을 쓰지 않는다. 묵시적으로 생성된 instance reference인 this가 return 된다. primitive value를 리턴하면 무시한다.
function Person() {
}
const person1 = Person();
console.log(person1);
const person2 = new Person();
console.log(person2);
var person3 = {};
console.log(person3);
실행 결과 : undefined
Person {}
{}
function Person(name) {
this.name = name;
this.getName = function() {
return `내 이름은 ${this.name}`;
}
}
const person1 = new Person('아이유');
const person2 = new Person('김연아');
console.log(person1.getName());
console.log(person2.getName());
실행 결과 : 내 이름은 아이유
내 이름은 김연아
// 함수 선언문
function add(x,y) {
return x + y;
}
var inst = new add();
console.log(inst); // add {}
function createUser(name, role) {
return {name, role};
}
inst = new createUser();
console.log(inst); // { name: undefined, role: undefined }
this
: this 키워드는 일반 함수인 경우에도 있고, 생성자 함수인 경우에도 존재한다.
일반 함수에서의 this : window
생성자 함수에서의 this : 앞으로 만들 instance를 가리키는 reference
메소드에서의 this : 현재 사용하는 객체<참고>
JavaScript engine이 코드를 실행하기 위해 기동한 이후 일어나는 일
1. buit-in 객체(생성자 함수 포함) 생성한다.
2. 실행 환경에 맞는 global 객체를 생성한다.
- browser 환경 -> window
- Node.js 환경 -> global// 생성자 함수 function Circle(radius) { this.radius = radius; this.getDiameter = function() { return 2 * this.radius; } } const circle = Circle(5); // 일반함수로 사용된 것이다. console.log(radius); // 5 // 일반함수이니까 window가 global 객체이고, window의 property로 radius가 생성된 것이다.
일반 객체 : 호출(call, invoke) 불가능
함수 객체 : 호출 가능
이 기능을 위해 함수 객체는 내부 slot과 내부 method를 가지고 있다.
1. [[Call]] : 함수를 외부에서 호출하면 JavaScript engine에 의해 실제적으로 호출된다. 이 내부 메소드를 가지고 있는 객체를 Callable 객체라고 한다.
2. [[Constructor]] : 객체가 생성이 될 때 이 내부 메소드를 이용한다.함수 객체를 생성하기 위해 new가 붙으면 constructor를 호출하고, new가 붙지 않으면 call을 호출하게 되는 것이다.
함수 생성
- constructor
: 함수선언문, 함수표현식, class -> [[Constructor]] 존재
이 3개로만 instance를 만들 수 있다. 모든 함수가 생성자인 것은 아니다.- non-constructor
: arrow function, ES6 함수 축약형(method) -> [[Constructor]] 존재 X// 함수 선언문 function foo() {} // 함수 표현식 var bar = function() {} // 객체의 property로 함수가 할당 const barx = { x: function() {} } new foo(); // foo 함수 객체가 내부 메소드 [[Constructor]] 가지고 있다. new bar(); // 내부 메소드 [[Constructor]] 가지고 있다. new barx.x(); // 내부 메소드 [[Constructor]] 가지고 있다. const arrow = () => {} new arrow(); // arrow is not a constructor const obj = { x() {} } new obj.x(); // TypeError: obj.x is not a constructor
: 객체를 생성할 때 객체의 상위 prototype 객체를 직접 지정할 수 있다.
const obj = Object.create(null); // 상위 prototype 객체가 없다. -> obj.__proto__ 을 사용할 수 없다.
console.log(obj.__proto__);
// 이런 경우 해결 방법
// Object가 가지고 있는 method를 이용한다.
// 객체의 상위 prototype 객체를 들고 온다.
console.log(Object.getPrototypeOf(obj));
실행 결과 : undefined
null
class Person {
constructor(name) {
this.name = name;
}
}
const me = new Person('홍길동');
var obj = {
myName: '홍길동'
}
obj.myAge = 20;
obj.!myPhone = "010-1234-5678"; // 식별자 Naming Rule에 맞지 않아 쓸 수 없다.
obj['!myPhone'] = "010-1234-5678"; // 브라켓에는 항상 문자열 형태로 써야 한다.
console.log(obj);
실행 결과 : { myName: '홍길동', myAge: 20, '!myPhone': '010-1234-5678' }
var obj = {
10: 100,
let: '권장X', // 키워드는 사용할 수는 있지만 권장되지는 않는다.
myName: '홍길동',
'!myName': '김길동',
myName: '김연아' // key 값을 중복해서 사용해도 된다. 덮어쓴다.
};
console.log(obj);
실행 결과 : { '10': 100, let: '권장X', myName: '김연아', '!myName': '김길동' }
delete obj.myAge;
: JavaScript 객체는 자유롭게 property의 추가, 삭제, 값의 read, 값의 write가 가능하다.
'use strict'; // 이 코드를 추가하면 person.adreess = '서울'; 과 같은 코드에서 에러가 발생한다.
const person = {
name: 'Lee',
};
// 객체가 확장이 가능한지
console.log(Object.isExtensible(person)); // true
person.age = 20;
console.log(person); // { name: 'Lee', age: 20 }
Object.preventExtensions(person);
person.address = '서울';
console.log(person); // { name: 'Lee', age: 20 }
: immutable vs mutable
var obj = { name: '홍길동' }
Primitive value는 식별자가 데이터가 저장된 곳을 가리키고 있다.
let myStr = '홍길동';
// primitive type을 마치 객체(배열)처럼 사용
console.log(myStr[0]); // 홍
console.log(myStr.length); // 3
원래는 primitive value였는데, myStr[0]을 실행하는 순간 객체가 만들어지고, 내부 슬롯에 값이 복사된다. 그리고 식별자가 해당 객체를 가르킨다. 객체가 되었기 때문에 나름대로의 property를 붙인다. ex) length, 0: 'H', 1: 'e', 2: 'l' ...
객체 사용이 끝나면 내부슬롯으로 가지고 있던 값을 다시 primitive value로 보내준다.
let myStr = 'Hello';
myStr[0] = 'h'; // 원래 값은 바뀌지 않는다.
console.log(myStr);
실행 결과 : Hello
객체의 property가 바뀌게 되는 것이다. 실제 값은 바뀌지 않는다.
let person = { name: 'Lee' }
let copy = person;
레퍼런스(식별자가 가르키고 있는 데이터가 있는 주소)가 복사되기 때문에 같은 객체를 가리키게 된다.
: 개발자가 의도적으로 생성할 수 없다. JavaScript engine에 의해서 생성된다.
사용하는 platform에 따라 다르다. window와 global을 합쳐서 globalThis(ES11)라고 한다.
platform
: 무언가를 실행시켜 줄 수 있는 환경
ex) os, window, 브라우저, ...
: 이런 현상은 좋지 않다. 코드의 신뢰도를 떨어뜨린다.
언어적인 차원에서 사용하지 못하게끔 처리할 수 있다. -> Strict mode
function foo() {
x = 10; // 전역변수화(window 객체의 property로 붙는다.)
}
foo();
console.log(x);
실행 결과 : 10
Strict Mode
: 'use strict';
1. 일반적으로 전역에 잡지 않는다. 다른 일반 library들이 동작을 안 할 수 있다.
2. 일반적으로 즉시 실행 함수(IIFE)를 만들어서 사용하는게 일반적이다.(function() { // non-strict mode var let = 10; // inner function, nested function function foo() { 'use strict'; let = 20; } }()); 실행 결과 : SyntaxError: Unexpected strict mode reserved word 오류 발생
strict mode 사용시 주의점
- implicit global(묵지석 전역)을 사용할 수 없다.
- 변수, 함수, 매개변수가 delete로 삭제가 안된다.
'use strict'; var obj = {} delete obj; 실행 결과 : SyntaxError: Delete of an unqualified identifier in strict mode. 오류 발생
- this의 의미가 달라진다.
: 일반 함수에서 this -> window 객체 (strict mode 사용시 undefined)