How can I call functions conditionally in function chaining? - javascript

I don't know what it's called, but in the canvas library i'm using you apply filters to a texture by calling
canvas.draw(texture).filter1().filter2().filter3().filter4();
and i'm trying to only draw certain filters if the value is not default, but i can't do it with regular if statements since the texture can only be set once, and if i try to set it again, the previous filter will get reset.
What i'm wondering is if i can somehow add "if" statements to the "train" of filters, like so:
canvas.draw(texture).if(something){filter1()}.if(somethingelse){filter2()}.if(something){filter3()};
and so on.
Sorry if i'm not very clear, i dont know what the filter "train" is called, where you apply filter after eachother on one line.

The construct you have shown in the question is called, function chaining. The function filter1, filter2, ... filterN would just modify the same canvas object and return the same object at the end of the function.
You can very well include if conditions, like this
var canvasObject = canvas.draw(texture);
if (condition1) {
canvasObject = canvasObject.filter1();
}
if (condition2) {
canvasObject = canvasObject.filter2();
}
...
A simple example for the same, would look something like this
function Canvas() {
actualCanvas = "";
return {
filter1: function() {
actualCanvas += "filter1 ";
return this; // return the same object
},
filter2: function() {
actualCanvas += "filter2 ";
return this; // return the same object
},
filter3: function() {
actualCanvas += "filter3 ";
return this; // return the same object
},
getActualCanvas: function() {
return actualCanvas;
}
}
}
And invoke the functions, like this
var canvasObject = new Canvas();
if (1 === 1) {
canvasObject = canvasObject.filter1();
}
if (1 !== 1) {
canvasObject = canvasObject.filter2();
}
if (2 === 2) {
canvasObject = canvasObject.filter3();
}
console.log(canvasObject.getActualCanvas());
// filter1 filter3
console.log(new Canvas().filter1().filter2().filter3().getActualCanvas());
// filter1 filter2 filter3

Related

Change a native function's body while keeping the same "identity"

I'm looking into a way to change a native JS function body, while making it not possible to see that it has been changed. Let's take an example with document.hasFocus():
document.hasFocus = ()=>true;
This method works well to spoof focus, but it can be easily detected that it was modified:
document.hasFocus.toString() // -> "()=>true"
Is there any way, in which I can modify such a function while making it impossible to see it has been tampered with?
You can overwrite toString method in Function prototype, and do something like that:
// https://stackoverflow.com/questions/1833588/javascript-clone-a-function
Function.prototype.clone = function() {
var that = this;
var temp = function temporary() {
return that.apply(this, arguments);
};
for (var key in this) {
if (this.hasOwnProperty(key)) {
temp[key] = this[key];
}
}
return temp;
};
Function.prototype.__oldToString = Function.prototype.toString.clone();
function __toStringHooked() {
if ((this.name == "")||(this.name == "hasFocus")) // on Firefox, hasFocus doesn't have any name
{
return eval+"" // this matches regexp
} else {
return this.__oldToString(); // we're returning default value
}
}
Function.prototype.toString = __toStringHooked
document.hasFocus = () => true
The code above is from Th3B0r3dD3v3l0p3r's GitHub repo, you can check it if you want: https://github.com/Th3B0r3dD3v3l0p3r/focus-spoofer/

How to use recursion in JavaScript for try do function?

I want to make this code prettier with recursion.
findModel = function(oldModel, ...modelStyles) {
let model = oldModel.elements;
let i = 0;
try {
do {
model = model.children.find(child => child.mStyle === modelStyles[i]);
i += 1;
} while (i < modelStyles.length);
return model;
} catch (e) {
return undefined;
}
};
tried this:
findModel = function(oldModel, ...modelStyles) {
let model = oldModel.elements;
let i = 0;
if (i < modelStyles.length) {
model = model.children.find(child => child.mStyle === modelStyles[i]);
i += 1;
return model;
} else {
return undefined;
}
};
but it's still not working well. in the first code I get only the element, in the second one I get also undefined.
What did I wrong?
As amply noted in comments, you are actually never calling the function recursively.
When it comes to "pretty", I would not go for recursion, but for reduce:
var findModel = function(oldModel, ...modelStyles) {
try {
return modelStyles.reduce((model, style) => model.children.find(child => child.mStyle === style), oldModel.elements);
} catch (e) {} // No need to explicitly return undefined. It is the default
};
If you really need recursion, then first realise that your function expects a first argument type that never occurs again. Only the toplevel model has an elements property, so you can only call this function for ... the top level of your hierarchy.
To make it work, you would need another function that takes the model type as it occurs in the children:
var findModel = function(oldModel, ...modelStyles) {
function recur(model, style, ...modelStyles) {
if (style === undefined) return model;
return recur(model.children.find(child => child.mStyle === style), ...modelStyles);
}
// Need to change the type of the first argument:
try {
return recur(oldModel.elements, ...modelStyles);
} catch (e) {}
};
If you would change the code where the function is called initially, you could of course pass mainmodel.elements instead of mainmodel, so that this type difference problem is resolved. If you can make that change, then the recursive function can become:
var findModel = function(model, style, ...modelStyles) {
if (style === undefined) return model;
try {
return recur(model.children.find(child => child.mStyle === style), ...modelStyles);
} catch (e) {}
};
Still, I would prefer the reduce variant.
The point of recursive function is to call themselves into themselves. In your case, you are calling the function once, but the function never call itself so it just go through once. I'm not sure of the context so I can't fix your code but i can give you an example of recursion.
Lets say we have an object with property. Some are string, some are number and some are objects. If you want to retrieve each key of this object you would need recursion, since you don't know how deep the object goes.
let objectToParse = {
id: 10,
title: 'test',
parent: {
id: 5,
title: 'parent',
someKey: 3,
parent: {
id: 1,
title: 'grand-parent',
parent: null,
someOtherkey: 43
}
}
};
function parseParentKey(object) {
let returnedKey = [];
let ObjectKeys = Object.keys(object);
for(let i = 0; i < ObjectKeys.length; i++) {
if(typeof object[ObjectKeys[i]] === "object" && object[ObjectKeys[i]] !== null) {
// we are calling the methode inside itself because
//the current property is an object.
returnedKey = returnedKey.concat(parseParentKey(object[ObjectKeys[i]]));
}
returnedKey.push(ObjectKeys[i]);
}
return returnedKey;
}
console.log(parseParentKey(objectToParse));
I know this does not answer your question but it gives you a hint on how to use recursion properly. If your first code works, I don't see why you would need to change it in the first place.

