Restoring a nullified function back in JavaScript - javascript

I was simply practicing a little bit of JavaScript. My goal was to create a function that can call another function with the .invoke() until .revoke() is called, which then nullifies the function.
Later on, I've added .porcupine() which was, in theory, supposed to take the firstly invoked function (in this case, alert()) and then reapply it to the original "temp". The issue is, though, after being revoked temp becomes unknown, therefore it can not call anything anymore. Is there something very obvious to this that I'm missing out or will the solution have to be fairly messy?
var denullifier;
function revocable(unary) {
if (denullifier === null)
denullifier = unary;
return {
invoke: function(x) {
return unary(x);
},
revoke: function() {
var nullifier = unary;
unary = null;
return nullifier.apply(this, arguments);
},
porcupine: function() {
unary = denullifier;
return unary.apply(denullifier, arguments);
}
};
};
console.log('----------');
temp = revocable(alert);
temp.invoke(7); ///alerts 7
temp.revoke();
temp.porcupine(); //exception
temp.invoke(7); //doesn't get here

I don't quite understand what you're doing, but there are a few problems with your code.
if (denullifier === null)
denullifier = unary;
denullifier is not null here, it's undefined - so the condition isn't met.
return nullifier.apply(this, arguments);
You can't call alert this way, the first param must be null or window.
return unary.apply(denullifier, arguments);
The same.

