I ran across some code I'm trying to refactor where they were writing JavaScript as a string and putting it inside HTML script tags, then writing it to the DOM. Very ugly and not maintainable. But one of the things this allowed them to do was build a function call by appending to a string.
var methods = '';
for (key in obj) {
methods += 'func1("'+key+'", "'+obj[key]+'").';
}
var scriptString = '<script>func2().' + methods + 'func3();</script>'
The result could be:
'<script>func2().func1("key1", "value1").func1("key2", "value2").func3();</script>'
So, since I really disapprove of writing JavaScript inside an HTML string inside of JavaScript ... Does anyone know how to accomplish the same result with pure JavaScript? Is there a way to continuously append methods to a function call by iterating over an object?
Array.reduce() should do most of what you need.
The tricky part is the .func1() method call chain, where you depend on an object's key/value pairs. If you're not used to working with the Array.reduce() method, I would suggest reading through the MDN documentation, but it basically loops through an array, performing a transformation on the previous result until it reaches the end, where it returns the final result. This can be used to our advantage since method chaining is just a method call on the previous method call's return value. But, since that's an array method, we need to get the Object's entries into an array first...and that's where Object.entries() comes in.
Note that much of my syntax here involves new features, which may or may not be supported by your target browsers. Be sure to use a transpiler and polyfills to handle going backwards, if needed.
See below for an example:
const wrapperFunc = (obj) => {
// Start with func2()
const func2Result = func2()
// Chain to func1() for each entry in obj (the tricky part)
const func1Result = Object.entries(obj).reduce(
// Call func1 method on previous result to get the next result
(prevResult, [ key, val ]) => prevResult.func1(key, val),
// Initial "prevResult" value used above
func2Result
)
// Chain to func3()
return func1Result.func3()
}
// Inject object to be used for func1() calls
wrapperFunc({
key1: 'value1',
key2: 'value2'
})
Also, here's a second, more complex example with some implemented methods. Unlike the example above, this one actually runs.
class MyObject {
constructor() {
this.innerString = ''
}
// Chainable method (returns this)
func1(key, val) {
this.innerString += `${key}, ${val} `
return this
}
func3() {
return this.innerString.trim()
}
}
const func2 = function () {
return new MyObject()
}
const wrapperFunc = (obj) => {
// Start with func2()
const func2Result = func2()
// Chain to func1() for each entry in obj (the tricky part)
const func1Result = Object.entries(obj).reduce(
// Call func1 method on previous result to get the next result
(prevResult, [ key, val ]) => prevResult.func1(key, val),
// Initial "prevResult" value used above
func2Result
)
// Chain to func3()
return func1Result.func3()
}
// Inject object to be used for func1() calls
console.log(wrapperFunc({
key1: 'value1',
key2: 'value2'
}))
You can create a function that calls the func1 method repeatedly for all key/value pairs in the object.
var script = function(obj) {
var value = func2();
for (var key in obj) {
value = value.func1(key, obj[key]);
}
value.func3();
};
Related
I am working on a problem and I am not allowed to use ES6 syntax so I can't use the spread/rest operator. How can I write a function that will take any number of arguments without a spread/rest operator?
function memoize(func) {
var cache = {};
return function (...args) {
if (args in cache) {
return cache[args];
} else {
cache[args] = func(...args);
return func(...args);
}
};
};
Firstly you have to use the arguments object. It is an older feature which makes a variable called arguments available in non arrow functions. The value of arguments is all the arguments a function receive. It is an array-like object, not an array. This eliminates the use of rest operator.
Also, it is better to create a key, when you are creating a generic function.
Why? Javascript object keys can only be string. If an object is used as a key it gets auto converted to a string : [object Object]. So basically all your object keys will override each other. Here is a demo:
const obj1 = { a : 1};
const obj2 = { b : 2};
const x = {};
x[obj1] = 2;
console.log(x);
x[obj2] = 2;
console.log(x);
To generate hash key you could use any method. Below is not a bullet proof implementation but I have just joined all the arguments into a . separated array.
Now we have to call your function with these arguments, in the else condition. How? When you want to forward you arguments to another function, and they are in the form of an array, you can use something like apply. It is pre ES6 feature which lets you run a function in a different context, or lets you pass arguments as an array. Here we won't be changing the context, but the second use case is what we will be using.
function memoize(func) {
var cache = {};
return function () {
const key = Object.values(arguments).join('.');
console.log(arguments);
console.log(cache);
if (key in cache) {
return cache[key];
} else {
cache[key] = func.apply(null,arguments);
return cache[key];
}
};
};
const conc = (str,str2,str3) => `${str}_${str2}_${str3}`;
const memoizedSq = memoize(conc);
memoizedSq('hello','hi','hey');
memoizedSq('bye','see you','so long');
memoizedSq('hello','hi','hey');
I have used join to create the string.
Minor optimization I did by changing your code in the else condition is not calling function multiple times. I just saved it in the object and returned the same value.
Note: This could break for cases where if a function takes a string, and the string itself contains ..
We can make a regular (POJSO) JS Object iterable, like so:
const tempObj = {a: 1, b: 2, c: 3};
tempObj[Symbol.iterator] = function () {
const self = this;
const keys = Object.keys(self);
return {
next() {
const k = keys.shift();
return {
done: !k,
value: [k, self[k]]
}
}
}
};
now we can use for..of loop:
for (let [k,v] of tempObj) {
console.log(k,v);
}
and we get:
a 1
b 2
c 3
my question is - is there another method we need to implement besides next()? if not, why did the iterator spec choose to return an object instead of just returning a function? why isn't the spec simply:
tempObj[Symbol.iterator] = function () {
return function next {
return {
done: Object.keys(this).length === 0,
value: Object.keys(this).shift()
}
}
};
my only guess is that by returning an object, it leaves room for updates/changes.
The Iterator interface also supports two more optional methods: return and throw. From the ES6 specification, section 25.1.1.2 (Table 54):
return
A function that returns an IteratorResult object. The returned
object must conform to the IteratorResult interface. Invoking this
method notifies the Iterator object that the caller does not intend to
make any more next method calls to the Iterator. The returned
IteratorResult object will typically have a done property whose value
is true, and a value property with the value passed as the argument of
the return method. However, this requirement is not enforced.
throw
A function that returns an IteratorResult object. The returned object
must conform to the IteratorResult interface. Invoking this method
notifies the Iterator object that the caller has detected an error
condition. The argument may be used to identify the error condition
and typically will be an exception object. A typical response is to
throw the value passed as the argument. If the method does not throw,
the returned IteratorResult object will typically have a done property
whose value is true.
The ES6 spec also says:
Typically callers of these methods should check for their existence before invoking them.
So you're definitely not required to implement them; the burden of checking for their existence is on the caller.
Is there another method we need to implement besides next()?
No, none that we need to implement, but we can implement throw and return for the full iterator interface. Generator objects do that, for example.
Why did the iterator spec choose to return an object instead of just returning a function?
Because an iterator is (usually) stateful, and from an OOP viewpoint it should be an object with a method not a (pure) function. This also allows prototypical inheritance for iterator instances.
You could yield a generator with Object.entries as value.
let tempObj = { a: 1, b: 2, c: 3 };
tempObj[Symbol.iterator] = function* () {
yield* Object.entries(this);
};
for (let [k, v] of tempObj) {
console.log(k, v);
}
I am admittedly a beginner with JavaScript, but I need to piece together someone else's code and I am having trouble understanding the following function and how to call it in node.
const invoke = method => object => object[method]
This obviously is a function that takes a method that returns another function that takes an object that then returns object[method], but what exactly is the purpose? How would one use this? Thank you.
the way i see it is as the const states, it invokes a function stored in an object ,
as you mentionned, this can be broken down to :
const invoke = (method) => {
return (object) => {
return object[method] ;
}
}
the goal of this i believe is that you can call it ( like you're telling a story ) expressively and concisely : invoke the function a from the functions object. ( functionnal-programming )
from this article about functional programming
Functional programming is declarative rather than imperative, and
application state flows through pure functions. Contrast with object
oriented programming, where application state is usually shared and
colocated with methods in objects.
but the term invoke got me thinking about Immediately invoked functions, so the usage of the const invoke can be :
getting function from the object ( without executing it ) not to have to instantiate the whole object and having the function in a variable and maybe manipulate it's prototype.
calling the function ( with parenthesis ).
getting a property from an object.
immediately invoke a function in an object.
const myFns = {
'a' : function(x){
console.log(x || 'something to log when no params passed');
},
'b': {
username : 'Doe'
}
}
const invoke = method => object => object[method]
let myFunction = invoke('a')(myFns);
myFunction('hello from myFunction'); // call it or modify myFunction.prototype ... etc.
invoke('a')(myFns)('hello'); // simply call it
let user = invoke('b')(myFns); // get a property
console.log(user.username);
(invoke('a')(myFns))(); // immidiatly invoke the function
probalby to avoid eval() :P
The name 'invoke' suggests it should really be written like this:
const invoke = method => object => object[method]()
The () is the invocation. It's very general, so hard to say exactly how it would be used, but here's a silly example.
class Dog {
speak () { console.log('woof') }
}
var dogs = [ new Dog(), new Dog(), new Dog() ];
dogs.forEach( invoke( 'speak' ) );
-> 'woof'
-> 'woof'
-> 'woof'
It's a pretty common pattern to let an array method like forEach do the second call of a higher-order function like this.
First of all, I don't want to add methods to Function.prototype. Doing that would make them available for all functions and that's not what I'm looking for.
In JavaScript you can create objects with custom prototypes like this:
function CustomObj() {}
CustomObj.prototype = {};
CustomObj.prototype.sayFoo = function () { return 'foo' };
var myCustomObj = new CustomObj(); //=> returns an object: {}
myCusomObj.sayFoo(); //=> 'foo'
You can also create array-like objects with custom prototypes like this:
function CustomArr() {}
CustomArr.prototype = [];
CustomObj.prototype.sayFoo = function () { return 'foo' };
var myCustomArr = new CustomArr(); //=> returns an ordered object: []
myCustomArr.sayFoo(); //=> 'foo'
What I'd like to do is use some kind of constructor to create a function with its own custom prototype in the same way. However, the following does not work:
function CustomFn() {}
CustomFn.prototype = function () {};
CustomFn.prototype.sayFoo = function () { return 'foo' };
var myCustomFn = new CustomFn(); //=> PROBLEM! returns an object: {}
myCustomFn.sayFoo(); //=> 'foo'
// ^^ Here, the prototype was applied but the output was not a function.
myCustomFn(); //=> TypeError: object is not a function
So is there any way to accomplish what I'm trying to do?
UPDATE
Maybe there's another way I could be asking this question that would make it a little clearer.
There's a problem with the idea of a closure:
function makeFn() {
var output = function () { /* do some stuff */ };
output.foo = function () { /* do some stuff */ };
return output;
}
var specialFn = makeFn();
Essentially, this technique gives me what I want. However, the problem is that every time I call makeFn, output.foo has to be created as a totally independent function that takes up its own memory. Gross. So I could move that method out of the closure:
var protoMethods = {
"foo" : function () { /* do some stuff */ }
};
function makeFn() {
var output = function () { /* do some stuff */ };
for (var i in protoMethods) {
Object.prototype.hasOwnProperty.call(protoMethods, i) &&
(output[i] = protoMethods[i]);
}
return output;
}
var specialFn = makeFn();
But now I have to manually do an iteration every time I call makeFn which would be less efficient than if I could just assign protoMethods to be the prototype of output. So, with this new update, any ideas?
It is a tricky thing indeed, more complicated than it should be if the language was designed well...
Basically, you just can't do it cleanly in current versions. Objects other than functions can not be callable.
In future Javascript versions, you can do it with a "proxy" object that can define a "call" handler. But it is still way too complicated and contrived in my opinion.
Another way to go about it is to make your object a real function, not a custom object. Then try to set its __proto__, which is non-standard yet but works in most modern browsers, except Opera and IE 8 or less. Also maybe set its constructor property for faking instanceof checks... such hacks are quite tricky though and results will vary a lot with environments.
The following example works fine on my Firefox:
http://jsfiddle.net/Q3422/2/
function MyFun() {
if (!this || this==window) {
return new MyFun();
}
var f = function() {
return "thanks for calling!";
}
f.__proto__ = MyFun.prototype;
f.constructor = MyFun;
return f;
}
MyFun.prototype = {
foo: function() {
return "foo:" + this();
},
__proto__: Function.prototype
};
var f = new MyFun();
alert("proto method:"+f.foo()); // try our prototype methods
alert("function method:"+f.call()); // try standard function methods
alert("function call:"+f()); // try use as a function
alert('typeof:' + typeof f); // "function", not "object". No way around it in current js versions
alert('is MyFun:' + (f instanceof MyFun)); // true
alert('is Function:' + (f instanceof Function)); // true
Just wanted to add that you should not be worried about "copying" functions to each instance of your objects. The function itself is an object, so is never really copied, nor is it recompiled or anything. It does not waste memory, except for the function object reference itself and any closure variables.
Iterating over the prototype to copy it should not concern you as well, I guess you will not have a gazillion methods.
So your own last solution is probably the best if you need to support environments where proto is not settable, and you are not worried that your prototype might get extended after some objects already got created and they may not pick up the changes.
You're at the heart of what inheritance in JavaScript is all about. Yes, since prototypes are objects, you'll want to set the prototype of CustomFn to an object instead of a function.
But that object can come from another function:
function ParentFn() {}
function CustomFn() {}
CustomFn.prototype = Object.create(ParentFn.prototype);
CustomFn.prototype.sayFoo = fun ...
If you don't have ES5 or a polyfill:
CustomFn.prototype = (function() {
function F(){}
F.prototype = ParentFn.prototype;
return new F();
}());
Some may tell you just to do the following but the above way is better:
CustomFn.prototype = new ParentFn();
I tried that too, when working on V library. I wanted to override the Function constructor to enforce a restricted syntax of constructor functions, that I'm calling "class functions" (and I'm confident to do so).
Answer is no, using the new operator you can only create new "object"s, but not new "function object"s.
However you can use a constructor function both as a constructor and as a function!
var CustomFn = function () {
if (this instanceof CustomFn) {
// here we are 'new CustomFn()'
}
else {
// here we are 'CustomFn()' or 'CustomFn.call()'
}
};
Or as I believe is the better concept, to do the function in first place and then let the constructor go:
var CustomFn = function () {
if (!(this instanceof CustomFn)) { // functioning
// here we are 'CustomFn()' or 'CustomFn.call()'
return new CustomFn(); // or return undefined or throw
}
// constructing
// here we are 'new CustomFn()'
// BaseCustomFn.call(this);
};
I'm trying to procedurally add getters/setters to objects in Javascript and although I think the code below should simply work, it doesn't act as I expected.
here is my code:
var data = {a:1, b:2, c:3};
function Abc(data) {
var data = data || {};
for ( var key in data ) {
console.log(key, data[key]);
this.__defineGetter__(key, function() {
console.log('using getter');
return data[key];
})
}
return this;
}
abc = Abc(data);
console.log('this should be 1', abc.a);
console.log('this should be 2', abc.b);
console.log('this should be 3', abc.c);
and this is my unexpected output
a 1
b 2
c 3
using getter
this should be 1 3
using getter
this should be 2 3
using getter
this should be 3 3
the output makes absolutely no sense to me, but I get the same output on Chrome and Webkit, so I'm guessing I'm just stupid and this is not a bug of the Javascript engines :)
as the comments mention my triple use of "data" isn't really good!
By the time the closure that was passed to __defineGetter__ is executed, the loop has finished and key remains at the last value. Try this:
function Abc(data) {
if(!(this instanceof Abc)) {
return new Abc(data);
}
data = data || {};
for(var key in data) {
(function(key) {
console.log(key, data[key]);
this.__defineGetter__(key, function() {
console.log('using getter');
return data[key];
});
}).call(this, key);
}
return this;
}
Some other things:
You weren't using var on key, so key was global. That wasn't causing your issue, but it's a good idea to add it anyway.
You were declaring a new variable named data when there was already a data in the scope of the function. It's not needed to use var there since there's already a data in the function; I removed it.
You weren't using new, which was also causing odd behavior; the new three lines at the top of the function cause it to act as if it was called with new.
#Robert Gould. main problem in your code sample is not in "defining getters and setters", but in "understanding closures".
See more about closures in MDN
Additionaly your code is invalid on using this keyword, because your Abc() function this object points to global window object. You must use new Abc() to properly use this keyword or must create new empty object inside Abc() and return it.
1)
function Abc(data) {
data=data||{};
for(key in data) {
console.log(key, data[key]);
(function(data,key) { // defining closure for our getter function
this.__defineGetter__(key, function() {
console.log('using getter');
return data[key];
});
}).call(this,data,key);
}
// dont need to return: when used *new* operator *this* is returned by default
}
var abc = new Abc(data);
or
2)
function Abc(data) {
data=data||{};
var obj={};
for(key in data) {
console.log(key, data[key]);
(function(data,key) { // defining closure for our getter function
obj.__defineGetter__(key, function() {
console.log('using getter');
return data[key];
});
})(data,key);
}
return obj; // return object
}
var abc = Abc(data);
If you realy need to known about "defining getters|setterns" read about:
Object.defineProperty() MDN and get operator MDN
and for back compatibility __defineGetter__ MDN
you must also understand what not all top used browsers currently supports getters and setters for properties
key is a local variable in the scope of Abc, the anonymous function you wrote has no variable key, so it uses the one from the outer scope. That variable has changed to the last value in the loop by the time the anonymous function is used, so you see the last value. You can fix this with a closure:
this.__defineGetter__(key, (function(l_key){
return function() {
console.log('using getter');
return data[l_key];
}
})(key));
In that code l_key is a local copy of key to the anonymous function returned be another anonymous function.