Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript setter override value before setting

Tags:

javascript

What I essentially want to do is this:

Blog.prototype = {
  set content(content) {
    this.content = JSON.parse(content);
  }
}

However, this results in infinite recursion.

I know I can do something like:

  set content(content) {
    this._content = JSON.parse(content);
  },

  get content() {
    return this._content;
  }

However, when I do JSON.stringify(blog), it doesn't include content, but includes _content, which is undesirable.

How can I go about doing this?

like image 728
Snowman Avatar asked Dec 11 '16 20:12

Snowman


2 Answers

Make the "_content" variable non-enumerable.

Blog.prototype = {
  set content(newContent) {
    Object.defineProperty(this, "_content", { 
      value: JSON.parse(newContent), 
      writable: true 
    });
  },
  get content() {
    return this._content;
  }
};

By default, an the "enumerable" flag for an object property is false if not supplied explicitly in the call to defineProperty().

Someday the Symbol type will be universally supported, and it'd be a better choice for this because you can make a guaranteed unique property key that way. If you don't need IE support and can use Symbols:

Blog.prototype = () => {
  const internalContent = Symbol("content key");
  return {
    set content(newContent) {
      this[internalContent] = newContent;
    },
    get content() {
      return this[internalContent];
    }
  };
}();

Symbol-keyed properties are ignored by JSON.stringify() so you don't have to bother with defineProperty(). The nice thing about the Symbol approach is that you don't have to worry about collisions. Every Symbol instance returned from Symbol() is distinct.

like image 179
Pointy Avatar answered Sep 18 '22 06:09

Pointy


Use Set and Get with _content, and implement .toJson() to provide JSON.stringify with content instead of _content.

toJSON() {
  return {
    content: this._content
  }
}

According to MDN .toJSON() role is:

If an object being stringified has a property named toJSON whose value is a function, then the toJSON() method customizes JSON stringification behavior: instead of the object being serialized, the value returned by the toJSON() method when called will be serialized.


Using with a constructor function

function Blog() {}

Blog.prototype = {
  set content(content) {
    this._content = JSON.parse(content);
  },

  get content() {
    return this._content;
  },
  
  toJSON() {
    return {
      content: this._content
    }
  }
};

var blog = new Blog();

blog.content = '{ "a": "5" }';

console.log(blog.content);

console.log(JSON.stringify(blog));

Using with ES6 class

class Blog {
  set content(content) {
    this._content = JSON.parse(content);
  }

  get content() {
    return this._content;
  }
  
  toJSON() {
    return {
      content: this._content
    }
  }
};

const blog = new Blog();

blog.content = '{ "a": "5" }';

console.log(blog.content);

console.log(JSON.stringify(blog));
like image 30
Ori Drori Avatar answered Sep 20 '22 06:09

Ori Drori