Concatenative inheritance and Functional inheritance. Code inside - javascript

I'm trying to understand the following code that appears in Eric Elliot's medium blog (https://medium.com/javascript-scene/3-different-kinds-of-prototypal-inheritance-es6-edition-32d777fa16c9
)
import Events from 'eventemitter3';
const modelMixin = Object.assign({
attrs: {},
set (name, value) {
this.attrs[name] = value;
this.emit('change', {
prop: name,
value: value
});
},
get (name) {
return this.attrs[name];
}
}, Events.prototype);
const george = { name: 'george' };
const model = Object.assign(george, modelMixin);
model.on('change', data => console.log(data));
model.set('name', 'Sam');
/*
{
prop: 'name',
value: 'Sam'
}
*/
does it make sense compose the model object with the george instance? I mean, after run const model = Object.assign(george, modelMixin);, the model is something like this
attrs:{}
get:function get(name){}
name: 'george'
set:function set(name,value){}
and when model.set('name', 'Sam'); is executed,the name property is added inside attrs attribute not in the root, so model.name always will be george.
Inside the same article, he carries on with the same example of code for a functional inheritance, but now he adds a rawMixin
import Events from 'eventemitter3';
const rawMixin = function () {
const attrs = {};
return Object.assign(this, {
set (name, value) {
attrs[name] = value;
this.emit('change', {
prop: name,
value: value
});
},
get (name) {
return attrs[name];
}
}, Events.prototype);
};
const mixinModel = (target) => rawMixin.call(target);
const george = { name: 'george' };
const model = mixinModel(george);
model.on('change', data => console.log(data));
model.set('name', 'Sam');
He made the above comment
Note in the example above, we have the 'mixinModel()' wrapper around the actual functional mixin, 'rawMixin()'. The reason we need that is because we need to set the value of 'this' inside the function, which we do with 'Function.prototype.call()'. We could skip the wrapper and let callers do that instead, but that would be obnoxious.
Again, I have the same question, because he is adding the name property in the root of the model object not inside the attr attribute.
does it make sense in both cases use the george object with Object.assign()?

Related

Javascript use decorator to change static class field value?

Is it possible for a JS field decorator to change its value?
A simplified use case would be something like this:
const addItem = (newValue) => {
return function (target) {
target.value.push(newValue);
};
};
class Test {
#addItem(4)
static values = [1,2,3];
}
const test = new Test();
console.log(test.constructor.values) // [1,2,3,4]
Using the following experimental decorators:
'#babel/plugin-proposal-decorators',
{
version: '2018-09',
decoratorsBeforeExport: true,
},
End goal is to make a decorator to inject tailwind style sheets into a lit elements static styles. Currently using a mixin for this but just doing this for fun and to learn whats possible with decorators.
Update to Barmars comments
When trying to return a value from the inner function, I end up getting an error:
export const addItem = (value) => {
return function (target) {
return [value];
};
};
Uncaught TypeError: An element descriptor's .kind property must be either "method" or "field", but a decorator created an element descriptor with .kind "undefined"
Looking at the documentation, the variables getting passed to each of these functions doesn't seem to match either.
function logged(value, { kind, name }) {
if (kind === "field") {
return function (initialValue) {
console.log(`initializing ${name} with value ${initialValue}`);
return initialValue;
};
}
}
When running that example, the 2nd parameter to logged() is undefined. "initialValue" also is an object, not the value:
Object { kind: "field", key: "styles", placement: "own", descriptor: {…}, initializer: value(), … }
Nicolo Ribaudo was able to help me over on Babel's discussions. The correct way to do this is to use the initializer function:
const addItem = (newValue) => {
return function (target) {
const { initializer } = target;
target.initializer = function () {
return [
...initializer.call(this),
newValue,
];
};
};
};
class Test {
#addItem(4)
static values = [1,2,3];
}
const test = new Test();
console.log(test.constructor.values) // [1,2,3,4]

How to define a function in javascript reduce function?

