원칙적으로 말해서 JS에는 class는 존재하지 않는다. 단지, 생성자 함수만 존재할 뿐이다.
아래는 Pet 이라는 클래스를 생성한다.
function Pet(name) { this.name = name; // name 필드 생성 } Pet.prototype.age = 0; // age 필드 생성 // toString 메소드 생성 Pet.prototype.toString = function() { return "Pet name : " + this.name + ", age : " + this.age; } var mong = new Pet("mong"); // 객체 생성 mong.age = 6; alert(mong);
상속할 때 중요한 부분은 Dog.prototype = new Pet()
과 Dog.prototype.constructor = Dog
부분이다.
// 위에서 이어서, Pet을 상속하는 Dog 생성 function Dog(name, age, breed) { Pet.call(this, name); this.age = age; this.breed = breed; } // Dog.prototype이 Pet.prototype으로부터 상속하도록 한다. Dog.prototype = new Pet(); // Pet 함수를 가리키는 생성자를 Dog 함수를 가리키도록 변경한다. Dog.prototype.constructor = Dog; // toString() 메소드 오버라이드 Dog.prototype.toString = function() { // 필요할 경우 부모클래스 메소드도 호출 가능 return "Dog " + Pet.prototype.toString.call(this) + ", breed : " + this.breed; } var miro = new Dog("Miro", 7, "Pomeranian"); alert(miro); alert("Is miro a Dog? " + (miro instanceof Dog)); alert("Is miro a Pet? " + (miro instanceof Pet)); alert("Is miro an Object? " + (miro instanceof Object));
JavaScript does not need classes를 참조하였다.
this.field
로 필드 값을 읽으면 된다.var jane = { name: "Jane", describe: function() { return "Person called " + this.name; } }; console.log(jane.describe()); // Person called Jane
var PersonProto = { describe: function () { return "Person called "+this.name; } }; var jane = { __proto__: PersonProto, name: "Jane" }; var tarzan = { __proto__: PersonProto, name: "Tarzan" }; console.log(jane.describe()); console.log(tarzan.describe());
Class 객체의 method는 원칙적으로 this가 객체를 가리켜야 하지만, 이벤트 핸들러로 등록되면 이벤트를 발생시킨 객체를 this로 가지게 되는 현상이 발생한다. 이 문제를 해결하려면 Class methods as event handlers in javascript? - Stack Overflow에 나온 방법을 사용해야 한다.
ClickCounter = function(buttonId) { this._clickCount = 0; var that = this; document.getElementById(buttonId).onclick = function(){ that.buttonClicked() }; } ClickCounter.prototype = { buttonClicked: function() { this._clickCount++; alert('the button was clicked ' + this._clickCount + ' times'); } }
핵심은 이벤트 핸들러를 등록할 때 this를 that으로 매핑하고, that.buttonClicked()
를 호출하도록 function()으로 감싸는 부분이다. 이렇게 하면 클래스 메소드에서는 this
를 원래 객체를 가리키는 것으로써 온전하게 사용할 수 있다.
setInterval과 setTimeout에서도 DOM 이벤트 바인딩과 동일한 원리가 적용된다.
var A = function(v) { this.value = v; this.intervalId = null; }; A.prototype.load = function() { console.log(new Date() + " : " + this.value); }; A.prototype.start = function() { var that = this; this.intervalId = setInterval(function() { that.load(); }, 2000); }; A.prototype.stop = function() { clearInterval(this.intervalId); }; var a = new A('hello'); a.start(); //... after a minute a.stop();
위에서 A.prototype.start
함수를 보면된다.
Class.extend({ … });
로 간결하게 클래스 생성// "use strict"가 가능하도록 살짝 수정 /* * Simple JavaScript Inheritance By John Resig http://ejohn.org/ * http://ejohn.org/blog/simple-javascript-inheritance/ MIT Licensed. */ // Inspired by base2 and Prototype (function () { "use strict"; var initializing = false, fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/; // The base Class implementation (does nothing) window.Class = function () { // edited for use strict }; // Create a new Class that inherits from this class Class.extend = function (prop) { var _super = this.prototype; // Instantiate a base class (but only create the instance, // don't run the init constructor) initializing = true; var prototype = new this(); initializing = false; var name; // Copy the properties over onto the new prototype for (name in prop) { // Check if we're overwriting an existing function prototype[name] = typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name]) ? (function (name, fn) { return function () { var tmp = this._super; // Add a new ._super() method that is the same method // but on the super-class this._super = _super[name]; // The method only need to be bound temporarily, so we // remove it when we're done executing var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; }(name, prop[name])) : prop[name]; } // The dummy class constructor function Class() { // All construction is actually done in the init method if (!initializing && this.init) this.init.apply(this, arguments); } // Populate our constructed prototype object Class.prototype = prototype; // Enforce the constructor to be what we expect Class.prototype.constructor = Class; // And make this class extendable Class.extend = window.Class.extend; // edited for use strict return Class; }; }());
// 용례 var Person = Class.extend({ init: function(isDancing){ this.dancing = isDancing; }, dance: function(){ return this.dancing; } }); var Ninja = Person.extend({ init: function(){ this._super( false ); }, dance: function(){ // Call the inherited version of dance() return this._super(); }, swingSword: function(){ return true; } }); var p = new Person(true); p.dance(); // => true var n = new Ninja(); n.dance(); // => false n.swingSword(); // => true // Should all be true p instanceof Person && p instanceof Class && n instanceof Ninja && n instanceof Person && n instanceof Class