简体   繁体   中英

ES6 circular dependency

This is an issue I run into fairly frequently, and I was hoping to discover the correct way to handle it.

So I have a setup like this:

parent.js :

export default {
  x: 1
}

a.js :

import parent from 'parent.js'
export default parent.extend(a, { title: 'a' })

b.js :

import parent from 'parent.js'
export default parent.extend(b, { title: 'b' })

Cool, now I've got some children. But I decide I would like to have a function in parent.js that checks if an object is an instance of a or b .

So I might do this:

parent.js :

import a from 'a'
import b from 'b'

export default {
  x: 1,
  checkType (obj) {
    if (obj instanceof a) {
      return 'a'
    } else if (obj instanceof b) {
      return 'b'
    }
  }
}

Well now that's a circular dependency. Is there an elegant way to handle this?

Having logic in the parent class that is aware of the subclasses is a serious anti-pattern. Instead, add methods in the subclasses that return the type. for instance, in a.js :

import parent from 'parent.js';
export default parent.extend(a, { title: 'a', checkObj() { return 'a'; }});

If the desired return from checkObj is always the value of the title property, then of course just:

// parent.js
export default {
  x: 1,
  checkObj() { return this.title; }
}

I don't know exactly what extend is doing here. I'm assuming it is some kind of subclassing mechanism.

In general, circular import dependencies, although there are ways to deal with them when truly necessary, are the universe trying to tell you that there is something wrong with the way you've structured your code.

If you're able to use es6 classes, then you can take advantage of the super() call in the constructor. I'll often do something like this:

Parent.js

export default class {
    constructor(options, child) {
        this.child = child;
        this.x = 1;
    }

    checkType() {
        return this.child;
    }
}

A.js

import Parent from './Parent';  

export default class extends Parent {
    constructor(options) {
        super(options, 'a');
    }
}

B.js

import Parent from './Parent';  

export default class extends Parent {
    constructor(options) {
        super(options, 'b');
    }
}

If you don't want to use classes, maybe want a more FP style. You could make parent a function:

parent.js

export default function(child) {
    return {
        x: 1,
        checkType (obj) {
            return child; 
        }
        extend (something) {
            // assuming the returns something as you said
        }
    }
}

a.js

import parent from 'parent.js'
export default parent('a').extend(a, { title: 'a' })

b.js

import parent from 'parent.js'
export default parent('b').extend(b, { title: 'b' })

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