Understand arrow function based on event - javascript

I have the following codes:
class Autocomplete {
constructor(ele, options) {
this.ele = ele;
this.options = options;
this.input = this.ele.querySelector('.input');
// with keyup we want to fire it right away
// this.input.addEventListener('keyup', (e) => this.display(e);
}
}
The display function will then display the user's input onto the page. However, I wonder what the difference is between these 3 functions:
1)
this.input.addEventListener('keyup', (e) => this.display(e));
this.input.addEventListener('keyup', this.display);
this.input.addEventListener('keyup', () => this.display());
Only function 1 and 2 work, and to my understanding, function 1 will put e as argument into the display function whereas function 2 will execute immediately on keyup. However, for 3, why does it not work, because I thought the e argument is passed in implicitly by the DOM API?
Out of the first 2 functions, which one is more performant and why?

Arrow function is not bound to the class where it is declared when it is called, you should use function only if you want to use the this in the function because in the arrow function, it's the global this that is get called

Related

Vanilla Javascript use of bind() with REST parameter

Why bind() passes even the type of event with rest parameters. How to avoid passing the event 'click'?
function say(...numbers) {
for (const number of numbers) {
console.log(number);
}
}
window.addEventListener('DOMContentLoaded', () => {
const el = document.getElementById('input_10_5');
el.addEventListener('click', say.bind(null, 1, 2, 3));
});
Console.log result:
1
2
3
click { target: input#input_10_5.medium, buttons: 0, clientX: 1062, clientY: 732, layerX: 96, layerY: 24
}
You can't. The event handling system always passes the event.
The callback function itself has the same parameters and return value as the handleEvent() method; that is, the callback accepts a single parameter: an object based on Event describing the event that has occurred, and it returns nothing.
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#the_event_listener_callback
If your goal is to iterate the arguments and treat them all the same then how about:
function say(numbers, event)
say.bind(null, [1, 2, 3])
This behavior doesn't have to do with rest parameters .... It is more to do with the behavior of .bind(). When you bind a function with arguments (in this case you've used 1, 2, 3), calling that bound function causes those bound arguments to be passed to the function first, then followed by any additional arguments you call the bound function with, eg:
function foo(one, two, three, evt) {
console.log(one, two, three, evt);
}
const bar = foo.bind(null, 1, 2, 3);
bar({type: "click"}); // becomes the 4th argument (after 1, 2, 3)
Under the hood, JavaScript is calling your bound function that you pass to .addEventListener() with the event object, which ends up becoming the fourth argument to your say function. This is then included as part of the numbers array. You can instead create a wrapper function that gets passed event (which you ignore), and then use .call() to call your function (using .call() here instead of just () allows you to explicitly define the this value like you're doing with .bind()):
window.addEventListener('DOMContentLoaded', () => {
const el = document.getElementById('input_10_5');
el.addEventListener('click', () => say.call(null, 1, 2, 3));
});
Look at the documentation:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
You can't prevent bind from doing that.
If you don't want that, don't use bind.
Just create a new function the traditional way instead.
el.addEventListener('click', function () { say(1, 2, 3) });

React curried arrow function in event handlers

How does react know to provide the event as a second argument in the code below?
const clickMe = (parameter) => (event) => {
event.preventDefault();
// Do something
}
<button onClick={clickMe(someParameter)} />
Does it generate this to:
<button onClick={(event) => clickMe(someParameter)(event)} />
Or how does it work?
Thanks.
Maybe this will help explain it a little better.
Closures are functions that carry the information (variables etc.) from their local environment with them when they're returned.
I'll work this example without arrow functions as they can be a little deceiving.
// `multplyBy` accepts a number as its argument
// It returns a new function that "remembers" that number
// when it's returned. But that new function *also*
// accepts a number
function multiplyBy(n) {
return function(n2) {
return n2 * n;
}
}
// So `multiplyBy(5)` returns a new function
// which we assign to the variable `five`
const five = multiplyBy(5);
// And when we call `five` with a number we get
// the result of calling 5 * 10.
console.log(five(10));
If you substitute multiplyBy(n) with clickMe(n) you'll see that you'll get a new function that gets used by the click listener, and the first argument of that function will always be the event.
Clickme variable in your code is a function which has return is function e => {...}. So when you specify like this:
<button onClick={clickMe(someParameter)} />
it is equivalent to
<button onClick={e => {...} />
which is basic form of a event handler in react

How to create a javascript functional 'class' so that I can access a 'method' from outside and inside the function

I am creating a function that handles a bunch of stuff around pagenating and sorting a table. It contains a key function that submits the db query and updates the display table.
I want to be able to access that inner function/method from both inside the function and also from outside on the object created.
testFunction = function() {
keyMethod = function() {
console.log('ya got me');
};
document.getElementById('test').addEventListener('click', function (e) {
keyMethod();
});
keyMethod();
};
myTest = new testFunction();
myTest.keyMethod();
testFunction = function() {
this.keyMethod = function() {
console.log('ya got me');
};
document.getElementById('test').addEventListener('click', function (e) {
// would have to use bind here which then messes up trying to
// find the correct target etc.
keyMethod();
});
this.keyMethod();
};
myTest= new DrawShape();
myTest.keyMethod();
Creating it the first way means that the keyMethod function is available everywhere within the testFunction but I cant call it from outside.
Creating it the second way means I can do myTest.keyMethod but I then cant call it from within an inner function without using bind everywhere.
Is there a better way..?
You could replace the function provided as callback with an arrow function or use bind the function first like you already said.
testFunction = function() {
this.keyMethod = function() {
console.log('ya got me');
};
// Replace callback by simply providing the function to call.
// This works as long as you don't use the `this` keyword inside the
// provided function.
document.getElementById('test').addEventListener('click', this.keyMethod);
// If your callback method does use the `this` keyword you can either use an
// arrow function or bind the function up front.
document.getElementById('test').addEventListener('click', event => this.keyMethod());
document.getElementById('test').addEventListener('click', this.keyMethod.bind(this));
this.keyMethod();
};
console.log("constructor output:");
myTest = new testFunction();
console.log(".keyMethod() output:");
myTest.keyMethod();
console.log("click event output:");
<button id="test">test</button>

Understanding Javascript's _this, different types of function calls and how to call a function inside a function?

I am learning how to reverse engineer an existing javascript code and I've ran into a few issues which is due to my lack of understanding how core javascript works. The code is below along with a screen shot of the comments I have.
The code starts out with var warper being declared.
And then warper variable equals a function inside a function? Why isn't it the usual call of function Warper(), but its inside another function?
I noticed the use of _this. How is that different from the regular this that is usually used?
The #btn-submit id is set to activate when it is clicked on. I can see that it calls the click_submit function, but why is it Warper.prototype.click_submit instead of just click_submit()?
And my final question, which is what I really want to do is call the click_submit function via js without having to click on the #btn-submit button.
Question: How do I call the warper.click_submit function using js without the need to click a button? I'm trying to integrate this into another piece of my code.
I tried warper.prototype.click_submit and it doesnt do anything. I'm assuming its because its inside a function in a function?
(function() {
var Warper;
Warper = (function() {
function Warper() {
this.check_compatibility();
this.attach_ux();
if (window.SALT_DEFAULT != null) {
$('#salt').val(window.SALT_DEFAULT);
$('#salt').attr('disabled', true);
$('.salt-label').text('Prefilled salt');
}
}
Warper.prototype.check_compatibility = function() {
if (typeof Int32Array === "undefined" || Int32Array === null) {
return $('.form-container').html('<p>\n Sorry, but your browser is too old to run WarpWallet, which requires Int32Array support.\n</p>');
}
};
Warper.prototype.attach_ux = function() {
$('#btn-submit').on('click', (function(_this) {
return function() {
return _this.click_submit();
};
})(this));
$('#btn-reset').on('click', (function(_this) {
return function() {
return _this.click_reset();
};
})(this));
return $('.what-salt').on('click', (function(_this) {
return function() {
return $('.salt-explanation').toggle();
};
})(this));
};
Warper.prototype.click_submit = function() {
$('#btn-submit').attr('disabled', true).html('Running...');
$('#btn-reset').attr('disabled', true).html('Running...');
$('#passphrase, #salt, checkbox-salt-confirm').attr('disabled', true);
$('.progress-pbkdf2, .progress-scrypt').html('');
$('.progress-form').show();
return warpwallet.run({
passphrase: $('#passphrase').val(),
salt: $('#salt').val(),
progress_hook: (function(_this) {
return function(o) {
return _this.progress_hook(o);
};
})(this),
params: window.params
}, (function(_this) {
return function(res) {
$('#passphrase, #checkbox-salt-confirm').attr('disabled', false);
if (window.SALT_DEFAULT == null) {
$('#salt').attr('disabled', false);
}
$('#private-key').val(res["private"]);
_this.write_qrs(res["public"], res["private"]);
return console.log;
};
})(this));
}; //click_submit
return Warper;
})(); // Warper End
$(function() {
return new Warper();
});
}).call(this); // End Function
The code starts out with var warper being declared.
And then warper variable equals a function inside a function? Why isn't it the usual call of function Warper(), but its inside
another function?
It is inside another function to create it's own scope. This practice is used mainly to separate non relative code and prevent global variables.
I noticed the use of _this. How is that different from the regular this that is usually used?
_this is just a variable that is set to point to outer this. Everytime you call a function it has own this (depending on how you called the function). So if define callback inside another function, and you need to refer to this of that outer ("another") function, you can save it temporarily to variable. This temporarily variables are usually referred to as _this, that or self.
The #btn-submit id is set to activate when it is clicked on. I can see that it calls the click_submit function, but why is it
Warper.prototype.click_submit instead of just click_submit()?
If you define function on prototype, every instance will use that same function. If you would define it on this.clik_submit = function(){...} then every instance would have to have it's own copy of that function. Last option is to just define function click_submit(){...} inside scope, but then the function wouldn't be accessible from outside the scope.
And my final question, which is what I really want to do is call the click_submit function via js without having to click on the
btn-submit button.
You need to gain access to warper instance to be able to call the click_submit function. Without it (and without being able to update the code) it is not possible to call it. But you could consider creating click event on button yourself which would trigger the function. Using jquery this is as easy as $("#btn-submit").click();
Warper is an object.
Warper.prototype.attach_ux = function() {
$('#btn-submit').on('click', (function(_this) { //2
return function() {
return _this.click_submit(); //3
};
})(this));//1.
on //1 'this' refers to the Warper object and is bound to the scope of Warper.prototype.attach_ux function.
on //2 _this is the name of argument which refers to the value passed at //1.
Since //1 refers to the Warper object the click_submit method is called.
This is IIFE pattern of function invocation.
If you were to call
$('#btn-submit').on('click', function(_this) {
return function() {
return _this.click_submit();
};
})
Here _this would refer to the click event and we would not be able to access the defined method conviniently. But we are able to pass the Warper object using IIFE pattern and access it easily.

Passing an function array to a button.onclick function

I have trouble with TypeScript/JavaScript
I have an array of function like this
private listeners: ((name: string) => void)[] = [];
I'm adding functions to it in another function that seems to work.
now I want to call those functions if a button is pressed
button.onclick = function(){
this.listeners.forEach((callback) => callback(username));
}
if I try my code this way nothing happens, I bet it is because it doesn't know listeners.
my next try was
button.onclick = function(listeners){
listeners.forEach((callback) => callback(username));
}
Now tsc tells me
error TS2339: Property 'forEach' does not exist on type 'MouseEvent'.
I bet I'm missing a type here, but idk how to tell him that this array is an array of functions of type ((name: string) => void)[] = [];
Would be cool if someone knows a quick workaround.
Before you ask: I'm sure that my function added functions to my array (at least in the add function the size of listerns got bigger) :D
Try to define your onclick handler like this to keep 'this' pointing to the current class instance:
private onClick(ev)
{
this.listeners.forEach((callback) => callback(username));
}
//Then somewhere later
button.onclick = (ev) => this.onClick(ev);
Your main problem is that this doesn't refer to your expected context. Instead, this refers to the MouseEvent context of your click handler.
As a simple solution for your problem you might use Function.prototype.bind().
Change
button.onclick = function(){
this.listeners.forEach((callback) => callback(username));
}
To this
button.onclick = function(){
this.listeners.forEach((callback) => callback(username));
}.bind(this);
Basic explanation:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
A more sophisticated way of dealing with your issue is not to use onclick at all, but rather addEventListener, and you can use the following little-known approach of passing an object with a handleEvent method to addEventListener, which makes all your issues with this moot:
class Foo {
private listeners: ((name: string) => void)[] = [];
constructor(button) {
button.addEventListener('click', this);
}
handleEvent(event) {
// check event type
this.listeners.forEach(callback => callback(username));
}
}
const new Foo(document.getElementById('myButton'));
Thanks for the awnsers
i guess that a direct add of a function wont work because its an Eventhandler function, which works somewhere in the code but not there where i defined it.
I changed to addeventhandler and defined with let a function inside my function, and call a new function for the Eventhandler which calls the function it should call.#funcception :D
let triggerOnClick = () => {
let username = tf_name.value;
this.listeners.forEach((callback) => callback(username));
};
btn_enter.addEventListener("click", () => {
triggerOnClick();
});
that code works :)

Categories

Resources