const student = {
// data property
firstName: 'Monica',
// accessor property(getter)
get getName() {
return this.firstName;
}
};
// accessing data property
console.log(student.firstName); // Monica
// accessing getter methods
console.log(student.getName); // Monica
// trying to access as a method
console.log(student.getName()); // error
I can't access the getName() method while using () 1st brackets. Why is the last line showing error?
You're using a getter:
the get syntax binds an object property to a function that will be
called when that property is looked up.
So what you have in your code is a getName property (and, for consistency, it should be called name) not a method.
It's the same of having:
Object.defineProperty(student, 'name', {
get() {
return this.firstName;
},
});
If you want to have the a method instead of a property, you should define it as such:
const student = {
// data property
firstName: 'Monica',
// method, not getter
getName() {
return this.firstName;
}
};
But since JS supports getter, you could just have as you wrote (just less confused code, removing the double "get get"):
const student = {
// data property
firstName: 'Monica',
// accessor property(getter)
get name() {
return this.firstName;
}
};
The reason is because it is a getter.
Getters (not to be confused with the getter design pattern in other languages) allow a property to be dynamic and are NOT functions. They are properties that have dynamic behavior. In order to achieve this dynamic behavior the implementation of getters require you to define them as functions.
Getters were introduced to close a capability gap where it was impossible to implement something like .innerHTML in javascript. Previously if you wanted a property that behaves like .innerHTML (that is, its value is dynamic) you would need to write it in C/C++ and create your own javascript interpreter (alternatively you could write a NaCl plugin in C/C++ for Chrome or Firefox, however most browsers these days have deprecated NaCl after they've removed support for Flash so you can't do that anymore).
So TLDR: it's because that's how it works: getters are NOT functions. They are variables that happen to have side-effects.
A getter binds an object property to a function[*]. So it isn't a function type by itself and can't be invoked as such.
However as a function is a valid type in javascript, you can make a getter return a function itself:
const student = {
// data property
firstName: 'Monica',
// accessor property(getter)
get getName() {
return function() {
return 'func result';
}
}
};
// accessing data property
console.log(student.firstName); // Monica
// accessing getter methods
console.log(student.getName); // Monica
// trying to access as a method
console.log(student.getName()); // error
Related
This might be a weird use case with no solution, but I would be happy to hear any answers on how to solve it.
Question:
I have a globally defined proxy:
this.appState = {}
global.globalState = new Proxy(this.appState, {
get: (target, prop, receiver) => {
console.log(this) // #3
return Reflect.get(...arguments)
},
set: (obj, prop, value) => {
console.log(this) // #2
obj[prop] = value
return true
}
})
And somewhere else I have a class:
export default class MyClass {
constructor() {
console.log(this) // #1
global.globalState["greet"] = "hi"
console.log(this.globalState["greet"])
}
}
Is it possible to bind this from the class constructor to be also this in getter and setter of the proxy? So basically, the context of the class constructor needs to be accessed from inside of the proxy getter and setter.
If your question is wether the line
global.globalState["greet"] = "hi"
can be somehow modified to change the this in the Proxies get method, then the answer is no. The arrow function is lexically bound (so its this can't change between different calls), and even if you would use a regular function instead there is still no way to call it by assigning a property, as the function is called by the runtime, and as Proxies are transparent, there is no way to influence that at all (you don't even know that the line calls a setter).
To dynamically change this you could use something like:
let context = null;
// inside the getter
(function thisInjection() {
}).call(context);
however that would require modifying the Proxies code, and in that case replacing this with context would be way easier.
I am using VueJS with typescrpit but this could be in any js framework.
I have componenets with some methods I want to expose globally so they can be used via the browser console. First idea is to attach them to the window object. This could be done from a lifecycle method such us mounted in my case, but I prefered a cleaner easier solution to write and to use, using decorators.
I've tried something like:
mycomponenet.ts :
function makeGlobal() {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
(window as any)[propertyKey] = () => target[propertyKey]();
};
}
Then this decorator will be used easily like :
#makeGlobal()
myMethodToGoGloabl(){
// do stuff ...
}
Till now everything works fine, until the function uses "this" like :
#makeGlobal()
myMethodToGoGloabl(){
this.firstName = "";
}
Then I get a error firstName of undefined. I understood by searching and reading that the decorator is executed before class instantiation, therefor what I exposed (correct me if I m mistaken) is a prototype method and not a method of the instance that why we have no access to this. I've tried to bind(this) but I failed for the same reason that I don't expose globally the method of the instance.
Is there a way to use decorators in my case or they are useless here?
Guess the issue is your decorator. You do not take over the this instance at all...
Also the way you do it, you won't be able to pass parameters.
Suggestion:
function makeGlobal() {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
var currentThis = this;
(window as any)[propertyKey] = () => target[propertyKey].apply(currentThis, args);
};
}
Also keep in mind what Christian says in the comment. It basically can only work for singletons, as it will override the method for multiple instances
Is there any point in repeating this pattern for every property in JavaScript?
class Thing {
get myProp() {
return this._myProp;
}
set myProp(value) {
this._myProp = value;
}
}
I understand that getters/setters can be useful if you're doing additional work in the methods, but recreating the basic functionality here seems like needless repetition. If I instantiate, I can still manipulate the backing property (._myProp) directly, so I feel like I could just as easily leave these out and perform my assignment and access in the more typical, ad-hoc fashion.
I suppose you could argue that defining the interface this way (with the underscore-prefixed property name) signals to users that it's not meant to manipulate the property, but that seems like a flimsy reason for potentially dozens of these.
In compiled languages, it's common for people to do this. This is because in those languages, assigning to a field and invoking a setter may be identical to the programmer, but they compile to two completely different operations.
If I wanted to add a side effect for setting a field in a C# class, for example, and that field was being set directly instead of through a setter? Changing it to a setter would cause some issues. I wouldn't have to rewrite any of the consuming code, but I would have to recompile all of it. This is, of course, a huge problem if your public releases are compiled.
JavaScript is subject to no such considerations, though, so making everything into a getter/setter prematurely is kind of silly. If you see someone doing this, more than likely you're dealing with someone who learned the convention from another language and carried it into JavaScript, without thinking a whole lot about why.
Using an accessor property in the fashion you describe (set and retrieve a "background" data property) is virtually semantically identical to using a data property directly. There are some differences: the accessor property will exist on instance's prototype, rather than on the instance directly (though the instance will have the "background" data property), but this won't really affect anything unless you are doing advanced introspection on your class instances.
The only advantage is ease of modifying the code if you want to introduce more sophisticated accessor behavior in the future. If you forsee a need to add accessor behavior, use this pattern to save yourself time in the future.
Property accessors are useful to provide side effects or change original behaviour:
class Thing {
get myProp() {
console.log('myProp was read');
return this._myProp;
}
set myProp(value) {
if (!value)
throw new Error('myProp cannot be falsy');
this._myProp = value;
}
}
There is no point in myProp getter/setter pure abstraction:
class Thing {
get myProp() {
return this._myProp;
}
set myProp(value) {
this._myProp = value;
}
}
If I instantiate, I can still manipulate the backing property
(._myProp) directly,
If private states are what you are looking for you can still use a weak map.
(function(scope) {
"use strict";
const prop = new WeakMap();
scope.Foo = class {
constructor() {
prop.set(this, {});
Object.seal(this);
}
get bar() {
return prop.get(this)._bar;
}
set bar(value) {
return prop.get(this)._bar = value;
}
}
}(this))
const f = new Foo;
f.bar = "bar";
f._bar = "_bar";
console.log(f.bar);
console.log(f._bar);
get and setters are also useful when implementing MVC, you can trigger events on property change.
(function(scope) {
"use strict";
const prop = new WeakMap();
scope.Foo = class {
constructor() {
prop.set(this, {});
prop.get(this)._target = new EventTarget
Object.seal(this);
}
get bar() {
return prop.get(this)._bar;
}
set bar(value) {
prop.get(this)._bar = value;
prop.get(this)._target.dispatchEvent(new CustomEvent('change', {
detail: value
}));
}
addEventListener(event, listener) {
prop.get(this)._target.addEventListener(event, listener)
}
}
}(this))
const f = new Foo;
f.addEventListener('change', function(event) {
console.log("changed!", event.detail);
});
f.bar = "bar";
We can use this pattern to create hooks when reading a property from an object:
const obj = {};
Object.defineProperty(obj, 'x', {
get: function () {
return require('x');
}
});
I am wondering if there is a way to create a hook when reading any and all properties on an object such as obj?
For example, when code tries to access property 'y' which doesn't exist on obj yet, the hook will still get invoked.
That's the key part: if I knew all of the object's properties ahead-of-time, I could loop through them. But in this case, properties may be added to the object later, or an unanticipated property may get read (end-user will call something unexpected.)
I think you're looking for a Proxy which is a new ECMAScript 2015 feature and allows you to define custom behavior for certain internal methods such as getting properties. Implementing the handler.get trap allows for a certain function to be called whenever a property is accessed:
const obj = {};
const proxyObj = new Proxy(obj, {
get(target, property, receiver) {
console.log('accessed');
}
});
proxyObj.foobar; //A property is accessed and the console is logged to
Here, accessing any property of proxyObj will result in a log in the console. All "trap" means here is that the getting of the property value is intercepted, or trapped, and then you can invoke custom behavior when it happens. target here is the target object (obj), property is the property being accessed and receiver is the proxy. To apply it here, check if property is y:
if(property === 'y') {
...
}
It doesn't matter if the property exists or not, the trap is still invoked. There's a variety of other traps you can set such as setting a property, using the in operator, using the delete operator, etc. You can see them all at MDN.
#Andrew Li beat me to it, but here is a real example, which is my use case:
// async and bluebird libraries are both installed in my local project
let obj = {};
const proxy = new Proxy(obj, {
get(target, property, receiver) {
return require(property);
}
});
const {async, bluebird } = proxy;
console.log('bluebird => ', bluebird);
now I can inject this proxy object anywhere, and myself or the end user can easily access dependencies. The purpose is so that you don't need to pre-load dependencies if you don't actually need them. It also saves a few lines of code here and there.
I am refactoring some JS code and need to access Objects like
Object1.Object2.IsValid();
this is what I have right now.
function _Object1(object) {
this._object1 = object;
this.Object2= new _Object2();
function IsValid() {
// tests here
}
}
function _Object2()
{
function IsValid() {
// tests here but needs to use Object1 property above.
}
}
Only problem being, I am not sure how to access Object1 in Object2 without passing any parameter. Nesting Object2 in Object1, perhaps?
Edit: I am tring to implement OOP in JS, which is like reinventing the wheel, but want to try it for now :)
I will explain the question in terms of OOP:
I have a class _Object1 and it has a method IsValid(). _Object1 also has a property Object2 which is of type _Object2.
Now, _Object2 also has method named IsValid(). But here is a catch, _Object2.IsValid need the value of _Object1 for tests.
For the above code if I do:
var Object1 = new _Object1();
I can simply call Object1.Object2.IsValid() for the result. Isn't it?
Disclaimer: I have been using JS for sometime now, but never had to dabble with things like these.
Give _Object2 what it needs:
function _Object1(object) {
this._object1 = object;
this.Object2= new _Object2(this);
function IsValid() {
// tests here
}
}
function _Object2(parentObject)
{
function IsValid() {
// parentObject refers to the _Object1 that created this object
}
}
I think what you're looking for is impossible, unless you are willing to pass the data in to the object.
Just because your _Object2 instance has been created inside the _Object1 constructor, it does not automatically have any reference to the data of your _Object1 instance. You would have to tell the _Object2 instance about the _Object1 values either in the constructor function or via some other method:
function _Object2(parentObject) { /* ... */ }
// or
_Object2.prototype.setParent = function(parent) { /* ... */}
// or
myObject2.parent = this._object1;