Override default get in javascript class such as __get in php - javascript

I'm building a javascript library and I would like to be able to do exactly like the PHP's __get does.
My library has a attributes property which stores each model's attributes. Now, I am force to get an attribute using a .get method. But I would be able to do it with a getter. Let's says that User extends my model class.
let instance = new User({firstname: 'John', lastname: 'Doe'});
console.log(instance.get('firstname')); // gives me 'John'
I want to be able to do instance.firstname which will call the .get method passing 'firstname' as parameter. In PHP you can do it that way : http://php.net/manual/fr/language.oop5.overloading.php#object.get
Is this something possible?
Thank you all

This is easy using ES 2015 classes:
class Foo {
constructor () {
this._bar = null;
}
get bar () {
doStuff();
return this._bar;
}
set bar (val) {
doOtherStuff();
this._bar = val;
return this;
}
};
var foo = new Foo();
foo.bar = 3; // calls setter function
console.log(foo.bar); // calls getter function
here's the (simplified) output from babel:
var Foo = function () {
function Foo() {
this._bar = null;
}
_createClass(Foo, [{
key: "bar",
get: function get() {
doStuff();
return this._bar;
},
set: function set(val) {
doOtherStuff();
this._bar = val;
return this;
}
}]);
return Foo;
}();
Note that this isn't just for classes, any arbitrary object can have these:
var baz = {
get qux() {
// arbitrary code
},
set qux(val) {
// arbitrary code
}
};
Source.
EDIT
What you want is possible but only in native ES 6 environments, as Proxy cannot be polyfilled.
var getter = function(target, property, proxy) {
console.log(`Getting the ${property} property of the obj.`);
return target[property];
};
var setter = function(target, property, value, proxy) {
console.log(`Setting the ${property} property to ${value}.`);
target[property] = value;
};
var emptyObj = {};
var obj = new Proxy(emptyObj, {
get: getter,
set: setter
});
obj.a = 3; // logs 'Setting the a property to 3'
var foo = obj.a; // logs 'Getting the a property of the obj'

Quite simply assign the properties in a loop:
User = function (attrs) {
for (var name in attrs) {
this[name] = attrs[name];
}
}
User.prototype = {
// further methods
}
Using the ES6 class syntax, - I have to admit I do not see the point of writing things this way:
class User {
constructor (attrs) {
for (var name in attrs) {
this[name] = attrs[name];
}
}
// further methods
}
Remember: the second syntax is exactly what happens with the first one, only with some sugar on top.

Related

Javascript adding setters in different scenarios

I am learning javascript(coming from php) and see there is multiple ways of class creation. Also learned about magic methods like get and set and i am wondering how they can be created in different scenarios (beside doing it when creating class with class keyword). Also i posted way of doing getter and setter in object literal and was wondering is there easier way. Here is code
//-------------------------------------------------------------------
//class
class create{
constructor(param1,param2){
this.name = param1;
this.name2 = param2;
}
fullname(){
console.log('...');
}
set name3(enteredValue){
this._name3 = enteredValue;
}
get name3(){
return this._name3;
}//This is how it is done in class
}
var obj2 = new create('test','test');
//-------------------------------------------------------------------
//object literal
var objLit = {
name: 'asas',
name2: 'assad'
}
Object.defineProperty(objLit, 'name3', {
get : function(){
return this._name3;
},
set : function(value){
this._name3 = value;
}
}); //This is how it is done in obj literal / Is there other way to do it in object?
//-------------------------------------------------------------------
//Factory Function
function createObj(param1, param2){
return{
name1: param1,
name2: param2,
fullName: function(){
console.log(this.name1+' '+this.name2);
}
}
}
var obj3 = createObj('Vukasin','Miljan');
//How to add setter in this scenario?
//-------------------------------------------------------------------
//Constructor function
function createObj2(param1,param2){
this.name1 = param1;
this.name2 = param2;
}
var obj4 = new createObj2('..','..');
//How to add setter in this scenario??
Adding getter/setter in the object:
let objLit = {
name: 'asas',
name2: 'assad',
get name3() {
return this._name3
},
set name3(value) {
this._name3 = value
}
}
In factory function:
function createObj(param1, param2) {
return {
set name1(value) {
param1 = value
},
set name2(value) {
param2 = value
},
get fullName() {
return `${param1} {param2}`
}
}
}

