ES6 Classes: Bind "this" to nested functions - javascript

I have multiple nested functions in a ES6 Class. Now I wonder how I can easily bind this of the class instance to all the subfunctions.
I know of...
subfunction1.bind(this)();
...but it feels like an awkward solution for multiple nested functions.
Does anyone know of a more elegant solution?
class User {
constructor(name) {
this.name = name;
}
mainFunction() {
console.log(this.name);//"Paul"
//In order for the nested functions to get "this" it is needed to bind it
//to the subfunction as shown below. Is there an easier way to achieve
//this for all the subfunctions at once?
subfunction1.bind(this)();
subfunction2.bind(this)();
function subfunction1() {
console.log(this.name);//"Paul"
}
function subfunction2() {
console.log(this.name);//"Paul"
}
}
}
const paul = new User("Paul");
paul.mainFunction();

You can use arrow functions. They work in rather a similar manner.
The arrow notation will replace this with the value of the context of the arrow function's scope.
class User {
constructor(name) {
this.name = name;
}
getSummary() {
console.log(this.name);
const subfunction1 = () => {
console.log(this.name);//"Paul"
}
const subfunction2 = () => {
console.log(this.name);//"Paul"
}
subfunction1();
subfunction2();
}
}
const paul = new User("Paul");
paul.getSummary();

Declare a local variable that will be in the subfunctions' scope, and use that instead of this.
class User {
constructor(name) {
this.name = name;
}
mainFunction() {
const self = this;
console.log(this.name);//"Paul"
subfunction1();
subfunction2();
function subfunction1() {
console.log(self.name);//"Paul"
}
function subfunction2() {
console.log(self.name);//"Paul"
}
}
}
const paul = new User("Paul");
paul.mainFunction();

Related

Handle function in const class?

