How to make a property/method invokable or not? - javascript

I want to achieve this functionality:
I have an object var obj = {};
I have three properties on that obj, obj.zero & obj.one& obj.binaryString
obj.zero & obj.one are methods while obj.binaryString is a string
When I chain the properties, I want them to add their respective digit to the binaryString. So for example:
obj.one.zero.zero.one => makes obj.binaryString = 1001
obj.one.zero.one.one.zero => makes obj.binaryString = 10110
I have achieved the above functionality with this:
function Binary () {
var obj = { binaryString: '' };
Object.defineProperty(obj, 'zero', {
get: function() {
obj.binaryString += '0';
return obj;
}
});
Object.defineProperty(obj, 'one', {
get: function() {
obj.binaryString += '1';
return obj;
}
});
return obj;
}
var binary = new Binary();
binary.one.zero.zero.one // => obj.binaryString becomes '1001'
Now I want to log out the completed binaryString, plus and additionalString which I have accomplished with the code below:
// placed inside Binary constructor function
Object.defineProperty(obj, 'log', {
get: function() {
return function(additionalString) {
console.log(obj.binaryString + additionalString);
};
}
});
So with this current code I can do this:
binary.one.zero.one.zero.one.log(' is the answer');
// logs out `10101 is the answer`
What I want to do is get rid of the log and make the one and zero methods invokable or not so I can achieve this functionality:
binary.one.one.zero.one(' is the result')
// => logs out `1101 is the result`
How can I do this?
I believe it would be similar functionality to how Chalk works:
chalk.blue.bold('Hello world!');
// `blue` is not invoked here but it adds the color blue to the style
chalk.blue('Hello world!');
// `blue` IS invoked here. It adds blue to the style and returns the stylized string

Just Make the obj as function , and print what ever you want .
function Binary () {
var obj = function(msg){ console.log(msg+this.binaryString ) };
obj.binaryString = ''
Object.defineProperty(obj, 'zero', {
get: function() {
obj.binaryString += '0';
return obj;
}
});
Object.defineProperty(obj, 'one', {
get: function() {
obj.binaryString += '1';
return obj;
}
});
return obj;
}
var binary = new Binary();
binary.one.zero.zero.one.zero(" is the result ")

I would like to point out that what you're doing is a very bad/dangerous idea: abusing read properties to mutate the object itself is asking for trouble. You may not see it now, but it's going to lead to pain and heartache down the line in the form of difficult-to-find bugs and convoluted patterns.
What you can do is, which is not so dangerous, is instead of mutating the object itself, return a new instance of Binary with every call to #one or #zero. For example:
function Binary(s) {
this.binaryString = s || ''
}
Object.defineProperty(Binary.prototype, 'zero', {
get: function() {
return new Binary(this.binaryString + '0')
}})
Object.defineProperty(Binary.prototype, 'one', {
get: function() {
return new Binary(this.binaryString + '1')
}})
This is the approach taken by Chalk, and will be much safer and less error-prone.
UPDATE:
After thinking about your problem, and seeing your question, I think the best approach at all is not to use classes at all. You can solve this problem with a pure function-based approach. It's immutable, it's safe, and I believe it's less confusing. Here it is in ES5:
function bin(str) {
if(!str) str = ''
function f(msg) { return str + ' ' + msg }
return Object.defineProperties(f, {
zero: {
get: function() {
return bin(str + '0')
},
},
one: {
get: function() {
return bin(str + '1')
},
},
})
}
And if you can use ES6 (aka ES2015), you can make it much more compact:
function bin(str = '') {
return Object.defineProperties(msg => `${str} ${msg}`, {
zero: { get() { return bin(str + '0') } },
one: { get() { return bin(str + '1') } },
})
}
You would use it like this:
bin().one.zero.one.zero('is the answer') // '1010 is the answer'

Related

Need help solving this method chaining question?

