简体   繁体   中英

Injecting a new class into the coffeescript inheritance chain

I have three coffeescript classes, set up like this:

class A
class C extends A
class B

so that the prototype chain looks like this:

A -> C
B

and I need the prototype chain to look like this:

A -> B -> C

The catch being that I can't touch the definitions of A and C.

What I'd like to do is make an inject function that can be called like this:

inject B, C

that injects B into C's prototype chain before A, and then set up B's prototype chain to whatever C's was before the injection.

I thought this would be simple, something like

C extends (B extends C.prototype)

But unfortunately, things aren't quite that simple, due to all the prototype/__super__ magic that coffeescript does. Does anyone know how to inject into the prototype chain such that it's basically like you said class C extends B and class B extends A in the first place?

Many thanks.

Clarification: The below code DOES NOT WORK, because the properties fail to be copied over.

class A
  foo: 1
class B
  bar: 2
class C extends A
  baz: 3

B extends A
C extends B

c = new C
console.log c.foo
console.log c.bar
console.log c.baz

[ Update: I originally answered that C extends B; B extends A C extends B; B extends A would work. This does indeed make C instanceof B and B instanceof A become true , but it doesn't copy prototypal properties as desired. So, I've rewritten the answer.]

Let's walk through this:

class A
  foo: 1
class B
  bar: 2
class C extends A
  baz: 3

At this point, C::foo is 1 and C::baz is 3. If we then run

C extends B

that overwrites C 's existing prototype with an instance of B ( child.prototype = ... ), so only C::bar is defined.

This doesn't happen when we use the class X extends Y syntax because properties are attached to X 's prototype only after its prototype is overwritten. So, let's write a wrapper around extends that saves existing prototype properties, then restores them:

inherits = (child, parent) ->
  proto = child::
  child extends parent
  child::[x] = proto[x] for own x of proto when x not of child::
  child

Applying this to our example:

inherits B, A
inherits C, B

console.log new C instanceof B, new B instanceof A  # true, true
console.log B::foo, B::bar, B::baz  # 1, 2, undefined
console.log C::foo, C::bar, C::baz  # 1, 2, 3

If you'd like to learn more about the inner workings of CoffeeScript classes, you might want to check out my book on CoffeeScript , published by the fine folks at PragProg. :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM