How to chain functions into one sentence - javascript

So i have to write a program, which could chain function like this:
name("Adam").place("cinema").movie("xxx") expected output should be like this: Adam goes to Cinema to watch movie called xxx, what i have so far:
var test = function(name){
var self = {};
console.log(name)
function someFunc(where) {
console.log("goes to cinema" + where)
return self;
}
function someOtherFunc(what) {
console.log("to watch movie" + what)
return self;
}
self.someFunc = someFunc;
self.someOtherFunc = someOtherFunc;
return self;
}
console.log(test("Adam").someFunc("cinema").someOtherFunc("xxx"));
But it gives me strings in different lines, and i want to be it in one sentence, any help will be appreciated.

You can do something like this:
var test = function(message) {
this.someFunc = function(where) {
message += ` goes to ${where}`;
return this; // Allow chaining
}
this.someOtherFunc = function(what) {
message += ` to watch movie ${what}`;
return this; // Allow chaining
}
this.value = function() {
return message; //End chain
}
return this; // Start chain
}
console.log(test("Adam").someFunc("cinema").someOtherFunc("xxx").value());
//Adam goes to cinema to watch movie xxx
Edit:
Is it possible to get that result without .value() ?
You can override .toString().
Every object has a toString() method that is automatically called when
the object is to be represented as a text value or when an object is
referred to in a manner in which a string is expected. By default, the
toString() method is inherited by every object descended from Object.
If this method is not overridden in a custom object, toString()
returns "[object type]", where type is the object type.
This would require to convert the object to string though.
var test = function(message) {
this.someFunc = function(where) {
message += ` goes to ${where}`;
return this;
}
this.someOtherFunc = function(what) {
message += ` to watch movie ${what}`;
return this;
}
this.toString = function() {
return message;
}
return this;
}
console.log(`${ test("Adam").someFunc("cinema").someOtherFunc("xxx")}`);

Assuming you need your functions to execute in that order, and produce a sentence that doesn't vary in order, you could do the following:
var test = function(name){
var self = {
message: name,
};
function someFunc(where) {
self.message += " goes to cinema" + where;
return self;
}
function someOtherFunc(what) {
self.message += " to watch movie" + what;
console.log(self.message);
}
self.someFunc = someFunc;
self.someOtherFunc = someOtherFunc;
return self;
}
console.log(test("Adam").someFunc("cinema").someOtherFunc("xxx"));

Related

Which logger architecture in javascript? [duplicate]

Is there a way to make any function output a console.log statement when it's called by registering a global hook somewhere (that is, without modifying the actual function itself) or via some other means?
Here's a way to augment all functions in the global namespace with the function of your choice:
function augment(withFn) {
var name, fn;
for (name in window) {
fn = window[name];
if (typeof fn === 'function') {
window[name] = (function(name, fn) {
var args = arguments;
return function() {
withFn.apply(this, args);
return fn.apply(this, arguments);
}
})(name, fn);
}
}
}
augment(function(name, fn) {
console.log("calling " + name);
});
One down side is that no functions created after calling augment will have the additional behavior.
As to me, this looks like the most elegant solution:
(function() {
var call = Function.prototype.call;
Function.prototype.call = function() {
console.log(this, arguments); // Here you can do whatever actions you want
return call.apply(this, arguments);
};
}());
Proxy Method to log Function calls
There is a new way using Proxy to achieve this functionality in JS.
assume that we want to have a console.log whenever a function of a specific class is called:
class TestClass {
a() {
this.aa = 1;
}
b() {
this.bb = 1;
}
}
const foo = new TestClass()
foo.a() // nothing get logged
we can replace our class instantiation with a Proxy that overrides each property of this class. so:
class TestClass {
a() {
this.aa = 1;
}
b() {
this.bb = 1;
}
}
const logger = className => {
return new Proxy(new className(), {
get: function(target, name, receiver) {
if (!target.hasOwnProperty(name)) {
if (typeof target[name] === "function") {
console.log(
"Calling Method : ",
name,
"|| on : ",
target.constructor.name
);
}
return new Proxy(target[name], this);
}
return Reflect.get(target, name, receiver);
}
});
};
const instance = logger(TestClass)
instance.a() // output: "Calling Method : a || on : TestClass"
check that this actually works in Codepen
Remember that using Proxy gives you a lot more functionality than to just logging console names.
Also this method works in Node.js too.
If you want more targeted logging, the following code will log function calls for a particular object. You can even modify Object prototypes so that all new instances get logging too. I used Object.getOwnPropertyNames instead of for...in, so it works with ECMAScript 6 classes, which don't have enumerable methods.
function inject(obj, beforeFn) {
for (let propName of Object.getOwnPropertyNames(obj)) {
let prop = obj[propName];
if (Object.prototype.toString.call(prop) === '[object Function]') {
obj[propName] = (function(fnName) {
return function() {
beforeFn.call(this, fnName, arguments);
return prop.apply(this, arguments);
}
})(propName);
}
}
}
function logFnCall(name, args) {
let s = name + '(';
for (let i = 0; i < args.length; i++) {
if (i > 0)
s += ', ';
s += String(args[i]);
}
s += ')';
console.log(s);
}
inject(Foo.prototype, logFnCall);
Here's some Javascript which replaces adds console.log to every function in Javascript; Play with it on Regex101:
$re = "/function (.+)\\(.*\\)\\s*\\{/m";
$str = "function example(){}";
$subst = "$& console.log(\"$1()\");";
$result = preg_replace($re, $subst, $str);
It's a 'quick and dirty hack' but I find it useful for debugging. If you have a lot of functions, beware because this will add a lot of code. Also, the RegEx is simple and might not work for more complex function names/declaration.
You can actually attach your own function to console.log for everything that loads.
console.log = function(msg) {
// Add whatever you want here
alert(msg);
}