Data structure adding and removing items using constructor functions

Its the beginning of a data structure exercise and I am trying to write an add and remove function -its should be so simple and I don't get why its wrong?! Also the way to do it 8using a constructor function, prototype etc. must stay the way it is)
Any help much appreciated!
function Thestack () {
this.array=[];
}
Thestack.prototype.plus = function (i) {
this.array.push(i);
return this; // cannot be edited
};
Thestack.prototype.minus = function () {
this.array.pop();
};
var smallstack = new Thetack();
smallstack.plus(something); //followed by
smallstack.minus();
should return: something
your minus function does not have a return statement, so it just returns undefined by default
You could as in the add function return this so you can continue chaining of methods, return the element removed or return the length of the remaing array
// return this for chaining
Thestack.prototype.minus = function () {
this.data.pop();
return this;
};
// return the removed item
Thestack.prototype.minus = function () {
//edits the data array in place and returns the last element
return this.data.pop();
};
// return the length of the remaining array
Thestack.prototype.minus = function () {
this.data.pop();
return this.data.length;
};

new Object with prototype function vs regular function which returns an object

The basic idea is to check if it starts with an underscore and if there is split the string and return whatever comes after the underscore. This function will be run many times, but for different strings, it is unlikely i will need to retrieve the information more than once for each stirng.
A simple function which will return an object with the data I need:
var parseElementName = function(i) {
var sliced = [i.slice(0, 1), i.slice(1, i.length)];
var obj = {
isClass: null,
name: ''
}
if(sliced[0] === '_') {
obj.name = sliced[1];
obj.isClass = true;
} else {
obj.name = i;
obj.isClass = false;
}
return obj
}
Called with parseElementName(i);
Object with prototyped function
var parsedElement = function(i) {
this.className =
this.isClass = null;
if(this.setElementName(i))
return true
}
parsedElement.prototype.setElementName = function(i) {
var sliced = [i.slice(0, 1), i.slice(1, i.length)];
if(sliced[0] === '_') {
this.className = sliced[1];
this.isClass = true
} else {
this.className = i;
this.isClass = false
}
}
Called with var parsed_element = new parsedElement();
then parsed_element.className or parsedElement.isClass
Which approach is recommended?
I like the object prototype approach best, but I have a few notes about your code:
Use semicolons at the end of each line
Class names should be capitalized. So it should be ParsedElement
I wouldn't call it className, because it is confusing when it is not a class, I would rename it name
The two ways have different outcomes - that constructor+prototype approach will yield an instance which has a setElementName method. Will you ever need this to change the fields of an existing object? It's a simple parser function, so I would assume no. In that case, you should go with returning the object literal:
function parseElementName(i) {
var isClass = i.charAt(0) == '_';
return {
isClass: isClass,
name = isClass ? i.slice(1) : i
};
}
If you really need that method later, consider #MaxMeier's and #HMR's points.

Function call with additional sub functions

I'm asking myself if it's possible to call a function in js, while having additional subfunction inside it
fn(s);
fn.subfn(s);
for example to make utils like this
var s = "123";
string(s) // true
string.blank(s) // false
I think it's possible like this:
function string(s) {
if(s) return typeof(s) === "string";
return {
blank: function(s) {
return s.trim().length === 0;
}
}
}
but every time i call string(s) i'm redefining blank fn, with possible poor performances and poor code, or i'm wrong?
Thanks.
Functions are just objects, so yes, you can just add properties to them:
function string(s) {
return typeof(s) === "string";
}
string.blank = function(s) {
return s.trim().length === 0;
}
This would allow you to make the calls
string(s);
string.blank(s);
just as shown in your example.
Comments to your code:
The function you defined returns an object when you call string, so you would require to call the function as
string().blank(s);
which would be different form the example you showed at the beginning.
You can create a Thing() class and instantiate "thing" objects to prevent redefining functions. (Thing() instead of string() to prevent any sort of collision.)
function Thing(s) {
return {
isString: function() {
return typeof(s) === "string";
},
isBlank: function() {
return s.trim().length === 0;
}
};
}
var t = new Thing("123");
t.isString() // true
t.isBlank() // false
http://jsfiddle.net/KKrsa/
You could try something like this (untested):
function string(s) {
if(s) return typeof(s) === "string";
}
string.blank = function(s) {
return s.trim().length === 0;
}
You might run into issues using "string" for the name of your function, though, because it may clash with the existing String object.

Categories

Resources