2017-12-20 232 views
10

że mam konstruktora obiektu oraz sposobu prototypu, jak:Czy mogę powiązać metody prototypowe konstruktora ze skonstruowanymi instancjami, zachowując oddzielne problemy?

function Human(name) { 
    this.name = name; 
} 

Human.prototype.sayName = function(){ 
    console.log('my name' + this.name); 
}; 

gdzie indziej w moim kodu, mam zdefiniowane wystąpienie Człowieka:

let jeff = new Human('jeff'); 

i wreszcie chcę przekazać jeff.sayName jako zwrotną innej funkcji, takich jak (na przykład szczególnie trywialne)

function callFunction(callback) { 
    callback(); 
} 

callFunction(jeff.sayName); 

Przywodząc callFunction(jeff.sayName) powyżej, kontekst musi być związany z jeff sam, jak callFunction(jeff.sayName.bind(jeff)). Ale to jest niezgrabne i wolałbym nie martwić się o tego rodzaju logikę za każdym razem, gdy używam tej metody jako wywołania zwrotnego.

Alternatywą byłoby zastąpienie Human.prototype.sayName z czymś jak

Human.prototype.createSayName = function(context){ 
    return function() { 
    console.log(context.name); 
    }; 
}; 

a następnie zdefiniować funkcję z

ale jest to trochę niewygodne, i chciałbym zachować jeff agnostyk do metod, które dziedziczy po Human.prototype.

Więc idealnie chciałbym obsługiwać tę logikę na samej Human.prototype, coś

Human.prototype.sayName.bind(WhateverObjectHoldsThisMethod) 

ale nie jestem świadomy JavaScript sposób to robi.

TL; DR Chciałbym sposób wiązania prototypowej metody obiektu do dowolnego obiektu, który go dziedziczy, bez konieczności wykonywania tego za każdym razem, gdy przekazuję tę metodę jako argument lub za każdym razem, gdy definiuję nowy obiekt. Przepraszam, jeśli to nie ma sensu. Dzięki!

+2

Czekaj, co? Nie rozumiem pytania ... 'jeff.sayName()' działa na własną rękę, nie? – Li357

+0

Pierdnięcie mózgu. Pozwól mi edytować mój przykład ... –

+0

"* ... kontekst musi być związany z' jeff' * ". Wartość * this * jest związana z * jeff * przez wywołanie: 'jeff.sayName()'. Masz na myśli, że chcesz wywołać metodę na jakimś nieludzkim obiekcie * jim *? – RobG

Odpowiedz

-1

Przypuszczam, może chcesz osiągnąć ten

function Human(name) { 
 
    this.name = name; 
 
} 
 

 
Human.prototype.sayName = function() { 
 
    console.log('my name' + this.name); 
 
}; 
 

 
let jeff = new Human('jeff'); 
 

 
function callFunction(callback) { 
 
    callback(); 
 
} 
 

 
callFunction(function() { 
 
    jeff.sayName() 
 
});

innego domyślać, prototyp związany z wystąpieniem, to działa, ale ma anty-Patten

function Human(name) { 
 
    this.name = name; 
 
} 
 

 
const jeff = new Human('jeff'); 
 

 
Human.prototype.sayName = function() { 
 
    console.log('my name' + jeff.name); 
 
}; 
 

 
function callFunction(callback) { 
 
    callback(); 
 
} 
 
callFunction(jeff.sayName);

inny przypuszczenie, relection

function Human(name) { 
 
    this.name = name; 
 
} 
 

 

 
Human.prototype.sayName = function() { 
 
    console.log('my name' + this.name); 
 
}; 
 

 

 
Human.prototype.reflectName = function(item) { 
 
    this.sayName =() => item.sayName() 
 
}; 
 

 
const jeff = new Human('jeff'); 
 

 
const tod = new Human('tod'); 
 
tod.reflectName(jeff) 
 

 
tod.sayName()

+0

Co masz na myśli przez archiwum tutaj? Ponadto wiele różnych "ludzi" konstruowanych jest różnymi metodami - z tego powodu nie mogę związać prototypu z instancją. –

+0

@JohnHartman 'function callFunction (callback) { oddzwanianie(); } 'zwiąże' callback' z 'niezdefiniowanym', więc zawiń' sayName' w funkcji rozwiąże kontekst bindowania –

+0

Archiwum jest błędem, zmodyfikowano @JohnHartman –

3

ze względu na sposób środowiskach leksykalnych, rozdzielczość Zakres, prototypal spadków i zapisów środowisko pracy w JavaScript, co prosisz, nie jest możliwe bez modyfikacji funkcja, która wywołuje funkcję zwrotną.

Jednak zamiast wywołania odwołania Human#sayName użyć funkcji strzałki, która z kolei wywołuje referencję Human#sayName, którą chcesz wywołać.

Nie jest idealny, ale jest prosty, czysty i czytelny.

function Human(name) { 
 
    this.name = name; 
 
} 
 

 
Human.prototype.sayName = function(){ 
 
    console.log('my name' + this.name); 
 
}; 
 

 
let jeff = new Human('jeff'); 
 

 
function callFunction(callback) { 
 
    callback(); 
 
} 
 

 
callFunction(_ => jeff.sayName());