JavaScript Chainable Method Delimma

I have 2 methods that I'd like to use as chainable methods. Other methods may be chained to further modify text.
left returns X characters from the left.
right returns X characters from the right.
Currently I can do this:
var txt = "hello";
S$(txt).left(4).right(2).val //returns "ll"
What I want to do is this.
Basically I want to return the results after the last chained method without having to call the property. Is this possible?
var txt = "hello";
S$(txt).left(4).right(2) //returns "ll"
Below is the main code:
(function (global) {
var jInit = function(text){
this.text = text;
this.val = text;
}
var jIn = function(text){
return new jInit(text);
}
var jStringy = jStringy || jIn;
jInit.prototype.left = function (num_char) {
if (num_char == undefined) {
throw "Number of characters is required!";
}
this.val = this.val.substring(0, num_char);
return this;
}
jInit.prototype.right = function (numchar) {
this.val = this.val.substring(this.val.length - numchar, this.val.length);
return this;
}
global.jStringy = global.S$ = jStringy;
return this;
}(window));
You can override valueOf and toString methods of Object to archieve it.
Example:
var myObject = {
value: 5,
valueOf: function(){
return this.value;
},
toString: function() {
return 'value of this object is' + this.value;
}
};
As Javascript is a duck typing language, nothing will prevent you from performing mathematical operations and string concatenation against primitive values/objects as these methods are called during expression evaluation process no matter where they came from.
Examples:
console.log(myObject + 10);
will print 15
alert(myObject);
will print 'value of this object is 5'

Why toString is not a generic function in javascript

I was trying to do something like this.
var myFunc = function() {}
myFunc.prototype = new String();
myFunc.prototype.replace = function() {return 'hii, Mr '+ this.toString();}
var oVal = new myFunc('Jyotirmay');
oVal.replace();
o/p :: Uncaught TypeError: String.prototype.toString is not generic(…)
Why "function not generic" error comes actually in general?
As to be more clear, How can i pass my argument i.e Jyotirmay from inherited class to base class i.e string. So that i can get that value by calling any proper string function.
I don't want to get my passed value from my function by handling that variable in it.
I want that to be handled by parent class. You can say super() in other languages.
It is unclear what exactly you are trying to achieve from your question and comments, but perhaps this is all you are trying to do?
function myFunc(inputArg) {
this.inputArg = inputArg;
}
myFunc.prototype = {
replace: function () {
return 'hii, Mr ' + this.inputArg;
},
toString: function () {
return '' + this.inputArg;
}
};
myFunc.prototype.valueOf = myFunc.prototype.toString;
function log(inputArg) {
document.getElementById('out').appendChild(document.createTextNode(inputArg + '\n'));
}
var oVal = new myFunc('Jyotirmay');
log(oVal);
log(oVal.replace());
<pre id="out"></pre>
As to Why is toString not generic, this is because not all objects can be represented as a string by the same conversion method.
Update based on your latest comment
Native objects are notoriously difficult, if not impossible, to subclass in Javascript. There are a few hacks that will allow you partial success, but I would not recommend them and good luck across different environments.
Two (but not the only) such hacks are:
Stealing from an iframe
function stealObject(objectName, myVariableName) {
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = 'javascript:parent.' + myVariableName + ' = ' + objectName;
document.body.appendChild(iframe);
document.body.removeChild(iframe);
return window[myVariableName];
}
function log(inputArg) {
document.getElementById('out').appendChild(document.createTextNode(inputArg + '\n'));
}
try {
stealObject('String', 'MyString');
MyString.prototype.replace = function () {
return 'hii, Mr ' + this;
};
var oVal = new MyString('Jyotirmay');
log(oVal);
log(oVal.toUpperCase());
log(oVal.replace());
} catch (e) {
log(e);
}
<pre id="out"></pre>
Doesn't work in SO snippets because SecurityError: Sandbox access violation: but can see it on this jsFiddle. typeof oVal will return object and not string and oVal instanceof String will be false. oVal.constructor === String will return false.
Another hack
function MyString() {
this.str = '' + arguments[0];
};
with(MyString.prototype = new String()) {
toString = valueOf = function () {
return this.str;
};
}
MyString.prototype.replace = function () {
return 'hii, Mr ' + this;
};
function log(inputArg) {
document.getElementById('out').appendChild(document.createTextNode(inputArg + '\n'));
}
var oVal = new MyString('Jyotirmay');
log(oVal);
log(oVal.toUpperCase());
log(oVal.replace());
<pre id="out"></pre>
The magic length property is broken in this one and you would need to call oVal.toString().length instead. typeof oVal will return object and not string but oVal instanceof String will be true. oVal.constructor === String will return true.

