How do I set the default property of an object (object)? - javascript

I need to add new objects to the list (Filters.list)
* Filters.prop - is a default prop
* list items also have prop - list[name].prop (equal default prop)
Chage default Filters.prop -> [not] change item list[name].prop
Where is the mistake?
function Filters() {
this.prop = 'some';
this.list = { };
this.add = function (name) {
this.list[name] = {
prop: this.prop,
};
}
}
let attempt = new Filters();
attempt.add('first');
attempt.prop = 'other';
document.writeln(attempt.prop);
document.writeln(attempt.list['first'].prop);
After run snippet output: other some
But I need: other other
I thought the property would be saved by reference. But this is not so, it does not change. When I change Filters.prop I expected that too it will change list[name].prop

The problem with this is that, as you noticed yourself, the value is passed the way you're doing it instead of the reference.
Thanks to JavaScript get, you can return the value of prop of the surrounding object within a function which behaves like an attribute.
function Filters() {
this.prop = 'some';
this.list = { };
this.add = function(name) {
let self = this;
this.list[name] = {
get prop() {
return self.prop;
},
};
}
}
let attempt = new Filters();
attempt.add('first');
attempt.prop = 'other';
document.writeln(attempt.prop)
document.writeln(attempt.list['first'].prop)
Side note: I use the variable self here because using this.prop within get prop() would reference the wrong object and hence cause a recursion.

Related

Javascript Setter is not a function error

When I tried to log the isCheckedOut setter on the console, I am getting an error testLib.isCheckedOut is not a function
I'm having a hard time figuring out why. Any help would be very great
/* Parent Class */
class Library {
constructor(title) {
this._title = title;
this._isCheckedOut = false;
this._ratings = [];
}
get title() {
return this._title;
}
get isCheckedOut() {
return this._isCheckedOut;
}
set isCheckedOut(value) {
this._isCheckedOut = value;
}
get ratings() {
return this._ratings;
}
getAverageRating() {
}
toggleCheckOutStatus() {
}
addRating() {
}
}
const testLib = new Library;
console.log(testLib.isCheckedOut(true));
Setters obfuscate the fact that they're functions to callers. When you have an object with a setter, to invoke the setter, assign to the property:
someObj.theSetterPropertyName = theArgumentToPassToSetter;
Similarly, to invoke a getter, reference the property as an expression:
someObj.theGetterPropertyName
So, you want:
class Library {
constructor(title) {
this._title = title;
this._isCheckedOut = false;
this._ratings = [];
}
get title() {
return this._title;
}
get isCheckedOut() {
return this._isCheckedOut;
}
set isCheckedOut(value) {
this._isCheckedOut = value;
}
get ratings() {
return this._ratings;
}
}
const testLib = new Library;
testLib.isCheckedOut = true; // invoke setter
console.log(testLib.isCheckedOut); // invoke getter
const testLib = new Library;
console.log(testLib.isCheckedOut = true);
JS setter
The set syntax binds an object property to a function to be called when there is an attempt to set that property.
You can't call setter like function.
a setter can be used to execute a function whenever a specified property
is attempted to be changed
Set a property using a setter:
const testLib = new Library;
testLib.isCheckedOut = true;
console.log(testLib.isCheckedOut);
Calling a function:
const testLib = new Library;
testLib.addRating();
I was having the same issue at this lesson, only used empty brackets instead of .isCheckedOut(true). Got the same error.
console.log(testLib.isCheckedOut(true));
Remove the brackets altogether after the isCheckedOut property and it'll work:
console.log(testLib.isCheckedOut);

JavaScript Class Getter/Setter

is there a way to listen for a property call on a JavaScript Class
for example when i go something like this:
myForm = new Form();
myForm.name = 'Name';
-> when i set the name i dont only want to set the property but i also want to update my Vuex store.
Same thing with get i would like to read from Vuex store.
I knoew there are thins like Proxy but for this i need to wrap my Class with a Proxy object. Not so sure if i like this.
module.exports = new Proxy(new Form({}), {
get (receiver, name) {
console.log('getting property from Vuex Store');
}
});
What i need is something like this:
module.exports = class Form {
//this should be triggered when form.something
get(property) {
return this[property];
}
//this should be triggered when from.something = 'something'
set(property, value) {
return this[property] = value;
}
};
it there a best practice for this?
Javascript supports getters and setters
class Form{
set foo(val){
console.log("setting foo")
this.fooValue = val;
}
get foo(){
console.log("getting foo");
return this.fooValue;
}
}
let frm = new Form();
frm.foo = "bar";
console.log(frm.foo);
You could make this more dynamic by writing a withGetterSetter method which wraps each property of an object with a getter/setter.
var form = {
a: "aValue",
b: "bValue"
}
function withGetterSetter(obj){
var keys = Object.keys(obj);
var result = {};
for(var i=0;i<keys.length;i++){
var key = keys[i];
result[key+"_internal"] = obj[key];
(function(k){
Object.defineProperty(result,k, {
get:function() {
console.log("getting property:",k);
return this[k + "_internal"];
},
set: function(x) {
console.log("setting property:",k);
this[k + "_internal"] = x
}
});
})(key)
}
return result;
}
var setterObj = withGetterSetter(form);
console.log(setterObj.a);
setterObj.a = "updated";
console.log(setterObj.a);
It works by copying each property p to a new object with p_internal and creating a dynamic get/set for the original property name.