This is your problem:
var denullifier;
function revocable(unary) {
if (denullifier === null)
denullifier = unary;
denullifier is undefined when declared without a value. However, you are checking for type-strict equality with null, which will be false, so denullifier is never set and porcupine is not able to restore the unary function.
I'd suggest:
Use == instead of === to get equality with undefined
Even better, use typeof denullifier != "function"
Or, (although I don't know your design) you should not make denullifier a global, static variable that will be shared amongst revocable instances, but instead make it instance-specific by putting the declaration inside the function body.

Related

OOP Question About Vanilla JS: The class's constructor won't accept the variable I'm feeding it as a parameter

I'm trying to learn OOP through practice, but I'm pretty stuck at this point.
This is the code:
const itemEdit = () => {
let editIndex = buttonObj.editArr.indexOf(editID);
console.log(`the editIndex outside of the class is ${editIndex}`);
if (typeof editIndex != "undefined") {
editText = new htmlTextualizer(editIndex);
console.log(
"new class successfully created as variable is not 'undefined' type"
);
}
editText.printOut();
This is the class/constructor:
class htmlTextualizer {
constructor(curr) {
this.curr = curr;
}
printOut() {
console.log(this.curr);
}
}
The output is either 'undefined' or nothing at all. The logic generally works outside of the function, so I suspect it's something to do with the scope of initiation, but I simply fail to work my way around it. Assistance would be much appreciated. Thanks.
JavaScript's indexOf() returns -1 if no match is found. That check should look something like this:
if (editIndex > -1) {…}
I'm not sure if that will resolve your problem or not, but it's a problem in general.
Also, if that if statement is not true, and if editText is not defined somewhere outside what you've pasted here, there will be an error because editText is undefined (and doesn't have methods available).
There are several things that are unclear about your example, since you reference several undefined objects: buttonObj.editArr, editID, editText.
In general, I would approach testing for existence more carefully. You don't want to attempt to access the indexOf method on something undefined.
I'm not sure what your business logic is exactly, but here is how to do what I think it is: always create the new object, unless buttonObj.editArr contains editID.
Here is how to do that:
const itemEdit = () => {
if ( !buttonObj ||
!buttonObj.editArr ||
(typeof buttonObj.editArr !== "object") ||
!editID ||
(buttonObj.editArr.indexOf(editID) < 0) ) {
editText = new htmlTextualizer(buttonObj.editArr.indexOf(editID));
console.log("creating instance of class htmlTextualizer");
}
}

Checking if parameter is a contructor or an instance in javascript

I wanna do the following, if the parameter passed is a contructor, then do new 'constructor' if not, just use the instance. How can I do that?
This is what I've done so far, but it doesn't work. I think something is wrong with my code:
JS
var showList = function (view, options) {
// checking if view is a conctructor or not
if (view instanceof view) {
app.getRegion('main').show(view(options));
} else {
app.getRegion('main').show(new view(options));
}
}
so the above function can be used as:
var listView = new ListView;
showList(listView);
or straight:
showList(new ListView);
I think you're going to want to test whether the argument is an object or a function:
if (typeof view === "function")
will tell you it's a function (a constructor function in your context)
if (typeof view === "object")
will tell you that it's an already constructed object.
var showScreen = function (view, options) {
// check if view is already an object
if (typeof view === "object") {
app.getRegion('main').show(view(options));
} else {
app.getRegion('main').show(new view(options));
}
}
One thing I'm confused about in your code is if view is already an object, then why do you do view(options). That doesn't make sense to me. Doing new view(options) when view is a function makes sense, but not the other option so I think something also needs to be corrected with that line of code. Do you perhaps mean to call a method on that object?
FYI, I tend to avoid using instanceof as a general practice if there is another option because instanceof can have issues with cross frame code whereas typeof does not have those issues.
var showScreen = function (view, options) {
// checking if view is a conctructor or not
if (view instanceof Function) {
app.getRegion('main').show(new view(options));
} else {
app.getRegion('main').show(view(options));
}
}
Maybe not the best way but well.
function A(){}
var a = new A();
a instanceof A // true
a instanceof Function // false
A instanceof Function // true
This seems like a code smell to me. I think it's better pass an instance instead of a constructor function.
In this case you can do:
showScreen(new ListView(options))
If it's hard to construct a ListView you should wonder why that is.

Overwriting property in Object.observe()

I want to use Chrome's experimental Object.observe() in order to overwrite all functions being set on the object:
→ jsFiddle
var obj = {};
Object.observe(obj, function (changes) {
changes.forEach(function (data) {
if ((data.type == "new" || data.type == "updated") &&
typeof data.object[data.name] == "function" &&
typeof data.object[data.name].isWrapper == "undefined") {
data.object[data.name] = function () {
};
data.object[data.name].isWrapper = true;
}
});
});
obj.helloWorld = function () {
console.log("helloWorld() was called");
};
obj.helloWorld();
Unfortunately, the console still displays "helloWorld() was called". Is it actually possible to overwrite the currently changed value in an object observer?
Since this is only an experiment (no production code!), I appreciate any kind of solution.
Well, you cannot really solve the problem at hand.
While you can overwrite changed values again in the observer, the observer is only executed asynchronously, unless Object.deliverChangeRecords is explicitly called, so it is only executed after obj.helloWorld() was already called in the same turn it was defined.
I updated your fiddle to show just that:
var obj = {};
function obs(changes) {
changes.forEach(function (data) {
if ((data.type == "new" || data.type == "updated") &&
typeof data.object[data.name] == "function" &&
typeof data.object[data.name].isWrapper == "undefined") {
data.object[data.name] = function () {
console.log("intercepted", data.name);
};
data.object[data.name].isWrapper = true;
}
});
}
Object.observe(obj, obs);
obj.helloWorld = function () {
console.log("helloWorld() was called");
};
// Will call the original function, as changes are not yet delivered.
obj.helloWorld();
Object.deliverChangeRecords(obs);
// Will call the intercepted function, as the changes were explicitly delivered synchronously.
obj.helloWorld();
obj.helloWorld2 = function () {
console.log("helloWorld2() was called");
};
// Will call the intercepted function, as first the changes will be delivered (end of turn) and only then the timeout callback will be called.
setTimeout(function() { obj.helloWorld2(); }, 0);
Not entirely sure if the setTimeout bits are implicitly mandated by the spec proposal or just an implementation detail, though.
Since there is no way to observe any changes immediately and synchronously without the modifying code explicitly executing Object.deliverChangeRecords, this API isn't really suited for what you're trying to achieve, at least when it comes to the current spec proposal.
A viable alternative to Object.observe might be Proxy, which is actually meant to do things like this and which IIRC is also available in Chrome (with experimental harmony features turned on).
Here is a fiddle using Proxy.

Overriding a Javascript method only if it does not exist

My goal here is to override a method if it isn't found, otherwise use the original method (for backwards compatibility of a library I can't alter).
This is what I have so far, but am still struggling with:
this.grid.getDataSource = function(){
if (typeof this.grid.getDataSource.getDataSource == "undefined")
return this.grid.getDataSource.getStore();
else return this.grid.getDataSource.getDataSource();
}
I want to have getDatasource() check if it exists, if not, call getStore(). If it does exist, just use the original getDatasource(). I know this breaks because I haven't figured out how to reference the parent 'this' scope. When I work around that issue I get into a recursive loop as it keeps trying to override itself. If you have a better way of doing this please let me know!
i think this should do what you want.
this.grid.getDataSource =
this.grid.getDataSource.getDataSource || this.grid.getDataSource.getStore;
this statement will try to find something that evaluates truish from left to right. when it finds that thing, it will use it as the value for the assignment. in this case if getDataSource is undefined it'll evaluate as false, and getStore will be checked. getStore exists so it'll evaluate to (roughly) true, and so the function reference will be assigned to this.grid.getDataSource.getDataSource;
If you're sure that getDataSource() will not throw an exception, you can try
this.grid.getDataSource.getDataSource = function(){
try {
return this.getDataSource();
}
catch(ex) {
return this.getStore();
}
};
or you can just change
if (typeof this.getDataSource == "undefined")
to
if (typeof this.getDataSource != "function")
UPDATE:
Does this work?:
this.grid.getDataSource = function(){
if (typeof this.getDataSource != "function")
return this.getStore();
else
return this.getDataSource();
}

Testing if value is a function

I need to test whether the value of a form's onsubmit is a function. The format is typically onsubmit="return valid();". Is there a way to tell if this is a function, and if it's callable? Using typeof just returns that it's a string, which doesn't help me much.
EDIT: Of course, I understand that "return valid();" is a string. I've replaced it down to "valid();", and even "valid()". I want to know if either of those is a function.
EDIT: Here's some code, which may help explain my problem:
$("a.button").parents("form").submit(function() {
var submit_function = $("a.button").parents("form").attr("onsubmit");
if ( submit_function && typeof( submit_function.replace(/return /,"") ) == 'function' ) {
return eval(submit_function.replace(/return /,""));
} else {
alert("onSubmit is not a function.\n\nIs the script included?"); return false;
}
} );
EDIT 2: Here's the new code. It seems that I still have to use an eval, because calling form.submit() doesn't fire existing onsubmits.
var formObj = $("a.button").parents("form");
formObj.submit(function() {
if ( formObj[0].onsubmit && typeof( formObj.onsubmit ) == 'function' ) {
return eval(formObj.attr("onsubmit").replace(/return /,""));
} else {
alert("onSubmit is not a function.\n\nIs the script included?");
return false;
}
} );
Suggestions on possibly how to do this better?
I'm replacing a submit button with an
anchor link. Since calling
form.submit() does not activate
onsubmit's, I'm finding it, and
eval()ing it myself. But I'd like to
check if the function exists before
just eval()ing what's there. – gms8994
<script type="text/javascript">
function onsubmitHandler() {
alert('running onsubmit handler');
return true;
}
function testOnsubmitAndSubmit(f) {
if (typeof f.onsubmit === 'function') {
// onsubmit is executable, test the return value
if (f.onsubmit()) {
// onsubmit returns true, submit the form
f.submit();
}
}
}
</script>
<form name="theForm" onsubmit="return onsubmitHandler();">
<a href="#" onclick="
testOnsubmitAndSubmit(document.forms['theForm']);
return false;
"></a>
</form>
EDIT : missing parameter f in function testOnsubmitAndSubmit
The above should work regardless of whether you assign the onsubmit HTML attribute or assign it in JavaScript:
document.forms['theForm'].onsubmit = onsubmitHandler;
Try
if (this.onsubmit instanceof Function) {
// do stuff;
}
You could simply use the typeof operator along with a ternary operator for short:
onsubmit="return typeof valid =='function' ? valid() : true;"
If it is a function we call it and return it's return value, otherwise just return true
Edit:
I'm not quite sure what you really want to do, but I'll try to explain what might be happening.
When you declare your onsubmit code within your html, it gets turned into a function and thus its callable from the JavaScript "world". That means that those two methods are equivalent:
HTML: <form onsubmit="return valid();" />
JavaScript: myForm.onsubmit = function() { return valid(); };
These two will be both functions and both will be callable. You can test any of those using the typeof operator which should yeld the same result: "function".
Now if you assign a string to the "onsubmit" property via JavaScript, it will remain a string, hence not callable. Notice that if you apply the typeof operator against it, you'll get "string" instead of "function".
I hope this might clarify a few things. Then again, if you want to know if such property (or any identifier for the matter) is a function and callable, the typeof operator should do the trick. Although I'm not sure if it works properly across multiple frames.
Cheers
What browser are you using?
alert(typeof document.getElementById('myform').onsubmit);
This gives me "function" in IE7 and FireFox.
using a string based variable as example and making use instanceof Function
You register the function..assign the variable...check the variable is the name of function...do pre-process... assign the function to new var...then call the function.
function callMe(){
alert('You rang?');
}
var value = 'callMe';
if (window[value] instanceof Function) {
// do pre-process stuff
// FYI the function has not actually been called yet
console.log('callable function');
//now call function
var fn = window[value];
fn();
}
Make sure you are calling typeof on the actual function, not a string literal:
function x() {
console.log("hi");
}
typeof "x"; // returns "string"
typeof x; // returns "function"
You can try modifying this technique to suit your needs:
function isFunction() {
var functionName = window.prompt('Function name: ');
var isDefined = eval('(typeof ' + functionName + '==\'function\');');
if (isDefined)
eval(functionName + '();');
else
alert('Function ' + functionName + ' does not exist');
}
function anotherFunction() {
alert('message from another function.');
}
form.onsubmit will always be a function when defined as an attribute of HTML the form element. It's some sort of anonymous function attached to an HTML element, which has the this pointer bound to that FORM element and also has a parameter named event which will contain data about the submit event.
Under these circumstances I don't understand how you got a string as a result of a typeof operation. You should give more details, better some code.
Edit (as a response to your second edit):
I believe the handler attached to the HTML attribute will execute regardless of the above code. Further more, you could try to stop it somehow, but, it appears that FF 3, IE 8, Chrome 2 and Opera 9 are executing the HTML attribute handler in the first place and then the one attached (I didn't tested with jQuery though, but with addEventListener and attachEvent). So... what are you trying to accomplish exactly?
By the way, your code isn't working because your regular expression will extract the string "valid();", which is definitely not a function.
If it's a string, you could assume / hope it's always of the form
return SomeFunction(arguments);
parse for the function name, and then see if that function is defined using
if (window[functionName]) {
// do stuff
}
Isn't typeof xxx === 'function' the best and the fastest?
I made an bench in wich you can try it out, compared to instanceof and _underscore
Its just seems to be faster than instanceof (using chrome)
It won't trow an error if the variable is not defined
Here a bench: https://jsbench.me/qnkf076cqb/1
Checking the call method on the value seems to be a good enough test. e.g., val.call && val()
> a = () => {}
[Function: a]
> function b() {}
undefined
> c = function(){}
[Function: c]
> d = 2
2
> e = []
[]
> f = {}
{}
> a.call
[Function: call]
> b.call
[Function: call]
> c.call
[Function: call]
> d.call
undefined
> e.call
undefined
> f.call
undefined
Note: Except when it's a class.
Well, "return valid();" is a string, so that's correct.
If you want to check if it has a function attached instead, you could try this:
formId.onsubmit = function (){ /* */ }
if(typeof formId.onsubmit == "function"){
alert("it's a function!");
}
You can always use one of the typeOf functions on JavaScript blogs such as Chris West's. Using a definition such as the following for the typeOf() function would work:
function typeOf(o){return {}.toString.call(o).slice(8,-1)}
This function (which is declared in the global namespace, can be used like this:
alert("onsubmit is a " + typeOf(elem.onsubmit));
If it is a function, "Function" will be returned. If it is a string, "String" will be returned. Other possible values are shown here.
I think the source of confusion is the distinction between a node's attribute and the corresponding property.
You're using:
$("a.button").parents("form").attr("onsubmit")
You're directly reading the onsubmit attribute's value (which must be a string). Instead, you should access the onsubmit property of the node:
$("a.button").parents("form").prop("onsubmit")
Here's a quick test:
<form id="form1" action="foo1.htm" onsubmit="return valid()"></form>
<script>
window.onload = function () {
var form1 = document.getElementById("form1");
function log(s) {
document.write("<div>" + s + "</div>");
}
function info(v) {
return "(" + typeof v + ") " + v;
}
log("form1 onsubmit property: " + info(form1.onsubmit));
log("form1 onsubmit attribute: " + info(form1.getAttribute("onsubmit")));
};
</script>
This yields:
form1 onsubmit property: (function) function onsubmit(event) { return valid(); }
form1 onsubmit attribute: (string) return valid()
// This should be a function, because in certain JavaScript engines (V8, for
// example, try block kills many optimizations).
function isFunction(func) {
// For some reason, function constructor doesn't accept anonymous functions.
// Also, this check finds callable objects that aren't function (such as,
// regular expressions in old WebKit versions), as according to EcmaScript
// specification, any callable object should have typeof set to function.
if (typeof func === 'function')
return true
// If the function isn't a string, it's probably good idea to return false,
// as eval cannot process values that aren't strings.
if (typeof func !== 'string')
return false
// So, the value is a string. Try creating a function, in order to detect
// syntax error.
try {
// Create a function with string func, in order to detect whatever it's
// an actual function. Unlike examples with eval, it should be actually
// safe to use with any string (provided you don't call returned value).
Function(func)
return true
}
catch (e) {
// While usually only SyntaxError could be thrown (unless somebody
// modified definition of something used in this function, like
// SyntaxError or Function, it's better to prepare for unexpected.
if (!(e instanceof SyntaxError)) {
throw e
}
return false
}
}
if ( window.onsubmit ) {
//
} else {
alert("Function does not exist.");
}
Beware that es6 class is also a function but not callable
class C {}
typeof C === "function" // true
C instanceof Function // true
C() // error
C.call() // error
new C() // okay
new C // okay
A simple check like this will let you know if it exists/defined:
if (this.onsubmit)
{
// do stuff;
}

Categories

Resources