I have two variables that hold objects:
var person1 = {
name: "Joe",
study: false
};
var person2 = {
name: "Tom",
study: true
};
I've created two functions such that after a function is called, it will call both of these functions:
var person1study = function() {
if (person1.study === true){
person.study = false;
}
else {
person1.study = true;
}
};
var person2study = function() {
if (person2.study === true){
person2.study = false;
}
else {
person2.study = true;
}
};
Is there a way that I can combine these who functions into a single function that does that same thing?
Just declare the object as a parameter:
function toggleStudy(person) {
person.study = !person.study;
}
toggleStudy(person1); // invert the "study" flag on the "person1" object
The ! operator evaluates its operand as a boolean value, and returns the opposite value.
var personStudy = function(person) {
person.study = !person.study;
};
like this?
Related
I'm building this Builder pattern and I'd like to merge some of the elements in a list together. But I'd like to do this in a cleaner way. This is what I've come up with so far which works but I'm sure there's a better way to do this.
function FiltersBuilder() {
this.filters = [];
};
FiltersBuilder.prototype.addFilter = function(options) {
var filter = {
'type': 'selector',
'dimension': options.dimension,
'value': options.value
}
this.filters.push(filter);
return this;
}
FiltersBuilder.prototype.addRegexFilter = function(options) {
var filter = {
'type': 'regex',
'dimension': options.dimension,
'pattern': options.value
}
this.filters.push(filter);
return this;
}
FiltersBuilder.prototype.not = function() {
var not = {
'type': 'not'
};
this.filters.push(not);
return this;
}
FiltersBuilder.prototype.getFilters = function() {
var result = [];
this.filters.forEach(function each(filter, index, theFilters) {
if (filter.type === 'not') {
var filterToMerge = theFilters[index + 1];
var mergedFilter = _.merge(filter, {field: filterToMerge});
result.push(mergedFilter);
} else {
if(index > 0) {
notFilter = theFilters[index - 1];
if(notFilter.type === 'not') {
return;
}
}
result.push(filter);
}
});
return result;
}
var filterBuilder = new FiltersBuilder();
filterBuilder.addFilter({
dimension: '_o',
value: 'origin'
});
filterBuilder.not().addRegexFilter({
dimension: 'cTy',
value: 'value'
});
console.log(filterBuilder.getFilters());
If not method is called before adding filter, I would like to merge the not filter with the next element and add that object to the result list. But if not is not called before adding filter then don't do anything just add the filter to result list.
https://jsfiddle.net/9wyqbovu/
Instead of treating not as a filter to be added to the list of filters, and processed when you get the filters, treat it as a modifier--a flag. Maintain a not property on the FiltersBuilder object, initialized to false, which a call to FilterBuilders.not will toggle. On each filter, also add a not property, which is set by the current value of the not flag on FilterBuilders (which is then reset). In other words:
function FiltersBuilder() {
this.filters = [];
this.not = false;
};
FiltersBuilder.prototype.addFilter = function(options) {
var filter = {
'type': 'selector',
'dimension': options.dimension,
'value': options.value,
not: this.not
}
this.not = false;
this.filters.push(filter);
return this;
}
FiltersBuilder.prototype.addRegexFilter = function(options) {
var filter = {
'type': 'regex',
'dimension': options.dimension,
'pattern': options.value,
not: this.not
}
this.not = false;
this.filters.push(filter);
return this;
}
FiltersBuilder.prototype.not = function() {
this.not = !this.not;
}
FiltersBuilder.prototype.getFilters = function() {
return this.filters;
}
var filterBuilder = new FiltersBuilder();
filterBuilder.addFilter({
dimension: '_o',
value: 'origin'
});
filterBuilder.not().addRegexFilter({
dimension: 'cTy',
value: 'value'
});
console.log(filterBuilder.getFilters());
If you would prefer to be able to say filterBuilder.not.addFilter (without the parentheses after not), then define not as a getter, ie
Object.defineProperty(FiltersBuilder.prototype, "not", {
get: function() {
this.not = !this.not;
return this;
}
});
Well, how about if we do iterate it from the right and use unshift instead of push:
getFilters = function(){
return this.filters.reduceRight(function(results, filter){
if(filter.type === 'not'){
//get filter to 'not'
var notFilter = results[0];
Object.assign(notFilter, filter); //Or _.merge
} else {
results.unshift(filter);
}
return results;
}, []);
}
Alternatively, you can use push instead of unshift, grab the last element of the array as the filter to negate (instead of the first one) and reverse the results.
getFilters = function(){
return this.filters.reduceRight(function(results, filter){
if(filter.type === 'not'){
//get filter to 'not'
var notFilter = results[results.length - 1];
Object.assign(notFilter, filter); //Or _.merge
} else {
results.push(filter);
}
return results;
}, []).reverse();
}
The below code (also in Plunker) is adapted from this SO post.
I am trying to implement the same logic, only add the uniqueId function in a special property of the Object.prototype to keep things clear.
The below code works when run using nodejs (also the HTML-ized Plunker example works as well). I.e. the console prints three unique object identifiers: 0, 1 and 2.
However, when the test switch variable is set to 1, console prints 0, 0 and 0.
What should I do to add the uniqueId function in the foo namespace?
function addUniqueId1(){
if (Object.prototype.foo===undefined) {
Object.prototype.foo = {};
console.log('foo "namespace" added');
}
if (Object.prototype.foo.uniqueId===undefined) {
var i = 0;
Object.prototype.foo.uniqueId = function() {
console.log('uniqueId function called');
if (this.___uniqueId === undefined) {
this.___uniqueId = i++;
}
return this.___uniqueId;
};
console.log('function defined');
}
}
function addUniqueId2() {
if (Object.prototype.uniqueId === undefined) {
var i = 0;
Object.prototype.uniqueId = function() {
if (this.___uniqueId === undefined) {
this.___uniqueId = i++;
}
return this.___uniqueId;
};
};
};
var test=2; // if you set this to 1 it stops working
if (test==1)
addUniqueId1();
else
addUniqueId2();
var xs = [{}, {}, {}];
for (var i = 0 ; i < xs.length ; i++) {
if (test==1)
console.log('object id is: ['+xs[i].foo.uniqueId()+']');
else
console.log('object id is: ['+xs[i].uniqueId()+']');
}
You need to define foo with a getter, to allow it to access this for use within the hash of sub-method(s):
function defineFoo() {
if (Object.prototype.foo) return;
var i = 0;
Object.defineProperty(Object.prototype, 'foo', {
get: function() {
var self = this;
return {
uniqueId: function() {
return self.__uniqueId = self.uniqueId || ++i;
}
};
}
});
}
Now
defineFoo();
obj.foo.uniqueId()
If you prefer, an alternative to using self:
Object.defineProperty(Object.prototype, 'foo', {
get: function() {
return {
uniqueId: function() {
return this.__uniqueId = this.uniqueId || ++i;
}.bind(this)
};
}
});
Writing Javascript, I have an object/class with the following attributes:
this.option1Active = null;
this.option2Active = null;
this.option3Active = null;
this.option4Active = null;
I would like to set one of those attributes to true based on the parameter genre
function selectGenre (genre) {
if (genre === 'option1') {
this.option1Active = true;
}
else if (genre === 'option2') {
this.option2Active = true;
}
else if (genre === 'option3') {
this.option3Active = true;
}
else if (genre === 'option4') {
this.option4Active = true;
}
}
Though writing if statements is not a sustainable solution.
I'd like to do something like this:
function selectGenre (genre) {
var options = {
'option1': this.option1Active,
'option2': this.option2Active,
'option3': this.option3Active,
'option4': this.option4Active
};
options[genre] = true;
}
But that only set options[index] to true, not e.g. this.option1Active.
Is there a way to change the reference a key of an object points to?
If not, other ways of refactoring the if statements is greatly appreciated.
You can use a string for the property name to set on this.
var genreOptions = {
'option1': 'option1Active',
'option2': 'option2Active',
'option3': 'option3Active',
'option4': 'option4Active'
};
function selectGenre (genre) {
this[genreOptions[genre]] = true;
}
It seems you can just append "Active" to genre to get the property itself:
function selectGenre (genre)
{
var prop = genre + 'Active';
if (typeof this[prop] != 'undefined') {
this[prop] = true;
}
}
Though, it would be easier if you could use an array as your property instead, i.e. this.optionActive[3] vs. this.option3Active.
Is this what you want ?
var obj = {};
obj.option1Active = null;
obj.option2Active = null;
obj.option3Active = null;
obj.option4Active = null;
var options = {
option1: 'option1Active',
option2: 'option2Active',
option3: 'option3Active',
option4: 'option4Active'
};
function selectGenre(genre) {
obj[options[genre]] = true;
}
console.log(obj);
selectGenre('option2');
console.log(obj);
I am having a lot of trouble writing an object oriented Cat class in Node.js. How can I write a Cat.js class and use it in the following way:
// following 10 lines of code is in another file "app.js" that is outside
// the folder "model"
var Cat = require('./model/Cat.js');
var cat1 = new Cat(12, 'Tom');
cat1.setAge(100);
console.log(cat1.getAge()); // prints out 100 to console
var cat2 = new Cat(100, 'Jerry');
console.log(cat1.equals(cat2)); // prints out false
var sameAsCat1 = new Cat(100, 'Tom');
console.log(cat1.equals(sameAsCat1)); // prints out True
How would you fix the following Cat.js class I have written:
var Cat = function() {
this.fields = {
age: null,
name: null
};
this.fill = function (newFields) {
for(var field in this.fields) {
if(this.fields[field] !== 'undefined') {
this.fields[field] = newFields[field];
}
}
};
this.getAge = function() {
return this.fields['age'];
};
this.getName = function() {
return this.fields['name'];
};
this.setAge = function(newAge) {
this.fields['age'] = newAge;
};
this.equals = function(otherCat) {
if (this.fields['age'] === otherCat.getAge() &&
this.fields['name'] === otherCat.getName()) {
return true;
} else {
return false;
}
};
};
module.exports = function(newFields) {
var instance = new Cat();
instance.fill(newFields);
return instance;
};
If I were to design an object like this, then I would have done like this
function Cat(age, name) { // Accept name and age in the constructor
this.name = name || null;
this.age = age || null;
}
Cat.prototype.getAge = function() {
return this.age;
}
Cat.prototype.setAge = function(age) {
this.age = age;
}
Cat.prototype.getName = function() {
return this.name;
}
Cat.prototype.setName = function(name) {
this.name = name;
}
Cat.prototype.equals = function(otherCat) {
return otherCat.getName() == this.getName()
&& otherCat.getAge() == this.getAge();
}
Cat.prototype.fill = function(newFields) {
for (var field in newFields) {
if (this.hasOwnProperty(field) && newFields.hasOwnProperty(field)) {
if (this[field] !== 'undefined') {
this[field] = newFields[field];
}
}
}
};
module.exports = Cat; // Export the Cat function as it is
And then it can be used like this
var Cat = require("./Cat.js");
var cat1 = new Cat(12, 'Tom');
cat1.setAge(100);
console.log(cat1.getAge()); // 100
var cat2 = new Cat(100, 'Jerry');
console.log(cat1.equals(cat2)); // false
var sameAsCat1 = new Cat(100, 'Tom');
console.log(cat1.equals(sameAsCat1)); // true
var sameAsCat2 = new Cat();
console.log(cat2.equals(sameAsCat2)); // false
sameAsCat2.fill({name: "Jerry", age: 100});
console.log(cat2.equals(sameAsCat2)); // true
I would use a class :
class Cat {
fields = {
age: null,
name: null
};
fill(newFields) {
for(var field in this.fields) {
if(this.fields[field] !== 'undefined') {
this.fields[field] = newFields[field];
}
}
}
getAge() {
return this.fields.age;
}
setAge(newAge) {
this.fields.age = newAge;
}
}
exports.Cat = Cat;
This code is working fine Person.js code here
exports.person=function(age,name)
{
age=age;
name=name;
this.setAge=function(agedata)
{
age=agedata;
}
this.getAge=function()
{
return age;
}
this.setName=function(name)
{
name=name;
}
this.getName=function()
{
return name;
}
};
call object code:
var test=require('./route/person.js');
var person=test.person;
var data=new person(12,'murugan');
data.setAge(13);
console.log(data.getAge());
data.setName('murugan');
console.log(data.getName());
There is error loop hole in answers by #thefourtheye, I will describe those below
Object modelling rules
Create empty new object
Create Filled Object
Only Initial object should be set by machine/code
After Initial object assignment Any changes in your object should happen by user interaction only.
After Initial object assignment Any changes in object by code without user intention is going to add some bugs
Problem Use case :
Eg - So when you create filled object , at that time if any property(somedate) is not having any value then due to below code the default value gets assigned to it(somedate) , which is against object modelling rules.
Bug
Constructor function is given Dual responsibility to create new
& filled object which he mixes up while creating filled object , And
its going to make mistakes.
Means there is some buggy code in your app that is going to set values by it self without users intention
function Cat(age, name, somedate ) { // Accept name and age in the constructor this.name = name || null; this.age = age || null; this.somedate = somedate || new Date(); //there will be lots of usecases like this }
So to solve this Problem i use below JavaScript data model.
So it allows user to create filled object Or new object intentionally only when need any one of them and not messing with each other
class Cat {
constructor(){
this.name = null;
this.age = null;
}
initModel (data) {
this.name = data.name;
this.age = data.age
}
getAge () { return this.age;}
setAge (age) { this.age = age; }
getName () {this.name;}
setName (name) {this.name = name;}
equals (otherCat) {
return otherCat.getName() == this.getName()
&& otherCat.getAge() == this.getAge();
}
fill (newFields) {
for (let field in newFields) {
if (this.hasOwnProperty(field) && newFields.hasOwnProperty(field)) {
if (this[field] !== 'undefined') {
this[field] = newFields[field];
}
}
}
};
}
let cat1 = new Cat();
cat1.initModel({age : 12,name :'Tom'})
cat1.setAge(100);
console.log(cat1.getAge()); // 100
let cat2 = new Cat();
cat2.initModel({age : 100,name : 'Jerry'})
console.log(cat1.equals(cat2)); // false
let sameAsCat1 = new Cat({age : 100,name : 'Tom'});
sameAsCat1.initModel({age : 100,name : 'Tom'})
console.log(cat1.equals(sameAsCat1)); // true
let sameAsCat2 = new Cat();
console.log(cat2.equals(sameAsCat2)); // false
sameAsCat2.fill({name: "Jerry", age: 100});
console.log(cat2.equals(sameAsCat2));
Note :
Constructor is only used For creating new Object
InitModel is only used For creating filled Object
I have this code (JSFiddle)
var OBJ = function(){
var privateVar = 23;
var self = this;
return {
thePrivateVar : function() {
return privateVar;
},
thePrivateVarTimeout : function() {
setTimeout(function() { alert(self.thePrivateVar()); } , 10);
}
}
}();
alert(OBJ.thePrivateVar());
OBJ.thePrivateVarTimeout();
This is an abstraction of a real problem I'm having.
So - I would expect the call to OBJ.thePrivateVarTimeout() to wait 10 and then alert with 23 (which I want it to access through the other exposed method).
However self doesn't seem to be setting correctly. When I am setting self = this it appears that this isn't a reference to the function but a reference to the global object. Why is this?
How can I make the public method thePrivateVarTimeout call the other public method thePrivateVar?
var OBJ = (function(){
var privateVar = 23;
var self = {
thePrivateVar : function() {
return privateVar;
},
thePrivateVarTimeout : function() {
setTimeout(function() { alert(self.thePrivateVar); } , 10);
}
};
return self;
}());
this === global || undefined inside an invoked function. In ES5 it's whatever the global environment is, in ES5 strict it is undefined.
More common patterns would involve using the var that = this as a local value in a function
var obj = (function() {
var obj = {
property: "foobar",
timeout: function _timeout() {
var that = this;
setTimeout(alertData, 10);
function alertData() {
alert(that.property);
}
}
}
return obj;
}());
or using a .bindAll method
var obj = (function() {
var obj = {
alertData: function _alertData() {
alert(this.property);
}
property: "foobar",
timeout: function _timeout() {
setTimeout(this.alertData, 10);
}
}
bindAll(obj)
return obj;
}());
/*
bindAll binds all methods to have their context set to the object
#param Object obj - the object to bind methods on
#param Array methods - optional whitelist of methods to bind
#return Object - the bound object
*/
function bindAll(obj, whitelist) {
var keys = Object.keys(obj).filter(stripNonMethods);
(whitelist || keys).forEach(bindMethod);
function stripNonMethods(name) {
return typeof obj[name] === "function";
}
function bindMethod(name) {
obj[name] = obj[name].bind(obj);
}
return obj;
}