Javascript overriding the property setting functionality

JavaScript is dynamic. Cool !
I have the following constructor function :
function Preferences () {
this._preferences = {}
}
var obj = new Preferences()
I want to achieve something like this:
>>> obj.something = 'value'
>>> this._preferences['something']
'value'
That is setting the property of the obj does not actually set it's own property but that of obj._preferences. That is I want to override the default behavior.
Is it possible ?
EDIT : I want to achieve this for all property names i.e the name of the property to be set is not already known.
Object.defineProperty(Preferences.prototype, 'something', {
get: function(){
return this._preferences.something;
},
set: function(value){
this._preferences.something = value;
}
});
should do it. It defines a property, 'something', using an accessor property instead of a data property, and will call the 'get' and 'set' functions to decide what do so when .something is accessed.
SOLUTION 1
Using Proxy object you can do something like this and handle runtime defined properties
function Preferences() {
this._preferences = {};
var prefProxy = new Proxy(this, {
get: function(target, property) {
return property in target?
target[property]:
target._preferences[property];
}
set: function(target, property, value, receiver) {
if(property in target){
target[property] = value;
} else {
target._preferences[property] = value;
}
}
});
return prefProxy;
};
SOLUTION 2
I can be wrong but i think what you are asking is solved returning _preferences
function Preferences () {
this._preferences = {};
return _preferences;
}
var obj = new Preferences()
SOLUTION 3
Using getter and setter you can redirect the property to _preferences
function Preferences () {
this._preferences = {}
Object.defineProperty(Preferences.prototype, 'something', {
get: function() {
return this._preferences['something'];
},
set: function(value) {
this._preferences['something'] = value;
}
});
}
var obj = new Preferences()

How to clone a prototype with property methods?

I am using the Typed.React library which includes a method to extend one prototype definition with that of another:
function extractPrototype(clazz) {
var proto = {};
for (var key in clazz.prototype) {
proto[key] = clazz.prototype[key];
}
return proto;
}
If the provided class defines property methods, this function has a side effect of executing the get method e.g.
var TestObject = (function () {
function TestObject() {
this.str = "test string";
}
Object.defineProperty(TestObject.prototype, "TestProperty", {
get: function () {
console.log("exec get");
return this.str;
},
set: function (value) {
console.log("exec set");
this.str = value;
},
enumerable: true,
configurable: true
});
return TestObject;
})();
var extracted = extractPrototype(TestObject);
When extactPrototype accesses TestObject.prototype["TestProperty"], it will execute the property get method and print:
exec get
How would I duplicate a prototype with property methods without executing them?
I think you are looking for the new ES6 Object.assign function.
Of course there's a simpler fix to your problem - just don't access and set properties, copy their property descriptors:
function extractPrototype(clazz) {
var proto = {};
for (var key in clazz.prototype) {
Object.defineProperty(proto, key, Object.getOwnPropertyDescriptor(clazz.prototype, key));
}
return proto;
}

JS getters and setters inside a class?

