JavaScript에서 발생하는 기본적인 다중 상속이 필요한 시점에 도달했습니다. (나는 이것이 좋은 아이디어인지 아닌지 논의하기 위해 여기에 있지 않으므로, 그 의견을 친절하게 유지하십시오.)
나는 누군가가 어떤 성공을 거두 었는지, 그리고 어떻게 성공했는지 알고 싶습니다.
그것을 정리하기 위해, 실제로 필요한 것은 둘 이상의 프로토 타입 체인 에서 속성을 상속 할 수있는 객체를 가질 수 있어야합니다 (즉, 각 프로토 타입은 고유 한 적절한 체인을 가질 수 있음). 첫 번째 정의를 위해 체인을 검색하십시오).
이것이 이론적으로 가능한 방법을 설명하기 위해, 2 차 체인을 1 차 체인의 끝에 부착하여 달성 할 수 있지만, 이는 이전 프로토 타입의 모든 인스턴스에 영향을 미치며 이는 내가 원하는 것이 아닙니다.
생각?
답변
프록시 객체 를 사용하여 ECMAScript 6에서 다중 상속을 수행 할 수 있습니다. .
이행
function getDesc (obj, prop) {
var desc = Object.getOwnPropertyDescriptor(obj, prop);
return desc || (obj=Object.getPrototypeOf(obj) ? getDesc(obj, prop) : void 0);
}
function multiInherit (...protos) {
return Object.create(new Proxy(Object.create(null), {
has: (target, prop) => protos.some(obj => prop in obj),
get (target, prop, receiver) {
var obj = protos.find(obj => prop in obj);
return obj ? Reflect.get(obj, prop, receiver) : void 0;
},
set (target, prop, value, receiver) {
var obj = protos.find(obj => prop in obj);
return Reflect.set(obj || Object.create(null), prop, value, receiver);
},
*enumerate (target) { yield* this.ownKeys(target); },
ownKeys(target) {
var hash = Object.create(null);
for(var obj of protos) for(var p in obj) if(!hash[p]) hash[p] = true;
return Object.getOwnPropertyNames(hash);
},
getOwnPropertyDescriptor(target, prop) {
var obj = protos.find(obj => prop in obj);
var desc = obj ? getDesc(obj, prop) : void 0;
if(desc) desc.configurable = true;
return desc;
},
preventExtensions: (target) => false,
defineProperty: (target, prop, desc) => false,
}));
}
설명
프록시 개체는 대상 개체와 일부 트랩으로 구성되어 있으며 기본 작업에 대한 사용자 지정 동작을 정의합니다.
다른 객체로부터 상속받은 객체를 만들 때을 사용 Object.create(obj)
합니다. 그러나이 경우에는 다중 상속을 원하므로obj
기본 작업을 적절한 객체로 리디렉션하는 프록시를 사용합니다.
나는이 함정을 사용한다 :
has
트랩 대한 트랩 인in
연산자 .some
적어도 하나의 프로토 타입에 속성이 포함되어 있는지 확인 하는 데 사용 합니다.get
트랩은 속성 값을 가져 오는 함정이다. 내가 사용find
하는 속성이 포함 된 첫 번째 프로토 타입을 찾아, 난 값을 반환, 또는 적합한 수신기의 게터를 호출합니다. 이에 의해 처리됩니다Reflect.get
. 프로토 타입에 속성이 포함되어 있지 않으면를 반환undefined
합니다.set
트랩은 속성 값을 설정하기위한 함정이다.find
해당 속성이 포함 된 첫 번째 프로토 타입을 찾는 데 사용 하고 해당 수신기에서 해당 setter를 호출합니다. 세터가 없거나 프로토 타입에 특성이 포함되지 않은 경우 해당 수신자에 값이 정의됩니다. 이것은 다음에 의해 처리됩니다Reflect.set
.enumerate
트랩 의 함정for...in
루프 . 첫 번째 프로토 타입과 두 번째 프로토 타입 등에서 열거 가능한 속성을 반복합니다. 속성이 반복되면 다시 반복되지 않도록 해시 테이블에 저장합니다.
경고 :이 트랩은 ES7 초안에서 제거되었으며 브라우저에서 더 이상 사용되지 않습니다.ownKeys
트랩 에 대한 함정입니다Object.getOwnPropertyNames()
. ES7부터for...in
루프는 [[GetPrototypeOf]]를 계속 호출하고 각각의 고유 한 속성을 가져옵니다. 따라서 모든 프로토 타입의 속성을 반복하기 위해이 트랩을 사용하여 열거 가능한 모든 상속 된 속성을 자체 속성처럼 보이게합니다.getOwnPropertyDescriptor
트랩 에 대한 함정입니다Object.getOwnPropertyDescriptor()
.ownKeys
트랩 에서 열거 가능한 모든 속성을 고유 한 속성처럼 보이게 만드는 것만으로는 충분하지 않습니다.for...in
루프는 디스크립터가 열거 가능한지 확인하도록합니다. 따라서find
해당 속성이 포함 된 첫 번째 프로토 타입을 찾고 속성 소유자를 찾을 때까지 프로토 타입 체인을 반복하고 설명자를 반환합니다. 프로토 타입에 속성이 포함되어 있지 않으면를 반환undefined
합니다. 디스크립터는 구성 가능하도록 수정되었습니다. 그렇지 않으면 프록시 불 변형을 깰 수 있습니다.preventExtensions
및defineProperty
트랩은 프록시 타겟을 수정이 조작을 방지하기 위해 포함된다. 그렇지 않으면 프록시 불변량을 깰 수 있습니다.
사용할 수없는 트랩이 더 있습니다.
getPrototypeOf
트랩을 추가 할 수 있지만, 여러 프로토 타입을 반환 할 적절한 방법이 없습니다. 이것은instanceof
작동하지 않습니다. 따라서 대상의 프로토 타입을 얻습니다. 처음에는 null입니다.setPrototypeOf
트랩을 추가하고 프로토 타입을 대체 할 것이다, 객체의 배열을 받아 들일 수 있습니다. 이것은 독자를위한 연습으로 남습니다. 여기서는 대상의 프로토 타입을 수정하도록했습니다. 트랩이 대상을 사용하지 않기 때문에별로 유용하지 않습니다.deleteProperty
트랩은 자신의 속성을 삭제하기위한 함정이다. 프록시는 상속을 나타내므로별로 의미가 없습니다. 어쨌든 속성이 없어야하는 대상에서 삭제를 시도했습니다.isExtensible
트랩은 확장 성을 얻기위한 함정이다. 불변으로 인해 대상과 동일한 확장 성을 반환한다는 점을 고려할 때별로 유용하지 않습니다. 따라서 작업을 대상으로 리디렉션하면 확장 가능합니다.apply
및construct
트랩은 전화 또는 인스턴스에 대한 함정입니다. 대상이 함수 또는 생성자 인 경우에만 유용합니다.
예
// Creating objects
var o1, o2, o3,
obj = multiInherit(o1={a:1}, o2={b:2}, o3={a:3, b:3});
// Checking property existences
'a' in obj; // true (inherited from o1)
'b' in obj; // true (inherited from o2)
'c' in obj; // false (not found)
// Setting properties
obj.c = 3;
// Reading properties
obj.a; // 1 (inherited from o1)
obj.b; // 2 (inherited from o2)
obj.c; // 3 (own property)
obj.d; // undefined (not found)
// The inheritance is "live"
obj.a; // 1 (inherited from o1)
delete o1.a;
obj.a; // 3 (inherited from o3)
// Property enumeration
for(var p in obj) p; // "c", "b", "a"
답변
업데이트 (2019) : 원래 게시물이 오래되었습니다. 이 기사 (현재 인터넷 아카이브 링크, 도메인이 사라 졌으므로 )와 관련 GitHub 라이브러리 는 훌륭한 현대적인 접근 방식입니다.
원본 게시물 :
다중 상속 [편집, 유형의 적절한 상속이 아니라 속성의 상속; Javascript의 mixins]는 일반 객체가 아닌 생성 된 프로토 타입을 사용하는 경우 매우 간단합니다. 상속 할 두 개의 상위 클래스는 다음과 같습니다.
function FoodPrototype() {
this.eat = function () {
console.log("Eating", this.name);
};
}
function Food(name) {
this.name = name;
}
Food.prototype = new FoodPrototype();
function PlantPrototype() {
this.grow = function () {
console.log("Growing", this.name);
};
}
function Plant(name) {
this.name = name;
}
Plant.prototype = new PlantPrototype();
각 경우에 동일한 “이름”멤버를 사용 했으므로 부모가 “이름”을 처리하는 방법에 대해 동의하지 않으면 문제가 될 수 있습니다. 그러나이 경우 호환 가능합니다 (중복, 실제로).
이제 우리는 둘 다로부터 상속받은 클래스가 필요합니다. 상속은 프로토 타입과 객체 생성자에 대해 생성자 함수 (새 키워드를 사용하지 않고)를 호출 하여 수행됩니다 . 먼저 프로토 타입은 상위 프로토 타입에서 상속되어야합니다.
function FoodPlantPrototype() {
FoodPrototype.call(this);
PlantPrototype.call(this);
// plus a function of its own
this.harvest = function () {
console.log("harvest at", this.maturity);
};
}
그리고 생성자는 부모 생성자로부터 상속 받아야합니다 :
function FoodPlant(name, maturity) {
Food.call(this, name);
Plant.call(this, name);
// plus a property of its own
this.maturity = maturity;
}
FoodPlant.prototype = new FoodPlantPrototype();
이제 다양한 인스턴스를 재배, 섭취 및 수확 할 수 있습니다.
var fp1 = new FoodPlant('Radish', 28);
var fp2 = new FoodPlant('Corn', 90);
fp1.grow();
fp2.grow();
fp1.harvest();
fp1.eat();
fp2.harvest();
fp2.eat();
답변
이것은 Object.create
실제 프로토 타입 체인을 만드는 데 사용 됩니다.
function makeChain(chains) {
var c = Object.prototype;
while(chains.length) {
c = Object.create(c);
$.extend(c, chains.pop()); // some function that does mixin
}
return c;
}
예를 들면 다음과 같습니다.
var obj = makeChain([{a:1}, {a: 2, b: 3}, {c: 4}]);
돌아올 것이다 :
a: 1
a: 2
b: 3
c: 4
<Object.prototype stuff>
그래서 obj.a === 1
, obj.b === 3
등
답변
John Resig의 클래스 구조 구현이 마음에 듭니다. http://ejohn.org/blog/simple-javascript-inheritance/
이것은 단순히 다음과 같이 확장 될 수 있습니다 :
Class.extend = function(prop /*, prop, prop, prop */) {
for( var i=1, l=arguments.length; i<l; i++ ){
prop = $.extend( prop, arguments[i] );
}
// same code
}
상속 할 여러 객체를 전달할 수 있습니다. instanceOf
여기서 능력 을 잃을 것이지만 다중 상속을 원한다면 그것은 주어집니다.
위의 다소 복잡한 예는 https://github.com/cwolves/Fetch/blob/master/support/plugins/klass/klass.js에 있습니다.
해당 파일에 일부 불완전한 코드가 있지만 살펴 보려는 경우 다중 상속을 허용합니다.
체인 상속을 원한다면 (다중 상속은 아니지만 대부분의 사람들에게 동일합니다) 클래스와 같이 수행 할 수 있습니다.
var newClass = Class.extend( cls1 ).extend( cls2 ).extend( cls3 )
원래 프로토 타입 체인을 유지하지만 무의미한 코드가 많이 실행됩니다.
답변
다중 상속의 JavaScript 프레임 워크 구현과 혼동하지 마십시오.
당신이해야 할 모든 사용이다 Object.create는 () 의 변화에 확인 후, 지정된 프로토 타입 객체 및 속성에 새로운 객체를 매번 생성 Object.prototype.constructor에게 당신이 인스턴스화 계획이라면 방법의 각 단계를B
합니다. 미래.
상속 인스턴스 속성에 thisA
와 thisB
우리가 사용 Function.prototype.call ()을 각 개체 함수의 끝에서. 프로토 타입 상속에만 관심이있는 경우 선택 사항입니다.
다음 코드를 어딘가에서 실행하고 관찰하십시오 objC
.
function A() {
this.thisA = 4; // objC will contain this property
}
A.prototype.a = 2; // objC will contain this property
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
function B() {
this.thisB = 55; // objC will contain this property
A.call(this);
}
B.prototype.b = 3; // objC will contain this property
C.prototype = Object.create(B.prototype);
C.prototype.constructor = C;
function C() {
this.thisC = 123; // objC will contain this property
B.call(this);
}
C.prototype.c = 2; // objC will contain this property
var objC = new C();
B
에서 프로토 타입을 상속A
C
에서 프로토 타입을 상속B
objC
의 예입니다C
위 단계에 대한 좋은 설명입니다.
답변
나는 자바 스크립트 OOP에 대한 전문가는 아니지만, 올바르게 이해하면 (의사 코드)와 같은 것을 원합니다.
Earth.shape = 'round';
Animal.shape = 'random';
Cat inherit from (Earth, Animal);
Cat.shape = 'random' or 'round' depending on inheritance order;
이 경우 다음과 같은 것을 시도합니다.
var Earth = function(){};
Earth.prototype.shape = 'round';
var Animal = function(){};
Animal.prototype.shape = 'random';
Animal.prototype.head = true;
var Cat = function(){};
MultiInherit(Cat, Earth, Animal);
console.log(new Cat().shape); // yields "round", since I reversed the inheritance order
console.log(new Cat().head); // true
function MultiInherit() {
var c = [].shift.call(arguments),
len = arguments.length
while(len--) {
$.extend(c.prototype, new arguments[len]());
}
}