I have an object student, it has property id, name, groupName.
allStudents is the array of student objects, I want to turn it to an id to student map, but for each student, I want to generate a new property "label", if the student has groupName, label value is "name + groupName", otherwise it is name. So I write below code, it works:
const idsToStudents = allStudents.reduce((tempMap, student) => {
const getStudentLabel = (student) => {
if (student.groupName) {
return [student.name, `(${student.groupName})`].join(' ');
}
return student.name;
};
const studentLabel = getStudentLabel(student);
return {
...tempMap,
[student.id]: { ...student, label: studentLabel}
};
}, {});
I define getStudentLabel function inside reducer function, is there a better way to do this instead of declare getStudentLabel function again and again in the reducer function? You can ignore what exactly getStudentLabel does, just think it takes each person as parameter and return something based on person, is there a way to define the function only once, but still I can call it for each person in the reducer?
Thank you!
You are passing student as a parameter to the function, so you don't need to declare it inside the reduce. This would work as well:
const getStudentLabel = (student) => {
if (student.groupName) {
return [student.name, `(${student.groupName})`].join(' ');
}
return student.name;
};
const idsToStudents = allStudents.reduce((tempMap, student) => {
const studentLabel = getStudentLabel(student);
return {
...tempMap,
[student.id]: { ...student, label: studentLabel}
};
}, {});
And you can also shorten the code a bit:
const getStudentLabel = ({ name, groupName }) => groupName
? `${name} (${groupName})`
: name;
const idsToStudents = allStudents.reduce((tempMap, student) => ({
...tempMap,
[student.id]: { ...student, label: getStudentLabel(student) }
}), {});
I wouldn't worry about redefining functions inside closures. Any reasonable javascript implementation will optimize that so that minimal extra memory is being used. I wouldn't say its accurate to say that you're "defining" the function more than once - you are only defining it once in your code. The function is being instantiated each time, but this instantiation will take advantage of caches of the static parts of the the function. So your way of doing it is perfectly fine. As Ori Drori mentioned, you don't have to have the student variable in the inner function, but it might be a good idea to anyway, so that you're very explicit about the function's dependencies.
Object.fromEntries can work instead of reduce.
const getStudentLabel = ({name, groupName}) => groupName
? name + ` (${groupName})`
: name;
const idsToStudents = Object.fromEntries(
allStudents.map(student => [student.id, { ...student, label: getStudentLabel(student) }])
);

Assign dynamically nested array of classes

