This is weird! I am struggling hard with the problem that javascript Proxy handler's apply trap does not get the Proxy itself passed as an argument, only the naked function. But I need to pass along some metadata to use for re-wrapping function results.
So I thought I could just use Object.create to make a specialization of the function to which I could stick additional information. But that doesn't work! And that's surprising!
function test() { return "hi!"; }
test() // 'hi!'
test1 = test // ƒ test() { return "hi!"; }
test1() // 'hi!'
test2 = Object.create(test) // Function {}
test2() // Uncaught TypeError: test2 is not a function
test3 = new Function([], "return 'lo!';") // ƒ anonymous() { return 'lo!'; }
test3() // 'lo!'
test2.prototype.constructor() // 'hi!'
test3.prototype.constructor() // 'lo!'
Object.getPrototypeOd(test2)() // 'hi!'
So I guess I can could help myself evaluate a function if I unwrapped it somehow:
while(fn instanceof Function && typeof fn != 'function')
fn = Object.getPrototypeOf(fn);
But that doesn't work if I just want to call it, i.e., make my special function transparent to any downstream user.
OK, here is a workaround, instead of using Object.create(fn) I can just wrap it:
fn = function() { return fn.apply(this, arguments); }
now I can stick my special metadata to this fn and I can also wrap it in a Proxy.
But my question is: what is the meaning of Object.create(fn) if you don't get an actual callable function?
Short answer: Function can be called; Object cannot be called.
See Function like inheritance of Object. Functions are object but objects are not function.
See this:
function test() { return "hi!"; }
test() // 'hi!'
typeof test // 'function'
test instanceof Object // true
test2 = Object.create(test)
typeof test2 // 'object'
test2 instanceof Object // true
typeof test2.prototype.constructor // 'function'
// something else that works
sample1 = Object.create({test})
typeof sample1 // 'object'
typeof sample1.test // 'function'
sample1.test() // 'hi!'
If you insist to make an Object callable, maybe your closest option is to create your own class that have function behaviour. There're some suggestions here: Can you make an object 'callable'?
Related
Colleague showed me the next code, which blown my mind:
const x = Object.create(function() {});
// variant: const x = Object.create(Array);
console.log(x.call) // ƒ call() { [native code] }
console.log(typeof x.call) // "function"
console.log(x.call instanceof Function) // true
x.call() // Uncaught TypeError: x.call is not a function
I understand that x.call is prototyped from function, it's not own x's property:
x.hasOwnProperty('call') // false
But why x.call can't actually being executed? Is it something related to call keyword?
The core idea behind Object.create boils down to this:
function create(prt){
var noOP = function(){};
noOP.prototype = prt;
return new noOP;
}
So, the returned value is NOT a function, it is an object. To illustrate, I'll first store a function:
var u = function(){return 5}
Now I'll use Object.create on it:
var res = create(u);
Consoling it will give you >noOP {}, so it is a plain object. The problem starts from here:
res.hasOwnProperty("prototype") //false
So the newly created object has "prototype" but this is in fact inherited from u:
res.prototype === u.prototype //true
Similary, "call" is again inherited from u which in turn u inherits from its constructor's (Function) prototype:
res.call === u.call //true
res.call === Function.prototype.call //also true
And here is your problem, if you look at the EcmaScript implementation of call, it expects a this and this should be callable. Isolate call from res :
var x = res.call; //ƒ call() { [native code] }
Now I will "call" the call, we will pass 3 arguments, 1st for what to call, 2nd for setting this inside that callable, 3rd and so forth for arguments for the callable:
x.call(function(a){console.log("hey");console.log(a);console.log(this);},5,5)
//hey
//5
//Number {5}
Now try the same on your created object res either by res.call or x.call:
x.call(res,5,5) //TypeError: x.call is not a function
In the end, it boils down to returned object from Object.create not being callable.
Cause x is an object that inherits call from Function.prototype, however call is meant to be called on a function, therefore it fails if you try to execute it on a plain object.
I'm trying to modify .offset() so that it never returns undefined, i.e. for invisible/hidden elements.
The problem that I'm having with that is that I don't know how to properly pass this to the original method, to be honest. Breakpoint-debugging reveals that I keep getting Window when it should be the element in question. Here's what I got:
(function( $ ){
var original = $.fn.offset;
$.fn.offset = function( elem, coordinates, pass ) {
console.log('step 1'); // successful.
console.log(this); // element confirmation.
var o = original(elem, coordinates, pass); // triggers an exception.
console.log('step 2'); // is never reached.
if (o === undefined ) {
console.log('step 3a');
return {};
}
else {
console.log('step 3b');
return o;
}
}
}( jQuery ));
[…]
$('#element').offset(); // method call.
And here's the exception:
Error in event handler for (unknown): TypeError: undefined is not a function
at jQuery.fn.extend.offset (chrome-extension://…/js/jquery-1.11.0.js:10109:10)
at $.fn.offset (chrome-extension://…/js/jquery-modifications.js:35:11)
I've tried different variations – original(arguments), this.original(), original(this, …) – but none worked. In this question an argument called elem is used next to three more arguments – but I'm not sure why. Is it because the API mentions attributeName, value and a callback function? If so then my attempt should work, analogous to the .offset() API. Looking at how jQuery defines these functions didn't help either because .fn.attr doesn't even show up like .fn.offset does.
As you said:
The problem that I'm having with that is that I don't know how to properly pass this to the original method, to be honest.
In order to change function scope (this) there are two Function Prototype methods, called call() and apply()
References to documentation of these two methods:
Function.prototype.call()
Function.prototype.apply()
So, your code should now looks like this:
var original = $.fn.offset;
$.fn.offset = function() {
var o = original.apply(this, arguments);
return typeof o === 'undefined' ? {} : o;
};
and
return typeof o === 'undefined' ? {} : o;
It's just a better (IMO) shorter version of:
if (typeof o === 'undefined') {
return {};
} else {
return o;
}
In PHP we can define the argument value for the functions if it's not set(called), so for example:
<?php
function blah($arg = false){
var_dump($arg);
}
?>
In the above example if we call the function like:
<?php
blah();
// ==> out put will be: false;
blah(true);
// ==> out put will be: true;
?>
So we can define a value for the arguments if they are not settled while we call the function, how this could be achieved in javascript functions?
I have it exactly like PHP:
<script>
function blah(arg = false){
//...
}
</script>
The above code works just fine in Mozilla Firefox, but in Chrome, the function is not working and gets fixed when I remove = false in the parenthesis, Chrome developer tools says:
Uncaught Syntax Error: Unexpected token =
This is not possible in Javascript.
Try this Conditional Operator statement instead:
<script>
function blah(arg){
arg = typeof arg !== 'undefined' ? arg : 'someValue';
}
</script>
Where 'someValue' is the default value that the arg variable will get when there are no arguments passed to the blah() function.
This is the cleanest pattern for implementing default arguments in javascript IMO.
function (arg) {
arg = arg || 'defaultVale';
}
However this can fail if you expect the value of arg to be some falsy value, e.g 0, false, NaN, null using it is not really recommended.
This version protects against this case by explicitly comparing with undefined.
function (arg) {
arg = arg === undefined ? 'defaultVale' : arg;
// As T.J Crowder pointer out if not in strict mode or if the code will run
// in an IFrame use typeof arg === "undefined" instead of directly
// comparing with undefined
}
Another nice pattern is using objects for arguments instead. This has two benefits
Order of arguments is not important
It's easy to implement default arguments
Code
var defaults = {
arg1: 10,
arg2: 20
};
var f = function (args) {
args = jQuery.extend(true, args, defaults); //jQuery
args = _.defaults(args, defaults); // Underscore
};
f({
a: 25 //Use a non default value
});
In JavaScript there is no default parameter.
Just write the code like:
function test(arg) {
if(arg) {
// do something as arg is defined.
} else {
// do something as arg is undefined.
}
}
test(true); // arg is defined
test(); // arg is undefined
Simple variation
function defaulter(p1) {
p1 = p1 || "default";
}
In Js you can't have default values for parameters. You can check if the data is of a known type with typeof operator:
function blah(arg)
{
if (typeof arg === 'undefined')
{
arg = false;
}
}
or setting his value in a short-circuit way
function blah(arg)
{
arg = arg || false;
}
For example in coffeescript you can set it by using
blah = (arg = "mydefaultvalue") ->
that is translated into
blah = function(arg)
{
if (arg == null)
{
arg = "mydefaultvalue";
}
}
I don't think there's such thing because I guess I would have found it by now but the only way to be certain is to be given a straight answer, therefore I must ask before I give up. I need a method that's going to be called every time I try to access an object's method. Is there such a thing in JavaScript?
Basically I need to run a couple of lines before and after each of my object's methods. Hardcoding them is really not an option. The other thing I thought of was to have a main method like
Mainmethod(ActualMethod, Parameters)
But this really doesn't look good to me, I'd really like not having to do this.
If this is just for a particular object or a particular type of object, you can dynamically replace all the methods with your own stub that does your pre-work, calls the original method and then does your post-work.
Something like this will work:
function overideMethods(obj) {
// can pass either an instantiated object (hooks existing methods on the object)
// or a function constructor (hooks methods in the prototype)
if (typeof obj === "function") {
obj = obj.prototype;
}
for (var prop in obj) {
if (typeof obj[prop] === "function") {
(function(origMethod) {
obj[prop] = function() {
var retVal, args;
// do your pre-work here
// make copy of args passed to this method
args = Array.prototype.slice.call(arguments, 0);
// call original method with proper args
retVal = origMethod.apply(this, args);
// do your post-work here
return retVal;
};
})(obj[prop]);
}
}
}
Working demo: http://jsfiddle.net/jfriend00/7LzQj/
You can now pass this function either an object or a constructor function. If you pass it an object, it will hook the existing enumerable methods on that object. If you pass it a constructor function, it will hook the methods on the constructor's prototype. This allows you to set up the hooks for all objects made from an entire constructor (in advance) or just hook an individual object.
If your object adds methods dynamically, either in the constructor or later in the life of the object and you want those methods hooked, then you will need to call overideMethods() on the object AFTER those methods are added - you will not be able to just call it on the constructor.
Here's a possible solution with function decorators. If you have something like Underscore at hand you can trim the code a bit, but I'm assuming you don't.
A decorator is a higher-order function that returns a modified version of another function. Decorators are a safer approach to monkey patching in some situations but it all depends on your needs.
Here's a demo: http://jsbin.com/ApaLAVab/1/edit
function compose(f, g) {
return function() {
return f(g.apply(this, arguments));
};
}
function before(fn) {
return function() {
console.log('before'); // code before method
fn.apply(this, arguments);
};
}
function after(fn) {
return function() {
fn.apply(this, arguments);
console.log('after'); // code after method
};
}
var run = compose(before, after);
function A() {}
A.prototype = {
say: run(function(name) { // decorate method
console.log('Hello '+ name);
})
};
var a = new A();
a.say('Peter');
//^ before
// Hello Peter
// after
You can also add it to the constructor, so you don't have to run it manually:
function SimpleClass(){
this.overideMethods();
}
SimpleClass.prototype.overideMethods = function() {
var obj = this;
for (var prop in obj) {
if (typeof obj[prop] === "function") {
console.log(prop);
(function(origMethod) {
obj[prop] = function() {
var retVal, args;
// do your pre-work here
alert("before function call");
// make copy of args passed to this method
args = Array.prototype.slice.call(arguments, 0);
// call original method with proper args
retVal = origMethod.apply(this, args);
// do your post-work here
alert("after function call");
return retVal;
};
})(obj[prop]);
}
}
}
SimpleClass.prototype.testFn = function(){
alert("In the function.");
};
var testObj = new SimpleClass();
testObj.testFn();
Working example: http://jsfiddle.net/awesomepeter/wvxAd/1/
Credit to jfriend00 though, i wanted to do the same thing as him just came a little bit too late ;) So i just copypasted his answer and improved.
This question already has answers here:
How to execute a JavaScript function when I have its name as a string
(36 answers)
Closed 8 years ago.
I am dynamically loading functions as needed in my web app. When I request these functions as I need them, I'd like to check if they already exist. So I pass in an array of the function names such as ['Result','Question']. I then loop through that array and would like to see if it's typeof is a function, if so, then I know I don't need to load it.
Here is a simplified version:
function Result(){}
var functionName = 'Result';
if (typeof functionName == 'function'){
alert('function exists, don't load it');
else
alert('function does not exist, load it');
I know the above example doesn't work because I need the value of functionName, not functionName itself. Any ideas how to do this?
Supposing that your function is global you can verify if it exists using window object (the object where are stored the global variables).
if (typeof window[functionName] === 'function') {
// function exists
}
JSFIDDLE
You can use window[functionName] as follows:
function Result(){}
var functionName = 'Result';
if (typeof window[functionName] == 'function'){
alert("function exists, don't load it");
}else{
alert("function does not exist, load it");
}
However you need to make sure you escape the single quote in don't or use double quotes since it terminates your String literal.
Here is a working example: http://jsfiddle.net/WN92K/
You may do this and see if your function is global.
if (window[functionName] instanceof Function){
You can use typeof and try catch : (window[fctName] doesn't always works because you aren't always in the window "scope" or you need to test object members (or simulated namespace etc.))
function isFunction(fctName) {
try {
return typeof(eval(fctName)) === "function";
} catch (err) {
return false;
}
}
function isFunction2(fctName) {
return typeof(window[fctName]) === "function";
}
var myFunc1 = function () {};
function myFunc2() {};
var aString = "hello world!";
console.log(myFunc1); // function () {}
console.log(myFunc2); // function myFunc2() {}
// Can't work : Exception throw
// console.log(myFunc3);
console.log(window['myFunc1']); // function () {}
console.log(window['myFunc2']); // function myFunc2() {}
console.log(window['myFunc3']); // undefined
console.log(isFunction("myFunc1")); // true
console.log(isFunction("myFunc2")); // true
console.log(isFunction("myFunc3")); // false
console.log(isFunction2("myFunc1")); // true
console.log(isFunction2("myFunc2")); // true
console.log(isFunction2("myFunc3")); // false
console.log(isFunction('aString.toLowerCase')); // true
console.log(isFunction('aString.toLower')); // false : toLower doesn't exist
console.log(isFunction2('aString.toLowerCase')); // false : aString.toLowerCase isn't defined ! (but aString was)
console.log(isFunction2('aString.toLower')); // false : same reason than above
// So Eval can be usefull
http://jsfiddle.net/L6Q9N/2