I have source code like this
const item = function(p) {
let model;
p.setup = function() {
}
}
I am not sure how should I call this class? function component?
What I want to do is add my function to this and call this function from outside.
const item = function(p) {
let model;
p.setup = function() {
}
function nextGame(){
console.log("test");
}
}
item.nextGame(); // error
However it doesn't work.
I am not familiar this style of javascript.
How should I add the function to this(is it class??) and call??
What you describe and demonstrate in your snippet javascript is an anonymous function, you could even say that it is a functional variable.
To access methods using dot notation, you need to create a class (or javascript object).
example using the keyword 'class':
class MyClass
{
var name;
var lastName;
constructor(_name, _lastname)
{
this.name = _name;
this.lastName = _lastname;
}
sayMyName()
{
window.alert(`${this.name} ${this.lastname}`);
}
}
Now create an instance of this class. Like this:
const myName = new MyClass("John", "Doe");
and now call with the method of your class using notation. Like this:
myName.sayMyName();
class Item {
// private field for whatever
// got passed to the constructor.
#value;
constructor(value) {
this.#value = value;
}
// prototypal methods with access to
// private fields and private methods.
setup() {
// do something with `this.#value`
console.log(this.#value);
}
nextGame() {
console.log("next game");
}
}
const item = new Item("OP's mysterious `p` value");
console.log({ item });
console.log('item.setup() ...');
item.setup();
console.log('item.nextGame() ...');
item.nextGame();
.as-console-wrapper { min-height: 100%!important; top: 0; }
For object-oriented programming in javascript there are a few methods, as mentioned in the comments.
1. Factory Function
A factory function is a simple approach, as it just creates and returns an object.
function Item(model) {
const item = {
model,
nextGame: () => { console.log('test') }
}
return item
}
const itemInstance = Item('Tesla');
itemInstance.nextGame()
console.log(itemInstance.model)
2. Constructor Function
Uses the new keyword to create an instance of the defined object type. It utilizes the function's prototype, which is accessible on all instances of the type.
function Item(model) {
this.model = model;
}
Item.prototype.nextGame = () => { console.log('test') }
const itemInstance = new Item('Tesla');
itemInstance.nextGame()
console.log(itemInstance.model)
3. Class
You could also use a Class. Classes in javascript are provided as an abstraction of functions.
class Item {
constructor(model) {
this.model = model
}
nextGame() {
console.log('test')
}
}
const itemInstance = new Item('Tesla');
itemInstance.nextGame()
console.log(itemInstance.model)

Why myObject.function() throwing 'is not a function' error? (trying to learn functional instantiation)

I am trying to create a class using a function and giving it some props and a method. If I do the same thing in C# in class-based way and call the mothod this will work fine but in JS it gives me an error. My question is why it behaves in a different way.
code snippet:
function Animal (name) {
let animal = {}
animal.name = name;
animal.eat = function (){
console.log(`${this.name} is eating`)
}
}
let myAnimal = new Animal('dog');
console.log(myAnimal.eat()); //Uncaught TypeError: myAnimal.eat is not a function at proto.js:11
This would be the prototypical way to achieve that in JavaScript:
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(`${this.name} is eating`)
}
new Animal('Pooch').eat();
Or with the introduction of classes (since ECMAScript 2015):
class Animal {
constructor(name) {
this.name = name;
}
eat() {
console.log(`${this.name} is eating`)
}
}
new Animal('Pooch').eat();
well if Animal is going to act as a constructor of your sort of class, then you would need to write it like a proper constructor, i.e. drop the let animal = {} bit, and just set properties on this:
function Animal (name) {
this.name = name;
this.eat = function (){
console.log(`${this.name} is eating`)
}
}
let myAnimal = new Animal('dog');
myAnimal.eat();
now this approach has the downside of creating a new function eat() for each instance of Animal instead of binding an existing function eat() to each instance, which is what happens normally with classes. that you can achieve by this:
function Animal (name) {
this.name = name;
}
Animal.prototype.eat = function (){
console.log(`${this.name} is eating`)
}
let myAnimal = new Animal('dog');
myAnimal.eat();
which is actually the proper way people used to define classes in javascript before class was introduced.
however, you can also drop the whole class-like concept, and just have an Animal() function that gives out an object with desired properties:
function Animal (name) {
let animal = {};
animal.name = name;
animal.eat = function (){
console.log(`${this.name} is eating`)
}
return animal;
}
let myAnimal = Animal('dog');
myAnimal.eat();
note how in this approach you have let myAnimal = Animal('dog') instead of let myAnimal = new Animal('dog'), as Animal() is no longer a constructor.
reminder: this is an explanation of how you can instantiate objects with a function, and by no means the recommended way of defining classes in javascript. in older environments, the prototype approach is the recommended way, for newer environments, just use class.
Go with Robby's approach above. For correcting your code, you missed a statement return animal; :)
function Animal (name) {
let animal = {}
animal.name = name;
animal.eat = function (){
console.log(`${this.name} is eating`)
}
return animal;
}
let myAnimal = new Animal('dog');
console.log(myAnimal.eat());

Copying the content of an arrow function to a regular function

I was reading about Arrow Functions and found out that they can't have their context changed.
I was creating a module that receives a function and then changes its context. But since the user could be inputing an arrow function I couldn't make it happen.
So I was wondering if, since it's not possible to change an arrow function context, I could copy its content and create a new function that does the same, but now with a controlled context.
Any ideas how it could be achieved?
An example is something like this:
class Foo {
constructor(name) {
this.name = name;
}
sayMyName() {
console.log(this.name);
return this.name;
}
}
class Scope {
constructor(reqId) {
this.foo = new Foo('Hi!');
this.reqId = reqId;
}
do(callback) {
const func = callback.bind(this, this);
func();
}
}
class Controller {
constructor() {
this.foo = new Foo('Hello!');
}
unscoped(req, res, next) {
var a = 1;
res.json({
success: this.foo.sayMyName()
});
}
scoped(req, res, next) {
req.scope.do((ctx) => {
var a = 1;
res.json({
success: this.foo.sayMyName()
});
});
}
}
I want this.foo.sayMyName() to return 'hi' in Controller.scoped and 'hello' in Controller.unscoped
Neither Function.prototype.bind nor Function.prototype.call nor Function.prototype.apply can be used on arrow functions to change their context.
var arrowFunc = () => {console.log(this === myObject);};
var functionExpression = function() { console.log(this === myObject); };
var myObject = { id : "sampleObject"};
var boundFunctionExpression = functionExpression.bind(myObject);
console.log("Function expression bound with Function.prototype.bind :");
boundFunctionExpression();
var boundArrowFunc = arrowFunc.bind(myObject);
console.log("Arrow function bound with Function.prototype.bind :");
boundArrowFunc();
console.log("Arrow function called with Function.prototype.call :");
arrowFunc.call(myObject);
console.log("Arrow function called with Function.prototype.apply :");
arrowFunc.apply(myObject, []);
So no, I don't think you can achieve this.
More on the differences between arrow function and function expressions / declarations.

