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

javascript - ECMAScript 6 Class properties underscore prefix - Stack Overflow

programmeradmin8浏览0评论

The Class patterns i've seen around pretty much es to something like this:

class Foo {
    constructor(x, y, z) {
      this._x = x;
      this._y = y;
      this._z = z;
    }

    get x() {
      return this._x;
    }
    set x(value) {
      //I acctually do some stuff here
      this._x = value;
    }

    get y() {
      return this._y;
    }
    set y(value) {
      //I acctually do some stuff here
      this._y = value;
    }

    get z() {
      return this._z;
    }
    set z(value) {
      //I acctually do some stuff here
      this._z = value;
    }
}

console.log(new Foo('x', 'y', 'z')) execution output:

Foo { _x: 'x', _y: 'y', _z: 'z' }

console.log(JSON.stringify(new Foo('x', 'y', 'z'))) execution output:

{"_x":"x","_y":"y","_z":"z"}

Which gives me underscore prefixed fields, and i wasnt aiming for that, how can i get the fields to have no underscore prefix, and yet, having getters and setters triggered by instance.prop interaction.

The Class patterns i've seen around pretty much es to something like this:

class Foo {
    constructor(x, y, z) {
      this._x = x;
      this._y = y;
      this._z = z;
    }

    get x() {
      return this._x;
    }
    set x(value) {
      //I acctually do some stuff here
      this._x = value;
    }

    get y() {
      return this._y;
    }
    set y(value) {
      //I acctually do some stuff here
      this._y = value;
    }

    get z() {
      return this._z;
    }
    set z(value) {
      //I acctually do some stuff here
      this._z = value;
    }
}

console.log(new Foo('x', 'y', 'z')) execution output:

Foo { _x: 'x', _y: 'y', _z: 'z' }

console.log(JSON.stringify(new Foo('x', 'y', 'z'))) execution output:

{"_x":"x","_y":"y","_z":"z"}

Which gives me underscore prefixed fields, and i wasnt aiming for that, how can i get the fields to have no underscore prefix, and yet, having getters and setters triggered by instance.prop interaction.

Share Improve this question edited Nov 3, 2016 at 18:45 vcorrea asked Nov 3, 2016 at 17:09 vcorreavcorrea 1012 silver badges8 bronze badges 5
  • I would say to remove the _ from behind the variables? like constructor(x, y, z) { this.x = x; this.y = y; this.z = z; } – Orelsanpls Commented Nov 3, 2016 at 17:14
  • 1 Yeah, in this example there is zero advantage over just assigning the properties directly and skipping the getter. – loganfsmyth Commented Nov 3, 2016 at 17:52
  • @loganfsmyth Sorry for the poor example, but im acctually using custom setter for properties in my real world application, ill edit my snippet for better prehension. – vcorrea Commented Nov 3, 2016 at 18:42
  • What are you doing with this JSON data after? I'd say generally you shouldn't rely on JSON auto-serialization of a class, if something is a class, you should have a method, either explicitly or via .toJSON for getting a serializable object version of it. – loganfsmyth Commented Nov 3, 2016 at 18:56
  • @loganfsmyth for now, im just persisting the data on mongodb with the native driver (which is saving the properties with underscore), and returning it in exposed JSON API (in this case, i use .toJSON only for classes i want to ommit fields i dont want to end up in front-ends, like passwords). – vcorrea Commented Nov 3, 2016 at 19:04
Add a ment  | 

4 Answers 4

Reset to default 15

You can add a toJSON method to adjust the output of JSON.stringify

class Foo {
    constructor(x, y, z) {
      this._x = x;
      this._y = y;
      this._z = z;
    }

    get x() {
      return this._x;
    }
    set x(value) {
      this._x = value;
    }

    get y() {
      return this._y;
    }
    set y(value) {
      this._y = value;
    }

    get z() {
      return this._z;
    }
    set z(value) {
      this._z = value;
    }

    toJSON() {
      return {
        x: this._x,
        y: this._y,
        z: this._z
      };
    }
}

var foo = new Foo('x', 'y', 'z');
console.log(JSON.stringify(foo));