I'd like to create a class in JS that uses native getters and setters. I know I can create getters/setters for objects, like so:
var obj = {
get value(){
return this._value;
},
set value(val){
this._value = val;
}
}
I also know that I can use this.__defineGetter__ inside a class/function, but MDN says that using __defineGetter__() etc is discauraged.
Is there any better way to add getters and setters to js class than:
function class(){
};
class.prototype = {
get value(){
//....
}
?
2019: Hooray for ES6!
class Person {
get name() {
return this._name + '!!!'
}
set name(newValue) {
this._name = newValue
}
constructor(name) {
this._name = name
}
}
const me = new Person('Zach')
console.log(me.name) // Zach!!!
me.name = 'Jacob'
console.log(me.name) // Jacob!!!
// Of course, _name is not actually private.
console.log(me._name) // Jacob
The cleanest way to define properties on a class is via Object.defineProperties. This allows you to define all of your properties in a single, easily readable block. Here's an example:
var MyClass = function() {
this._a = undefined;
this._b = undefined;
};
Object.defineProperties(MyClass.prototype, {
//Create a read-only property
a : {
get : function() {
return this._a;
}
},
//Create a simple read-write property
b : {
get : function() {
return this._b;
},
set : function(value) {
this._b = value;
}
}
});
There are a plethora of other options when defining properties, so be sure to check out the link I posted for more information. It's also important to keep in mind that even the most basic getter/setter property is only as fast as a method call in current browsers, so they can become a bottleneck in performance-intensive situation.
How about this implementation:
function makeObject(obj, name) {
// The property
var value;
// The setter
obj["get" + name] = function() {return value;};
// The getter
obj["set" + name] = function(v) {
value = v;
};
}
To experiment:
var obj = {};
makeObject(obj, "Name");
obj.setName("Lolo");
print(obj.getName());
Of course, you can test name for validity before storing it in value. The test can be supplied as an additional argument to the makeObject function.

How do I compute a variable in JavaScript if and only if it is used?

This is what I'm doing right now.
var foo = function() {
var x = someComplicatedComputationThatMayTakeMoreTime();
this.foo = function() { return x; };
return x;
}
It works but only if foo is called as a function like so
foo();
But what if I want to call it as a normal variable with a value? I could modify the code to be
var foo = function() {
var x = someComplicatedComputationThatMayTakeMoreTime();
this.foo = x;
return x;
}
That would allow me to only call it once as a function and after that as a regular variable. But it's still not what I want. Plus it gets complicated if it accidentally gets called as a function again, returning an error.
Is this even possible in JavaScript?
BTW, this is for a Chrome/Firefox extension, so IE compatibility does not matter.
Ended up using toString because getters don't allow me to redefine the whole attribute, a function must be associated with it. And toString has cleaner syntax.
How about using toString?
var foo = function() {
function someComplicatedComputationThatMayTakeMoreTime() {
//your calculations
}
return {
toString: function() {
return someComplicatedComputationThatMayTakeMoreTime();
}
}
}
More about Object-to-Primitive Conversions in JavaScript
EDIT based on comment. Use a singleton (I think it's called):
myObject.prop = (function(){
function someComplicatedComputationThatMayTakeMoreTime() {
//your calculations
}
return {
toString: function() {
return someComplicatedComputationThatMayTakeMoreTime();
}
}
})()
If only Internet Explorer didn't exist, you could use getters and setters as described by John Resig in this blog article:
John Resig: JavaScript Getters and Setters
... They allow you to bind special functions to an object that look like normal object properties, but actually execute hidden functions instead.
Using a function is your best option for now, however the new JavaScript standard (ECMAScript 5th Ed.) which is being implemented now by all major browser vendors, gives you a method to create accessor properties, where you can define a property with a get and set functions that will be internally called, without worrying to treat this properties as functions, e.g.:
var obj = {};
Object.defineProperty(obj, 'foo', {
get: function () { // getter logic
return 'foo!';
},
set: function (value) {
// setter logic
}
});
obj.foo; // "foo!", no function call
This new standard will take some time to be implemented for all browsers, (the IE9 preview version really disappointed me), and I wouldn't recommend you to use it for production, unless you have total control on the environment where your application will be used.
What I think you want is a lazily instantiated variable, which can be implemented like this.
var myProperty = null;
function getMyProperty() {
return (myProperty = myProperty || builder());
}
This is not practical on the web because IE does not support it, but you can look at
https://developer.mozilla.org/en/defineGetter for examples how to do this.
There are a couple ways to do it, here is one example:
var data = {};
data.__defineGetter__("prop",
(function () {
var value = null;
return function () {
if (null == value) {
value = getYourValueHere();
}
return value;
};
})());
and now you can use it like:
var a = data.prop;
var b = data.prop;
I would recommend a variation on ChaosPandion's answer, but with a closure.
var myProperty = (function () {
var innerProperty = null;
return function() {
return (innerProperty = innerProperty || someComplicatedComputationThatMayTakeMoreTime());
};
})();
and then use myProperty() every time you need to access the variable.
You could define a JavaScript getter. From the Apple JavaScript Coding Guidelines:
myObject.__defineGetter__( "myGetter", function() { return this.myVariable; } );
var someVariable = myObject.myGetter;
See John Resig's post, JavaScript Getters and Setters, and the Defining Getters and Setters page at the Mozilla Developer Centre for more information.
I would use explicit lazy evaluation. Here's my implementation of it based on Scheme's take:
var delay, lazy, force, promise, promiseForced, promiseRunning;
(function () {
var getValue = function () {
return this.value;
};
var RUNNING = {};
var DelayThunk = function (nullaryFunc) {
this.value = nullaryFunc;
};
DelayThunk.prototype.toString = function () {
return "[object Promise]";
};
DelayThunk.prototype.force = function () {
if (promiseRunning (this)) {
throw new Error ("Circular forcing of a promise.");
}
var nullaryFunc = this.value;
this.value = RUNNING;
this.value = nullaryFunc ();
this.force = getValue;
return this.value;
};
var LazyThunk = function (nullaryFunc) {
DelayThunk.call (this, nullaryFunc);
};
LazyThunk.prototype = new DelayThunk (null);
LazyThunk.prototype.constructor = LazyThunk;
LazyThunk.prototype.force = function () {
var result = DelayThunk.prototype.force.call (this);
while (result instanceof LazyThunk) {
result = DelayThunk.prototype.force.call (result);
}
return force (result);
};
delay = function (nullaryFunc) {
return new DelayThunk (nullaryFunc);
};
lazy = function (nullaryFunc) {
return new LazyThunk (nullaryFunc);
};
force = function (expr) {
if (promise (expr)) {
return expr.force ();
}
return expr;
};
promise = function (expr) {
return expr instanceof DelayThunk;
};
promiseForced = function (expr) {
return expr.force === getValue || !promise (expr);
};
promiseRunning = function (expr) {
return expr.value === RUNNING || !promise (expr);
};
}) ();
Example Syntax:
var x = lazy (function () { return expression; });
var y = force (x);
var z = delay (function () { return expression; });
var w = force (z);
Note values are stored once evaluated, so repeated forcing will not do extra computations.
Example usage:
function makeThunk (x, y, z) {
return lazy (function () {
// lots of work done here
});
}
var thunk = makeThunk (arg1, arg2, arg3);
if (condition) {
output (force (thunk));
output (force (thunk)); // no extra work done; no extra side effects either
}
You can use the javascript Proxy class for creating such functionality.
var object = {};
var handler = {
resolvers: {},
get ( target, property, proxy ) {
if ( ! target.hasOwnProperty( property ) && this.resolvers.hasOwnProperty( property ) ) {
// execute the getter for the property;
target[ property ] = this.resolvers[ property ]();
}
return target[ property ];
},
set ( target, property, value, receiver ) {
// if the value is function set as a resolver
if ( typeof value === 'function' ) {
this.resolvers[property] = value;
// otherwise set value to target
} else {
target.property = value;
}
},
has ( target, property, receiver ) {
//true when proxy handler has either a resolver or target has a value;
return this.resolvers.hasOwnProperty( property ) || target.hasOwnProperty( property );
}
};
var lazyObject = new Proxy( object, handler );
Now you can use it like this:
'exampleField' in lazyObject; //returns false
lazyObject.exampleField = function(){ return 'my value' }; // add a resolver function
'exampleField' in lazyObject; //returns true
lazyObject.exampleField; //executes your resolver function and returns 'my value'
This example is to demonstrate the working. You can change after your needs.
Here is a fiddle with a demonstration

Categories

Resources