the problem is that when i use test.call(), it calls into my call implementation of the prototype but when i use test(), it doesnt call call(). i want to be able to use test to trigger prototype.call(). code below:
Function.prototype.call = function () {
//do something...
return this(); // call original method
}
function test() {
alert("test");
}
test.call(); //calls prototype.call()
test(); //doesnt call prototype.call()
Why would you expect test() to invoke Function.prototype.call? They're different functions.
The native .call() method that you're overwriting is not invoked every time a function is invoked. It's only invoked when you invoke it.
Invoking .call() does invoke test() because that's what it's designed to do. It expects a function as its context (this value), and invokes that function. But that doesn't mean .call() has anything to do with any other function invocation.
Here's a solution that I just whipped up (credit goes to cliffsofinsanity for pointing out a crucial error with the code and correcting it). It logs all non-standard functions called after a certain point, in the order that they were called.
// array of all called functions
var calledFunctions = [];
// custom func object that has name of function and
// and the actual function on it.
function func(_func, name) {
return function() {
calledFunctions.push(name)
return _func.apply(this, arguments);
};
}
test = function() {
alert("hi");
}
otherTest = function() {
alert("hello");
}
// put this bit somewhere after you've defined all your functions
// but *before* you've called any of them as all functions called
// after this point are logged. It logs all non-standard functions
// in the order that they are called.
for (prop in window) {
if (window.hasOwnProperty(prop) && typeof window[prop] === 'function' && window[prop].toString().indexOf('[native code]') < 0) {
window[prop] = func(window[prop], prop);
}
}
otherTest();
test();
otherTest();
console.log(calledFunctions);
Working demo can be found here.
Related
I'm getting the following error in the console:
Uncaught ReferenceError: jason is not defined
Here is my javascript:
$(document).ready(function jason() {
console.log("test");
});
var addEvent = function(object, type, callback) {
if (object == null || typeof(object) == 'undefined') return;
if (object.addEventListener) {
object.addEventListener(type, callback, false);
}
};
addEvent(window, "resize", jason());
Thanks so much for the help!
When you put it inside $(document).ready() it is not available outside that function. Make it global and define it without $(document).ready(), but this will run even before the page loads.
function jason() {
console.log("test");
};
var addEvent = function(object, type, callback) {
if (object == null || typeof(object) == 'undefined') return;
if (object.addEventListener) {
object.addEventListener(type, callback, false);
}
};
addEvent(window, "resize", jason());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
In Javascript, there are many ways to define functions. They generally fall into two categories though: function declarations and function expressions.
Function declarations look like this:
function jason() {
console.log("test");
}
A function declaration has global scope regardless of where it is in the code, and it can be called earlier in the program than where it is defined. (Edit: actually, if a function declaration is nested within another function, then it can only be called within that outer function)
Function expressions look like this:
// Anonymous function expression set to a variable
let jason = function() {
console.log("test");
};
// Named function expression set to a variable
let jason = function freddy() {
console.log("test");
};
They are scoped just like any other variable. They must be set before they can be called.
In your case, you created jason() as a function expression but didn't assign it to a variable. You did name it jason, but that name only works inside of the function (like for recursion), not elsewhere. If you were only going to use the jason() function as an argument to $(document).ready(), then defining as you did would be ok. But since you seem to want to use jason() in more than one place, you either need to 1) change it a function declaration or 2) make it a function expression that is assigned to a variable prior to being passed to $(document).ready() and addEvent().
By the way, when passing a function as an argument, you need to leave off the parentheses, like this:
$(document).ready(jason);
addEvent(window, "resize", jason);
If you do the following instead, it will execute jason() and pass the value it returns to each of those funtions. In this case, that value would be undefined.
// Not what you want
$(document).ready(jason());
addEvent(window, "resize", jason());
Function expressions can be given a name, but as noted in the MDN link, the name is only available within the function's body.
Function declarations which cause a named function to be hoisted to the top of the function or script element in which they reside are also function statements.
Naming and providing the code for jsan as an argument value for a call to ready prevents it being treated as a statement and creates a function expression.
Obviously if you want to call or reference the jsan function in multiple places it needs to be declared in the scope of everywhere it's referenced - which could require creating a global function or declaring it in a common outer function, depending on the structure of the code.
Jason is wrongly define. Just check the below code, first you define a function and then call it.
$(document).ready(function() {
addEvent(window, "resize", jason());
console.log("test");
});
var addEvent = function(object, type, callback) {
if (object == null || typeof(object) == 'undefined') return;
if (object.addEventListener) {
object.addEventListener(type, callback, false);
}
};
function jason(){
console.log("call jason function");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
You can write your code just in document .ready function().
$(document).ready(function(){
console.log("test");
})
var addEvent = function(object, type, callback) {
if (object == null || typeof(object) == 'undefined') return;
if (object.addEventListener) {
object.addEventListener(type, callback, false);
}
};
addEvent(window, "resize");
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
for my own personal improvement, I was fiddling with closures and functions in JS, when I found this behaviour that deeply puzzles me.
Take this function, assign it to a variable, and call it from two different HTML elements via jQuery:
var print = function(){
console.log("Hello" );
};
document.getElementById('element1').onclick = print();
document.getElementById('element1').onclick = print;
Why on earth is the second element, if clicked, to be the one that prints correctly "hello"?
I always thought you need to put brackets after a function's name to call it.
In fact, if I just call the function by itself (and not via a jQuery event) it works as expected:
var print = function(){
console.log("Hello" );
};
print; //does nothing
print() //prints "Hello"
What I am missing here? Is something related to jQuery?
Thanks in advance!
The difference is calling a function vs taking a reference to a function.
The syntax func() immediately calls the function provided. In element.onclick = func(), the event will be bound to the return value of func.
With element.onclick = func, you are not calling func, simply referencing it and assigning it to the event.
If func happened to return a function, then you could use the element.onclick = func() syntax, and it would do what you expect.
document.getElementById('element1').onclick = print(); // Assigns the *return value* of print
document.getElementById('element1').onclick = print; // Assigns print
function returnPrinter() {
return function print() {
// do stuff
}
}
document.getElementById('element1').onclick = returnPrinter(); // Assigns the return value, which is a function, thus doing what you want
(Too long for comment.)
If you really want to mess with your own head:
var print = function() {
return function() {
console.log("Hello");
};
};
document.getElementById('element1').onclick = print();
This will do what you expect, but perhaps not why you expect it to.
Using
document.getElementById('element1').onclick = print();
is equivalent to the value returned from print (but there is no returned value, so it is undefined)
document.getElementById('element1').onclick = undefined;
When the onclick event handler is called, it calls the assigned value expecting a function. undefined() will also give you the same error. print() will do what you were expecting, and that is why when you use this it works
document.getElementById('element1').onclick = print;
A function is just like any other variable, except it has an additional operator:
Just like with objects and arrays, you can use the dot and square-brace operators to retrieve their properties, you can use the call operator to call the function, but that function is still a variable nevertheless.
function a() {
console.log("potato");
}
var b = a;
b() //prints "potato" on the console
On your example, using the print function on its own (without the call operator did nothing, but that would be the same as if you wrote a number on its own: the result is the value itself!
console.log(a); //prints function a()
console.log(42); //prints 42
When you type a something on its own line, it's just going to return that value to the expression, but without anything to do, nothing happens:
a; //nothing happens
42; //nothing happens
Now, when you use the call operator, it runs your function and the expression's value is whatever the function returned, or undefined if it didn't return anything.
function doStuff() {
return "potato";
}
Now suppose you have this expression:
console.log(doStuff());
It will run doStuff, and return to the expression:
console.log("potato");
Then it will run console.log, with "potato" as its parameter, and it will do its magic to show on the console.
Events are just like that, saving a function to a variable to use later:
function doom() {
return Infinity / 0;
}
onDoomsday = doom;
//somewhere else in your code:
onDoomsday(); //destroys the world
you have to bind reference of a function to onclick but in first case you actually doing nothing other than calling a function:
document.getElementById('element1').onclick = print();
It will actually invoke the function print() and since function has nothing in return so it will not impact on element1 click event.
on the other hand
document.getElementById('element1').onclick = print;
In order to make first case working you need to return a function so it can be invoked on event:
var print = function() {
// when this will invoke it will return function
return function() {
console.log("Hello");
};
};
will assign reference of function to onclick event and it invoke the function on each click event.
I've this situation:
<script>
var cb;
function doSomething(c) {
cb = c();
}
cb();
</script>
But it doesn't work. I want to set a variable as function, to make a callback called by other functions.. Some ideas?
c() executes the function and returns a value, you need to pass a reference to it:
cb = c
Also, you should call the function doSomething(func) to make the assignment.
doSomething(function(){ alert('hello'); });
cb(); // "Hello"
But if what you want is a callback then you don't need a global variable:
function doSomething(callback) {
// do something
if (callback) callback();
}
When you run the function with another function as parameter the callback will run.
You need to assign cb first with your function:
<script>
var cb;
function doSomething(c) {
cb = c;
}
var myFunc = function(){
window.alert("test");
}
doSomething(myFunc);
cb();
</script>
And if you do cb=c(); you will execute function c instantly and return the value to cb if you want the variable cb as the result of c, do like this. otherwise, assign without running it.
You have 3 options to set a variable to a functiion
var fn = functionname;
var fn = function(param){}; this will be an anonymous function
var fn = function FunctionName(param){}; this will be a named function, comes in handy when debugging, since it will present you with a function name (console.log(c);
You cann call it like var returnVal = fn(); or pass it to a function var returnVal = myFunc(fn); where myFunc calls the param, let it be inFn it like inFn();
What might be interesting to note:
Since such a function is related to the global context, you can bind an object to it alter its scope. That gives you the possibility of thisreferencing the bound object. (Be aware, bind is not supported by all browsers as it is defined ECMAScript 5, but there are quite some polyfills out there.)
fn.bind(object);
Or call it in another context with fn.call(object, param1, param2) or fn.apply(object, [param1, param2]). Nice write up on this Link to odetocode.com/blog.
Is it possible to get the this that a function's caller was called with in JavaScript without passing this to the arguments in a way which supports IE as well as Firefox/Chrome et al?
For example:
var ob = {
callme: function() {
doSomething();
}
}
ob.callme();
function doSomething() {
alert(doSomething.caller.this === ob); // how can I find the `this` that
// `callme` was called with
// (`ob` in this case) without
// passing `this` to `doSomething`?
}
I'm starting to suspect it's not, but I thought I may as well ask as it'd make my code much shorter and easier to read.
Well, the closest I can think, without technically passing the value as an argument, would be to set the this value of the doSomething function.
Since the doSomething function is not bound to any object, by default, if you call it like doSomething(); the this value inside it will refer to the Global object, and that's normally not too useful...
For example:
var ob = {
callme: function () {
doSomething.call(this); // bind the `this` value of `doSomething`
}
};
function doSomething () {
alert(this === ob); // use the bound `this` value
}
ob.callme();
I have a javascript function (class) that takes a function reference as one paremter.
function MyClass ( callBack ) {
if (typeof callBack !== 'function')
throw "You didn't pass me a function!"
}
For reasons I won't go in to here, I need to append something to the function by enclosing it in an anonymous function, but the only way I've been able to figure out how to do it is by adding a public function to MyClass that takes the callBack function as a parameter and returns the modified version.
function MyClass () {
this.modifyCallBack = function ( callBack ) {
var oldCallBack = callBack;
callBack = function () {
oldCallBack(); // call the original functionality
/* new code goes here */
}
return callBack;
}
}
/* elsewhere on the page, after the class is instantiated and the callback function defined */
myCallBackFunction = MyClassInstance.modifyCallBack( myCallBackFunction );
Is it possible to make this work when passing the callBack function as a parameter to the class? Attempting to modify the function in this manner when passign it as a parameter seems to only affect the instance of it in within the class, but that doesn't seem like it's a valid assumption since functions are Objects in javascript, and are hence passed by reference.
Update: as crescentfresh pointed out (and I failed to explain well), I want to modify the callBack function in-place. I'd rather not call a second function if it's possible to do all of this when the class is instantiated.
Function objects don't provide methods to modify them. Therefore, what you want to do is impossible the way you want to do it. It's the same thing Jon Skeet likes to point out about Java: Objects are not really passed by reference, but instead a pointer to them is passed by value. That means that changing the value of an argument variable to a new one won't affect the original one at all.
There are only two ways to do what you want in call-by-value languages like Java and JavaScript: The first one would be to use the (function) object's methods to modify it. As I already stated, function objects don't have those. The other one is to pass the object of which the function object is a property as a second argument and set the appropriate property to a new function which wraps the old one.
Example:
var foo = {};
foo.func = function() {};
function wrapFunc(obj) {
var oldFunc = obj.func;
obj.func = function() {
// do some stuff
oldFunc.call(obj, _some_argument__);
};
}
wrapFunc(foo);
This works for global functions as well: they are properties of the window object.
As Javascript uses lexical scoping on variables the following is possible:
var modifiableCallback=function() { alert('A'); };
function ModifyCallbackClass(callback)
{
modifiableCallback=function() { callback(); alert('B'); };
}
function body_onload()
{
var myClass=new ModifyCallbackClass(modifiableCallback);
modifiableCallback();
}
This does what you want, however the function "modifiableCallback" must be referred to with the same name inside ModifyCallbackClass, otherwise the closure will not be applied. So this may limit the usefulness of this approach for you a little.
Using eval (performance may suffer a bit) it is also possible to make this approach more flexible:
var modfiableCallback1=function() { alert('A'); };
var modfiableCallback2=function() { alert('B'); };
var modfiableCallback3=function() { alert('C'); };
function ModifyCallbackClass(callbackName)
{
var temp=eval(callbackName);
var temp2=eval(callbackName);
temp= function() { temp2(); alert('Modified'); };
eval(callbackName + " = temp;");
}
function body_onload()
{
var myClass=new ModifyCallbackClass("modfiableCallback1");
modfiableCallback1();
myClass=new ModifyCallbackClass("modfiableCallback2");
modfiableCallback2();
myClass=new ModifyCallbackClass("modfiableCallback3");
modfiableCallback3();
}
I assume you are saving this callback somewhere... Any reason this won't work?
function MyClass ( callBack ) {
var myCallBack;
if (typeof callBack !== 'function')
throw "You didn't pass me a function!"
var oldCallBack = callBack;
callBack = function () {
oldCallBack(); // call the original functionality
/* new code goes here */
}
myCallBack = callback;
}
You want to do something like:
function MyClass () {
this.modifyCallBack = function ( callBack ) {
var oldCallBack = callBack;
callBack = function () {
oldCallBack(); // call the original functionality
alert("new functionality");
}
return callBack;
}
}
/* elsewhere on the page, after the class is instantiated and the callback function defined */
var myCallBackFunction = function () {alert("original");};
var MyClassInstance = new MyClass();
myCallBackFunction = MyClassInstance.modifyCallBack( myCallBackFunction );
myCallBackFunction();