I was reviewing someones code and he have wrote a syntax which looks like this
export const actions = {
[ACTIONS.SOMEACTION.ATTEMPT.name] ({ commit }, payload) {
return new Promise((resolve, reject) => {
Can someone please explain me what the person is trying to do here? like if someone could explain this syntax?
There are two thing in the code.
Computed Property Names:
[ACTIONS.SOMEACTION.ATTEMPT.name](... this is setting the method for the object whose name will be equal to the value ofACTIONS.SOMEACTION.ATTEMPT.name.
Unpacking fields from objects passed as function parameters
({ commit }, payload)
The line { commit } take out the property commit of the object passed as first parameter to this function.
Eaxample
let str = "func"
let obj = {
[str]({commit},other){
console.log(commit,other);
}
}
obj.func({commit:"the value of commit"},"Other parameter");
This is either inside of an object or a class and declares a method.
[ACTIONS.SOMEACTION.ATTEMPT.name] is a
computed property name, the methods name will be whatever is stored inside ACTIONS.SOMEACTION.ATTEMPT.name.
({ commit }, payload) those are the two parameters the method takes, the first being an object that gets destructured, so the commit property gets taken out of it.
The method then creates and returns a Promise:
return new Promise((resolve, reject) => {
This is using computed property names with destructuring assignment.
Here,
[ACTIONS.SOMEACTION.ATTEMPT.name]
will be converted to the name of the function (due to computer property names). For instance, if ACTIONS.SOMEACTION.ATTEMPT.name was equal to "foo" your result will be somewhat equivlant to:
foo({commit}, payload) {
// ... function body ...
}
which can later be called using .foo(arg1, arg2)
Note: as we are inside an object the function keyword can be omitted.
The {commit} is using destructuring assignment which can be used to "unpack" properties from an object. In this case, commit will be equal to the commit property from arg1. So if you used your function like so:
.foo({commit:10, id:1}, "bar")
Then your function will "unpack" 10 from your first argument object and make that equal to commit.
I am trying to solve.
`let str = "func"
let obj = {
[str]({commit},other){
console.log(commit,other);
}
}
obj.func({commit:"the value of commit"},"Other parameter");`
Related
I need to insert one object inside another, and I'm using this logic:
// Create object store
const store = {}
// Function to create 'Product' Objects
function createProduct (type, name, price) {
return { type, name, price }
}
// Function to add 'Product' Objects inside the 'Store' Object
function addToStore (obj) {
store.obj = obj
return store
}
const strawberry = createProduct ('fruit', 'strawberry', '0.40')
const peach = createProduct ('fruit', 'peach', '0.80')
addToStore(strawberry)
addToStore(peach)
console.log(store) // < { obj: { type: 'fruit', name: 'peach', price: '0.90' } }
How should I write this function so that store.obj be the same obj passed by parameter?
function addToStore (obj) {
store.obj = obj
return store
// What's happening in the run
function addToStore (peach) {
store.obj = peach
return store
// What I need to happen
function addToStore (peach) {
store.peach = peach
return store
If you mean that you want the property name to come from the name of the variable you use when calling addToStore, you can't quite do that, because when addToStore gets called, it just receives the value of that variable (the reference to the object), it doesn't receive any information about the variable itself.
The simple solution is to pass the name as a second argument:
function addToStore(name, obj) {
store[name] = obj;
return store;
}
// ...
addToStore("peach", peach);
Note the use of [] to make the property name come from the name parameter rather than using . with a literal name.
There is a way to make that a bit more automatic, at the (very small) cost of creating a temporary object, by using shorthand property syntax. Pass an object wrapper around the object you want, where the name of the property comes from the variable name, and have addToStore take the object from that wrapper (or perhaps take all of them, if you pass more than one):
function addToStore(objects) {
Object.assign(store, objects); // Will do the loop and assignments for you
return store;
}
// ...
addToStore({peach});
// ^^^^^^^−−−−−−−−−−− creating the object to pass in using shorthand syntax
You probably wouldn't want to do that in code that's going to do it millions of times in an hour because of the overhead, but short of that I wouldn't worry about it until/unless you ran into a performance problem you tracked down to it.
I have been experimenting with getters and setters with the following pattern:
var mytab = {
_tab: undefined,
get: function () {
return this._tab;
},
set: function (tab) {
this._tab = tab;
return tab;
}
}
My question is, given you have to access those methods explicitly, ie:
mytab.get();
mytab.set('thistab');
Why bother having get or set at all? Why not call them whatever you like? ie:
var mytab = {
_tab: undefined,
getthetab: function () {
return this._tab;
},
setthetab: function (tab) {
this._tab = tab;
return tab;
}
}
I may have missed some fundamental principle here, but both these objects behave exactly the same.
I assumed having special 'setters' and 'getters' would allow the object to be modified using it's object name, ie:
var a = mytab;
mytab = 'thistab';
Or even:
var a = mytab();
mytab() = 'thistab';
This is what I expected, and what I wanted, however those instructions give errors, namely that mytab() is not a function.
I would appreciate some clarity on what special significance the set and get object methods actually have.
In your first example, you haven't declared getters/setters. You've created an object with two methods called get and set.
To declare getters/setters, you'll have to choose an arbitrary name, and prefix it with get or set, like:
var mytab = {
_tab: undefined,
get tab() {
return this._tab;
},
set tab(tab) {
this._tab = tab;
return tab;
}
}
In this case, they form a so-called accessor property, that has the chosen name:
console.log(mytab.get) //undefined
console.log(mytab.set) //undefined
mytab.tab = 'foo'
console.log(mytab._tab) //foo
mytab._tab = 'bar'
console.log(mytab.tab) //bar
console.log(Object.getOwnPropertyDescriptor(mytab, 'tab')) /*
{
get: function(){...},
set: function(tab){...},
...
}
*/
However, you cannot overload operators or otherwise define a single getter/setter pair for your objects, that would allow you to assign a value or read a value from the object itself.
You can only define getters/setters for the properties on the object.
So,
var a = mytab
or
mytab = a
cannot be intercepted, and doesn't do what you expect (the first assigns the object itself to another variable (a), while the second reassigns the variable mytab with the value of a without even affecting / interacting with the object).
The following use case can illustrate advantage of using getters and setters
var person = {
firstName: "John",
lastName: "Doe",
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
};
console.log(person.fullName);
Using getter we could use get fullName as if it was a property of person without the need of maintaining separate field.
I'm reading "Mastering React Test-Driven Development", and one of the refactorings the book recommends is extracting a common test into a helper function, by changing this:
it('saves existing first name when submitted', async () => {
expect.hasAssertions();
render(<CustomerForm {...{firstName: 'Ashley'}} onSubmit={(customer) =>
expect(customer.firstName).toEqual('Ashley')} />);
await ReactTestUtils.Simulate.submit(form('customer'));
});
it('saves existing last name when submitted', async () => {
expect.hasAssertions();
render(<CustomerForm {...{lastName: 'Jones'}} onSubmit={(customer) =>
expect(customer.lastName).toEqual('Jones')} />);
await ReactTestUtils.Simulate.submit(form('customer'));
});
...to this:
const itSavesExistingValueWhenSubmitted = (fieldName, fieldValue) => {
it('saves existing value when submitted', async () => {
expect.hasAssertions();
render(<CustomerForm {...{[fieldName]: fieldValue}} onSubmit={(props) =>
expect(props[fieldName]).toEqual(fieldValue)} />);
await ReactTestUtils.Simulate.submit(form('customer'));
});
}
itSavesExistingValueWhenSubmitted('firstName', 'Ashley');
itSavesExistingValueWhenSubmitted('lastName', 'Jones');
My question is about the code snippet {...{[fieldName]: fieldValue}} in the refactored test. I get that the ... is a spread attribute for the subsequent {} object. But why does fieldName need to be wrapped in square brackets? What is the grammar here?
{...{[fieldName]: fieldValue}}
Here [fieldName] is a computed property name. Computed property name is a feature that allows to use a value of a variable as a property name.
So when you pass fieldName as "firstName", "firstName" will be used as the property name, whereas if you omit [], the property name will literally be "fieldName" not the value of fieldName.
This is a feature of ES6. Refer this for more details
The square brackets is used to evaluate the object key in ES6.
You can do this way, for example:
var person = {};
var key = "name";
person[key] = "John";
console.log(person); // should print Object { name="John"}
But if you are using ES6 you can do the following to set the object:
var key = "name";
var person = {[key]:"John"};
console.log(person); // should print Object { name="John"}
I am using Proxy to Proxy an object. The getter and setter work fine like expected. However, the apply method is never called.
var p = new Proxy({}, {
/* getter */
get(target, name) {
return target[name]
},
/* setter */
set(target, name, value) {
target[name] = value
},
/* supposedly called apply */
apply(target,that,arg) {
console.log('apply is called, what to do here?')
}
})
This way, I can assign something to p or return something even if it doesn't exist.
When I for instance let the getter function return this
get(target, name) {
return 'getting ' + name
},
and then console.log(p.flappy) I will get the response "getting flappy" even when it doesn't exist.
So far so good but when I try to call flappy doing p.flapppy() it wil throw an error that flappy is not a function.
This is still somewhat obvious because the getter does not return a function. When I let the getter return a function like this
get(target, name) {
return function() { return 'this is '+name }
},
I can call the property without it having to exist.
console.log(
p.flappy() // this is flappy!
)
So when does apply get called? Not in the snippet I just showed here and also not in this case:
p.foo = function() {
console.log('yay!')
return 'foo!'
}
It does not work to do p.foo() or p.foo.call() or p.foo.apply(), in neither cases apply is called.
The ultimate purpose of this journey is that I want to return a DIFFERENT value depending on whether a property is being read or being called. Like this:
p.someNewProperty // return what the getter function returns
p.anotherProperty() // return something else here because this is a function call
Is this possible?
I know this is question is a year old, but I ran into this as well and I found a way to do what you are trying to do. So this is for future reference, as I didn't find correct solutions elsewhere.
Short version: accessing functions inside an object (or a class) is essentially getting the property of the object that has the function. The trick is to return another Proxy with apply so you can proxy these functions correctly.
Consider the following object:
const myObject = {
a: 'Hello world!',
b: x => x * x
};
Accessing a or b shall both be caught by a Proxy's get, because they are properties of the object. You should catch all get and then filter for functions. Once you have a function, you return a new Proxy that catches this particular function with Proxy.apply.
Then, to let the function execute as intended, inside the Proxy.apply we return a Reflect.apply, which calls the original function with the correct arguments as expected.
You will end up with this code:
const myProxyObject = new Proxy(myObject, {
get(target, propKey, receiver) {
// Calling functions
if (typeof target[propKey] === 'function') {
return new Proxy(target[propKey], {
apply(applyTarget, thisArg, args) {
console.log(`Calling ${thisArg.constructor.name}.${propKey}(${args})`);
return Reflect.apply(applyTarget, thisArg, args);
}
});
}
// Accessing properties
if (target.hasOwnProperty(propKey)) {
console.log(`Get value of ${target.constructor.name}.${propKey}`);
console.log(`Value: ${target[propKey]}`);
}
return target[propKey];
}
});
Demo on jsfiddle
You don't get the result of the function, because that would require you to execute it.
Note: it is possible to use this with classes and it works very nicely. The only caveat is that your Proxy will be catching all internal functions as well. In order to prevent logging dozens of valueOfs, I highly recommend to test if a function is native or not with something like this isNative function
As documented on MDN, the apply proxy method is for proxying a function call on the proxy object itself, not a call on a method of the object.
It only works with functions (as the Proxy target), not regular object instances, but here is how it would work:
var p = new Proxy(function() {}, {
apply: function() {
console.log('apply called');
}
});
p();
The ultimate purpose of this journey is that I want to return a DIFFERENT value depending on whether a property is being read or being called.
It is not possible to directly do what you intend, nor would it really make sense. To call is to read the property.
and after some years...
yes, you can! you can return a DIFFERENT value depending on whether a property is being read or being called!
const authUser = { id: 1 }
const user = new Proxy(function () {}, {
get (target, property) {
return authUser.id
},
apply (target, thisArg, args) {
// some stuff to get user
return { id: args[0] }
}
})
console.log(user.id)
console.log(user(2).id)
or you can use two step proxy.
const authUser = { id: 1 }
const user = new Proxy(function () {}, {
get (target, property) {
return userProxy(authUser.id, property)
},
apply (target, thisArg, args) {
return userProxy(args[0])
}
})
function userProxy(id, property) {
// some stuff to get user
const user = { id }
return property ? user[property] : new Proxy(user, {
get (target, property) {
return user[property]
}
})
}
console.log(user.id)
console.log(user(2).id)
I'm currently using Object.create() to construct objects like so:
const Tab = ({id, windowId}) => Object.assign(Object.create(Tab.prototype), {id, windowId})
Tab.prototype = {
constructor: Tab,
toString: function() {
return `${this.constructor.name} ${JSON.stringify(Object.values(this))}`
}
}
One of the goals is to avoid using the new keyword, so that I can use the constructor function, for example, like this: [{id: 1, windowId: 2}].map(Tab) (same as it's possible with native constructors like String; for example, [1,2,3].map(String) works). The problem is that it's not nice to have to define the constructor property manually, so is there some way to get around it and have the constructor set automatically, like with the new keyword, while still using Object.create()?
Update Fixed version based on answers:
const Tab = function({id, windowId}) {
return Object.assign(Object.create(Tab.prototype), {id, windowId})
}
Object.assign(Tab.prototype, {
toString: function() {
return `${this.constructor.name} ${JSON.stringify(Object.values(this))}`
}
})
The new operator does not create a constructor property. It just calls the [[Construct]] internal method.
Instances don't have any constructor property by default. They only inherit it from their [[Prototype]], which is the prototype of the constructor.
The prototype and constructor properties are created only once, when you create the function.
If you want to be able to call a constructor as a function, you can use
function Constructor() {
if(this instanceof Constructor) {
// Called as a constructor
// ...
} else {
// Called as a function
return new Constructor();
}
}
This also allows you to implement different behaviors for each case, like with String, e.g.
typeof String(); // "string"
typeof new String(); // "object"`.
As Oriol mentioned, the prototype and the prototype.constructor properties are assigned when the function is created. His solution still contains the new keyword though, which you seem to want to avoid.
Arrow functions don't assign the constructor property
Arrow functions however don't have a prototype or prototype.constructor property created automatically and they cannot be instantiated using the new keyword.
If you don't have a specific need to stick with an arrow function, I would recommend using a classic named function expression. Since chrome seems to infer names on anonymous functions, you might not need the name though.
Overriding Tab.prototype overrides the constructor property as well
The other reason why you don't keep the .constructor property, is because you are overriding the entire Tab.prototype when assigning an object to it. Instead, you could just assign the properties in a singular fashion:
const Tab = function Tab({id, windowId}) { return Object.assign(Object.create(Tab.prototype), {id, windowId}) };
Tab.prototype.toString = function() {
return `${this.constructor.name} ${JSON.stringify(Object.values(this))}`
}
};
Or you could use Object.assign to add the extra properties to Tab.prototype:
const Tab = function Tab({id, windowId}) { return Object.assign(Object.create(Tab.prototype), {id, windowId}) };
Object.assign(Tab.prototype, {
toString() {
return `${this.constructor.name} ${JSON.stringify(Object.values(this))}`
}
});
And in case you are writing a publicly accessible function and want to forbid others to use the new operator on your function a well, you can use new.target to stop this from happening:
const Tab = function Tab({id, windowId}) {
if (new.target) {
throw new Error(`${ this.constructor.name } is not a constructor`);
}
return Object.assign(Object.create(Tab.prototype), {id, windowId})
};
// ...