객체 복습
{ key: value, key: value, … }
형태key: value
를 property라고 한다.📍 JavaScript에서 객체를 생성하는 방법
1. 객체 literal ⇒ (지난 시간에 살펴보았다!)
2. Object 생성자 함수
3. 사용자 정의 생성자 함수
4. Object.create() method
5. ‘ES6’ → class
💡 “함수”와 “생성자 함수”는 다른 것!
객체를 사용하는 방법
.
dot notation(점 표기법)[ ]
bracket notation(괄호 표기법)💡 만약 property key가 JavaScript의 identifier naimg rule을 따르면 객체 literal을 이용할 때
‘ ‘
(quotation)를 생략해도 된다.var obj = { name: '홍길동', // key가 식별자 명명 규칙에 맞다면 quotation으로 감싸지 않아도 된다. 'name'(o), name(o) "!myPhone": '010-1234-5678', // key가 식별자 명명 규칙에 맞지않다면 quotation으로 감싸주어야한다. 10: 300 // key를 숫자(10)으로 써도 자동 casting해서 string('10')으로 인식한다. };
간단한 객체 literal
name
과 같은 property key는 이미 내부적으로 사용하고 있을 가능성이 높기 때문에 사용하지 않는 것이 좋다.📍 property 축약형
아래 예시를 통해 method와 property 구분하기
var age = 20;
var obj = {
userName: '홍길동',
getName: function(){ // 통칭 method라고 부르지만 진짜 method는 아니다. "property"일뿐.
console.log("this is a property not a method");
},
setName(){ // (ES6)JavaScript에서 말하는 "method"는 이렇게 축약표현으로 나타낸 것을 말한다.
console.log("this is a method")
},
age // age: age 라고 쓰는 것과 같다. 축약표현이 가능하다.
// key는 "age"가 되고, value는 변수 age의 값이 된다.
}
console.log(obj);
/* 출력 결과
{
userName: '홍길동',
getName: [Function: getName],
setName: [Function: setName],
age: 20
}
*/
Primitive value vs Reference(Object)
💡 객체를 mutable로 지정해놓은 이유
메모리 효율성을 위해서!!
만약 객체가 immutable이라면 메모리에 garbage collection이 계속해서 발생하기 때문이다.
함수는 객체다.
parameter
: 매개변수(formal parmeter), 함수 내에서 선언한 argument를 받아들이기 위한 역할을 하는 지역변수argument
: 실인자(actual parameter), 인수라고 한다. 함수를 호출할 때 실제로 사용하는 값💡 간단하게 함수 선언 시
parameter(매개변수)
, 함수 호출 시argument(인자)
라고 기억해도 좋다.
function literal
💡
literal
이란 자바스크립트 엔진에 의해서 하나의 값으로 떨어지는 것!!
/* 함수 literal */
function add(x,y) {
return x + y;
};
함수를 정의(definition)하는 방법
함수 표현식과 함수 선언문
/* 함수 표현식 */
var myAdd = function add(x,y) {
return x + y;
};
/* 함수 선언문 */ // 함수 literal과 똑같이 생겼다.
function add(x,y) {
return x + y;
};
Function( ) 생성자 함수
var myFunc = new Function('x', 'y', 'return x+y');
var myFunc
: 함수 객체'x', 'y'
: parameter'return x+y'
: 함수 실행 코드‘ES6’ → 화살표함수(Arrow function)
화살표 함수에서는 this
를 사용할 수 없다.(화살표 함수는 non-constructor이기 때문이다. 관련 내용은 생성자 함수, 상속 부분에서 다룬다.)
var add = (x, y) => x + y;
함수 호출(call)
→ 함수 내부에 arguments라는 유사 배열 객체가 있다.
IIFE(즉시 실행 함수)
→ 함수 선언과 동시에 호출(전역변수의 사용을 지양)
중첩함수
JavaScript는 함수 안쪽에 또 다른 함수를 선언하는 중첩(nested) 함수가 가능하다.
Callback 함수
Property attribute
[[ ]]
대괄호 2개가 중첩된 형태로 표현된다.💡 Property의 상세(Property attribute)
1.[[Value]]
property의 값
2.[[Writable]]
property의 값을 수정할 수 있는지 여부(key에 대한 value를 고정시킬지 말지)
3.[[Enumerable]]
해당 property가 열거될 수 있는지 여부
4.[[Configurable]]
property attribute를 재정의 할 수 있는지 여부
객체의 property 변경
💡 JavaScript의 객체는 자유롭게 아래의 4가지가 가능하다.
- Property의 추가
- Property의 삭제
- Property 값의 read
- Property 값의 write
Object.preventExtensions()
Object.seal()
Object.freeze()
💡
use strict;
ES6에서 추가된 strict mode를 사용하기 위한 코드로 strict mode를 사용하면 JavaScript의 확장성은 떨어지지만 정형성은 올라간다.
/* 객체의 property 변경 */
'use strict'; // strict mode
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 = 'Seoul'; // 'use strict'; 코드를 상단에 추가하여 strict 모드를 사용해서 error를 발생시킬 수 있다.
console.log(person); //{ name: 'Lee', age: 20 }
// address가 새로운 property로 추가되지 않는다.
// 밀봉
Object.seal(person);
delete person.name;
console.log(person); //{ name: 'Lee', age: 20 }
// name property가 삭제되지 않는다.
// 동결
Object.freeze(person);
person.name = '아이유';
console.log(person); //{ name: 'Lee', age: 20 }
// name property의 값이 변경되지 않는다.
💡 첫글자를 대문자로 쓰면 생성자 함수를 쓴다는 의미로 관용적으로 사용된다.
(소문자로 쓴다고 생성자 함수로 쓸 수 없는 것은 아니다.)
생성자 함수의 이름 식별자는 PascalCase(첫글자 대문자로 시작)
대표적인 생성자 함수(built-in)
Object()
String()
Number()
Boolean()
Function()
Array()
💡 JavaScript Engine이 코드를 실행하기 위해 기동하면
1. built-in 생성자 함수를 포함한 built-in 객체들을 생성한다.
2. 실행 환경에 맞는 global 객체를 생성한다.
- browser 환경 → window
- Node.js 환경 → global
(환경에 따라서 만들어지는 전역 객체가 다르다.)function Person(){ /* this */ // this라는 keyword는 생성자 함수일 경우도 있고 // 일반 함수인 경우에도 있다. // 생성자 함수에서의 this => 생성자 함수에 의해서 만들어질 instance를 가리키는 reference // 일반 함수에서의 this => window console.log(this); } Person();
browser 환경에서는 window라는 이름의 객체로 생성됨
Node.js 환경에서는 global이라는 이름의 객체로 생성됨
생성자 함수에 의한 객체 생성
Object 생성자 함수를 이용한 객체 생성
new
keyword를 이용해서 생성자 함수 호출 ⇒ instance(우리가 알고있는 일반적인 객체와 다름)/* 생성자 함수를 이용해서 객체 만들기 */
const person1 = {};
console.dir(person1);
// 객체 literal을 이용한 객체 생성
const person2 = new Object();
console.dir(person2);
// 생성자 함수를 이용한 객체 생성
// => instance
(User Define) 생성자 함수
undefined
가 리턴된다.// 생성자 함수의 이름 식별자는 PascalCase(첫글자 대문자로 시작)
function Person(){
}
// 일반 함수로 호출
const person1 = Person();
console.log(person1); //undefined (함수에서 return문이 없을 때)
// 생성자 함수로 객체(instance) 만들기 -> new 키워드 사용
const person2 = new Person();
console.log(person2); //Person {} (빈 객체)
// 생성자 함수는 기본적으로 return문을 쓰지 않는다.
// 객체 literal로 객체 만들기
var person3 = {};
console.log(person3); //{}
💡 브라우저로 확인해보면 생성자 함수로 객체(instance)를 생성했을 때와 객체 literal로 객체를 생성했을 때 내부적인 구조가 다름을 알 수 있다.
💡 생성자 함수는 return 구문을 쓰지 않는다!!
→ 묵시적으로 생성된 instance reference인 this가 리턴된다./* return값으로 객체를 넘겼을 때 */ function Person(name){ this.name = name; this.getName = function(){ return `내 이름은 ${this.name}`; } return {}; // 생성자 함수로 사용할 경우는 return 구문이 있으면 안된다. // 특히 객체를 return하게 되면 return값을 객체로 넘기게 되어 우리가 원하는 객체를 생성할 수 없다. -> Error } const person1 = new Person('아이유'); const person2 = new Person('김연아'); console.log(person1.getName()); //TypeError: person1.getName is not a function console.log(person2.getName()); /* return값으로 primitive value를 넘겼을 때 */ function Person(name){ this.name = name; this.getName = function(){ return `내 이름은 ${this.name}`; } return 100; // return값으로 primitive value가 들어오면 이 값을 무시하기 때문에 error가 발생하지 않는다. } const person1 = new Person('아이유'); const person2 = new Person('김연아'); console.log(person1.getName()); console.log(person2.getName());
관용적으로 생성자 함수는 return 구문을 쓰지않기로 약속🤗
this의 의미
1. 일반 함수 → window
2. 생성자 함수 → 생성될 instance
3. method에서 사용 → 현재 사용하는 객체
“함수는 그 자체로 객체”
/* this의 의미 */
function foo(){}
// foo() 함수에 property 추가하기
foo.myName = '홍길동';
foo.getName = function(){
console.log(this);
}
foo(); // 함수 호출
new foo(); // 생성자 함수 호출
foo.getName(); // method 호출 -> 여기서 this는 함수 객체 foo이다.
객체의 호출
[[Call]]
, [[Constructor]]
→ 함수 객체에만 있는 내부 method[[Call]]
이라는 내부 method가 호출된다.[[Call]]
을 가지고 있어야만 호출 할 수 있다.[[Call]]
을 가지고 있는 객체를 callable 객체라고 한다. → 함수는 callable 객체[[Constructor]]
는 모든 함수 객체가 가지고 있지는 않다.[[Constructor]]
는 객체가 생성될 때 이용된다.[[Constructor]]
를 가지고 있음[[Constructor]]
를 가지고 있지 않음. /* 객체의 호출 */
// 함수 선언문
function foo() {}
// 함수 표현식
var bar = function(){};
// 객체 literal로 만든 일반 객체
const barx = {
x: function(){} // 객체의 property로 함수가 할당
}
new foo(); // foo 함수 객체가 내부적으로 [[Constructor]] method를 가지고 있기 때문에 new 키워드로 instance를 만들어낼 수 있다.
new bar(); // [[Constructor]]를 가지고 있다.
new barx.x(); // [[Constructor]]를 가지고 있다.
// arrow function
const arrow = () => {}; // [[Constructor]]를 가지고 있지않다.
new arrow(); //TypeError: arrow is not a constructor
// ES6 함수 축약법 method
const obj = {
x(){} // [[Constructor]]를 가지고 있지않다.
}
new obj.x(); //TypeError: obj.x is not a constructor
// 함수 선언문
function add(x, y) {
return x + y;
}
var inst = new add();
console.log(inst); //add {}
function createUser(name, role){
return {name, role};
}
var inst = new createUser();
console.log(inst); //{ name: undefined, role: undefined }
// 생성자 함수
function Circle(radius){
this.radius = radius; // 여기서 가리키는 this는 window
// window 객체에 radius라는 property를 붙이는거임.
this.getDiameter = function(){
return 2 * this.radius;
}
}
const circle = Circle(5); // 일반함수
console.log(radius); //5 window 객체에 붙은 radius값
console.log(circle); //undefined 함수에 return 구문이 없으니까
함수 객체의 property
일반 객체에 비해 더 가지고 있는 property
/* 일반 객체와 함수 객체의 차이 */
// 브라우저에서 확인하자
var obj = {
name: '홍길동'
}
// 일반 객체
console.dir(obj); //Object
function square(number){
return number * number;
}
// 함수 객체
console.dir(square); //f square(number)
브라우저에는 등록되어있지만 ECMA 비표준이다. (표준화될 확률도 낮음)
함수 자신을 호출한 함수에 대한 reference
/* caller */
function foo(f){
return f();
}
function bar(){
return 'caller: ' + bar.caller; // 여기서 쓴 bar는 함수 이름 bar를 지칭한다.
}
console.log(bar()); //caller: null 브라우저에서 확인
console.log(foo(bar)); // foo의 caller가 호출된다.
// 출력 결과
// caller: function foo(f){
// return f();
// }
[[Prototype]]
로 참조하는 객체를 가리킨다. 다시 정리
JavaScript Engine이 시작하면
모든 객체가 window의 property이므로
window.객체
라고 써야하지만 모~~든 객체에 window.이 붙으니까 생략하는 것.
let, const 키워드로 선언한 변수들은 전역 변수 scope를 가지지만 window 객체의 property로 붙지 않는다. 오직 var 키워드로 선언한 변수들만 window 객체의 property로 붙는다.
⭐ 모든 객체는
[[Prototype]]
내부 slot을 가지고 있다.
(상위) prototype (객체) → 객체 생성 방법에 따라 종류가 달라진다.
[[Prototype]]
을 사용해서 직접 접근이 불가능하므로 __Proto__
접근자를 이용해서 접근할 수 있다.
// 생성자 함수로 사용할 목적으로 만들었어요!
// 함수 선언문
function Person(name){
// 생성자 함수로 만들어질 instance가
// 가지는 property를 설정
this.name = name;
}
const person1 = new Person('홍길동');
const pereon2 = new Person('김길동');
💡 왜 이런 구조를 가질까???
Inheritance(상속)이라는 개념을 구현하기 위해서!! → 코드의 재활용을 높이기 위해
"JavaScript는 Prototype기반의 상속을 구현한다."
// 생성자 함수
function Circle(radius) {
this.radius = radius;
this.getDiameter = function(){
return 2 * this.radius;
}
}
const circle1 = new Circle(5);
const circle2 = new Circle(10);
console.log(circle1.getDiameter === circle2.getDiameter); //false
// instance마다 method를 각자 따로따로 가지므로 메모리가 낭비된다.
// instance가 생길 때마다 method도 새로 생기니까 메모리 낭비가 발생하는 것.
// 그러면 공통으로 사용하는 method를 (상위)Prototype에 넣어두자! -> "상속"
function Circle(radius) {
this.radius = radius;
// this.getDiameter = function(){
// return 2 * this.radius;
// }
Circle.prototype.getDiameter = function(){
return 2 * this.radius;
}
}
const circle1 = new Circle(5);
const circle2 = new Circle(10);
console.log(circle1.getDiameter === circle2.getDiameter); //true
자바스크립트는 상속에 대한 내용을 동적으로 바꿀 수 있다.
function Circle(radius) {
this.radius = radius;
Circle.prototype.getDiameter = function(){
return 2 * this.radius;
}
// 공용변수를 만들어 쓸 수 있다.
Circle.prototype.name = '홍길동'
}
const circle1 = new Circle(5);
const circle2 = new Circle(10);
console.log(circle1.name, circle2.name); //홍길동 홍길동
// name을 바꿔주려면??
circle1.name = '아이유'; // circle1에 name property를 새로 추가한다.
console.log(circle1.name, circle2.name); //아이유 홍길동
// 이렇게 바꿔주면 안된다.
// 이렇게 바꿔주거나
Circle.prototype.name = '아이유';
console.log(circle1.name, circle2.name); //아이유 아이유
// 이렇게 바꿔줘야 한다.
circle1.__proto__.name = '아이유';
console.log(circle1.name, circle2.name); //아이유 아이유
Rest parameter(ES6)
…
점 3개로 만든다.function foo(...args){
console.log(arguments); //[Arguments] { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
console.log(args); //[ 1, 2, 3, 4, 5 ]
return args.pop(); // 배열은 pop과 같은 메서드 사용이 가능
}
foo(1,2,3,4,5);
var result = foo(1,2,3,4,5);
console.log(result); //5
function foo(...args1, ...args2){ // 불가능
console.log(arguments);
console.log(args);
}
→ arguments와 rest parameter 둘다 가변 인자 함수를 만들 때 사용한다.
💡 함수내에서 arguments와 rest parameter 둘 다 이용할 수 있다.
단, arrow function에서는 arguments를 사용할 수 없고, rest parameter만 사용가능하다.
언니 잘 보고 갑니당 φ(゜▽゜*)♪