I got asked this in an Interview and I couldn't solve it. Was wondering if any of you guys can help me.
fn("hello").fn("world").fn("!!!").fn();
function fn (str){
// Enter Solution Here
}
The solution should return 'hello world !!!'.
I tried method chaining and was able to get a partially right answer which is as follows:
function fn(str) {
var string = str;
this.fn1 = function(str1) {
string += " "+str1;
return this;
}
this.fn = function() {
console.log(string)
}
}
new fn("hello").fn1("world").fn1("!!!").fn();
but as you can see I cant get it to work unless I use fn1 as the function to concat the string. Any help will be appreciated, thanks.
Have the function return an object with one fn method. If, when you call it, it has an argument, update the string, otherwise return the string so you can log it.
function fn(str = '') {
return {
fn: function (s) {
if (s) {
str += ` ${s}`;
return this;
}
return str;
}
};
}
const output = fn('hello').fn('world').fn('!!!').fn();
console.log(output);
Additional documentation
Template/string literals
You could return an object with two properties, one for returning the complete string and another for collecting parts and retuning the object.
function fn(str) {
const
fns = {
fn: function () {
return str;
},
fn1: function (s) {
str += ' ' + s;
return fns;
}
};
return fns;
}
console.log(fn("hello").fn1("world").fn1("!!!").fn());
I think this should do the trick:
function fn(s){
return new function(){
this.str = s;
this.fn = (ns) => {if(ns){this.str += " "+ns; return this;} else return this.str;};
}
}
let a = fn("hello").fn("world").fn("!!!").fn();
console.log(a);
Seems like you need to use objects
const generic = {
"fn1":null,
"current":"",
"fn": () => {
//what do you want to do with "this.current"?
}
}
function fn(str) {
var ret = generic;
ret.fn1 = (wa) =>{
var again = generic;
again.current +=wa;
return again;
}
ret.current += str;
return ret;
}
You can return an object with a .fn() method which will
check if an argument is passed in or not to determine when to terminate the chain or continue chaining.
When no argument is sent, then it simply returns the accumulated string.
Otherwise, it calls fn() function again to accumulate to the string and get the next copy of the same structure as before:
const result = fn("hello").fn("world").fn("!!!").fn();
console.log(result);
function fn (str){
return {
fn(nextString) {
if (nextString === undefined)
return str;
return fn(`${str} ${nextString}`);
}
};
}
Since this operation is immutable, it means each link in the chain is independent, therefore it is no problem with assigning to variables to continue with different chains:
const helloWorld = fn("hello").fn("world");
const one = helloWorld.fn("one").fn();
const two = helloWorld.fn("two").fn();
const three = helloWorld.fn("three").fn();
console.log(one);
console.log(two);
console.log(three);
function fn (str){
return {
fn(nextString) {
if (nextString === undefined)
return str;
return fn(`${str} ${nextString}`);
}
};
}

Object property that can be getter or function

I am coding a chainable library and I want the API to allow calling part of the chain as static values (with a default value) and sometimes as functions, so parameters could be pass to them.
Simplified example:
var obj = {};
var chainCache = [];
Reflect.defineProperty(obj, 'color', {
get(){
chainCache.push('red');
return obj;
}
})
Reflect.defineProperty(obj, 'background', {
get(){
chainCache.push('black');
return obj;
}
})
Reflect.defineProperty(obj, 'end', {
value(){
var value = chainCache.join(" ");
chainCache.length = 0;
return value;
}
})
console.log( obj.color.background.end() ) // red black
This is a very simplified example and in reality I would also like to include an ability in the above "API" to optionally use the same color key, like this:
obj.color.background.end() // current API (great)
obj.color('#FF0').background.end() // optionally call "color" as function
obj.color().background.end() // bad API, I do not want this
Can color be both function and property at the same time, depending how it is called?
I took a harder look at this than I intended. It looks like with the advent of Proxies this is possible. Building on your example:
let chainCache = [];
let obj = {
color: new Proxy(()=>{}, {
get: (target, prop) => {
chainCache.push("red");
if(prop === "background") {
chainCache.push("black");
return { end: obj.end };
}
},
apply: (target, prop, args) => {
if(args.length > 0) {
chainCache.push(args.pop());
} else {
throw new Error("color expects argument if called like function");
}
return { background: new Proxy(()=>{}, {
get: (target,prop) => { chainCache.push("black"); return obj.end;}
})};
}
}),
end: () => {
let value = chainCache.join(" ");
chainCache = [];
return value;
}
};
console.log(obj.color.background.end());
console.log(obj.color("#FF0").background.end());
console.log(obj.color().background.end());
In the debug console you get:
red black
#FF0 black
Error: color expects argument if called like function
Essentially color is a proxy that wraps an anonymous arrow function, if color is accessed like a property the get() trap gets called, if color gets accessed as a function the apply() trap gets called. This allows for a certain degree of meta-programming like you're looking for.
You will need to make obj a function.
A function is just an object and can have properties as well - obj.color must return a function (for obj.color()) that has properties (for obj.color.background).
But no, when the getter is accessed you don't know yet whether it will be used for a method invocation or not - obj.color() is a property access plus a function call.
var chainCache = [];
var obj = Object.defineProperties(function obj(...args) {
chainCache.push(args);
return obj;
}, {
color: {
get() {
chainCache.push('color');
return obj;
}
},
background: {
get() {
chainCache.push('background');
return obj;
}
},
end: {
value: function() {
var res = chainCache.reduce((acc, val) =>
Array.isArray(val)
? `${acc}(${val.join(',')})`
: `${acc}.${val}`
, "obj");
chainCache.length = 0;
return res;
}
}
})

