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
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 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()
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;
}
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.
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