I am trying to create a flux store for a React app I am building. I am using an object-assign polyfill npm package and Facebook's Flux library.
Initially I was getting the error "Cannot read property '_data' of null' error in the console which was refering to var currIds = this._data.map(function(m){return m.id;});. That method is currently the only one being called directly. I then did console.log(this) which returned "null".
I find this strange. What is going on?
My code:
var Assign = require('object-assign');
var EventEmitterProto = require('events').EventEmitter.prototype;
var CHANGE_EVENT = 'CHANGE';
var StoreMethods = {
init: function() {},
set: function (arr) {
console.log(this);
var currIds = this._data.map(function(m){return m.id;});
arr.filter(function (item){
return currIds.indexOf(item.id) === -1;
}).forEach(this.add.bind(this));
},
add: function(item){
console.log(this);
this._data.push(item);
},
all: function() {
return this._data;
},
get: function(id){
return this._data.filter(function(item){
return item.cid === id;
})[0];
},
addChangeListener: function(fn) {
this.on(CHANGE_EVENT, fn);
},
removeChangeListener: function(fn) {
this.removeListener(CHANGE_EVENT, fn);
},
emitChange: function() {
this.emit(CHANGE_EVENT);
},
bind: function(actionType, actionFn) {
if(this.actions[actionType]){
this.actions[actionType].push(actionFn);
} else {
this.actions[actionType] = [actionFn];
}
}
};
exports.extend = function(methods) {
var store = {
_data: [],
actions: {}
};
Assign(store, EventEmitterProto, StoreMethods, methods);
store.init();
require('../dispatcher').register(function(action){
if(store.actions[action.actionType]){
store.actions[action.actionType].forEach(function(fn){
fn.call(null, action.data);
})
}
});
return store;
};
I can't see where set is called, however your this can be null if the function is invoked through call (see here) or apply, and your first argument is null.
This also happens in your require.register callback:
fn.call(null, action.data) //First parameter is your 'this'.
Related
Working on a performance reviewing tool on wechat mini apps platform (javascript + native hybrid based on wechat app), I am trying to inject codes into its prototypes, for example the wx.request function.
This is how you would use a wx.request function:
wx.request({
url: 'test.php',
data: {
x: '' ,
y: ''
},
header: {
'content-type': 'application/json'
},
success: function(res) {
console.log(res.data)
}
})
So in order to know how long the request has taken without manually writing adding all the anchors, I tried to inject code by:
var owxrequest = wx.request
wx.request = function() {
console.log('test', Date.now())
return owxrequest.apply(owxrequest, arguments)
}
This failed and I got an Cannot set property "prop" of #<Object> which has only a getter error.
So I realized the the object must have been defined similar to:
wx = {
request: get function(){
...
}
...
}
So I tried:
var owxrequest = wx.request
Object.defineProperty(wx, 'request', {
get: function() {
console.log('test', Date.now())
return owxrequest.apply(owxrequest, arguments)
}
})
This failed with an error (request: fail parameter error: parameter.url should be String instead of Undefined). Then I tried:
var owxrequest = wx.request
Object.defineProperty(wx, 'request', {
set: function() {
console.log('test', Date.now())
return owxrequest.apply(owxrequest, arguments)
}
})
This wouldn't throw an error but it also has no effect when calling wx.request()...
You can implement this by re-define the getter. The point is: the re-defined getter should return a function object, as wx.request is a function:
Object.defineProperty(wx, 'request', {
get: function() {
return function() {
//...
};
}
});
Why I get the error: request: fail parameter error: parameter.url should be String instead of Undefined?
You are trying to access the arguments of the getter itself (the arguments of function in get: function(){...}). This arguments is an empty object and it can be verified by console.log() statement. As it is empty, arguments.url is undefined, that's why wx complains about the parameter.
Here is an working example:
let wx = {
get request(){
return function() {
console.log(10);
return 88;
};
}
};
let oldF = wx.request;
Object.defineProperty(wx, 'request', {
get: function() {
return function() {
console.log(new Date());
return oldF.apply(wx, arguments);
};
}
});
console.log(wx.request());
The above code would print:
2017-08-28T06:14:15.583Z // timestamp
10
88
You could just shadowing the request function.
Simple example:
Shadowing the getter:
// original getter latest
let base = {
num: 1,
get latest() {
return this.num;
}
}
console.log(base.latest);
// shadowing getter latest
Object.defineProperty(base, 'latest', {
get: function() {
return this.num + 1;
}
});
console.log(base.latest);
Simple shadowing a object property
// your original object
let base = {
request(){
console.log('request original');
}
};
base.request()
base.request = () => {
console.log('request new implementation');
};
// now shadow the original request implementation
base.request()
I'm fairly new to getters and setters and am looking for a way to listen for changes in an object to store the data immediately, without calling a Save() function everytime a value gets changed. This is how I do it right now:
var myObject = {
Data: {
enabled: true,
show: false
},
Save: function () {
//store myObject.Data to local storage
},
Load: function () {
//load data from local storage and assign it to myObject.Data
},
doSomething: function () {
myObject.Load();
if (myObject.Data.enabled) {
myObject.Data.show = true;
myObject.Save();
}
}
Now I would like to optimize this code so everytime a property in myObject.Data is changed, myObject.Save() is executed. The problem I'm experiencing is that it seems only possible to define a getter for a property that has just one value, but not for a property that is an object itself.
var myObj = {
_Data: {
a: 0,
b: 1,
c: 3
},
set Data (a) {
console.log(a);
}
};
myObj.Data.a = 2;
This obviously doesn't work since myObj.Data is not an object and doesn't have the same properties as myObj._Data.
Thanks in advance for any help.
You are likely interested in the Proxy object.
I used a very simple debounce function callHandler in order to avoid calling the onSet method dozens of times during array modifications. Otherwise, [1, 2, 3].splice(0, 1) would call the set handler once per item in the original array.
'use strict';
var myObject = {
Data: {
a: [1, 2, 3],
b: {c: ['test']}
},
Save: function() {
console.log('Save called');
},
}
function recursiveProxy(target, onSet) {
// For performance reasons, onSet will only be called one millesecond
// after the set handler has last been called.
var timeout;
function callHandler() {
clearTimeout(timeout);
timeout = setTimeout(onSet, 1);
}
var recursiveHandler = {
get: function(target, property) {
// If the property is something that could contain another object,
// we want to proxy it's properties as well.
if (typeof target[property] == 'object' && target[property] != null) {
return new Proxy(target[property], recursiveHandler);
}
return target[property];
},
set: function(target, property, value) {
console.log('Set called - queueing onSet');
callHandler();
target[property] = value;
return true;
}
}
return new Proxy(target, recursiveHandler);
}
myObject.Data = recursiveProxy(myObject.Data, myObject.Save);
myObject.Data.a.splice(0, 1);
myObject.Data.b.c[0] = 'test 2';
I believe you are looking for Defining a getter on existing objects using defineProperty
To append a getter to an existing object later at any time, use
Object.defineProperty().
var o = { a:0 }
Object.defineProperty(o, "b", { get: function () { return this.a + 1; } });
console.log(o.b) // Runs the getter, which yields a + 1 (which is 1)
For e.g:
var Data = {
enable: true,
show: false
};
Object.defineProperty(Data, 'doSomething', {
get: function() {
// get something;
},
set: function(something) {
// set something
}
});
I know this may be a silly question but I'm new to javascript so would appreciate some guidance.
I have the following notificationSocketEventHandler object created:
const notificationSocketEventHandler = Object.create(socketHandlerProto, {
validators: {
created: [],
destroyed: [],
loadedFromSocket: [],
updated: [],
addedto: {
relation: []
},
removedfrom: {
}
},
created: function (data) {
if (this.validateProfileData(data, validators.created)) {} else {}
},
destroyed: function (data) {},
updated: function (data) {},
loadedFromSocket: function (data) {
console.log('Loaded from socket')
console.log(data)
}
})
This event handler is being used to listen for notifications from a socket
so and is set like this
$.globals.socket.on('notifications',notificationSocketEventHandler);
the event prototype is defined as such:
$.globals.socket = {
events: {},
on: function (attr, func) {
if (!attr) return false
this.events[attr] = this.events[attr] || [];
this.events[attr].push(func)
return true
},
remove(attr, func) {
if (!events[attr]) return false
this.events[attr].forEach(function (f, indx) {
if (f === func) {
events[attr].slice(indx, 1)
}
})
},
trigger: function (attr, thisArg, paramArgs) {
if (Array.isArray(attr) && attr.length) {
var obj = this.events[attr[0]]
for (var i = 1; i < attr.length; i++) {
if (!obj) return
obj = obj[attr[i]]
}
if (typeof obj == 'function') obj.apply(thisArg, paramArgs)
if (Array.isArray(obj)) {
obj.forEach(function (c) {
if (typeof c == 'function') c.apply(thisArg, paramArgs)
})
}
return
}
if(this.events[attr]){
console.log(this.events[attr])
this.events[attr].forEach(function (f) {
if (typeof f === 'function')
f.apply(thisArg, paramArgs)
})
}
}
}
The problem I am having is that after the notificationSocketEventHandler object gets passed to the $.globals.socket.on function and ultimately pushed into the events object, the properties of the notificationSocketEventHandler such as created, destroyed,loadedFromSocket which are defined as functions before being passed to the $.globals.socket.on function, suddenly become `undefined' once within the events object, why is this?
Object.create is a little confusing - you can't use a normal object as the second parameter, it has to be an object of 'property descriptors'. To work properly your code would need to be formatted along these lines:
const notificationSocketEventHandler = Object.create(socketHandlerProto, {
'validators': {
value: {
'created': {
value: []
},
'destroyed': {
value: []
},
'loadedFromSocket': {
value: []
}
}
}
});
Unless you iterate over those properties it's going to be tedious. You would be much better off avoiding Object.create and just making an object normally:
const notificationSocketEventHandler = {
created: [],
destroyed: [],
etc...
}
or adding the properties to the constructor's prototype (as an aside, people usually make constructor names begin with an uppercase letter so it's immediately obvious they're constructors - saves you having to add 'Proto' at the end of the name for one thing. Anyway):
var SocketHandler = function {
this.created = [];
this.destroyed = []
etc...
}
const notificationSocketEventHandler = new SocketHandler
or
var SocketHandler = {};
SocketHandler.prototype.created = [];
SocketHandler.prototype.destroyed = [];
etc...
Im trying to seal an object property .
My question is ,here i have given Object.seal(personObject),this particular object is sealed and does not allow to configure or make any extensions in this object,but as i did not mention on personObject_2 it does allow to extend or configure
How can i make it on prototype .I mean like any class of type person should have/respect this seal.Can we achieve such behaviour
"use strict";
var personModule=(function (module) {
var person=function (fname,lname) {
Object.defineProperty(this,'firstName',{
get:function () {
return fname;
}
,set:function (newValue) {
fname=newValue;
},
configurable:true
});
Object.defineProperty(this,'lastName',{
get:function () {
return lname;
}
,set:function (newValue) {
lname=newValue;
},
configurable:true
});
Object.defineProperty(this,'fullName',{
get:function () {
return fname+lname;
},
configurable:true
});
}
module.person=person;
return module;
})(personModule || {});
var personObject=new personModule.person( "Raju","Rani");
console.log(personObject.fullName);
Object.seal(personObject);
//delete personObject.firstName;-->It throws error here
var personObject2=new personModule.person( "Shiva","Kumar");
delete personObject2.firstName;
console.log(personObject2.firstName);
Thanks
Here is Proxy version in case you do not prefer adding Object.seal on constructor
"use strict";
var personModule=(function (module) {
var person=function (fname,lname) {
Object.defineProperty(this,'firstName',{
get:function () {
return fname;
}
,set:function (newValue) {
fname=newValue;
},
configurable:true
});
Object.defineProperty(this,'lastName',{
get:function () {
return lname;
}
,set:function (newValue) {
lname=newValue;
},
configurable:true
});
Object.defineProperty(this,'fullName',{
get:function () {
return fname+lname;
},
configurable:true
});
}
module.person=new Proxy(person, {
construct(target, args){
args.unshift(null);
let ctor = target.bind.apply(target, args);
let result = new ctor();
Object.seal(result);
return result;
}
});
return module;
})(personModule || {});
var personObject=new personModule.person( "Raju","Rani");
console.log(personObject.fullName);
Object.seal(personObject);
//delete personObject.firstName;-->It throws error here
var personObject2=new personModule.person( "Shiva","Kumar");
delete personObject2.firstName;
console.log(personObject2.firstName);
Did you tried - immutable-js
var personObject = new personModule.person("Raju", "Rani");
var sealed = Immutable.Map(personObject);
I'd like to create a Javascript object that can save and load its state (to local storage).
This is the basic pattern I'm using:
var obj = function () {
// private members
//
return {
// public members
load: function () {
this.state = JSON.parse(localStorage.getItem('obj'));
if (this.state === null) {
this.state = {
name: 'foo'
};
}
},
save: function () {
localStorage.setItem('obj', JSON.stringify(this.state));
}
};
}();
// load state
obj.load();
console.log(obj.state.name);
// save state
obj.state.name = 'bar';
obj.save();
But there's one thing that annoys me about this pattern: I have to access the object's persistent properties through the 'state' property.
How can I rewrite this so I can use the object in a more natural way, like:
// load state
obj.load();
console.log(obj.name);
// save state
obj.name = 'bar';
obj.save();
This is a very simple 'state', but the solution has to work for a complex state object with nested objects, arrays etc., so simply adding a 'name' property to my object is not what I'm after.
If you don't care which properties get loaded/saved then you can simply copy all from state into self. For example, after reading into var state (instead of this.state since you don't want state to be a part of this anymore): for(x in state) this[x] = state[x];
similarly, you'd save out: var state = {}; for(x in this) state[x] = this[x]
However, if you want to have a pre-defined list, then I'd recommend: var fields = ['name', 'zip', 'age'];
And then use for(x in fields) this[x] = state[x] to load and for(x in fields) state[x] = this[x]; to save.
Sorry it's a bit pieced together, but I hope you can follow what I mean :)
EDIT: Added full example per OPs request.
An example of a full solution using this technique is as follows:
var obj = function () {
// private members
//
return {
// public members
load: function () {
var state = JSON.parse(localStorage.getItem('obj'));
if(state == null) state = { name: 'foo' };
for(x in state) this[x] = state[x];
},
save: function ()
{
var state = {};
// check if it's a function. This version taken from underscorejs
var isFunction = function(obj) {
return !!(obj && obj.constructor && obj.call && obj.apply);
};
for(x in this)
{
if(isFunction(this[x])) continue; // skip functions
state[x] = this[x];
}
localStorage.setItem('obj', JSON.stringify(state));
}
};
};
You can also accomplish a direct save when a property changes,
by using ES5 getters/setters or by using Watch.js
Watch.js example:
var obj = (function () {
// private members
//
var self = {
// Some properties
name: '',
otherName: '',
// Try to load state or use "foo state"
state: JSON.parse(localStorage.getItem('obj')) || {
name: 'foo'
},
save: function () {
localStorage.setItem('obj', JSON.stringify(this.state));
}
};
// Watch the object and save it to local storage, when a property changes
// (Of course, you don't need to call the save method here...)
watch(self, function(property, value) {
console.log('saving state!');
self.state[property] = value;
self.save();
});
return self;
}());
// Update some properties and see that it is saved to local storage.
obj.name = "Some name";
obj.otherName = "Some other name";
console.log(JSON.parse(localStorage.getItem('obj')));
Example on JsFiddle.
You could make the state internal and surface getters and setters:
var obj = function () {
// private members
var state = {};
return {
// public members
load: function () {
var loadedState = JSON.parse(localStorage.getItem('obj'));
if (loadedState === null) {
state = {
name: 'foo'
};
} else {
state = loadedState;
}
},
save: function () {
localStorage.setItem('obj', JSON.stringify(state));
},
getState: function (key) {
return state[key];
},
setState: function (key, value) {
state[key] = value;
}
};
};
Using jQuery's extend():
var obj = (function () {
return {
load: function () {
var stored = localStorage.getItem("obj");
var state = stored ? JSON.parse(stored) : {
name: 'foo'
};
$.extend(this, state);
},
save: function () {
localStorage.setItem("obj", JSON.stringify(this));
}
};
})();
// load state
obj.load();
console.log(obj);
// save state
obj.name = 'bar';
obj.save();
jsfiddle
All credit to pimvdb.