2023.06.16
class Color {
constructor(r = 0, g = 0, b = 0) {
this.r = r;
this.g = g;
this.b = b;
}
get rgb() {
return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
}
set rgb(value) {
// ...code shown later...
}
toString() {
return this.rgb;
}
static fromCSS(css) {
// ...code shown later...
}
}
let c = new Color(30, 144, 255);
console.log(String(c)); // "rgb(30, 144, 255)"
// 클래스 정의
class Color {}
// 익명 클래스 정의
let Color = class {};
// 클래스 정의
let C = class Color {};
생성자 정의의 닫는 중괄호 뒤에는 세미콜론이 없다. 클래스 본문의 생성자 및 메서드 정의는 선언과 비슷하며 뒤에 세미콜론이 없다(세미콜론은 존재하는 경우 허용된다. 문법은 이러한 쉬운 실수를 구문 오류로 만들지 않도록 특별히 허용한다).
Color(); // TypeError: Class construct Color cannot be invoked without 'new'
var Color = function Color(r, g, b) {
if (!(this instanceof Color)) {
throw new TypeError("Class construct Color cannot be invoked without 'new'");
}
// ...
};
class Color {
constructor(r = 0, g = 0, b = 0) {
this.r = r;
this.g = g;
this.b = b;
}
}
class Color {
toString() {
return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
}
}
// 이전까지(~ES5)와 거의 동등한 코드
Color.prototype.toString = function toString() {
return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
};
class Color {
toString() {
return "rgb(" + this.r + ", " + this.g + ", " + this.b + ")";
}
}
const c = new Color(30, 144, 255);
console.log(typeof c.toString.prototype); // undefined
...
var Color = function Color(r, g, b) {};
Color.prototype.toString = function toString() {}
const c = new Color(30, 144, 255);
console.log(typeof c.toString.prototype); // object
const c = Color.fromCSS('#1E90FF');
console.log(c.toString()); // "rgb(30, 144, 255)"
Color.fromCSS = function fromCSS(css) {};
class Color {
get rgb() {
return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
}
toString() {
return this.rgb;
}
}
let name = 'foo' + Math.floor(Math.random() * 100);
class SomeClass {
[name]() {}
}
class SomeClass {}
class Guide {
static [6 * 7]() {
console.log('...');
}
}
Guide['42'](); // ...
class Color {
constructor(r = 0, g = 0, b = 0) {
this.r = r;
this.g = g;
this.b = b;
}
get rgb() {
return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
}
set rgb(value) {
let s = String(value);
let match = /^rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)$/i.exec(s.replace(/\s/g, ''));
if (!match) {
throw new Error("Invalid rgb color string '" + s + "'");
}
this.r = parseInt(match[1], 10);
this.g = parseInt(match[2], 10);
this.b = parseInt(match[3], 10);
}
toString() {
return this.rgb;
}
static fromCSS(css) {
const match = /^#?([0-9a-f]{3}|[0-9a-f]{6});?$/i.exec(css);
if (!match) {
throw new Error('Invalid CSS code: ' + css);
}
let vals = match[1];
if (vals.length === 3) {
vals = vals[0] + vals[0] + vals[1] + vals[1] + vals[2] + vals[2];
}
return new this(
parseInt(vals.substring(0, 2), 16),
parseInt(vals.substring(2, 4), 16),
parseInt(vals.substring(4, 6), 16),
);
}
}
// Usage
let c = new Color(30, 144, 255);
console.log(String(c)); // "rgb(30, 144, 255)"
c = Color.fromCSS('00A');
console.log(String(c)); // "rgb(0, 0, 170)"
'use strict';
var Color = function Color(r, g, b) {
if (!(this instanceof Color)) {
throw new TypeError("Class constructor Color cannot be invoked without 'new'");
}
this.r = r || 0;
this.g = g || 0;
this.b = b || 0;
};
Object.defineProperty(Color.prototype, 'rgb', {
get: function () {
return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
},
set: function (value) {
var s = String(value);
var match = /^rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)$/i.exec(s.replace(/\s/g, ''));
if (!match) {
throw new Error("Invalid rgb color string '" + s + "'");
}
this.r = parseInt(match[1], 10);
this.g = parseInt(match[2], 10);
this.b = parseInt(match[3], 10);
},
configurable: true,
});
Color.prototype.toString = function () {
return this.rgb;
};
Color.fromCSS = function (css) {
var match = /^#?([0-9a-f]{3}|[0-9a-f]{6});?$/i.exec(css);
if (!match) {
throw new Error('Invalid CSS code: ' + css);
}
var vals = match[1];
if (vals.length === 3) {
vals = vals[0] + vals[0] + vals[1] + vals[1] + vals[2] + vals[2];
}
return new this(
parseInt(vals.substring(0, 2), 16),
parseInt(vals.substring(2, 4), 16),
parseInt(vals.substring(4, 6), 16),
);
};
// Usage
var c = new Color(30, 144, 255);
console.log(String(c)); // "rgb(30, 144, 255)"
c = Color.fromCSS('00A');
console.log(String(c)); // "rgb(0, 0, 170)"
class Color {
constructor(r = 0, g = 0, b = 0) {
this.r = r;
this.g = g;
this.b = b;
}
get rgb() {
return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
}
toString() {
return this.rgb;
}
}
class ColorWithAlpha extends Color {}
const c = new ColorWithAlpha(30, 144, 255);
console.log(String(c)); // rgb(30, 144, 255)
consturctor(/* ...여기에 임의의 수의 인수가 있다... */){
super(/* ...모두 super로 전달... */);
}
var ColorWithAlpha = function ColorWithAlpha() {
Color.apply(this, arguments);
};
ColorWithAlpha.prototype = Object.create(Color.prototype);
ColorWithAlpha.prototype.constructor = ColorWithAlpha;
class ColorWithAlpha extends Color {
constructor(r = 0, g = 0, b = 0, a = 0) {
this.a = a;
super(r, g, b);
}
}
super.methodName()
을 통해 슈퍼클래스 메서드를 호출한다.class ColorWithAlpha extends Color {
constructor(r = 0, g = 0, b = 0, a = 1) {
super(r, g, b);
this.a = a;
}
brightness(bgColor) {
let result = super.brightness() * this.a;
if (bgColor && this.a !== 1) {
result = (result + bgColor.brightness() * (1 - this.a)) / 2;
}
return result;
}
}
ColorWithAlpha.prototype.brightness = function brightness(bgColor) {
var result = Color.prototype.brightness.call(this) * this.a; // 또는 아래와 같이 선언 가능
if (bgColor && this.a !== 1) {
result = (result + bgColor.brightness() * (1 - this.a)) / 2;
}
return result;
};
// case1
var superproto = Object.getPrototypeOf(ColorWithAlpha.prototype);
var result = superproto.brightness.call(this) * this.a;
// case2
var superproto = Object.getPrototypeOf(Object.getPrototypeOf(this));
var result = superproto.brightness.call(this) * this.a;
class Color {
// ...
static fromCSS() {
// ...
}
}
class ColorWithAlpha extends Color {
// ...
}
const ca = ColorWithAlpha.fromCSS('#1E90FF');
console.log(String(ca)); // "rgba(30, 144, 255, 1)"
console.log(ca.constructor.name); // "ColorWithAlpha"
console.log(ca instanceof ColorWithAlpha); // true
extends
절을 사용하면 두 개의 상속 체인이 생성된다.ColorWithAlpha
→ Color
→ Function.Prototype
→ Object.prototype
ColorWithAlpha.prototype
→ Color.prototype
→ Object.prototype
Function.prototype
이 아닌 진정한 자바스크립트 함수를 갖는 표준 방법이 없었다.class ColorWithAlpha extends Color {
// ...
static fromCSS(css, a = 1) {
const result = super.fromCSS(css);
result.a = a;
return result;
}
}
class Color {
halfBright() {
const ctor = this.constructor || Color;
return new ctor(Math.round(this.r / 2), Math.round(this.g / 2), Math.round(this.b / 2));
}
}
class Base {
constructor(data) {
this.data = data;
}
static get [Symbol.species]() {
return this;
}
static create(data) {
// Symbol.species를 사용하지 않는다.
const ctor = this || Base;
return new ctor(data);
}
clone() {
// Symbol.species를 사용한다.
const ctor = (this && this.constructor && this.constructor[Symbol.species]) || Base;
return new ctor(this.data);
}
}
// Sub1은 기본 동작을 사용한다.
class Sub1 extends Base {}
// Sub2는 패턴을 따르는 모든 메서드가 Sub2 대신 Base를 사용하게 한다.
class Sub2 extends Base {
static get [Symbol.species]() {
return Base;
}
}
const a = Base.create(1);
console.log(a.constructor.name); // "Base"
const aclone = a.clone();
console.log(aclone.constructor.name); // "Base"
const b = Sub1.create(2);
console.log(b.constructor.name); // "Sub1"
const bclone = b.clone();
console.log(bclone.constructor.name); // "Sub1"
const c = Sub2.create(3);
console.log(c.constructor.name); // "Sub2"
const d = new Sub2(4);
console.log(d.constructor.name); // "Sub2"
console.log(d.data); // 4
const dclone = d.clone();
console.log(dclone.constructor.name); // "Base"
console.log(dclone.data); // 4
class Elements extends Array {
select(source) {
// ...
return this;
}
style(props) {
// ...
return this;
}
}
new Elements().select('div').style({ color: 'green' }).slice(1).style({ border: '1px solid gray' });
class SuperClass {
test() {
return "SuperClass's test";
}
}
class SubClass extends SuperClass {
test1() {
return "SubClass's test1: " + super.test();
}
}
SubClass.prototype.test2 = function () {
return "SubClass's test2: " + super.test(); // ERROR HERE
};
const obj = new SubClass();
obj.test1();
obj.test2();
function getFakeSuper(o) {
return Object.getPrototypeOf(Object.getPrototypeOf(o));
}
class Base {
test() {
console.log("Base's test");
return 'Base test';
}
}
class Sub extends Base {
test() {
console.log("Sub's test");
return 'Sub test > ' + getFakeSuper(this).test.call(this);
}
}
class SubSub extends Sub {
test() {
console.log("SubSub's test");
return 'SubSub test > ' + getFakeSuper(this).test.call(this);
}
}
const obj = new SubSub();
console.log(obj.test()); // "SubSub's test" 후 "Sub's test" 반복
// stack overflow error가 발생할 때까지
class A {
constructor() {}
}
class B extends Object {
constructor() {
super();
}
}
new
를 통하지 않고 직접 호출된 경우 new.target
은 undefined
, new
연산자의 직접 대상인 경우엔 현재 함수를 참조function example() {
console.log(new.target);
}
example(); // undefined
class Base {
constructor() {
console.log(new.target);
}
}
new Base(); // "Base"
class Sub extends Base {
constructor() {
super();
}
}
new Sub(); // "Sub"
new.target
이 자기 자신을 바라보면 오류를 발생시켜 구현class Shape {
constructor(color) {
if (new.target === Shape) {
throw new Error("Shape can't be directly instantiated");
}
this.color = color;
}
// ...
}
class Triangle extends Shape {
get sides() {
return 3;
}
}
const t = new Triangle('orange');
const s = new Shape('red'); // Error: "Shape can't be directly instantiated"
new.target
이 클래스의 자체 생성자와 같지 않으면 오류를 발생시켜 구현class Thingy {
constructor() {
if (new.target !== Thingy) {
throw new Error("Thingy subclasses aren't supported.");
}
}
}
class InvalidThingy extends Thingy {}
const can = new Thingy(); // works
const cannot = new InvalidThingy(); // Error: "Thingy subclasses aren't supported."
const TwoWays = function TwoWays() {
if (!new.target) {
console.log("Called directly; using 'new' instead");
return new TwoWays();
}
console.log("Called via 'new'");
};
console.log('With new:');
let t1 = new TwoWays();
// "Called via 'new'"
console.log('Without new:');
let t2 = TwoWays();
// "Called directly; using 'new' instead"
// "Called via 'new'"
// 선언
class Class1 {}
// 익명 클래스 표현식
let Color = class {};
// 명명된 클래스 표현식
let C = class Color {};
// 여기서 TheClass를 사용하려고하면 TDZ때문에 ReferenceError가 발생한다.
let name = 'foo' + Math.floor(Math.random() * 1000);
class TheClass {
// 선언은 단계별 코드의 일부로 처리되기 때문에
// 여기에서 name을 사용할 수 있고 위에서 할당한 값을 가진다.
[name]() {
console.log('This is the method ' + name);
}
} // 세미콜론 필요 없음
// 전역이 생성됨
console.log(typeof TheClass); // "function"
// 전역 객체애 대한 속성이 없음
console.log(typeof this.TheClass); // "undefined"
let name = 'foo' + Math.floor(Math.random() * 1000);
// 표현식
const C = class TheClass {
[name]() {
console.log('This is the method ' + name + ' in the class ' + TheClass.name);
// The class name is in-scope -^
// within the definition
}
}; // 세미콜론 필요함
// 클래스 이름이 이 스코프 영역에 추가되지 않음
console.log(typeof TheClass); // "undefined"
// 표현식의 값은 클래스임
console.log(typeof C); // "function"