Array.prototype엔 요소 사이에 쉼표를 넣어 요소 전체를 합친 문자열을 반환하는 자체 메서드 toString이 있다.
let arr = [1, 2, 3]
console.log(arr); // 1,2,3 <-- Array.prototype.toString의 결과
그런데 Object.prototype에도 메서드 toString이 있다. 이렇게 중복 메서드가 있을 때는 체인 상에서 가까운 곳에 있는 메서드가 사용된다. Array.prototype 이 체인 상에서 더 가깝기 때문에 예시에서는 Array.prototype의 toString이 사용된다.
Chrome 개발자 콘솔과 같은 도구를 사용해 console.dir를 사용하면 내장 객체의 상속 관계를 확인할 수 있다.
배열이 아닌 다른 내장 객체들 또한 같은 방법으로 동작한다.
함수도 마찬가지며 call, apply를 비롯한 함수에서 사용할 수 있는 메서드는 Function.prototype에서 받아온다.
class MyClass {
// 여러 메서드를 정의할 수 있음
constructor() { ... }
method1() { ... }
method2() { ... }
method3() { ... }
...
}
위처럼 클래스를 만들고 new Myclass()를 호출하면 객체가 생성된다.
constructor()는 생성자로 new에 의해 자동으로 호출되고, 객체의 기본 상태를 설정해준다.
class User {
constructor(name) {
this.name = name;
}
sayHi() {
console.log("안녕하세요");
}
}
// 사용법:
let user = new User("Yam");
user.sayHi();
위처럼 new User("Yam")를 호출하면, 새로운 객체가 생성되고,
넘겨받은 매개변수와 함께 constructor가 자동으로 실행된다.
자바스크립트에서 클래스는 함수의 한 종류로 취급한다.
class User {
constructor(name) { this.name = name; }
sayHi() { console.log(this.name); }
}
// User가 함수라는 증거
console.log(typeof User); // function
class User 문법 구조가 하는일은 다음과 같다.
1. User라는 이름을 가진 함수를 만들고, 함수 본문은 constructor에서 가져온다.
constructor가 없으면 본문이 비워진 채로 함수가 만들어진다.
2. sayHi와 같은 메서드를 User.prototype에 저장한다.
new User를 호출해 객체를 만들고, 객체의 메서드를 호출하면 메서드를 prototype 프로퍼티를 통해 가져온다.
이 과정이 있어서 객체에서 메서드에 접근할 수 있다.
class User {
constructor(name) { this.name = name; }
sayHi() { alert(this.name); }
}
// 클래스는 함수다.
console.log(typeof User); // function
// 정확히는 생성자 메서드와 동일하다.
console.log(User === User.prototype.constructor); // true
// 클래스 내부에서 정의한 메서드는 User.prototype에 저장된다.
console.log(User.prototype.sayHi); // console.log(this.name);
// 현재 프로토타입에는 메서드가 두 개다.
console.log(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi
class라는 키워드 없이도 클래스 역할을 하는 함수를 선언할 수 있다.
// class User와 동일한 기능을 하는 순수 함수를 만들어보자.
// 1. 생성자 함수를 만든다.
function User(name) {
this.name = name;
}
// 모든 함수의 프로토타입은 'constructor' 프로퍼티를 기본으로 갖고 있기 때문에
// constructor 프로퍼티를 명시적으로 만들 필요가 없다.
// 2. prototype에 메서드를 추가한다.
User.prototype.sayHi = function() {
console.log(this.name);
};
// 사용법:
let user = new User("John");
user.sayHi();
위 처럼 순수 함수로 클래스 역할을 하는 함수를 선언하는 방법과 class 키워드를 사용하는 방법의 결과는 거의 같다.
이 두 방법에는 중요한 차이가 몇가지 있다.
1. class로 만든 함수엔 특수 내부 프로퍼티인 IsClassConstructor : true 가 이름표처럼 붙는다.
이것만으로도 두 방법엔 분명한 차이가 있음을 알 수 있다.
class User {
constructor() {}
}
console.log(typeof User); // User의 타입은 함수이긴 하지만 그냥 호출할 수 없다
User(); // TypeError: Class constructor User cannot be invoked without 'new'
자바스크립트는 다양한 경우 IsClassConstructor : true를 활용한다.
클래스 생성자를 new와 함께 호출하지 않으면 에러가 발생하는데 이 때 IsClassConstructor : true 가 사용된다.
또 다른 차이점으로는
2. 클래스에 정의된 메서드는 열거할 수 없다.
클래스의 prototype 프로퍼티에 추가된 메서드의 enumerable 플래그는 false 이기 때문.
for in 으로 객체를 순회할 때, 메서드는 순회 대상에서 제외하고자 하는 경우가 많아 이 특징은 꽤 유용하다.
3. 클래스는 항상 엄격 모드로 실행된다. 클래스 생성자 안 코드 전체에 자동으로 엄격 모드가 적용된다.
클래스 표현식
함수처럼 클래스도 다른 표현식 내부에서 정의, 전달, 반환, 할당할 수 있다.
클래스 표현식을 만들어보자.
let User = class {
sayHi() {
console.log("안녕하세요.");
}
};
기명 함수 표현식과 유사하게 클래스 표현식에도 이름을 붙일 수 있다.
클래스 표현식에 이름을 붙이면, 이 이름은 오직 클래스 내부에서만 사용할 수 있다.
// 기명 클래스 표현식(Named Class Expression)
// (명세서엔 없는 용어이지만, 기명 함수 표현식과 유사하게 동작한다.)
let User = class MyClass {
sayHi() {
console.log(MyClass); // MyClass라는 이름은 오직 클래스 안에서만 사용할 수 있다.
}
};
new User().sayHi(); // 원하는대로 MyClass의 정의를 보여준다.
console.log(MyClass); // ReferenceError: MyClass is not defined, MyClass는 클래스 밖에서 사용할 수 없다.
getter와 setter
클래스는 getter와 setter를 지원한다.
get과 set을 이용해 user.name을 조작해보자!
class User {
constructor(name) {
// setter를 활성화합니다.
this.name = name;
}
get name() {
return this._name;
}
set name(value) {
if (value.length < 4) {
console.log("이름이 너무 짧습니다.");
return;
}
this._name = value;
}
}
let user = new User("성원");
console.log(user.name); // 성원
user = new User(""); // 이름이 너무 짧습니다.
참고로 getter와 setter는 User.prototype에 정의된다.
클래스 필드
클래스 필드라는 문법을 사용하면 어떤 종류의 프로퍼티도 클래스에 추가할 수 있다.
클래스 User에 name 프로퍼티를 추가해보자.
class User {
name = "성원";
sayHi() {
console.log(`${this.name}님 안녕하세요!`);
}
}
new User().sayHi(); // 성원님 안녕하세요!
클래스를 정의할 때 <프로퍼티 이름> = <값> 을 써주면 간단히 클래스 필드를 만들 수 있다.
클래스 필드의 중요한 특징 중 하나는 User.prototype이 아닌 개별 객체에만 클래스 필드가 설정된다는 점이다.
class User {
name = "성원";
}
let user = new User();
console.log(user.name); // 성원
console.log(User.prototype.name); // undefined
선택 정렬( Selection Sort )은 버블 정렬과 유사한 알고리즘으로, 해당 순서에 원소를 넣을 위치는 이미 정해져 있고, 어떤 원소를 넣을지 선택하는 알고리즘이다. 선택 정렬과 삽입 정렬를 헷갈릴 수 있는데, 선택 정렬은 배열에서 해당 자리를 선택하고 그 자리에 오는 값을 찾는 것이라고 생각하면 된다.
선택 정렬은 3가지의 과정을 반복하며 정렬을 수행한다.
1. 선택한 곳의 데이터보다 더 작은 데이터가 있는지 순환하며 찾는다.
2. 구한 가장 작은 최솟값을 맨 앞에 위치한 값과 교체한다.
3. 맨 앞의 위치를 하나씩 앞으로 옮겨가며 반복한다.
#include<iostream>
using namespace std;
int main()
{
int Array[] = { 10,9,8,7,6,5,4,2,3,1 };
int MinIndex;
int Number;
for (int i = 0; i < 9; i++)
{
// 맨 앞 위치 선택
MinIndex = i;
for (int j = i + 1; j < 10; j++)
{
// 선택한 위치 값과 다른 위치 값들비교
// 더 작은 값이 있으면 위치값 갱신
if (Array[MinIndex] > Array[j])
{
MinIndex = j;
}
}
// 한번 반복하고 나면 스왑
swap(Array[i], Array[MinIndex]);
}
return 0;
}
시간복잡도
데이터의 개수가 n개라고 할 경우,
첫 번째 회전에서의 비교횟수 : 1 ~ ( n - 1 ) => n - 1
두 번째 회전에서의 비교횟수 : 2 ~ ( n - 1 ) => n - 2
...
(n-1) + (n-2) + ... + 2 + 1 => n(n-1) / 2
비교하는 것이 상수 시간에 이루어진다는 가정 아래에서 n개의 주어진 배열을 정렬하는데
O( n^2 ) 만큼의 시간이 걸린다. 최선, 평균, 최악의 경우 시간복잡도는 O( n^2 )로 동일하다.