Why after "freezing" the document.body object, I can modify its properties?

Object.freeze()
Values cannot be changed for data properties. Accessor properties (getters and setters) work the same (and still give the illusion that you are changing the value). Note that values that are objects can still be modified, unless they are also frozen. As an object, an array can be frozen whereafter its elements cannot be altered. No elements can be added or removed from it as well.
'use strict'
document.body.innerHTML = "Hello#1";
var frozen = Object.freeze(document.body);
document.body = frozen;
console.log("Same frozen body (First):", document.body === frozen)
console.log(document.body['innerHTML']);
// Why I can modify this property?
document.body.innerHTML = "Hello#2";
console.log("Same frozen body (Second):", document.body === frozen);
console.log(document.body['innerHTML']);
This code snippet is using getter and setter (This approach fails)
'use strict'
var myObj = {
a: "Ele#1",
get innerHTML() {
return this.a;
},
set innerHTML(html) {
this.a = html;
}
}
myObj = Object.freeze(myObj);
myObj.innerHTML = "Ele";
console.log(myObj);
Why after "freezing" the body object, I can modify its properties? for example, innerHTML?
Probably I have a lack of information, I don't know!
innerHTML is an accessor property. If you debug document.body to the console and drill down the prototype chain, you can see it's defined in Element as get innerHTML() and set innerHTML().
Here's a barebones example of how you can accomplish the same:
var myObj = (function() {
var html = null;
return {
get innerHTML() { return html; },
set innerHTML(value) { html = value; }
};
})();
Accessor properties (getters and setters) work the same (and still give the illusion that you are changing the value).
innerHTML is indeed an accessor property as you can see below, and in the specification:
function getPropertyDescriptor(object, property) {
while (typeof object === 'object' && object !== null) {
const descriptor = Object.getOwnPropertyDescriptor(object, property)
if (descriptor) {
return descriptor
}
object = Object.getPrototypeOf(object)
}
}
const desc = getPropertyDescriptor(document.body, 'innerHTML')
console.log(desc)
Modifying your simplified example to use a WeakMap instead of a property, you can make it work:
'use strict'
const Element = (() => {
const weakPrivate = new WeakMap()
return class Element {
constructor () {
weakPrivate.set(this, { innerHTML: 'initial' })
}
get innerHTML () {
return weakPrivate.get(this).innerHTML
}
set innerHTML (value) {
return weakPrivate.get(this).innerHTML = value
}
}
})()
let object = new Element()
Object.freeze(object)
console.log(object.innerHTML)
object.innerHTML = 'new value'
console.log(object.innerHTML)

Getter/Setter as function

Is it possible to create property getter/setter as a function?
Standard getter/setter work like following:
class Test {
something: any;
get prop() {
return something;
}
set prop(value) {
something = value;
}
}
let instance = new Test();
instance.prop = 'Foo';
console.log(instance.prop); // = Foo
I would need following:
let instance = new Test();
instance.prop('Bar') = 'Foo'; // access setter as a function of prop
console.log(instance.prop('Bar')); // = Foo
Yes, I know it's unorthodox usage and yes, I know that I could implement this functionality bit differently. I'm just interested if this is possible in JS/TS/ES6.
Update
This is the closest I got:
class Test {
something: any;
prop(area /* my custom symbol type */) {
const obj: any = {};
Object.defineProperty(obj, 'value', {
// get child object of my complex object
get: () => this.something[area];
// replace part of my complex object
set: (value) => {
this.something = {...this.something, [area]: value}
}
});
}
}
let instance = new Test();
instance.prop('Bar').value = 'Foo';
console.log(instance.prop('Bar').value); // = Foo
So in short, I'd like to get rid of value suffix if possible.
As #deceze mentioned in the first comment, it is not possible to assign to function call, so solution in the update is as best as it gets.

Javascript library template

Let say that i need to create a javascript library like so:
;(function(){
var root = this;
var Ctor = function(value) {
this.value = value;
};
var _ = new Ctor(value);
_.doSome = function(value) {
// do some work to the value
// if no value assigned, get the value of the previous method
};
_.doSome2 = function(value) {
// do some work to the value
// if no value assigned, get the value of the previous method
};
_.doSome3 = function(value) {
// do some work to the value
// if no value assigned, get the value of the previous method
};
root._ = _;
}.call(this));
If doSome method work the value of the _ object, and doSome2 and doSome3 too.
But what about chaining the methods like so:
// the doSome2 and doSome3 work with the value of doSome
_.doSome(value).doSome2().doSome3();
// the doSome3 work with the value of doSome2 cuz it has a value
_.doSome(value).doSome2(value).doSome3();
// every method work with the value assigned to it
_.doSome(value).doSome2(value).doSome3(value); // the same as:
_.doSome(value);
_.doSome2(value);
_.doSome3(value);
note: methods can be chained randomly, like:
_.doSome2(value).doSome().doSome3();
Live example: https://jsbin.com/vijehotora/edit?js,console
You could do something like this:
var Ctor = function() {};
Ctor.prototype = {
doSome: function(value) {
if(value) {
this.value = value;
}
return this;
},
doSome2: function(value) {
if(value) {
this.value = value;
}
return this;
}
};
new Ctor().doSome('value1').doSome2('value2').doSome();
Working example

Categories