Can we specify a generic getter on an object in javascript?

I want to do:
properties.email.value without triggering an error like: Can't read 'value' of 'undefined'
However, I don't want to do:
properties.email && properties.email.value and I don't want to use an helper, something like: get(properties, 'email.value').
I really want to keep the syntax properties.email.value
I can solve this by doing:
Object.defineProperty(properties, 'email', {
get: () => properties.email && properties.email.value,
enumerable: true,
configurable: true
});
Now the getter is in charge of doing my safety check. Perfect.
But I also want to be able to do properties.name.value safely.
But as the properties object comes from the API (json), I don't know the full list of properties possible.
So, is there a way to use this "magical" get syntax for any prop access like: properties[ANYTHING].value ?
OK, I've got something like this.
But you must create properties that way.
Hope this help :)
var properties = {
phone : {
value: "123456789"
}
}
var handler = {
get: function(target, name) {
return target.hasOwnProperty(name) ? target[name] : {};
}
};
var new_properties = new Proxy(properties, handler);
console.log("phone.value = " + new_properties.phone.value);
console.log("email.value = " + new_properties.email.value);
new_properties.email = {
value: 1
};
console.log("email.value after assign = " + new_properties.email.value);
The document reference here.
Edited
Even if the original properties object is unknown, this kind of usage works as well.
You could use a Proxy and get known properties and a custom result for unknow properties.
For changing properties, you could take the same approach and set the value.
var properties = { email: { value: 'foo#example.com' } },
proxy = new Proxy(
properties,
{
get: function(target, prop, receiver) {
if (prop in target) {
return target[prop] && target[prop].value
} else {
return;
}
},
set: function(target, prop, value) {
if (prop in target) {
target[prop].value = value;
} else {
target[prop] = { value };
}
}
}
);
console.log(proxy.email);
console.log(proxy.bar);
proxy.email = '41';
console.log(proxy.email);
I can't believe I'm doing this...
var wordlength = 7;
var alphabet="abcdefghijklmnopqrstuvwxyz";
alphabet += alphabet.toUpperCase() + "0123456789_";
var alen = alphabet.length;
var buildWord = function(number){
if(number===0){
return '';
}
return alphabet[number%alen]+buildWord(Math.floor(number/alen));
};
var total = Math.pow(alen, wordlength);
for(var i = 1; i<total; i++){
var w = buildWord(i);
if(isNaN(w[0]) && Object.prototype[w]===undefined){
Object.prototype[w]={};
}
}

Javascript: always execute function in execution context

