객체란?
객체(object)란 실생활에서 우리가 인식할 수 있는 사물로 이해할 수 있다.
자바스크립트에서는 이름과 값으로 구성된 속성을 가지는 기본 데이터 타입을 말한다.
자바스크립트에서의 객체는?
typeof([])
> "object"
typeof({})
> "object"
// 함수는 객체의 특성을 가지고 있지만 typeof로는 function을 출력함.
typeof(function(){})
'function'
자바스크립트에서는 원시 값을 제외한 나머지 값(함수, 배열 정규표현식)은 모두 객체이다.
자바스크립트를 구성하는 거의 모든 것이 객체라고 생각하면 된다.
자바스크립트는 객체 기반의 프로그래밍 언어라고 할 수 있다.
객체 구조 살펴보기
var counter = {
num: 0, // 프로퍼티
// 메서드
increase: function () {
this.num++;
}
};
프로퍼티 : 객체의 상태를 나타내는 값
메서드 : 프로퍼티(상태 데이터)를 참조하고 조작할 수 있는 동작
키: 값 으로 이루어져 있음.
객체는 왜 사용할까?
객체는 객체의 상태를 나타내는 값(프로퍼티)과 프로퍼티를 참조하고 조작할 수 있는 동작(메서드)을 모두 포함할 수 있기 때문에 상태와 동작을 하나의 단위로 구조화할 수 있어 유용하다.
프로퍼티와 메서드가 따로 존재한다고 생각하면 얼마나 불편할지 상상도 못 하겠다.
객체를 생성해보자
객체를 생성하는 방법들
1. 객체 리터럴
const person = {
name: 'Jang',
sayHello: function () {
console.log(`Hello My name is ${this.name}.`);
}
};
객체 리터럴의 중괄호는 코드 블록을 의미하지 않아서 세미콜론을 붙이지 않고, 객체 리터럴은 값으로 평가되는 표현식이라서 닫는 중괄호 뒤에는 세미콜론을 붙인다.
객체 리터럴 방식 외의 방법은 모두 함수를 사용해 객체를 생성한다.
2. Object 생성자 함수
// 빈 객체의 생성
const person = new Object();
//= {};
// 프로퍼티 추가
person.name = 'Lee';
person.sayHello = function () {
console.log('Hi: My name is' + this.name);
);
console.log(person); // {name: "Lee", sayHello: f}
person.sayHello(); // Hi! My name is Lee
new 연산자와 함께 Object 생성자 함수를 호출하면 빈 객체를 생성하여 반환한다. 빈객체를 생성하고 프로퍼티 또는 메서드를 추가하여 객체를 완성한다.
생성자 함수란 new 연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수를 말한다. 생성자 함수에 의해 생성된 객체를 인스턴스라 한다.
객체 리터럴 방식이 훨씬 간편하기 때문에 특별한 이유가 없다면 그다지 유용하지 않다.
3. 생성자 함수
객체 리터럴 방식은 간편하기는 하지만 동일한 프로퍼티를 갖는 객체를 여러 개 생성해야 하는 경우에는 비효율적이다.
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()); // 10
console.log(circle2.getDiameter()); // 20
생성자 함수를 이용하면 프로퍼티 구조가 동일한 객체 여러 개를 간편하게 생성할 수 있다.
4. Object.create 메서드
// obj -> null
let obj = Object.create(null);
// obj -> Object.prototype -> null
obj = Obejct.create(Object.prototype);
// obj -> Object.prototype -> null
// obj = { x: 1 };와 동일
obj = Object.create(Object.prototype, {
x: { value: 1, wiriteable: true, enumerable: true, configurable: true }
});
const myProto = { x: 10 };
// obj -> myProto -> Object.prototype -> null
obj = Object.create(myProto);
function Person(name) {
this.name = name;
}
// obj -> Person.prototype -> Object.prototype -> null
obj = Object.create(Person.prototype);
프로토타입 체인의 종점에 위치
Object.create 메서드의 장점
- new 연산자가 없이도 객체를 생성할 수 있다.
- 프로토타입을 지정하면서 객체를 생성할 수 있다.
- 객체 리터럴에 의해 생성된 객체도 상속받을 수 있다.
5. 클래스(ES6)
// ES5
var Person = (function () {
function Person(name) {
this.name = name;
}
Person.prototype.sayHi = function() {
console.log('Hi! My name is ' + this.name);
};
return person;
}());
// ES6 class
class Person {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(`Hi! My name is ${this.name}`);
}
static sayHello() {
console.log('Hello!');
}
}
프로토타입 기반 프로그래밍 언어는 클래스가 필요가 없지만 ES6에 클래스를 도입하게 되었음
정적 메서드 : 인스턴스를 생성하지 않아도 호출 가능
객체 다루기
프로퍼티 접근
var person = {
name: 'Lee'
};
//마침표로 프로퍼티 접근 (dot notation)
console.log(person.name);
// 대괄호로 프로퍼티 접근 (bracket notation)
console.log(person['name']);
마침표로 접근하거나 대괄호로 접근하는 방법 두 가지 존재
대괄호는 내부는 반드시 문자열로 써주어야 함
프로퍼티 값 갱신
var person = {
name: 'Lee'
};
person.name = 'Jang';
프로퍼티 동적 생성
var person = {
name: 'Lee'
};
person.age = 26;
프로퍼티 삭제
var person = {
name: 'Lee'
};
person.age = 26;
delete person.age; // age 삭제
delete person.address; // 오류 안남
ES6 추가 기능
프로퍼티 축약 표현
// ES5
var x = 1, y = 2;
var obj = {
x: x,
y: y
};
// ES6
let x = 1, y =2;
const obj = { x, y};
프로퍼티의 키와 값이 같은 경우 축약할 수 있는 방법이 추가됨
메서드 축약 표현
// ES5
var obj = {
name: 'Lee',
sayHi: function() {
console.log('Hi! ' + this.name);
}
};
obj.sayHi();
// ES6
var obj = {
name: 'Lee',
sayHi() {
console.log('Hi! ' + this.name);
}
};
obj.sayHi();
키와 익명 함수를 꼭 써주어야 했지만 함수도 축약해서 프로퍼티를 추가해줄 수 있다.
함수도 객체라구요
다음 조건을 만족한다면 일급 객체라고 할 수 있으며, 함수는 다음의 조건을 모두 만족하므로 일급 객체다.
일급객체
1. 무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다.
2. 변수나 자료구조(객체, 배열 등)에 저장할 수 있다.
3. 함수의 매개변수에 전달할 수 있다.
4. 함수의 반환 값으로 사용할 수 있다.
// 1. 함수는 무명의 리터럴로 생성할 수 있다.
// 2. 함수는 변수에 저장할 수 있다.
// 런타임(할당 단계)에 함수 리터럴이 평가되어 함수 객체가 생성되고 변수에 할당된다.
const increase = function (num) {
return ++num;
};
const decrease = function (num) {
return --num;
};
// 2. 함수는 객체에 저장할 수 있다.
const predicates = { increase, decrease };
// 3. 함수의 매개변수에 전달할 수 있다.
// 4. 함수의 반환값으로 사용할 수 있다.
function makeCounter(predicate){
let num = 0;
return function () {
num = predicate(num);
return num;
};
}
// 3. 함수는 매개변수에게 함수를 전달할 수 있다.
const increaser = makeCounter(predicates.increase);
console.log(increeaser()); // 1
console.log(increeaser()); // 2
함수가 일급 객체라는 것은 함수를 객체와 동일하게 사용할 수 있다는 의미이다. 객체는 값이므로 함수는 값과 동일한 취급할 수 있다.
일급 객체로서 함수가 가지는 가장 큰 특징은 일반 객체와 같이 함수의 매개변수에 전달할 수 있으며, 함수의 반환값으로 사용할 수 도 있다는 것이다. 이는 함수형 프로그래밍을 가능케 하는 자바스크립트의 장점 중 하나다.
함수 객체와 일반 객체와는 차이
- 일반 객체는 호출할 수 없지만 함수 객체는 호출할 수 있다.
- 함수 객체는 일반 객체에는 없는 함수 고유의 프로퍼티를 소유한다.
function makeCounter(predicate){
let num = 0;
return function () {
num = predicate(num);
return num;
};
}
console.log(makeCounter.name);
// makeCounter
📝 익명 함수와 일반 함수
1) 일반 함수
전역적이며, 전체가 다 호이스팅 되므로 호출의 위치와 구현의 위치 간에 연관관계가 없다. 또한, 재사용될 기능에 주로 사용된다.
2) 익명 함수
선언부만 호이스팅되며 호출의 위치와 구현의 위치 간에 순서가 알맞아야 하고, 한 번만 사용하는 기능에 사용된다.
위 말만 보면 익명 함수가 오히려 번거로운 것처럼 보일 수 있지만, 이는 메모리 관리에 효과적인 방안이 될 수 있다.
일반 함수는 자바스크립트를 초기에 읽어올 때 모두 호이스팅된다고 하였다. 만약, 전체 자바스크립트 내에서 단 한 번만 쓰이는 함수가 일반 함수로 구현되어 있다고 가정해보자. 이 함수는 자신이 사용될 단 한번을 기다리며 불필요하게 메모리를 차지하고 있어야 한다. 메모리 사용량이 성능에 중요한 영향을 미칠 수 있는 웹 애플리케이션에서, 이는 메모리 낭비라고 볼 수 있다.
따라서 단 한번만 사용되는(재사용이 필요 없는) 함수의 경우, 불필요한 시간 동안 메모리를 차지하지 않도록 익명 함수로 구현한다면, 정확히 해당 함수가 필요한 위치에서만 해당 함수가 구현되고 사라지면서 메모리를 아낄 수 있게 된다.
https://dev-note-97.tistory.com/273
함수 객체의 프로퍼티
함수는 객체이기 때문에 프로퍼티를 가질 수 있다. 함수 객체 내부를 들여다보자.
function square(number) {
return number * number;
}
// 객체 데이터 출력
console.dir(square);
// 함수의 모든 프로퍼티의 어트리뷰트 확인
console.log(Object.getOwnPropertyDescriptors(square));
함수 객체의 프로퍼티를 하나씩 살펴보자.
1) arguments 프로퍼티
function multiply(x, y) {
console.log(arguments);
return x * y;
}
console.log(multiply()); // NaN
console.log(multiply(1)); // NaN
console.log(multiply(1, 2)); // 2
console.log(multiply(1, 2, 3)); // 2
살펴보기
- 매개변수보다 인자를 적게 전달한 경우
- 인자가 초과된 경우
arguments 객체는 매개변수 개수를 확정할 수 없는 가변 인자 함수를 구현할 때 유용하다.
function sum() {
let res = 0;
for(let i=0; i<arguments.length; i++) {
res += arguments[i];
}
return res;
}
sum(1,2,3,4,5);
2) caller 프로퍼티
함수 객체의 caller 프로퍼티는 함수 자신을 호출한 함수를 가리킨다.
ECMAScript 사양에 포함되지 않는 비표준 프로퍼티라서 몰라도 상관없다.
3) length 프로퍼티
함수를 정의할 때 선언한 매개변수의 개수를 가리킨다.
function foo() {}
console.log(foo.length); // 0
function bar(x) {
return x;
}
console.log(bar.length); // 1
arguments.length와 값이 다를 수 있으므로 주의해야 한다.
4) name 프로퍼티
함수 객체의 name 프로퍼티는 함수 이름을 나타낸다. ES6부터 정식 표준이 되었다.
var namedFunc = function foo() {};
console.log(namedFunc.name); // foo
var anonymousFunc = function () {};
// ES5 : 빈 문자열
// ES6 : 함수 객체를 가리키는 변수 이름
console.log(anonymousFunc.name); // anonymousFunc
5) __proto__ 접근자 프로퍼티
모든 객체는 [[Prototype]]이라는 내부 슬롯을 갖는다. 내부 슬롯은 객체지향 프로그래밍의 상속을 구현하는 프로토타입 객체를 가리킨다.
내부 슬롯에는 직접적으로 접근할 수 없으므로 간접적인 방법으로만 접근 가능한데, [[prototype]] 내부 슬롯에도 직접 접근할 수 없으며 proto 접근자 프로퍼티를 통해 간접적으로 프로토타입 객체에 접근 가능하다.
const obj = { a: 1};
console.log(obj.__proto__ === Object.prototype); // true
console.log(obj.hasOwnProperty('a')); // true
console.log(obj.hasOwnProperty('__proto__')); // false
6) prototype 프로퍼티
// 함수 객체는 prototype 프로퍼티를 소유한다.
(function () {}).hasOwnProperty('prototype');
// 일반 객체는 prototype 프로퍼티를 소유하지 않는다.
({}).hasOwnProperty('prototype');
prototype 프로퍼티는 생성자 함수로 호출할 수 있는 함수 객체, 즉 constructor만이 소유하는 프로퍼티다. 일반 객체와 생성자 함수로 호출할 수 없는 경우에는 prototype 프로퍼티가 없다.
prototype 프로퍼티는 함수가 객체를 생성하는 생성자 함수로 호출될 때 생성자 함수가 생성할 인스턴스의 프로토타입 객체를 가리킨다.
프로토타입이 뭘까?
상속과 프로토타입
객체지향 프로그래밍의 핵심 개념인 상속은 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 말한다.
자바스크립트는 프로토타입을 기반으로 상속을 구현해서 불필요한 중복을 제거한다.
function Circle(radius) {
this.radius = radius;
this.getArea = function () {
return Math.PI * this.radius ** 2;
};
}
const circle1 = new Circle(1);
const circle2 = new Circle(2);
console.log(circle1.getArea === circle2.getArea); //false
getArea를 각각의 객체가 모두 가지고 있는 비효율적인 형태이다. 프로토타입을 이용해서 중복을 제거해보자.
function Circle(radius) {
this.radius = radius;
}
Circle.prototype.getArea = function () {
return Math.PI * this.radius ** 2;
};
const circle1 = new Circle(1);
const circle2 = new Circle(2);
// 같은 메서드를 공유하고 있다.
console.log(circle1.getArea === circle2.getArea); //true
프로토타입 객체
모든 객체는 [[Prototype]]이라는 내부 슬롯을 가진다.
모든 객체는 하나의 프로토타입을 갖는다.
모든 프로토타입은 생성자 함수와 연결되어 있다.
객체와 프로토타입과 생성자 함수는 서로 연결되어 있다.
__proto__ 접근자 프로퍼티
[[Prototype]] 내부 슬롯에는 직접적으로 접근이 불가능하다.
하지만 모든 객체는 __proto__ 접근자 프로퍼티를 통해 자신의 프로토타입에 간접적으로 접근할 수 있다.
__proto__ 접근자 프로퍼티를 사용하는 이유
상호 참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위해서
const parent = {};
const child = {};
child.__proto__ = parent;
parent.__proto__ = child; //TypeError: Cyclic __proto__ value
// 만약에 된다면
child.parent // ????? 무한루프
프로토타입 체인은 단방향 링크드 리스트로 구현되어야 하며 프로퍼티 검색 방향이 한쪽 방향으로만 흘러가야 하기 때문.
순환 참조하는 프로토타입 체인이 만들어지면 프로토타입 체인 종점이 없어져서 프로퍼티 검색 시에 무한루프에 빠진다.
빌트인 객체에 대해 알아보자
표준 빌트인 객체
표준 빌트인 객체는 ECMAScript 사양에 정의된 객체를 말하며, 애플리케이션 전역의 공통 기능을 제공한다. 표준 빌트인 객체는 ECMAScript 사양에 정의된 객체이므로 자바스크립트 실행 환경과 관계없이 언제나 사용할 수 있다. 표준 빌트인 객체는 전역 객체의 프로퍼티로서 제공된다. 따라서 별도의 선언 없이 전역 변수처럼 언제나 참조할 수 있다.
호스트 객체
브라우저에서는 DOM, BOM, Canvas, XMLHttpRequest, fetch, requestAnimationFrame, SVG, Web Storage, Web Component, Web Worker와 같은 클라이언트 사이드 Web API를 호스트 객체로 제공하고, Node.js 환경에서는 Node.js 고유의 API를 호스트 객체로 제공한다.
호스트 객체는 ECMAScript 사양에 정의되어 있지 않지만 자바스크립트 실행환경에서 추가로 제공하는 객체를 말한다.
사용자 정의 객체
사용자 정의 객체는 표준 빌트인 객체와 호스트 객체처럼 기본 제공되는 개체가 아닌 사용자가 직접 정의한 객체를 말한다.
원시 값이 있는데 빌트인 객체가 존재하는 이유
const str = 'hello';
// 원시 타입인 문자열이 프로퍼티와 메서드를 갖고 있는 객체처럼 동작
console.log(str.length);
console.log(str.toUpperCase());
원시 값인 문자열, 숫자, 불리언 값의 경우 이들 원시 값에 대해 마치 객체처럼 마침표 표기법으로 접근하면 자바스크립트 엔진이 일시적으로 원시 값을 연관된 객체로 변환해 주기 때문이다.
원시값을 객체처럼 사용하면 연관된 객체를 생성한 다음 이후에 다시 원시 값으로 되돌린다.
원시 값에 대해 객체처럼 접근하면 생성되는 임시 객체를 래퍼 객체라 한다.
기본 자료형의 일시적 승급
기본 자료형인데 객체처럼 메서드나 프로퍼티를 호출할 수 있는 경우는 어떤 걸까?
'hello world'.length
이게 왜 됨?
자바스크립트는 사용의 편리성을 위해서 기본 자료형의 속성과 메서드를 호출할 때 일시적을 기본 자료형을 객체로 승급시킨다. 그래서 속성과 메소드를 사용할 수 있는 것이다.
> const h = 'hello';
undefined
//일시적으로 객체처럼 승급됐다가
> h.age = 26
26
// 다시 승급 해제
> h.age
undefined
프로토타입으로 메서드 추가하기
Number 객체에 power() 메서드를 추가해보자.
Number.prototype.power = function (n = 2) {
return this.valueOf() ** n;
}
const a = 5
console.log('a.power():', a.power()) //25
console.log('a.power(3):', a.power(3)) //125
이렇게 필요에 따라 빌트인 객체를 커스텀할 수도 있다
표준 빌트인 객체 찍먹하기
Number
프로퍼티
Number.EPSILON : 표현할 수 있는 두 숫자 중 가장 작은 차이
Number.MAX_VALUE : 가장 큰 양수 값
Number.MIN_VALUE : 가장 작은 양수 값
Number.MAX_SAFE_INTEGER : 안전하게 표현 가능한 가장 큰 정수 값
Number.MIN_SAFE_INTEGER : 안전하게 표현 가능한 가장 작은 정수 값
Number.POSITIVE_INFINITY : 양의 무한대 (Infinity)
Number.NEGATIVE_INFINITY : 음의 무한대 (-Infinity)
Number.NAN : Not a Number
메서드
Number.isFinite()
Number.isFinite : 유한 값 검사
Number.isInteger : 정수 값 검사
Number.isNaN : NaN 검사
Number.isSafeInteger : 안전한 정수인지 검사
Number.prototype.toExponential : 지수 표기법 반환
Number.prototype.toFixed : 숫자 반올림
Number.prototype.toPrecision :자릿수 유효 나머지는 반올림
Number.prototype.toString : 숫자를 문자열로 변환
Math
프로퍼티
Math.PI : 원주율
메서드
Math.abs : 절댓값 반환
Math.round : 소수점 이하 반올림
Math.ceil : 소수점 이하 올림
Math.floor : 소수점 이하 내림
Math.sqrt : 제곱근 반환
Math.random : 0~1 사이(1 제외) 랜덤 값
Math.pow : 지수 계산 (**연산자 ES7)
Math.max : 가장 큰 수 리턴
Math.min : 가장 작은 수 리턴
String
문자열은 변경 불가능한 원시 값이기 때문에 String 래퍼 객체는 읽기 전용 객체로 제공
프로퍼티
length : 문자열의 길이
메서드
원본을 수정하는 메서드(mutator method)와 새로운 배열을 리턴하는 메서드(accessor method)를 구분해야 함
String.prototype.indexOf : 찾은 문자열의 인덱스 출력, 실패 시 -1
[String.prototype.search](<http://String.prototype.search>) : 정규표현식과 일치하는 문자열 검색, 실패시 -1
String.prototype.includes : 문자열 포함 확인, 성공 true, 실패 false
String.prototype.startsWith : 문자열로 시작하면 true, 아니면 false
String.prototype.endsWith : 문자열로 끝나면 true, 아니면 false
String.prototype.charAt : 문자열 index로 탐색, 벗어난 범위는 빈 문자열 반환 string []
String.prototype.substring : 첫 번째 인덱스부터 두 번째 인덱스 전까지의 문자열 반환
String.prototype.slice : substring과 동일하지만 인덱스 음수를 전달할 수 있다. (-1) 문자열 끝
String.prototype.splice : substring과 동일하지만 인덱스 음수를 전달할 수 있다. (-1) 문자열 끝 원본 변함
String.prototype.toUpperCase : 문자열을 모두 대문자로 변경
String.prototype.toLowerCase : 문자열을 모두 소문자로 변경
String.prototype.trim : 문자열 앞뒤 공백 제거
String.prototype.repeat : 문자열을 전달받은 정수만큼 반복해서 리턴 ‘*’. repeat(10);
String.prototype.replace : 문자열 또는 정규표현식을 두 번째 인수로 전달한 문자열로 변경
String.prototype.split : 첫 번째 인자로 문자열 또는 정규표현식을 기준으로 분리된 배열 리턴
추가로 유용한 객체
Set : 중복 요소의 존재를 허용하지 않음
Map : 객체와 같은 형태이지만 편리한 메서드를 지원
레퍼런스
https://jongminfire.dev/객체지향-프로그래밍이란
'javascript' 카테고리의 다른 글
[Javascript] 모듈 기능으로 살펴보는 자바스크립트 (0) | 2022.12.19 |
---|---|
[ESLint / Prettier] 레퍼런스 모아보기 (0) | 2022.12.10 |
[Javascript] 이번에야말로 regex를 써보자 (0) | 2022.12.03 |
[Javascript] Array filter 맛깔나게 써보기 (0) | 2022.11.13 |