最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

class - Javascript es6 override static properties - Stack Overflow

programmeradmin3浏览0评论

Trying out ES6 and tried to create a class with static properties and function for parsing. Then I want to extend the base parser for each different type I am parsing. Not sure if I am doing a anti-pattern but I cannot override static properties.

This is my base parser

class Module {

  static name = 'Default Module'
  static version = {major:10000, minor: 10000}

  static checkVersion({majorVersion = 10000, minorVersion = 10000}) {
    if(this.version.major !== majorVersion || this.version.minor > minorVersion) {
      throw `${this.name} requires version ${this.version.major}.${this.version.minor} got ${majorVersion}.${minorVersion}`;
    }
  }

  static parse(data) {
    try {
      this.checkVersion(data);
      return this.internalParser(data);

    } catch (e) {
      throw e;
    }
  }

  static internalParser(data) {
    throw `${this.name} has no parser implemented`;
  }
}

And then I want to extend like this

class ExtendedModule extends Module {
  static name = 'Extended';
  static version = {major: 1, minor:0}

  static internalParser(data) {
    //Some stuff
  }
}

But when compiling in node with babel I get

true; if ('value' in descriptor) descriptor.writable = true; Object.defineProp
                                                                    ^
TypeError: Cannot redefine property: name
    at Function.defineProperty (native)

Anyone got a clue if this is even possible or just plain wrong?

Trying out ES6 and tried to create a class with static properties and function for parsing. Then I want to extend the base parser for each different type I am parsing. Not sure if I am doing a anti-pattern but I cannot override static properties.

This is my base parser

class Module {

  static name = 'Default Module'
  static version = {major:10000, minor: 10000}

  static checkVersion({majorVersion = 10000, minorVersion = 10000}) {
    if(this.version.major !== majorVersion || this.version.minor > minorVersion) {
      throw `${this.name} requires version ${this.version.major}.${this.version.minor} got ${majorVersion}.${minorVersion}`;
    }
  }

  static parse(data) {
    try {
      this.checkVersion(data);
      return this.internalParser(data);

    } catch (e) {
      throw e;
    }
  }

  static internalParser(data) {
    throw `${this.name} has no parser implemented`;
  }
}

And then I want to extend like this

class ExtendedModule extends Module {
  static name = 'Extended';
  static version = {major: 1, minor:0}

  static internalParser(data) {
    //Some stuff
  }
}

But when compiling in node with babel I get

true; if ('value' in descriptor) descriptor.writable = true; Object.defineProp
                                                                    ^
TypeError: Cannot redefine property: name
    at Function.defineProperty (native)

Anyone got a clue if this is even possible or just plain wrong?

Share Improve this question asked Oct 13, 2015 at 12:44 Johan KvintJohan Kvint 9171 gold badge8 silver badges17 bronze badges 5
  • 3 This is not ES6. ES6 doesn't have class property initializers. – Felix Kling Commented Oct 13, 2015 at 13:31
  • 2 Also, Module.name is "Module" (it's still a named constructor function). You cannot put another .name on it. – Bergi Commented Oct 13, 2015 at 15:19
  • 1 @Bergi Though it might result in issues with frameworks or similar e.g. trying to read the name for the sake of creating some debugging output, you definitely can put another .name on it. That's great about Javascript: you can break things that easily, but you still can do. ;) – Thomas Urban Commented May 28, 2017 at 22:16
  • @cepharum My comment was aimed at the non-writability of the property, not whether one should rename things. – Bergi Commented May 28, 2017 at 22:27
  • @Bergi Your comment was about developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… in context of TOs class Module. This property isn't writable, right, but configurable. And thus it is still possible to "put another .name on it". – Thomas Urban Commented May 29, 2017 at 7:29
Add a comment  | 

2 Answers 2

Reset to default 11

You might try using static getter to achieve the initially intended hierarchy in code:

class Module {
    static get name() { return "Default Module"; }
    static get version() { return {major:10000, minor: 10000}; }

    static parse() {
        console.log( this.name );
    }
}

class ExtendedModule extends Module {
    static get name() { return "Extended"; }
    static get version() { return {major:1, minor: 0}; }
}

ExtendedModule.parse();

Using BabelJS this becomes

"use strict";

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Module = function () {
    function Module() {
        _classCallCheck(this, Module);
    }

    _createClass(Module, null, [{
        key: "parse",
        value: function parse() {
            console.log(this.name);
        }
    }, {
        key: "name",
        get: function get() {
            return "Default Module";
        }
    }, {
        key: "version",
        get: function get() {
            return { major: 10000, minor: 10000 };
        }
    }]);

    return Module;
}();

var ExtendedModule = function (_Module) {
    _inherits(ExtendedModule, _Module);

    function ExtendedModule() {
        _classCallCheck(this, ExtendedModule);

        return _possibleConstructorReturn(this, (ExtendedModule.__proto__ || Object.getPrototypeOf(ExtendedModule)).apply(this, arguments));
    }

    _createClass(ExtendedModule, null, [{
        key: "name",
        get: function get() {
            return "Extended";
        }
    }, {
        key: "version",
        get: function get() {
            return { major: 1, minor: 0 };
        }
    }]);

    return ExtendedModule;
}(Module);

ExtendedModule.parse();

Running code it is displaying

Extended

on JS console.

  • ES5-fiddle: https://jsfiddle.net/dwq698r8/
  • ES6-fiddle: https://jsfiddle.net/yd8bf7am/

Classes are functions (in transpiled code), and when you define static properties, they are attached directly to the class constructor function, so:

class Foo {
    static name = 'foo';
}

is the same as doing

function Foo(){}
Object.defineProperty(Foo, 'name', {
    configurable: true,
    writable: true,
    value: 'foo'
});

If you try doing that in your browser, you will get an error, which is exactly what you are seeing. This is because the function already has a property called name and it is Foo. In ES5, the name property was configurable: false, so what you are trying to do will not work, hence the TypeError: Cannot redefine property: name error and you need to rename your static to something else.

In ES6, name is actually configurable: true so what you are trying to do will work eventually, but browsers need to update themselves first.

The bigger question here is why you need to use a class. If you are using all static variables, you might as well just use a module that exports everything directly without the class, and wrap it. It have a module that exports a creation function that you pass an innerParser method or something. Your current code way over-uses classes.

发布评论

评论列表(0)

  1. 暂无评论