I wrote this fast-templating function:
var templatize = function(string) {
return function (string) {
return string.replace(/{{(.*?)}}/g, function(pattern, match) {
value = this[match];
if (value) {
return value;
} else {
return pattern;
}
});
}.call(this, string);
}
Which does this:
var foo = "bar", bar = "foo";
templatize("We are {{foo}} and {{bar}}, but not {{crazy}}"); // "We are bar and foo but not {{crazy}}"
I'm quite happy with this except that I have scoping problem. For sure, the templatize method will be accessible through namedscope, but then, the current context of execution of templatize is not accessible in my function automatically.
Something like calling $.proxy(templatize, this)("We are {{foo}} and {{bar}}, but not {{crazy}}") should work, right?
But I'd like to achieve this without needing to call $.proxy() (and without any jQuery preferably) so that context is automatically transfered to the execution one.
I'm struggling with .call(), .apply(), and other closures, but I think I read somewhere over the internet that it was possible. Thanks
You can avoid using jQuery doing this :
var templatize = function(string) {
var me = this; // the data source
return string.replace(/{{(.*?)}}/g, function (full, key) {
// "this" refers to the string itself
return me[key] || full;
});
}
In case you want to use jQuery.proxy(), wrap the replacement function :
var templatize = function(string) {
return string.replace(/{{(.*?)}}/g, jQuery.proxy(function (full, key) {
// "this" now refers permanently to the data source
return this[key] || full;
}, this));
}
In both cases you can bind the data source to this using call :
templatize.call({ hello: 'Hi!' }, '{{hello}}');
Going further
You could optimize by compiling the template for reuse :
function compile(tpl) {
var i = -1, tmp = [];
tpl = tpl.split(/{{([^{}]+)}}/);
while (++i < tpl.length) {
if (i % 2) tmp.push('this["' + tpl[i] + '"]');
else if (tpl[i]) tmp.push('"' + tpl[i].replace(/"/g, '\\"') + '"');
}
return new Function(
'return [' + tmp.join() + '].join("");'
);
}
Usage example :
var tpl = compile('{{hello}} {{hello}}');
tpl.call({ hello: 'Hi!' }); // "Hi! Hi!"
tpl.call({ hello: 'Yo!' }); // "Yo! Yo!"
Regarding the example above, here is the function returned by compile :
function () {
return [this["hello"]," ",this["hello"]].join("");
}
Note that you can use an array as well :
var tpl = compile('{{1}} {{0}}');
tpl.call(['a', 'b']); // "b a"
Performance test : http://jsperf.com/template-compiling.
why don't you pass an object containing the view variables? would be cleaner then potentially displaying any existing variable in your view.
var templatize = function(string, variables) {
return function (string) {
return string.replace(/{{(.*?)}}/g, function(pattern, match) {
value = variables[match];
if (value) {
return value;
} else {
return pattern;
}
});
}.call(this, string);
}

Dynamic Parameters for metaprogramming in JavaScript?

I want to delegate several methods from one JavaScript-Object to another. So i thought about using metaprogramming to not have several methods be defined just as delegates. So far i ended up with this method:
function delegate_to(_method, _obj) {
return function(_args) { // One parameter, what's about multiple parameters?
return _obj[_method](_args)
}
}
So as an example-code how it should work:
var that = {}
var delegate = {}
that.foo = function(_message) { console.log("foo: " + _message) }
that.bar = function(_message) { console.log("bar: " + _message) }
that.baz = function(_message) { console.log("baz: " + _message) }
function delegate_to(_method, _obj) {
return function(_args) { // One parameter, what's about multiple parameters?
return _obj[_method](_args)
}
}
['foo', 'bar', 'baz'].forEach(function(method) {
delegate[method] = delegate_to(method, that)
})
delegate.foo('Hello JS') // foo: Hello JS
delegate.bar('Hello JS') // bar: Hello JS
delegate.baz('Hello JS') // baz: Hello JS
The code does work, but what's if i want to delegate a method that does have more than one parameter? How about n parameters? Is it possible to change the code to have any number of parameters? Is this running in any browser?
Regards, Rainer
Try this:
function delegate_to(_method, _obj) {
return function() {
return _obj[_method].apply(_obj, [].slice.call(arguments))
}
}
Function has methods called 'apply' to pass variable number of parameters as an array. Refer MDC:Function.apply
You can convert all the parameters passed to a function into an array by
Array.prototype.slice.call(arguments, 0)
Using these two principals, I have modified you code to taken multiple number of parameters. See the JSBin http://jsbin.com/iwiwix/3/watch
Relevant code extract:
delegate.foo('Hello JS', "from foo"); // foo: Hello JS
function delegate_to(_method, _obj) {
return function() {
var argArray = Array.prototype.slice.call(arguments, 0);
return _obj[_method].apply(_obj, argArray);
};
}
that.foo = function() { console.log("foo: " + arguments[0] + ' ' + arguments[1]); };

Categories