I need to be able to receive data from an external API and map it dynamically to classes. When the data is plain object, a simple Object.assign do the job, but when there's nested objects you need to call Object.assign to all nested objects.
The approach which I used was to create a recursive function, but I stumble in this case where there's a nested array of objects.
Classes
class Organization {
id = 'org1';
admin = new User();
users: User[] = [];
}
class User {
id = 'user1';
name = 'name';
account = new Account();
getFullName() {
return `${this.name} surname`;
}
}
class Account {
id = 'account1';
money = 10;
calculate() {
return 10 * 2;
}
}
Function to initialize a class
function create(instance: object, data: any) {
for (const [key, value] of Object.entries(instance)) {
if (Array.isArray(value)) {
for (const element of data[key]) {
// get the type of the element in array dynamically
const newElement = new User();
create(newElement, element)
value.push(newElement);
}
} else if (typeof value === 'object') {
create(value, data[key]);
}
Object.assign(value, data);
}
}
const orgWithError = Object.assign(new Organization(), { admin: { id: 'admin-external' }});
console.log(orgWithError.admin.getFullName()); // orgWithError.admin.getFullName is not a function
const org = new Organization();
const data = { id: 'org2', admin: { id: 'admin2' }, users: [ { id: 'user-inside' }]}
create(org, data);
// this case works because I manually initialize the user in the create function
// but I need this function to be generic to any class
console.log(org.users[0].getFullName()); // "name surname"
Initially I was trying to first scan the classes and map it and then do the assign, but the problem with the array of object would happen anyway I think.
As far as I understand from your code, what you basically want to do is, given an object, determine, what class it is supposed to represent: Organization, Account or User.
So you need a way to distinguish between different kinds of objects in some way. One option may be to add a type field to the API response, but this will only work if you have access to the API code, which you apparently don't. Another option would be to check if an object has some fields that are unique to the class it represents, like admin for Organization or account for User. But it seems like your API response doesn't always contain all the fields that the class does, so this might also not work.
So why do you need this distinction in the first place? It seems like the only kind of array that your API may send is array of users, so you could just stick to what you have now, anyway there are no other arrays that may show up.
Also a solution that I find more logical is not to depend on Object.assign to just assign all properties somehow by itself, but to do it manually, maybe create a factory function, like I did in the code below. That approach gives you more control, also you can perform some validation in these factory methods, in case you will need it
class Organization {
id = 'org1';
admin = new User();
users: User[] = [];
static fromApiResponse(data: any) {
const org = new Organization()
if(data.id) org.id = data.id
if(data.admin) org.admin = User.fromApiResponse(data.admin)
if(data.users) {
this.users = org.users.map(user => User.fromApiResponse(user))
}
return org
}
}
class User {
id = 'user1';
name = 'name';
account = new Account();
getFullName() {
return `${this.name} surname`;
}
static fromApiResponse(data: any) {
const user = new User()
if(data.id) user.id = data.id
if(data.name) user.name = data.name
if(data.account)
user.account = Account.fromApiResponse(data.account)
return user
}
}
class Account {
id = 'account1';
money = 10;
calculate() {
return 10 * 2;
}
static fromApiResponse(data: any) {
const acc = new Account()
if(data.id) acc.id = data.id
if(data.money) acc.money = data.money
return acc
}
}
const data = { id: 'org2', admin: { id: 'admin2' }, users: [ { id: 'user-inside' }]}
const organization = Organization.fromApiResponse(data)
I can't conceive of a way to do this generically without any configuration. But I can come up with a way to do this using a configuration object that looks like this:
{
org: { _ctor: Organization, admin: 'usr', users: '[usr]' },
usr: { _ctor: User, account: 'acct' },
acct: { _ctor: Account }
}
and a pointer to the root node, 'org'.
The keys of this object are simple handles for your type/subtypes. Each one is mapped to an object that has a _ctor property pointing to a constructor function, and a collection of other properties that are the names of members of your object and matching properties of your input. Those then are references to other handles. For an array, the handle is [surrounded by square brackets].
Here's an implementation of this idea:
const create = (root, config) => (data, {_ctor, ...keys} = config [root]) =>
Object.assign (new _ctor (), Object .fromEntries (Object .entries (data) .map (
([k, v]) =>
k in keys
? [k, /^\[.*\]$/ .test (keys [k])
? v .map (o => create (keys [k] .slice (1, -1), config) (o))
: create (keys [k], config) (v)
]
: [k, v]
)))
class Organization {
constructor () { this.id = 'org1'; this.admin = new User(); this.users = [] }
}
class User {
constructor () { this.id = 'user1'; this.name = 'name'; this.account = new Account() }
getFullName () { return `${this.name} surname`}
}
class Account {
constructor () { this.id = 'account1'; this.money = 10 }
calculate () { return 10 * 2 }
}
const createOrganization = create ('org', {
org: { _ctor: Organization, admin: 'usr', users: '[usr]' },
usr: { _ctor: User, account: 'acct' },
acct: { _ctor: Account }
})
const orgWithoutError = createOrganization ({ admin: { id: 'admin-external' }});
console .log (orgWithoutError .admin .getFullName ()) // has the right properties
const data = { id: 'org2', admin: { id: 'admin2' }, users: [ { id: 'user-inside' }]}
const org = createOrganization (data)
console .log (org .users [0] .getFullName ()) // has the right properties
console .log ([
org .constructor .name,
org .admin .constructor.name, // has the correct hierarchy
org .users [0]. account. constructor .name
] .join (', '))
console .log (org) // entire object is correct
.as-console-wrapper {min-height: 100% !important; top: 0}
The main function, create, receives the name of the root node and such a configuration object. It returns a function which takes a plain JS object and hydrates it into your Object structure. Note that it doesn't require you to pre-construct the objects as does your attempt. All the calling of constructors is done internally to the function.
I'm not much of a Typescript user, and I don't have a clue about how to type such a function, or whether TS is even capable of doing so. (I think there's a reasonable chance that it is not.)
There are many ways that this might be expanded, if needed. We might want to allow for property names that vary between your input structure and the object member name, or we might want to allow other collection types besides arrays. If so, we probably would need a somewhat more sophisticated configuration structure, perhaps something like this:
{
org: { _ctor: Organization, admin: {type: 'usr'}, users: {type: Array, itemType: 'usr'} },
usr: { _ctor: User, account: {type: 'acct', renameTo: 'clientAcct'} },
acct: { _ctor: Account }
}
But that's for another day.
It's not clear whether this approach even comes close to meeting your needs, but it was an interesting problem to consider.

Unit test Vuex getters that depend on other getters

I've manage to test Vuex getters that are isolated from other code. I'm now facing some issues when a getter depends on other getters, see the following example:
getters.js
export const getters = {
getFoo(state) => prefix {
return `${prefix}: ${state.name}`;
},
getFancyNames(state, getters) {
return [
getters.getFoo('foo'),
getters.getFoo('bar')
]
}
}
getters.spec.js
import { getters } = './getters';
const state = {
name: 'stackoverflow'
};
describe('getFoo', () => {
it('return name with prefix', () => {
expect(getters.getFoo(state)('name')).toBe('name: stackoverflow');
});
});
describe('getFancyNames', () => {
// mock getters
const _getters = {
getFoo: getters.getFoo(state)
}
it('returns a collection of fancy names', () => {
expect(getters.getFancyNames(state, _getters)).toEqual([
'foo: stackoverflow',
'bar: stackoverflow'
]);
});
});
When the tested getter depends on other getter that has arguments this means that I've reference the original getter.getFoo on the mock, and this breaks the idea of mocking, since the tests start to have relation with each other. When the getters grow, and the dependency graph has several levels it makes the tests complex.
Maybe this is the way to go, just wanted to check that I'm not missing anything...
I agree with you that referencing the actual collaborator in your mock defeats the purpose of a mock. So instead I would simply directly return whatever you want your collaborator to return.
In your example, instead of doing something like this:
// mock getters
const _getters = {
getFoo: getters.getFoo(state)
}
You would simply put in whatever getters.getFoo(state) would return:
const _getters = {
getFoo: 'foobar'
}
If you have a getter that takes an additional argument you would simply return a function that returns a constant:
const _getters = {
getFoo: x => 'foobar',
}
Since I'm using Jest there is an option in the jest mock function that let's specify the return value when called:
mockReturnValueOnce or mockReturnValue
More information can be found here: https://facebook.github.io/jest/docs/en/mock-functions.html#mock-return-values
Using the same code as in the question this could be solved like this:
const state = {
name: 'stackoverflow'
}
describe('getFancyNames', () => {
const getFoo = jest.fn()
getFoo.mockReturnValueOnce('foo: stackoverflow')
getFoo.mockReturnValueOnce('bar: stackoverflow')
it('returns a collection of fancy names', () => {
expect(getters.getFancyNames(state, { getFoo })).toEqual([
'foo: stackoverflow',
'bar: stackoverflow'
])
})
})
A cleaner way that I have found is to create your own mocked getters object. This only works if the getter uses the unaltered state like the question does.
const state = {
name: 'stackoverflow'
}
describe('getFancyNames', () => {
const mockedGetters = {
...getters, // This can be skipped
getFoo: getters.getFoo(state), // We only overwrite what is needed
};
it('returns a collection of fancy names', () => {
expect(getters.getFancyNames(state, mockedGetters)).toEqual([
'foo: stackoverflow',
'bar: stackoverflow'
])
})
})
Extra
In the case that you do need to call other getter functions just pass the mocked getters objects into another mocked getters object. It sounds worse than is actually is.
getters.py
export const getters = {
getBar(state) = { // new extra hard part!
return state.bar,
},
getFoo(state, getters) => prefix {
return `${prefix}: ${state.name} with some ${getters.getBar}`;
},
getFancyNames(state, getters) {
return [
getters.getFoo('foo'),
getters.getFoo('bar')
]
}
}
const _mockedGetters = {
...getters, // This can be skipped
getFoo: getters.getFoo(state), // We only overwrite what is needed
};
const mockedGetters = {
.._mockedGetters, // Use the mocked object!
getBar: getters.getBar(state, _mockedGetters), // We only overwrite what is needed
};
// continue down the line as needed!

use state instead of outside class variable?

I want to use my data which keep in my state instead of the outside class variable (I mean languages if u look at the code below)
In the getSuggestion I change languages.filter(lang.... to this.state.myState.filter(lang... but it's not work
It seem like this.state.myState can't be reach
The error appear at the line return inputLength === 0 ? [] : this.state.myState.filter(lang => in the getSuggestion
import Autosuggest from 'react-autosuggest';
// Imagine you have a list of languages that you'd like to autosuggest.
const languages = [
{
name: 'C',
year: 1972
},
{
name: 'Elm',
year: 2012
},
];
// Teach Autosuggest how to calculate suggestions for any given input value.
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
return inputLength === 0 ? [] : languages.filter(lang =>
lang.name.toLowerCase().slice(0, inputLength) === inputValue
);
};
// When suggestion is clicked, Autosuggest needs to populate the input
// based on the clicked suggestion. Teach Autosuggest how to calculate the
// input value for every given suggestion.
const getSuggestionValue = suggestion => suggestion.name;
// Use your imagination to render suggestions.
const renderSuggestion = suggestion => (
<div>
{suggestion.name}
</div>
);
class Example extends React.Component {
constructor() {
super();
// Autosuggest is a controlled component.
// This means that you need to provide an input value
// and an onChange handler that updates this value (see below).
// Suggestions also need to be provided to the Autosuggest,
// and they are initially empty because the Autosuggest is closed.
this.state = {
value: '',
suggestions: []
myState: [
{
name: 'C',
year: 1972
},
{
name: 'Elm',
year: 2012
}
] ,
};
}
onChange = (event, { newValue }) => {
this.setState({
value: newValue
});
};
// Autosuggest will call this function every time you need to update suggestions.
// You already implemented this logic above, so just use it.
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value)
});
};
// Autosuggest will call this function every time you need to clear suggestions.
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
};
render() {
const { value, suggestions } = this.state;
// Autosuggest will pass through all these props to the input.
const inputProps = {
placeholder: 'Type a programming language',
value,
onChange: this.onChange
};
// Finally, render it!
return (
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
/>
);
}
}
It looks like you are attempting to access this in a function outside of your class, so your this will not be defined in getSuggestions.
If getSuggestions needs to be defined outside of your class, you'll need to make two changes:
define getSuggestions as a normal function as opposed to an arrow function so that we can set getSuggestions's this object manually.
Use Function.prototype.call() to call getSuggestions with a specified value for this.
I wrote up a few examples to show my point. First, notice in the example below, where getSuggestions is an arrow function and it is called by method of Example class:
const getThis = () => {
console.log(this === window || this === undefined); // this is coerced to window or undefined (Strict Mode)
};
class Example {
onSuggestionsFetchRequested() {
console.log(this); // prints correctly
getThis();
};
}
var x = new Example();
x.onSuggestionsFetchRequested(); // prints true once
In getThis, the this object will either be Window or undefined (Strict Mode).
To make the setup above work, we make the two changes proposed above:
const getThis = function() { // change #1: use function() {} vs. () => {}
console.log(this); // correct value for this
};
class Example {
onSuggestionsFetchRequested() {
console.log(this);
getThis.call(this); // change #2: use call()
};
// NOTE: in your code, define onSuggestionsFetchRequested using arrow function syntax like so:
// onSuggestionsFetchRequested = () => { ... };
// I did not use the arrow syntax above so that this example would run in the browser,
// but you would need to in order to use onSuggestionsFetchRequested in callbacks.
}
var x = new Example();
x.onSuggestionsFetchRequested();
Note: I made a small modification above to make the snippet run in the browser. Namely, I defined onSuggestionsFetchRequest as a class method as opposed to a class property (using arrow function syntax) as you have in your code. For your use case, you'll want to keep onSuggestionsFetchRequest defined using arrow function syntax so that it can be used in callbacks.

Categories