Define getter using defineProperty - javascript

In our application we compress our JavaScript classes using UglifyJS which doesn't support being able to compress syntax like onBlur = (event) => {} as it returns Unexpected token: operator (=).
To solve this problem we have used the following function to define them:
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
And then we use it inside our constructor like so:
class OurClass {
constructor(...args) {
super(...args);
_defineProperty(this, "onBlur", event => {
});
}
}
Which works great! however it doesn't work for defining getters:
static get values() {
return { index: Number }
}
Like so:
_defineProperty(this, 'values', () => {
return { index: Number };
});
The getter is never defined and isn't accessible in the rest of the class in the same way that we were able to define the other methods...
How can we define the getter using this same function with defineProperty?

Object.defineProperty accepts two different formats for its third argument:
data descriptor, which you currently use
accessor descriptor, which allows for defining a getter/setter
So, for instance, you could extend your own _defineProperty function with an optional argument to indicate that a getter/setter is intended:
function _defineProperty(obj, key, value, accessor) {
if (accessor == "getter") {
Object.defineProperty(obj, key, {
get: value,
enumerable: true,
configurable: true,
});
} else if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
// simple demo
class MyClass {
constructor() {
_defineProperty(this, "num", () => 3, "getter");
}
}
console.log(new MyClass().num);
If you want it as a static method, then define it on the class (i.e. on the constructor):
function _defineProperty(obj, key, value, accessor) {
if (accessor == "getter") {
Object.defineProperty(obj, key, {
get: value,
enumerable: true,
configurable: true,
});
} else if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
// simple demo
class MyClass {}
_defineProperty(MyClass, "num", () => 3, "getter");
console.log(MyClass.num);

Related

babel use _defineProperty with class properties

I use babel with preset-env, when I use a class with properties it transform it with _defineProperty
example:
class A {
foo = 'bar'
}
is transformed to:
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
class A {
constructor() {
_defineProperty(this, "foo", 'bar');
}
}
this transformation seems to be caused by #babel/plugin-proposal-class-properties but I do not understand what this transformation provides nor to which new feature of ECMAScript it corresponds.

Property descriptors in ES6 classes

How do I translate the following code to an ES6 class:
var Mergesort = (function() {
function Mergesort(){}
Object.define(Mergesort.prototype,'readOnlyMethod',{
value:function(){/*DO SOMETHING, RETURN SOME VAL*/},
configurable:false,
writable:false,
enumerable:false
});
return Mergesort;
})()
The literal ES6 translation would be
const Mergesort = (() => {
class Mergesort {}
Object.defineProperty(Mergesort.prototype, 'readOnlyMethod', {
value() { /*DO SOMETHING, RETURN SOME VAL*/ },
configurable: false,
writable: false,
enumerable: false
});
return Mergesort;
})();
but assuming you want to define the method as part of the class expression, just change the property descriptor afterwards:
class Mergesort {
readOnlyMethod() { /*DO SOMETHING, RETURN SOME VAL*/ }
}
Object.defineProperty(Mergesort.prototype, 'readOnlyMethod', {configurable:false, writable:false});

Why autobind of core-decorators define THE BIND FUNCTION twice?

This is the source code autobind.js.
I am confused of the getter method.The getter method return a boundFn, but also define a boundFn on "this",why? For the efficiency?
get() {
// ...omit
const boundFn = bind(fn, this);
defineProperty(this, key, {
configurable: true,
writable: true,
// NOT enumerable when it's a bound method
enumerable: false,
value: boundFn
});
return boundFn;
},
I test like this.
function Foo(){}
Object.defineProperty(Foo.prototype, 'test', {
get() {
console.log('123');
Object.defineProperty(this, 'test', {
get(){
console.log('456');
return '456';
}
})
return '123'
}
});
var a = new Foo();
a.test; //FirstTime: 123,
a.test; //and then always: 456,
For the efficiency.It will not always lookup the prototype chain.I think so.

How to define getter and setter properties in a subclass

I have the following code for inheritance:
SubClass= function () {
ParentClass.call(this);
}
SubClass.prototype = Object.create(ParentClass.prototype);
SubClass.prototype.constructor = SubClass;
However, I want to define some properties in the subclass as well:
SubClass.prototype = {
get x() {
return this.newX;
},
set x(val) {
this.newX = val;
alert("X has a value of " + this.newX);
}
}
The problem I'm having is combining the two. In other words, in the first code sample I'm saying:
SubClass.prototype = Object.create(ParentClass.prototype);
But then in the second code sample I'm saying:
SubClass.prototype = {...
How can I achieve both? What is the syntax that would allow me to inherit from a parent class and define properties using the same prototype definition?
Thank you :)
Define your properties by passing a property descriptor to Object.defineProperty:
Object.defineProperty(SubClass.prototype, 'x', {
configurable: true,
get: function () {
return this.newX;
},
set: function (val) {
this.newX = val;
alert("X has a value of " + this.newX);
},
});
It’s also possible to pass an object containing property descriptors to Object.create:
function SubClass() {
ParentClass.call(this);
}
SubClass.prototype = Object.create(ParentClass.prototype, {
constructor: {
configurable: true,
writable: true,
value: SubClass,
},
x: {
configurable: true,
get: function () {
return this.newX;
},
set: function (val) {
this.newX = val;
alert("X has a value of " + this.newX);
},
}
});
ES6 classes are nicer if you can use them:
class SubClass extends ParentClass {
get x() {
return this.newX;
}
set x(val) {
this.newX = val;
alert("X has a value of " + this.newX);
}
}
You can also make this sort of helpful function:
function extend(target, source) {
Object.getOwnPropertyNames(source).forEach(function (name) {
var descriptor = Object.getOwnPropertyDescriptor(source, name);
Object.defineProperty(target, name, descriptor);
});
}
and use it like so:
extend(SubClass.prototype, {
get x() {
return this.newX;
},
set x(val) {
this.newX = val;
alert("X has a value of " + this.newX);
},
});

Access object from javascript property decorator (babel legacy decorator)

Is it any way to access created object from property decorator?
function decorator(target, name, descriptor) {
// somehow get object when it will be created
return {
configurable: true,
enumerable: true,
get: function () {
// ...
},
set: function (value) {
// ...
},
}
}
class MyClass {
#decorator
property = 0;
}
I found one solution but it is not fitting my use case. Babel transforms upper code to:
var _desc, _value, _class, _descriptor;
function _initDefineProp(target, property, descriptor, context) {
if (!descriptor) return;
Object.defineProperty(target, property, {
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
});
}
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object['ke' + 'ys'](descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object['define' + 'Property'](target, property, desc);
desc = null;
}
return desc;
}
function _initializerWarningHelper(descriptor, context) {
throw new Error('Decorating class property failed. Please ensure that transform-class-properties is enabled.');
}
function decorator(target, name, descriptor) {
// somehow get object when it will be created
return {
configurable: true,
enumerable: true,
get: function get() {
// ...
},
set: function set(value) {
// ...
}
};
}
var MyClass = (_class = function MyClass() {
_classCallCheck(this, MyClass);
_initDefineProp(this, "property", _descriptor, this);
}, (_descriptor = _applyDecoratedDescriptor(_class.prototype, "property", [decorator], {
enumerable: true,
initializer: function initializer() {
return 0;
}
})), _class);
So if you return descriptor with initializer field then _initDefineProp will call it with created object. The problem is setter and getter will be missed in this case.
Is it any other way? Keep in mind I have access to decorator only (not class).

Categories

Resources