JS this po przypisaniu referencji do zmiennej.

1

Dlaczego po zapisaniu referencji obj.speak (instancja klasy) do zmiennej speak, this ma wartość undefined. Spodziewałem się, że będzie wskazywał na objekt window, tak jak się to dzieje, kiedy tworzę obiekt bez klasy.

class Animal {
  speak() {
    return this;
  }
}
let obj = new Animal();
let speak = obj.speak;
console.log(speak());  // undefined

//-----------------------------

const base = {
  showColor: function() {
    console.log(this)
  }
}
let x = base.showColor
x()   // window
4

Trochę ciężko odpowiedzieć na to inaczej niż bo twórcy języka tak akurat chcieli :-)

base.showColor zwraca wskaźnik na funkcję, ale bez powiązania jej z this - jeśli chcesz ustawić referencję na obiekt, możesz wykorzystać .bind():

let x = base.showColor.bind(base);
3

Odpowiedź @Patryk27 wyjaśnia jak obejść Twój problem, który jasno wynika z niezrozueminia jak działa bound context w JS'ie; więc wyjaśnię jak to działa i jaki był początkowy zamysł za tym pomysłem.

user656 napisał(a):

Spodziewałem się, że będzie wskazywał na objekt window, tak jak się to dzieje, kiedy tworzę obiekt bez klasy.

Tylko że tam dostałeś window, w konkretnym przypadku, bo w Twoim przykładzie scope to faktycznie window. Gdybyś był w innym scopie, dostałbyś coś innego. Więc to nie jest tak, że to "zawsze" jest window.

user656 napisał(a):

Dlaczego po zapisaniu referencji obj.speak (instancja klasy) do zmiennej speak, this ma wartość undefined.

Krótka odpowiedź - bo nie zdefiniowałeś kontekstu.
Długa odpowiedź - below

Mamy takie dwie klasy.

class Square {
  constructor(wysokosc) {
    this.wysokosc = wysokosc;
  }

  printHeight() {
    const figura = this;
    console.log("Wysokość figury to " + figura.wysokosc);
  }
}

class Rectangle {
  constructor(wysokosc, szerokosc) {
    this.wysokosc = wysokosc;
    this.szerokosc = szerokosc;
  }
}

I teraz zagadka

const square = new Square(5);
const rectangle = new Rectangle(13, 17);

rectangle.printHeight = square.printHeight;

rectangle.printHeight(); // jak myślisz, co tu będzie? Wysokość kwadratu, czy prostokąta? ;)

PS: Odpowiedź brzmi: square.printHeight() wypisze wysokość kwadratu, a rectangle.printhHeight() wysokość prostokąta.
PS: Jeśli ten przykład również wydaje Ci się bez sensu, to pamiętaj że JavaScript od początku był językiem prototypowanym, więc oczywiście nastawienie na funkcje jest większe, niż zastawienie na dane.

rectangle.printHeight = square.printHeight; // ten kod nie może kopiować "this";

Rectangle.prototype.printHeight = Square.prototype.printHeight; // żeby ten kod mógł poprawnie edytować prototyp
1

Ciekawe, nie wiedziałem, że to tak działa. Gdybym miał obstawiać, to bym obstawiał, że albo w obu przypadkach this będzie window, albo w obu przypadkach będzie undefined (jeśli użyjemy strict mode). A tu taki psikus.

Ale MDN znajduje odpowiedź na to:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#binding_this_with_prototype_and_static_methods

(...) This behavior will be the same even if the "use strict" directive isn't present, because code within the class body's syntactic boundary is always executed in strict mode.

EDIT:
I faktycznie, jak sprawdziłem, to działa i jak się robi klasy, to np. jak się odwołuję do nieistniejącej zmiennej to nawet bez strict mode zwraca błąd. Czyli:

class Foo {
	doSomething() {
		someVariable = 1;	
	}
}

const foo = new Foo;
const doSomething = foo.doSomething;
doSomething();

da błąd, nawet bez strict mode.

A obiekty bez strict mode nie zwrócą błędu:


const bar = {
	doSomething() {
	   someVariable = 1;
	}
};
const doSomething = bar.doSomething;
doSomething();
3
LukeJL napisał(a):

Ciekawe, nie wiedziałem, że to tak działa. Gdybym miał obstawiać, to bym obstawiał, że albo w obu przypadkach this będzie window, albo w obu przypadkach będzie undefined (jeśli użyjemy strict mode). A tu taki psikus.

Ale MDN znajduje odpowiedź na to:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#binding_this_with_prototype_and_static_methods

(...) This behavior will be the same even if the "use strict" directive isn't present, because code within the class body's syntactic boundary is always executed in strict mode.

Ale wiesz że

const base = {
  showColor: function() {
    console.log(this)
  }
}

to jest dokładnie to samo co

const showColor = function() {
    console.log(this)
  };
const base = {
  showColor
}

prawda?

Więc to czemu drugi przykład zwraca window już masz wyjaśnione. Co do tego, czemu pierwszy zwraca undefined zapraszam tutaj: https://tc39.es/ecma262/#sec-resolvethisbinding

1 użytkowników online, w tym zalogowanych: 0, gości: 1