Dla lepszego zrozumienia tych wymyślnych słów, odniesione wcześniej, i jak one działają w JavaScript, polecam rozdział 8.1 ECMAScript 2017 Language Specification czytania. Podrozdział 8.1.1.3 zawiera konkretne informacje, których szukasz, ale reszta sekcji do tego momentu jest niezbędna do zrozumienia tej podsekcji.

Zasadniczo, kiedy przechodzą Human#sayName do callFunction, jesteś przechodzącą odniesienie do oryginalnego sayName funkcji, więc może być pan robi, jak również to: (przepraszam za kalambur)

function callFunction(callback) { 
    callback(); 
} 

callFunction(function(){ 
    console.log('my name' + this.name); 
}); 

Zawartość Funkcja nie jest oceniana, dopóki nie zostanie wykonana, co oznacza, że ​​do czasu jej wykonania wartość this już się zmieniła. Aby dodać do klęski, oryginalna funkcja nie ma wiedzy o instancji, do której zażądałaś. Nigdy nie istnieje on na obiekcie jeff. Istnieje ona w prototypie obiektu funkcji, a po wykonaniu wyszukiwania właściwości obiektu silnik JavaScript przeszukuje łańcuch prototypów, aby znaleźć tę funkcję.

Bardzo dobrze, możesz uzyskać zachowanie, o które prosisz, ale nie pod ograniczeniami, które określiłeś. Na przykład, jeśli funkcja nie musi istnieć w łańcuchu prototypów i może istnieć w instancji (pamiętaj, że tworzy to nowy obiekt funkcji dla każdej instancji, więc zwiększy koszt), możesz zdefiniować funkcję w konstruktorze, a następnie przechowywać odniesienie do prawidłowego this używając identyfikatora, które nie zostaną nadpisane:

function Human(name) { 
 
    const _this = this; 
 
    this.name = name; 
 
    this.sayName = function(){ 
 
    console.log('my name' + _this.name); 
 
    }; 
 
} 
 

 
let jeff = new Human('jeff'); 
 

 
function callFunction(callback) { 
 
    const _this = { name: 'hello' }; // does not affect output 
 
    callback(); 
 
    callback.call(_this); // does not affect output 
 
} 
 

 
callFunction(jeff.sayName);

byłoby to bezpieczniejsza opcja, bo wiesz, że _this zawsze odnoszą się do obiekt, który ma się odnosić w kontekście konstruktora, definiują wszystkie obiekty funkcji d w tym obiekcie funkcyjnym odziedziczą identyfikatory ich macierzystego zakresu, a konteksty wywołania nie będą miały wpływu na te identyfikatory.

Albo, można pójść o krok dalej, a nie opierać się na wartości this na wszystkich:

function Human(name) { 
 
    const sayName = function(){ 
 
    console.log('my name' + name); 
 
    }; 
 
    
 
    Object.assign(this, { name, sayName }); 
 
} 
 

 
let jeff = new Human('jeff'); 
 

 
function callFunction(callback) { 
 
    const name = 'hello'; // does not affect output 
 
    callback(); 
 
    callback.call({ name: 'world' }); // does not affect output 
 
} 
 

 
callFunction(jeff.sayName);

Ma to zalety:

  • Istoty łatwiejszy do odczytania,
  • Mniej kodu,
  • Umożliwia wyraźne określenie właściwości i metod ujawnianych przez obiekt, oraz
  • Nigdy nie musisz się martwić, jaka będzie wartość this.
+0

Dzięki za jasną odpowiedź, nawet jeśli jest ujemna. Chociaż czy mogę zapytać, co masz na myśli przez "Zrozumiałe, w jaki sposób zakresy leksykalne działają w JavaScript"? Czy mógłbyś wskazać mi coś, co tłumaczyłoby, dlaczego nie jest to możliwe? –

+1

Edytowałem swoją odpowiedź, aby poszerzyć wyjaśnienie, dlaczego nie jest to możliwe, i podać inną alternatywę, która nie jest zgodna z Twoimi ograniczeniami, ale zapewnia zachowanie, którego szukasz. –

+0

Proszę, nikt nie powinien czytać specyfikacji języka dla * zrozumienia * niczego. Nie ma (lub przynajmniej, zdecydowanie za mało) prozy, która wszystko tłumaczy. Nie możesz po prostu połączyć odpowiedniego rozdziału [YDKJS] (https://github.com/getify/You-Dont-Know-JS), [Understanding ES6] (https://leanpub.com/understandinges6/read) lub [Exploring ES6] (http://exploringjs.com/es6/index.html)? – Bergi

1

Rozszerzając TinyGiant, możesz użyć funkcji strzałki, ale jeśli połączysz ją z geterem, możesz zdefiniować ją jako metodę prototypu i nie zawracać sobie głowy definiowaniem swojego połączenia zwrotnego jako funkcji strzałki, która może być bardziej elastyczny w zależności od Twoich potrzeb. Tak:

function Human(name) { 
 
    this.name = name; 
 
} 
 

 
Object.defineProperty(Human.prototype, "sayName", { 
 
    get: function() { 
 
    return() => { 
 
     console.log("my name is", this.name); 
 
    } 
 
    } 
 
}); 
 

 

 
function callfunction(callback) { 
 
    callback(); 
 
} 
 

 
let jeff = new Human('jeff'); 
 

 
callfunction(jeff.sayName); 
 

 
// just to show it works even as a regular function 
 
jeff.sayName(); 
 

 
// in fact it overrides every context you bind 
 
jeff.sayName.bind(window)()