using prototype in custom object in javascript

I have made my custom object and I want to add a method into. I want to uppercase my values. But it is giving me [object object].Any idea how to get it done. fiddle
function checkObj (name,title,salary){
this.name= name;
this.title= title;
this.salary= salary;
}
var woo=new checkObj("rajora","this is a test",2000);
checkObj.prototype.inc=function (){
for(i=0;i<this.length;i++){
this[i]= this[i].toUpperCase();
}
};
woo.inc();
console.log(woo)
You just have to change your inc function like this
checkObj.prototype.inc = function() {
for (var key in this) {
if (this.hasOwnProperty(key)) {
if (typeof this[key] === 'string') {
this[key] = this[key].toUpperCase();
}
}
}
};
and this gives me the following output
{ name: 'RAJORA', title: 'THIS IS A TEST', salary: 2000 }
When you call console.log() and pass it an object like woo, it uses woo.toString() to get the string representation of it and print it.
woo inherits toString() from Object.prototype which by default prints the string you are getting, i.e. [object object].
You have to override toString() like this:
checkObj.prototype.toString = function() {
var result = "checkObj {";
for (var prop in this) {
if (this.hasOwnProperty(prop))
result += (prop + " : " + String(this[prop]).toUpperCase() + ", ");
}
result += ("}");
return result;
}
Now you can just console.log(woo) and it would work as expected.
Demo here.
js code like this :
function checkObj (name,title,salary){
this.name= name;
this.title= title;
this.salary= salary;
}
checkObj.prototype.inc=function(){
var self=this;
for(var i in self){
if(self.hasOwnProperty(i)){
output(i);
}
}
function output(item){
if(typeof self[item]==='string'){
self[item]=self[item].toUpperCase();
console.log(self[item]);
}
}
};
Is helpful for you ?

Javascript "return this" meet the "return"?

var Person = function(){};
function klass() {
initialize = function(name) {
// Protected variables
var _myProtectedMember = 'just a test';
this.getProtectedMember = function() {
return _myProtectedMember;
}
this.name = name;
return this;
};
say = function (message) {
return this.name + ': ' + message + this.getProtectedMember();
// how to use "return this" in here,in order to mark the code no error.
};
//console.log(this);
return {
constructor:klass,
initialize : initialize,
say: say
}
//return this;
}
Person.prototype = new klass();
//console.log(Person.prototype);
new Person().initialize("I :").say("you ").say(" & he");
how to use "return this" in "say",in order to mark the code no error.
i want to know that how to 'Chain call' in the function which has return alrealy?
You need return a class instance to chain call. I would suggest you create a base class for all your objects that is able to store the outputs of the class, and will return it in the "toString" or similar function, maybe "output".
Your code then becomes:
(new Person()).initialize("I :").say("you ").say(" & he").toString();
Only one object can be returned.
So you have two options.
One - Show the message inside the function ans return this
say = function (message) {
// show the message here e.g. using an alert
alert(this.name + ': ' + message + this.getProtectedMember());
// then return instance
return this;
};
Two - Return an object containing instance and message
say = function (message) {
message = this.name + ': ' + message + this.getProtectedMember();
return {message:message, instance:this};
};
and call it like
new Person().initialize("I :").say("you ").instance.say(" & he");

Categories