I've the following code which is parse object and and provide getters to it like following and this is working,The problem is that I want to access to this object from different module and avoid the parsing again ,how can I do that without define global var?
var ymlParser = require('yamljs');
function ymlParse(src) {
if (!(this instanceof ymlParse)) return new ymlParse(src);
this.data = ymlParser.parse(src);
}
ymlParse.prototype = {
getA: function () {
return this.data.default_A;
},
getB: function () {
return this.data._B;
}
};
module.exports = ymlParse;
Lets say I want to access to A from module A and B from module B,how can I do that without sending the src again when I call to getB since when I call to getA I already pass the src...
You can use Memoization pattern - http://addyosmani.com/blog/faster-javascript-memoization/. The one issue with implementation in others answers is not hashing the arguments. So you should have something like this:
var ymlParser = require('yamljs');
function ymlParse(src) {
var hash = JSON.stringify(src);
if (!(this instanceof ymlParse)) return new ymlParse(src);
if (this.cache[hash]) {
this.data = this.cache[hash];
} else {
this.data = ymlParser.parse(src);
this.cache[hash] = this.data;
}
}
ymlParse.prototype = {
cache: {},
getA: function () {
return this.data.default_A;
},
getB: function () {
return this.data._B;
}
};
module.exports = ymlParse;
Take a closer look to JSON.stringify method. You must implement hashing algorithm here that will be associate hash with src data as unique identifier. Usually is JSON.stringify but you can use your own.
Or another solution in functional style:
var _ = require('lodash');
var ymlParser = require('yamljs');
function ymlParse(src) {
var result = ymlParser.parse(src);
return {
getA: function() {
return result.default_A;
},
getB: function() {
return result._B;
}
};
}
module.exports = _.memoize(ymlParse);
Usage the same, you just call exported function as usual function.
You can make caching in your ymlParse class (it's designed like class, isn't it?).
Just store src objects and results of parsing. If ymlParse will be executed with cached src just return stored result without parsing.
Try to change your code like this:
var ymlParser = require('yamljs');
function ymlParse(src) {
if (!(this instanceof ymlParse)) return new ymlParse(src);
if (this.cache[src]) {
this.data = this.cache[src];
} else {
this.data = ymlParser.parse(src);
this.cache[src] = this.data;
}
}
ymlParse.prototype = {
cache: {},
getA: function () {
return this.data.default_A;
},
getB: function () {
return this.data._B;
}
};
module.exports = ymlParse;
Notice that I'm not using deep copy of this.data object. If it's not read-only it may cause some problems.
Related
So I know I can add public properties to functions directly like this.
const print = function (string) {
console.log(string);
};
print.uppercase = function (string) {
print(string.toUpperCase());
};
print("apple"); // apple
print.uppercase("apple"); // APPLE
But I always follow this pattern when I make objects.
const object = function () {
let field;
const getField = function () {
return field;
};
const setField = function (_field) {
field = _field;
};
return Object.freeze({
getField,
setField
});
};
Is it possible to make function objects with public properties while sticking to this pattern? Without using this?
const factory = function () {
const generic = function () {};
const specific = function () {};
return Object.freeze({
// DO SOMETHING HERE SO THAT
});
};
// factory() invokes the generic function, and
// factory.specific() invokes the specific function
I think you are looking for
generic.specific = specific;
Object.freeze(generic);
return generic;
or for short,
return Object.freeze(Object.assign(generic, {
specific,
}));
I get potentially invalid usage of this when calling isDataMatchingnamespace how to overcome and how to call isDataMatchingnamespace in a proper way?
function Client() {
var namespace = "default";
this.addnamespaceTodata = function(data) {
data.namespace = namespace;
return data;
};
this.isdataMatchingnamespace = function(data) {
return data.namespace === namespace;
};
this.filterdatasBynamespace = function(datas) {
var result = [];
_.forEach(datas, function(data) {
if (this.isdataMatchingnamespace(data)) { // I get potentially invalid usage of this so how to overcome and how to call isDataMatchingnamespace in a proper way?
result.push(data);
}
});
}
}
module.exports = Client;
That is an invalid usage of this, since this is undefined inside that function.
underscore.js allows you to pass an optional additional argument to forEach to specify what this should be inside the function. If you want it to be the same as this from outside the function, then pass this as the third argument into _.forEach:
_.forEach(datas, function(data) {
if (this.isdataMatchingnamespace(data)) {
result.push(data);
}
}, this); // Added ", this"
there is other way also by storing this value into variable.
let's say var _thisRef = this; define this below var namespace = "default"; and use _thisRef.isdataMatchingnamespace(data) without changing your code
your updated code as follow :
function Client() {
var namespace = "default";
var _thisRef = this;
this.addnamespaceTodata = function(data) {
data.namespace = namespace;
return data;
};
this.isdataMatchingnamespace = function(data) {
return data.namespace === namespace;
};
this.filterdatasBynamespace = function(datas) {
var result = [];
_.forEach(datas, function(data) {
if (_thisRef.isdataMatchingnamespace(data)) { // I get potentially invalid usage of this so how to overcome and how to call isDataMatchingnamespace in a proper way?
result.push(data);
}
});
}
}
module.exports = Client;
Another better way would be to declare a variable at a class level like:
export class SomeComponent {someVarible = []; }
someFunction() { const tempVar = []; _.forEach(datas, function(data) {
tempVar.push(data) // now use this variable to pass or
assign the data
}, this);
this.someVarible = tempVar;
// OR
otherFunction(tempVar); // where you can use your varuiable
}
es6 arrow functions does that for you automatically !
Just change the syntax to arrow function syntax
data => {
your function goes here
}
Let's say I have a function named fna() that does a simple thing such as:
var fna = function(ar) {
console.log("argument: ", ar);
return "return value is argument too: " + ar;
}
fna() is coded by some other developer and I can't access to it. He didn't bother casting any events and when it is called, I have to be aware of it. Hopefully, his method is accessible by window.fna().
I want some additional code to be executed. Let's say, add this console.log
var fna = function(ar) {
console.log("Hola, I am some additional stuff being rewired");
console.log("argument:", ar);
return "return value is argument too: " + ar;
}
And I want this to be executed even when called from fnb() by some other part of the code.
var fnb = function() {
return fna("Bonjour, I am fnb and I call fna");
}
Here is a way I found, using the utils.rewire() method. utils is just some utility belt, and it could be added to your favorite framework as a plugin. Unfortunately, it only works on Firefox.
var utils = utils || {};
// Let's rewire a function. i.e. My.super.method()
utils.rewire = function(functionFullName, callback) {
var rewired = window[functionFullName];
console.log("%s() is being rewired", functionFullName)
window[functionFullName] = function() {
callback();
return rewired.apply(this, arguments);
}
}
Use it like this.
utils.rewire("fna",function(){
console.log("Hola, I am some additional stuffs being rewired");
});
This seems to work such as shown in this jsbin, but (and here is my question:) How do I rewire obja.fna()?
var obja = {
fna = function(ar) {
console.log("argument:", ar);
return "return value is argument too: " + ar;
}
};
I cannot make it work to rewire the some.object.method() method.
Extra bonus question: Is there a more cleaner way to do this? Out-of-the-box clean concise and magic library?
Refactor rewire into a rewireMethod function which acts on any given object:
var utils = utils || {};
utils.rewireMethod = function (obj, functionName, prefunc) {
var original = obj[functionName];
obj[functionName] = function () {
prefunc();
return original.apply(this, arguments);
};
};
Note that rewire can now be written as:
utils.rewire = function (functionName, prefunc) {
utils.rewireMethod(window, functionName, prefunc);
};
Then you just call it as:
utils.rewireMethod(obja, "fna", function () {
console.log("Hola, I am some additional stuff being rewired");
});
Note that nothing special is required if you have a method like window.ideeli.Search.init(). In that case, the object is window.ideeli.Search, and the method name is init:
utils.rewireMethod(window.ideeli.Search, "init", function () {
console.log("Oh yeah, nested objects.");
});
Add a parameter to rewire that is the object containing the function. If it's a global function, pass in window.
var utils = utils || {};
// let's rewire a function. i.e. My.super.method()
utils.rewire = function(object, functionName, callback) {
var rewired = object[functionName];
console.log("%s() is being rewired", functionName)
object[functionName] = function() {
callback();
return rewired.apply(this, arguments);
}
}
utils.rewire(some.object, "method", function(){} );
You can simply use a closure to create a generic hook function that allows you to specify another function to be called immediately before or after the original function:
function hookFunction(fn, preFn, postFn) {
function hook() {
var retVal;
if (preFn) {
preFn.apply(this, arguments);
}
retVal = fn.apply(this, arguments);
if (postFn) {
postFn.apply(this, arguments);
}
return retVal;
}
return hook;
}
So, for any function that you want to hook, you just call hookFunction and pass it the function you want to hook and then an optional pre and post function or yours. The pre and post function are passed the same arguments that the original function was.
So, if your original function was this:
var fna = function(ar) {
console.log("argument:",ar);
return "return value is argument too:"+ar;
}
And, you want something to happen every time that function is called right before it's called, you would do this:
fna = hookFunction(fna, function() {
console.log("Hola, I am some additional stuff being rewired right before");
});
or if you wanted it to happen right after the original was called, you could do it like this:
fna = hookFunction(fna, null, function() {
console.log("Hola, I am some additional stuff being rewired right after");
});
Working demo: http://jsfiddle.net/jfriend00/DMgn6/
This can be used with methods on objects and arbitrary nesting levels of objects and methods.
var myObj = function(msg) {
this.greeting = msg;
};
myObj.prototype = {
test: function(a) {
log("myObj.test: " + this.greeting);
}
}
var x = new myObj("hello");
x.test = hookFunction(x.test, mypreFunc2, myPostFunc2);
x.test("hello");
Based on Claudiu's answer, which seems to be the most appreciated way, here is a solution using a for loop and proxying the context... But still, I find this ugly.
var utils = utils || {};
// Let's rewire a function. i.e. My.super.method()
utils.rewire = function(method, callback) {
var obj = window;
var original = function() {};
var tree = method.split(".");
var fun = tree.pop();
console.log(tree);
// Parse through the hierarchy
for (var i = 0; i < tree.length; i++) {
obj = obj[tree[i]];
}
if(typeof(obj[fun]) === "function") {
original = obj[fun];
}
var cb = callback.bind(obj);
obj[fun] = function(ar) {
cb();
return original.apply(this, arguments);
}
}
Well, this looks strange. Consider this
function wrap(fn, wrapper) {
return function() {
var a = arguments;
return wrapper(function() { return fn.apply(this, a) })
}
}
Example:
function foo(a, b) {
console.log([a, b])
return a + b
}
bar = wrap(foo, function(original) {
console.log("hi")
var ret = original()
console.log("there")
return ret
})
console.log(bar(11,22))
Result:
hi
[11, 22]
there
33
To wrap object methods, just bind them:
obj = {
x: 111,
foo: function(a, b) {
console.log([a, b, this.x])
}
}
bar = wrap(obj.foo.bind(obj), function(fn) {
console.log("hi")
return fn()
})
How can I best handle a situation like the following?
I have a constructor that takes a while to complete.
var Element = function Element(name){
this.name = name;
this.nucleus = {};
this.load_nucleus(name); // This might take a second.
}
var oxygen = new Element('oxygen');
console.log(oxygen.nucleus); // Returns {}, because load_nucleus hasn't finished.
I see three options, each of which seem out of the ordinary.
One, add a callback to the constructor.
var Element = function Element(name, fn){
this.name = name;
this.nucleus = {};
this.load_nucleus(name, function(){
fn(); // Now continue.
});
}
Element.prototype.load_nucleus(name, fn){
fs.readFile(name+'.json', function(err, data) {
this.nucleus = JSON.parse(data);
fn();
});
}
var oxygen = new Element('oxygen', function(){
console.log(oxygen.nucleus);
});
Two, use EventEmitter to emit a 'loaded' event.
var Element = function Element(name){
this.name = name;
this.nucleus = {};
this.load_nucleus(name); // This might take a second.
}
Element.prototype.load_nucleus(name){
var self = this;
fs.readFile(name+'.json', function(err, data) {
self.nucleus = JSON.parse(data);
self.emit('loaded');
});
}
util.inherits(Element, events.EventEmitter);
var oxygen = new Element('oxygen');
oxygen.once('loaded', function(){
console.log(this.nucleus);
});
Or three, block the constructor.
var Element = function Element(name){
this.name = name;
this.nucleus = {};
this.load_nucleus(name); // This might take a second.
}
Element.prototype.load_nucleus(name, fn){
this.nucleus = JSON.parse(fs.readFileSync(name+'.json'));
}
var oxygen = new Element('oxygen');
console.log(oxygen.nucleus)
But I haven't seen any of this done before.
What other options do I have?
Update 2:
Here is an updated example using an asynchronous factory method. N.B. this requires Node 8 or Babel if run in a browser.
class Element {
constructor(nucleus){
this.nucleus = nucleus;
}
static async createElement(){
const nucleus = await this.loadNucleus();
return new Element(nucleus);
}
static async loadNucleus(){
// do something async here and return it
return 10;
}
}
async function main(){
const element = await Element.createElement();
// use your element
}
main();
Update:
The code below got upvoted a couple of times. However I find this approach using a static method much better:
https://stackoverflow.com/a/24686979/2124586
ES6 version using promises
class Element{
constructor(){
this.some_property = 5;
this.nucleus;
return new Promise((resolve) => {
this.load_nucleus().then((nucleus) => {
this.nucleus = nucleus;
resolve(this);
});
});
}
load_nucleus(){
return new Promise((resolve) => {
setTimeout(() => resolve(10), 1000)
});
}
}
//Usage
new Element().then(function(instance){
// do stuff with your instance
});
Given the necessity to avoid blocking in Node, the use of events or callbacks isn't so strange(1).
With a slight edit of Two, you could merge it with One:
var Element = function Element(name, fn){
this.name = name;
this.nucleus = {};
if (fn) this.on('loaded', fn);
this.load_nucleus(name); // This might take a second.
}
...
Though, like the fs.readFile in your example, the core Node APIs (at least) often follow the pattern of static functions that expose the instance when the data is ready:
var Element = function Element(name, nucleus) {
this.name = name;
this.nucleus = nucleus;
};
Element.create = function (name, fn) {
fs.readFile(name+'.json', function(err, data) {
var nucleus = err ? null : JSON.parse(data);
fn(err, new Element(name, nucleus));
});
};
Element.create('oxygen', function (err, elem) {
if (!err) {
console.log(elem.name, elem.nucleus);
}
});
(1) It shouldn't take very long to read a JSON file. If it is, perhaps a change in storage system is in order for the data.
I have developed an async constructor:
function Myclass(){
return (async () => {
... code here ...
return this;
})();
}
(async function() {
let s=await new Myclass();
console.log("s",s)
})();
async returns a promise
arrow functions pass 'this' as is
it is possible to return something else when doing new (you still get a new empty object in this variable. if you call the function without new. you get the original this. like maybe window or global or its holding object).
it is possible to return the return value of called async function using await.
to use await in normal code, need to wrap the calls with an async anonymous function, that is called instantly. (the called function returns promise and code continues)
my 1st iteration was:
maybe just add a callback
call an anonymous async function,
then call the callback.
function Myclass(cb){
var asynccode=(async () => {
await this.something1();
console.log(this.result)
})();
if(cb)
asynccode.then(cb.bind(this))
}
my 2nd iteration was:
let's try with a promise instead of a callback.
I thought to myself: strange a promise returning a promise, and it worked. .. so the next version is just a promise.
function Myclass(){
this.result=false;
var asynccode=(async () => {
await new Promise (resolve => setTimeout (()=>{this.result="ok";resolve()}, 1000))
console.log(this.result)
return this;
})();
return asynccode;
}
(async function() {
let s=await new Myclass();
console.log("s",s)
})();
callback-based for old javascript
function Myclass(cb){
var that=this;
var cb_wrap=function(data){that.data=data;cb(that)}
getdata(cb_wrap)
}
new Myclass(function(s){
});
One thing you could do is preload all the nuclei (maybe inefficient; I don't know how much data it is). The other, which I would recommend if preloading is not an option, would involve a callback with a cache to save loaded nuclei. Here is that approach:
Element.nuclei = {};
Element.prototype.load_nucleus = function(name, fn){
if ( name in Element.nuclei ) {
this.nucleus = Element.nuclei[name];
return fn();
}
fs.readFile(name+'.json', function(err, data) {
this.nucleus = Element.nuclei[name] = JSON.parse(data);
fn();
});
}
This is a bad code design.
The main problem is in the callback your instance it's not still execute the "return", this is what I mean
var MyClass = function(cb) {
doAsync(function(err) {
cb(err)
}
return {
method1: function() { },
method2: function() { }
}
}
var _my = new MyClass(function(err) {
console.log('instance', _my) // < _my is still undefined
// _my.method1() can't run any methods from _my instance
})
_my.method1() // < it run the function, but it's not yet inited
So, the good code design is to explicitly call the "init" method (or in your case "load_nucleus") after instanced the class
var MyClass = function() {
return {
init: function(cb) {
doAsync(function(err) {
cb(err)
}
},
method1: function() { },
method2: function() { }
}
}
var _my = new MyClass()
_my.init(function(err) {
if(err) {
console.error('init error', err)
return
}
console.log('inited')
// _my.method1()
})
I extract out the async portions into a fluent method. By convention I call them together.
class FooBar {
constructor() {
this.foo = "foo";
}
async create() {
this.bar = await bar();
return this;
}
}
async function bar() {
return "bar";
}
async function main() {
const foobar = await new FooBar().create(); // two-part constructor
console.log(foobar.foo, foobar.bar);
}
main(); // foo bar
I tried a static factory approach wrapping new FooBar(), e.g. FooBar.create(), but it didn't play well with inheritance. If you extend FooBar into FooBarChild, FooBarChild.create() will still return a FooBar. Whereas with my approach new FooBarChild().create() will return a FooBarChild and it's easy to setup an inheritance chain with create().
You can run constructor function with async functions synchronously via nsynjs. Here is an example to illustrate:
index.js (main app logic):
var nsynjs = require('nsynjs');
var modules = {
MyObject: require('./MyObject')
};
function synchronousApp(modules) {
try {
var myObjectInstance1 = new modules.MyObject('data1.json');
var myObjectInstance2 = new modules.MyObject('data2.json');
console.log(myObjectInstance1.getData());
console.log(myObjectInstance2.getData());
}
catch (e) {
console.log("Error",e);
}
}
nsynjs.run(synchronousApp,null,modules,function () {
console.log('done');
});
MyObject.js (class definition with slow constructor):
var nsynjs = require('nsynjs');
var synchronousCode = function (wrappers) {
var config;
// constructor of MyObject
var MyObject = function(fileName) {
this.data = JSON.parse(wrappers.readFile(nsynjsCtx, fileName).data);
};
MyObject.prototype.getData = function () {
return this.data;
};
return MyObject;
};
var wrappers = require('./wrappers');
nsynjs.run(synchronousCode,{},wrappers,function (m) {
module.exports = m;
});
wrappers.js (nsynjs-aware wrapper around slow functions with callbacks):
var fs=require('fs');
exports.readFile = function (ctx,name) {
var res={};
fs.readFile( name, "utf8", function( error , configText ){
if( error ) res.error = error;
res.data = configText;
ctx.resume(error);
} );
return res;
};
exports.readFile.nsynjsHasCallback = true;
Full set of files for this example could be found here: https://github.com/amaksr/nsynjs/tree/master/examples/node-async-constructor
While i am trying to create object like this
new Ext.TitleCheckbox ()
I am getting "not a constructor error"
my Object is
Ext.TitleCheckbox = {
checked:false,
constructor : function() {
},
getHtml : function (config) {
var prop = (!config.checked)?'checkbox-checked':'checkbox-unchecked';
var html = config.title+'<div class="'+prop+'" onclick="Ext.TitleCheckbox.toggleCheck(this)"> </div>';
return html;
},
toggleCheck : function (ele){
if(ele.className == 'checkbox-checked') {
ele.className = 'checkbox-unchecked';
}
else if(ele.className == 'checkbox-unchecked') {
ele.className = 'checkbox-checked';
}
},
setValue : function(v){
this.value = v;
},
getValue : function(){
return this.value;
}
};
whats the mistake in here?
Ext.TitleCheckbox is not a function, you cannot make a function call to an object literal.
If you want to use the new operator, you should re-structure your code to make TitleCheckbox a constructor function.
Something like this (assumming that the Ext object exists):
Ext.TitleCheckbox = function () {
// Constructor logic
this.checked = false;
};
// Method implementations
Ext.TitleCheckbox.prototype.getHtml = function (config) {
//...
};
Ext.TitleCheckbox.prototype.toggleCheck = function (ele) {
//...
};
Ext.TitleCheckbox.prototype.setValue = function (v) {
//...
};
Ext.TitleCheckbox.prototype.getValue = function () {
//...
};
See CMS's answer for why. As a work-around, if you really need to do this, you can do it via inheritence. In javascript Constructors inherit from objects (a constructor is just a function). So:
function MyCheckbox () {} ; /* all we really need is a function,
* it doesn't actually need to do anything ;-)
*/
// now make the constructor above inherit from the object you desire:
MyCheckbox.prototype = Ext.TitleCheckbox;
// now create a new object:
var x = new MyCheckbox();