how to extends es6 class using Dojo

I have some es6 class
class Human {
constructor(){
this.age = 0;
}
}
I want to inherit from this class using dojo toolkit
define(["dojo/_base/declare"],
function (declare) {
return declare("Man", Human, {
});
});
I'm getting the error Class constructor Human cannot be invoked without 'new'.
Tried to inherit from Human.constructor and from Human.funcWrapper
class Human {
constructor(){
this.age = 0;
}
static funcWrapper(){
return new Human()
}
}
Nothing worked.
I know that I can use babel to transform my code to functions but I don't want because of some political reasons.
As usual, i'm posting banging my head for hours, and then i'm coming with a solution. So far i've tested it, and look like it is working good. including calling this.inherited(arguments, ["bla"]) (dojo way of calling super("bla"))
So, I've created this function to convert es6 class to function class
function funcClass(type) {
const FuncClass = function (...args) {
const _source = Reflect.construct(type, args, this.constructor);
const keys = Reflect.ownKeys(_source);
for (const key of keys) {
if (!key.match || !key.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/)) {
const desc = Object.getOwnPropertyDescriptor(_source, key);
!this[key] && Object.defineProperty(this, key, desc);
}
}
}
FuncClass.prototype = type.prototype;
return FuncClass;
}
And usage:
define(["dojo/_base/declare"],
function (declare) {
return declare("Man", funcClass(Human), {
});
});

ES6 - Rebinding class methods

I'm currently experimenting with ES6 classes and I've come across this issue that I don't fully understand. I want to make a class which is initialized by giving it a function. The class has a method that runs the function, but with any given context. I've attempted to do so with something like this:
class MyClass {
constructor(action) {
this.action = action;
}
run(context, n) {
this.action.call(context, n);
}
}
// instantiate my class with a function that uses 'this' to log a given number
let a = new MyClass((n) => {
this.log(n);
})
// calls the run method of my class, giving console as the context and 1 as n.
a.run(console, 1);
I would expect this code to result in 1 being logged to the console. However, I'm instead getting a TypeError.
I assume I am misunderstanding something about how classes and context-binding work in ES6. Can anyone help me out?
Edit
Well this was a good lesson on the difference between arrow functions and function declarations. Thanks for the answers!
Your use of arrow functions is breaking things
let a = new MyClass((n) => {
this.log(n);
});
is the same as
let _this = this;
let a = new MyClass(function(n) {
_this.log(n);
});
because arrow functions look up the scope to their parent. You should use a normal function as
let a = new MyClass(function(n) {
this.log(n);
});
The problem stems from how arrow functions work.
Arrow functions bind this to their lexical environment:
let obj = {
a: 1,
init() {
this.log = () => console.log(this.a);
}
};
obj.init();
obj.log();
// And the `this` value cannot be changed
obj.log.call({ a: 500 });
To fix your code, just define action with a regular function expression:
class MyClass {
constructor(action) {
this.action = action;
}
run(context, n) {
this.action.call(context, n);
}
}
let a = new MyClass(function(n) {
this.log(n);
})
a.run(console, 1);

Categories

Resources