outputs: "{"x":"x","y":"y","z":"z"}"

If your problem really is only the underscores, then you could try using a naming convention more similar to C#'s properties where the get/set methods are using PascalCase but the member variables use camelCase, like so:

class Foo {
    constructor(x, y, z) {
      this.x = x;
      this.y = y;
      this.z = z;
    }

    get X() {
      return this.x;
    }
    set X(value) {
      this.x = value;
    }

    get Y() {
      return this.y;
    }
    set Y(value) {
      this.y = value;
    }

    get Z() {
      return this.z;
    }
    set Z(value) {
      this.z = value;
    }
}

Ultimately, due to how objects work in ECMAScript 6 there is no way to have both the member variable and get/set methods named 100% the same. In point of fact, this is why using the underscore format is so mon. The underscore tells anyone looking at the code that the property is intended to be "private". In ECMAScript 6, the concept of private members doesn't really exist.

If you want to skip the underscore properties, define them as non-enumerable:

class Foo {
  constructor(x, y, z) {
    this._x = x;
    this._y = y;
    this._z = z;
    Object.defineProperties(this, {
      _x: {enumerable: false},
      _y: {enumerable: false},
      _z: {enumerable: false}
    });
  }
  get x() { return this._x; }
  set x(value) { this._x = value; }
  get y() { return this._y; }
  set y(value) { this._y = value; }
  get z() { return this._z; }
  set z(value) { this._z = value; }
}
console.log(JSON.stringify(new Foo('x', 'y', 'z')))

You can also consider symbols instead of underscored properties:

class Foo {
  constructor(x, y, z) {
    this[Foo.x] = x;
    this[Foo.y] = y;
    this[Foo.z] = z;
  }
  get x() { return this[Foo.x];  }
  set x(value) { this[Foo.x] = value; }
  get y() { return this[Foo.y]; }
  set y(value) { this[Foo.y] = value; }
  get z() { return this[Foo.z]; }
  set z(value) { this[Foo.z] = value; }
}
Foo.x = Symbol('x');
Foo.y = Symbol('y');
Foo.z = Symbol('z');
console.log(JSON.stringify(new Foo('x', 'y', 'z')))

As you said you wanted to avoid using toJSON in every class (but I also think using toJSON is the "right" thing to do).

Javascript let you do weird things, but at least you can control it in an enclosed function scope.

I guess the regex could be refined but I just wanted to show the idea, not pretty but should work.

class Foo {
  constructor(x, y, z) {
    this._x = x;
    this._y = y;
    this._z = z;
  }

  get x() {
    return this._x;
  }
  set x(value) {
    //I acctually do some stuff here
    this._x = value;
  }

  get y() {
    return this._y;
  }
  set y(value) {
    //I acctually do some stuff here
    this._y = value;
  }

  get z() {
    return this._z;
  }
  set z(value) {
    //I acctually do some stuff here
    this._z = value;
  }
}

var originalJSON = JSON;

var foo = new Foo('x', 'y', 'z');

(function () {

  var JSON = {
    stringify: function (obj) {
      var json = originalJSON.stringify(obj);
      return json.replace(/"_+(\w+)":/g, '"$1":');
    },
    parse: function(str) {
      return originalJSON.parse(str.replace(/"(\w+)":/g, '"_$1":'));
    }
  };

  console.log('Weird hack');

  var r = JSON.stringify(foo);    
  console.log('stringify');
  console.log(r);

  console.log('parse');
  console.log(JSON.parse(r));
}).call();

console.log('\nBack to normal');

var r = JSON.stringify(foo);
console.log('stringify');
console.log(r);

console.log('parse');
console.log(JSON.parse(r));

Output:

Weird hack
stringify
{"x":"x","y":"y","z":"z"}
parse
{ _x: 'x', _y: 'y', _z: 'z' }
Back to normal
stringify
{"_x":"x","_y":"y","_z":"z"}
parse
{ _x: 'x', _y: 'y', _z: 'z' }
发布评论

评论列表(0)

  1. 暂无评论