자바 스크립트 상속 [닫힘]
나는 자바 스크립트에서 상속을 구현하려고합니다. 나는 그것을 지원하기 위해 다음과 같은 최소한의 코드를 생각해 냈습니다.
function Base(){
this.call = function(handler, args){
handler.call(this, args);
}
}
Base.extend = function(child, parent){
parent.apply(child);
child.base = new parent;
child.base.child = child;
}
전문가 여러분, 이것이 충분하거나 내가 놓친 다른 중요한 문제가 있는지 알려주십시오. 직면 한 유사한 문제를 바탕으로 다른 변경 사항을 제안하십시오.
다음은 완전한 테스트 스크립트입니다.
function Base(){
this.call = function(handler, args){
handler.call(this, args);
}
this.superalert = function(){
alert('tst');
}
}
Base.extend = function(child, parent){
parent.apply(child);
child.base = new parent;
child.base.child = child;
}
function Child(){
Base.extend(this, Base);
this.width = 20;
this.height = 15;
this.a = ['s',''];
this.alert = function(){
alert(this.a.length);
alert(this.height);
}
}
function Child1(){
Base.extend(this, Child);
this.depth = 'depth';
this.height = 'h';
this.alert = function(){
alert(this.height); // display current object height
alert(this.a.length); // display parents array length
this.call(this.base.alert);
// explicit call to parent alert with current objects value
this.call(this.base.superalert);
// explicit call to grandparent, parent does not have method
this.base.alert(); // call parent without overriding values
}
}
var v = new Child1();
v.alert();
alert(v.height);
alert(v.depth);
ECMAScript 5 에서 자바 스크립트 상속을 구현 하기 Object.create
위해 객체의 프로토 타입을 정의하고 상속에 사용할 수 있습니다. 원하는만큼 속성을 추가 / 재정의 할 수도 있습니다.
예:
/**
* Transform base class
*/
function Transform() {
this.type = "2d";
}
Transform.prototype.toString = function() {
return "Transform";
}
/**
* Translation class.
*/
function Translation(x, y) {
// Parent constructor
Transform.call(this);
// Public properties
this.x = x;
this.y = y;
}
// Inheritance
Translation.prototype = Object.create(Transform.prototype);
// Override
Translation.prototype.toString = function() {
return Transform.prototype.toString() + this.type + " Translation " + this.x + ":" + this.y;
}
/**
* Rotation class.
*/
function Rotation(angle) {
// Parent constructor
Transform.call(this);
// Public properties
this.angle = angle;
}
// Inheritance
Rotation.prototype = Object.create(Transform.prototype);
// Override
Rotation.prototype.toString = function() {
return Transform.prototype.toString() + this.type + " Rotation " + this.angle;
}
// Tests
translation = new Translation(10, 15);
console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false
console.log(translation.toString()) // Transform2d Translation 10:15
나는 Crockfords 솔루션이 John과 마찬가지로 너무 복잡하다고 생각합니다. 둘 다 설명하는 것보다 자바 스크립트 상속을 얻는 것이 훨씬 간단합니다. 중히 여기다:
//Classes
function A() {
B.call(this);
}
function B() {
C.call(this);
this.bbb = function() {
console.log("i was inherited from b!");
}
}
function C() {
D.call(this);
}
function D() {
E.call(this);
}
function E() {
//instance property
this.id = Math.random()
}
//set up the inheritance chain (order matters)
D.prototype = new E();
C.prototype = new D();
B.prototype = new C();
A.prototype = new B();
//Add custom functions to each
A.prototype.foo = function() {
console.log("a");
};
B.prototype.bar = function() {
console.log("b");
};
C.prototype.baz = function() {
console.log("c");
};
D.prototype.wee = function() {
console.log("d");
};
E.prototype.woo = function() {
console.log("e");
};
//Some tests
a = new A();
a.foo();
a.bar();
a.baz();
a.wee();
a.woo();
console.log(a.id);
a.bbb();
console.log(a instanceof A);
console.log(a instanceof B);
console.log(a instanceof C);
console.log(a instanceof D);
console.log(a instanceof E);
var b = new B();
console.log(b.id)
위의 솔루션에 대한 완전한 설명을 블로그에 작성했습니다 .
JS 객체를 가지고 놀면서 더 최소한의 해결책을 찾았습니다. :-) 즐기세요!
function extend(b,a,t,p) { b.prototype = a; a.apply(t,p); }
예
function A() {
this.info1 = function() {
alert("A");
}
}
function B(p1,p2) {
extend(B,A,this);
this.info2 = function() {
alert("B"+p1+p2);
}
}
function C(p) {
extend(C,B,this,["1","2"]);
this.info3 = function() {
alert("C"+p);
}
}
var c = new C("c");
c.info1(); // A
c.info2(); // B12
c.info3(); // Cc
여기에 가장 간단하고 JS에서 상속을 이해하는 가장 쉬운 방법이 있기를 바랍니다. 이 예제는 PHP 프로그래머에게 가장 도움이 될 것입니다.
function Mother(){
this.canSwim = function(){
console.log('yes');
}
}
function Son(){};
Son.prototype = new Mother;
Son.prototype.canRun = function(){
console.log('yes');
}
이제 아들은 하나의 재정의 된 방법과 하나의 새로운
function Grandson(){}
Grandson.prototype = new Son;
Grandson.prototype.canPlayPiano = function(){
console.log('yes');
};
Grandson.prototype.canSwim = function(){
console.log('no');
}
이제 손자는 두 개의 재정의 된 메서드와 하나의 새로운
var g = new Grandson;
g.canRun(); // => yes
g.canPlayPiano(); // => yes
g.canSwim(); // => no
함수 대신 객체를 사용하지 않는 이유 :
var Base = {
superalert : function() {
alert('tst');
}
};
var Child = Object.create(Base);
Child.width = 20;
Child.height = 15;
Child.a = ['s',''];
Child.childAlert = function () {
alert(this.a.length);
alert(this.height);
}
var Child1 = Object.create(Child);
Child1.depth = 'depth';
Child1.height = 'h';
Child1.alert = function () {
alert(this.height);
alert(this.a.length);
this.childAlert();
this.superalert();
};
그리고 다음과 같이 부릅니다.
var child1 = Object.create(Child1);
child1.alert();
이 접근 방식은 함수보다 훨씬 더 깔끔합니다. 함수를 사용한 상속이 JS에서 적절한 방법이 아닌 이유를 설명하는이 블로그를 찾았습니다. http://davidwalsh.name/javascript-objects-deconstruction
편집하다
var Child는 다음과 같이 작성할 수도 있습니다.
var Child = Object.create(Base, {
width : {value : 20},
height : {value : 15, writable: true},
a : {value : ['s', ''], writable: true},
childAlert : {value : function () {
alert(this.a.length);
alert(this.height);
}}
});
Lorenzo Polidori의 답변에 설명 된 표준 프로토 타입 상속 방법을 기반으로하는 내 솔루션은 다음과 같습니다 .
먼저 다음과 같은 도우미 메서드를 정의하여 시작합니다.이를 통해 나중에 이해하기 쉽고 가독성이 높아집니다.
Function.prototype.setSuperclass = function(target) {
// Set a custom field for keeping track of the object's 'superclass'.
this._superclass = target;
// Set the internal [[Prototype]] of instances of this object to a new object
// which inherits from the superclass's prototype.
this.prototype = Object.create(this._superclass.prototype);
// Correct the constructor attribute of this class's prototype
this.prototype.constructor = this;
};
Function.prototype.getSuperclass = function(target) {
// Easy way of finding out what a class inherits from
return this._superclass;
};
Function.prototype.callSuper = function(target, methodName, args) {
// If methodName is ommitted, call the constructor.
if (arguments.length < 3) {
return this.callSuperConstructor(arguments[0], arguments[1]);
}
// `args` is an empty array by default.
if (args === undefined || args === null) args = [];
var superclass = this.getSuperclass();
if (superclass === undefined) throw new TypeError("A superclass for " + this + " could not be found.");
var method = superclass.prototype[methodName];
if (typeof method != "function") throw new TypeError("TypeError: Object " + superclass.prototype + " has no method '" + methodName + "'");
return method.apply(target, args);
};
Function.prototype.callSuperConstructor = function(target, args) {
if (args === undefined || args === null) args = [];
var superclass = this.getSuperclass();
if (superclass === undefined) throw new TypeError("A superclass for " + this + " could not be found.");
return superclass.apply(target, args);
};
이제를 사용하여 클래스의 수퍼 클래스를 설정할 SubClass.setSuperclass(ParentClass)
수있을뿐만 아니라 다음을 사용하여 재정의 된 메서드를 호출 할 수도 있습니다 SubClass.callSuper(this, 'functionName', [argument1, argument2...])
.
/**
* Transform base class
*/
function Transform() {
this.type = "2d";
}
Transform.prototype.toString = function() {
return "Transform";
}
/**
* Translation class.
*/
function Translation(x, y) {
// Parent constructor
Translation.callSuper(this, arguments);
// Public properties
this.x = x;
this.y = y;
}
// Inheritance
Translation.setSuperclass(Transform);
// Override
Translation.prototype.toString = function() {
return Translation.callSuper(this, 'toString', arguments) + this.type + " Translation " + this.x + ":" + this.y;
}
/**
* Rotation class.
*/
function Rotation(angle) {
// Parent constructor
Rotation.callSuper(this, arguments);
// Public properties
this.angle = angle;
}
// Inheritance
Rotation.setSuperclass(Transform);
// Override
Rotation.prototype.toString = function() {
return Rotation.callSuper(this, 'toString', arguments) + this.type + " Rotation " + this.angle;
}
// Tests
translation = new Translation(10, 15);
console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false
console.log(translation.toString()) // Transform2d Translation 10:15
물론 헬퍼 함수를 사용하더라도 여기의 구문은 꽤 어색합니다. 고맙게도 ECMAScript 6에서는 훨씬 더 예쁘게 만들기 위해 몇 가지 구문 설탕 ( 최대 최소 클래스 )이 추가되었습니다. 예 :
/**
* Transform base class
*/
class Transform {
constructor() {
this.type = "2d";
}
toString() {
return "Transform";
}
}
/**
* Translation class.
*/
class Translation extends Transform {
constructor(x, y) {
super(); // Parent constructor
// Public properties
this.x = x;
this.y = y;
}
toString() {
return super(...arguments) + this.type + " Translation " + this.x + ":" + this.y;
}
}
/**
* Rotation class.
*/
class Rotation extends Transform {
constructor(angle) {
// Parent constructor
super(...arguments);
// Public properties
this.angle = angle;
}
toString() {
return super(...arguments) + this.type + " Rotation " + this.angle;
}
}
// Tests
translation = new Translation(10, 15);
console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false
console.log(translation.toString()) // Transform2d Translation 10:15
ECMAScript 6은이 시점에서 아직 초안 단계에 있으며 내가 아는 한 주요 웹 브라우저에서는 구현되지 않습니다. 그러나 원한다면 Traceur 컴파일러 와 같은 것을 사용 ECMAScript 6
하여 평범한 오래된 ECMAScript 5
기반 JavaScript 로 컴파일 할 수 있습니다 . 여기에서 Traceur를 사용하여 컴파일 된 위의 예제를 볼 수 있습니다 .
위의 모든 답변에 동의하지만 JavaScript가 객체 지향적 일 필요는없고 (상속 방지) 대부분의 경우 객체 기반 접근 방식 으로 충분해야한다고 생각합니다.
저는 Eloquent JavaScript 가 OO에 대해 이야기하는 객체 지향 프로그래밍에 관한 8 장을 시작 하는 방식을 좋아합니다 . 상속을 구현하는 가장 좋은 방법을 해독하는 대신 JavaScript의 기능적 측면을 배우는 데 더 많은 에너지를 투자해야하므로 함수형 프로그래밍에 대한 6 장을 더 흥미롭게 발견했습니다.
//This is an example of how to override a method, while preserving access to the original.
//The pattern used is actually quite simple using JavaScripts ability to define closures:
this.somefunction = this.someFunction.override(function(args){
var result = this.inherited(args);
result += this.doSomethingElse();
return result;
});
//It is accomplished through this piece of code (courtesy of Poul Krogh):
/***************************************************************
function.override overrides a defined method with a new one,
while preserving the old method.
The old method is only accessible from the new one.
Use this.inherited() to access the old method.
***************************************************************/
Function.prototype.override = function(func)
{
var remember = this;
var f = function()
{
var save = this.inherited;
this.inherited = remember;
var result = func.apply(this, Array.prototype.slice.call(arguments));
this.inherited = save;
return result;
};
return f;
}
이 간단한 접근 방식은 어떻습니까
function Body(){
this.Eyes = 2;
this.Arms = 2;
this.Legs = 2;
this.Heart = 1;
this.Walk = function(){alert(this.FirstName + ' Is Walking')};
}
function BasePerson() {
var BaseBody = new Body(this);
BaseBody.FirstName = '';
BaseBody.LastName = '';
BaseBody.Email = '';
BaseBody.IntroduceSelf = function () { alert('Hello my name is ' + this.FirstName + ' ' + this.LastName); };
return BaseBody;
}
function Person(FirstName,LastName)
{
var PersonBuild = new BasePerson();
PersonBuild.FirstName = FirstName;
PersonBuild.LastName = LastName;
return PersonBuild;
}
var Person1 = new Person('Code', 'Master');
Person1.IntroduceSelf();
Person1.Walk();
기본 프로토 타입 상속
JavaScript에서 상속을 수행하는 간단하지만 효과적인 방법은 다음 두 줄을 사용하는 것입니다.
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
이것은 이것을하는 것과 유사합니다 :
B.prototype = new A();
둘 다의 주요 차이점은의 생성자 A
가를 사용할 때 실행되지 않는다는 것입니다 Object.create
. 이는 더 직관적이고 클래스 기반 상속과 더 유사합니다.
의 생성자 에 추가하여 A
새 인스턴스를 만들 때 의 생성자를 선택적으로 실행하도록 선택할 수 있습니다 .B
B
function B(arg1, arg2) {
A(arg1, arg2); // This is optional
}
당신의 모든 인수 전달하려는 경우 B
에를 A
, 당신은 또한 사용할 수 있습니다 Function.prototype.apply()
:
function B() {
A.apply(this, arguments); // This is optional
}
다른 객체를의 생성자 체인에 혼합 B
하려면 다음 Object.create
과 결합 할 수 있습니다 Object.assign
.
B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;
데모
function A(name) {
this.name = name;
}
A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;
function B() {
A.apply(this, arguments);
this.street = "Downing Street 10";
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
function mixin() {
}
mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;
mixin.prototype.getProperties = function() {
return {
name: this.name,
address: this.street,
year: this.year
};
};
function C() {
B.apply(this, arguments);
this.year = "2018"
}
C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;
var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());
나만의 래퍼 만들기
코드 전체에서 대략 동일한 2 줄짜리를 작성하는 것을 좋아하지 않는다면 다음과 같은 기본 래퍼 함수를 작성할 수 있습니다.
function inheritance() {
var args = Array.prototype.slice.call(arguments);
var firstArg = args.shift();
switch (args.length) {
case 0:
firstArg.prototype = Object.create(Object.prototype);
firstArg.prototype.constructor = firstArg;
break;
case 1:
firstArg.prototype = Object.create(args[0].prototype);
firstArg.prototype.constructor = firstArg;
break;
default:
for(var i = 0; i < args.length; i++) {
args[i] = args[i].prototype;
}
args[0] = Object.create(args[0]);
var secondArg = args.shift();
firstArg.prototype = Object.assign.apply(Object, args);
firstArg.prototype.constructor = firstArg;
}
}
이 래퍼의 작동 원리 :
- 하나의 매개 변수를 전달하면 프로토 타입이
Object
. - 두 개의 매개 변수를 전달하면 첫 번째 프로토 타입이 두 번째 매개 변수에서 상속됩니다.
- 두 개 이상의 매개 변수를 전달하면 첫 번째 프로토 타입이 두 번째 매개 변수에서 상속되고 다른 매개 변수의 프로토 타입이 혼합됩니다.
데모
function inheritance() {
var args = Array.prototype.slice.call(arguments);
var firstArg = args.shift();
switch (args.length) {
case 0:
firstArg.prototype = Object.create(Object.prototype);
firstArg.prototype.constructor = firstArg;
break;
case 1:
firstArg.prototype = Object.create(args[0].prototype);
firstArg.prototype.constructor = firstArg;
break;
default:
for(var i = 0; i < args.length; i++) {
args[i] = args[i].prototype;
}
args[0] = Object.create(args[0]);
var secondArg = args.shift();
firstArg.prototype = Object.assign.apply(Object, args);
firstArg.prototype.constructor = firstArg;
}
}
function A(name) {
this.name = name;
}
inheritance(A);
function B() {
A.apply(this, arguments);
this.street = "Downing Street 10";
}
inheritance(B, A);
function mixin() {
}
inheritance(mixin);
mixin.prototype.getProperties = function() {
return {
name: this.name,
address: this.street,
year: this.year
};
};
function C() {
B.apply(this, arguments);
this.year = "2018"
}
inheritance(C, B, mixin);
var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());
노트
Object.create
IE9 +를 포함한 모든 최신 브라우저에서 안전하게 사용할 수 있습니다. Object.assign
IE의 모든 버전이나 일부 모바일 브라우저에서는 작동하지 않습니다. 폴리 필 Object.create
및 / 또는 Object.assign
이를 사용하고이를 구현하지 않는 브라우저를 지원하려는 경우 권장됩니다 .
당신의 polyfill을 찾을 수 있습니다 Object.create
여기 와 하나 Object.assign
여기 .
//
// try this one:
//
// function ParentConstructor() {}
// function ChildConstructor() {}
//
// var
// SubClass = ChildConstructor.xtendz( ParentConstructor );
//
Function.prototype.xtendz = function ( SuperCtorFn ) {
return ( function( Super, _slice ) {
// 'freeze' host fn
var
baseFn = this,
SubClassCtorFn;
// define child ctor
SubClassCtorFn = function ( /* child_ctor_parameters..., parent_ctor_parameters[] */ ) {
// execute parent ctor fn on host object
// pass it last ( array ) argument as parameters
Super.apply( this, _slice.call( arguments, -1 )[0] );
// execute child ctor fn on host object
// pass remaining arguments as parameters
baseFn.apply( this, _slice.call( arguments, 0, -1 ) );
};
// establish proper prototype inheritance
// 'inherit' methods
SubClassCtorFn.prototype = new Super;
// (re)establish child ctor ( instead of Super ctor )
SubClassCtorFn.prototype.constructor = SubClassCtorFn;
// return built ctor
return SubClassCtorFn;
} ).call( this, SuperCtorFn, Array.prototype.slice );
};
// declare parent ctor
function Sup( x1, x2 ) {
this.parent_property_1 = x1;
this.parent_property_2 = x2;
}
// define some methods on parent
Sup.prototype.hello = function(){
alert(' ~ h e l l o t h e r e ~ ');
};
// declare child ctor
function Sub( x1, x2 ) {
this.child_property_1 = x1;
this.child_property_2 = x2;
}
var
SubClass = Sub.xtendz(Sup), // get 'child class' ctor
obj;
// reserve last array argument for parent ctor
obj = new SubClass( 97, 98, [99, 100] );
obj.hello();
console.log( obj );
console.log('obj instanceof SubClass -> ', obj instanceof SubClass );
console.log('obj.constructor === SubClass -> ', obj.constructor === SubClass );
console.log('obj instanceof Sup -> ', obj instanceof Sup );
console.log('obj instanceof Object -> ', obj instanceof Object );
//
// Object {parent_property_1: 99, parent_property_2: 100, child_property_1: 97, child_property_2: 98}
// obj instanceof SubClass -> true
// obj.constructor === SubClass -> true
// obj instanceof Sup -> true
// obj instanceof Object -> true
//
AWeb 라이브러리 를 사용하는 가장 쉬운 방법 . 공식 샘플 :
/**
* A-class
*/
var ClassA = AWeb.class({
public : {
/**
* A-class constructor
*/
constructor : function() {
/* Private variable */
this.variable1 = "A";
this.calls = 0;
},
/**
* Function returns information about the object
*/
getInfo : function() {
this.incCalls();
return "name=" + this.variable1 + ", calls=" + this.calls;
}
},
private : {
/**
* Private function
*/
incCalls : function() {
this.calls++;
}
}
});
/**
* B-class
*/
var ClassB = AWeb.class({
extends : ClassA,
public : {
/**
* B-class constructor
*/
constructor : function() {
this.super();
/* Private variable */
this.variable1 = "B";
},
/**
* Function returns extended information about the object
*/
getLongInfo : function() {
return this.incCalls !== undefined ? "incCalls exists" : "incCalls undefined";
}
}
});
/**
* Main project function
*/
function main() {
var a = new ClassA(),
b = new ClassB();
alert(
"a.getInfo " + (a.getInfo ? "exists" : "undefined") + "\n" +
"a.getLongInfo " + (a.getLongInfo ? "exists" : "undefined") + "\n" +
"b.getInfo " + (b.getInfo ? "exists" : "undefined") + "\n" +
"b.getLongInfo " + (b.getLongInfo ? "exists" : "undefined") + "\n" +
"b.getInfo()=" + b.getInfo() + "\n" +
"b.getLongInfo()=" + b.getLongInfo()
);
}
확장하고 프로토 타이핑하는 것보다 훨씬 쉬운 솔루션을 찾았습니다. 실제로 깨끗하고 기능적으로 보이지만 이것이 얼마나 효율적인지 모르겠습니다.
var A = function (p) {
if (p == null) p = this;
p.a1 = 0;
this.a2 = 0;
var a3 = 0;
};
var B = function (p) {
if (p == null) p = this;
p.b1 = new A(this);
this.b2 = new A(this);
var b3 = new A(this);
this b4 = new A();
};
var a = new A ();
var b = new B ();
결과:
a
a1 0
a2 0
b
a1 0
b1
a2 0
b2
a2 0
b4
a1 0
a2 0
실제 예 :
var Point = function (p) {
if (p == null) p = this;
var x = 0;
var y = 0;
p.getPoint = function () { return [x,y]; };
p.setPoint = function (_x,_y) { x = _x; y = _y; };
};
var Dimension = function (p) {
if (p == null) p = this;
var w = 0;
var h = 0;
p.getDimension = function() { return [w,h] };
p.setDimension = function(_w,_h) { w = _w; h = _h };
};
var Rect = function (p) {
if (p == null) p = this;
var dimension = new Dimension(this);
var location = new Point(this);
};
var rect = new Rect ();
rect.setDimension({w:30,h:40});
rect.setPoint({x:50,y:50});
참고URL : https://stackoverflow.com/questions/7486825/javascript-inheritance
'Development Tip' 카테고리의 다른 글
AngularJS의 경우 정의되지 않거나 null (0) | 2020.10.06 |
---|---|
Gulp를 사용하여 동일한 디렉토리 내의 여러 파일 확장자 (0) | 2020.10.06 |
NSOperation 및 NSOperationQueue 작업 스레드 대 기본 스레드 (0) | 2020.10.06 |
JavaScript에서 두 객체 배열의 차이를 얻는 방법 (0) | 2020.10.06 |
테이블의 행을 업데이트하거나 존재하지 않는 경우 INSERT하는 방법은 무엇입니까? (0) | 2020.10.06 |