Ключевое слово 'this' возвращает объект окна в прототипе объекта в Javascript?

У меня есть следующая функция в классе:

MyClass.prototype.myFunction = function(item, args) 
{       
    console.log(this);
}

Эта функция вызывается из внешней библиотеки, у меня нет доступа к изменению. Когда он вызывается, консоль регистрирует "this" как объект окна вместо фактического объекта instanced. После поиска stackoverflow я нашел эту цитату:

это устанавливается в соответствии с тем, как вызывается метод, а не в соответствии с тем, как этот метод написан. Итак, для obj.method() это будет установлено в obj внутри метода(). Для obj.method.call(x) это внутри метода() будет установлено в x. Он определяется тем, как он называется. Это также означает, что если вы передадите его в качестве обратного вызова, например, onclick, этот параметр будет установлен на глобальный объект окна, а не на то, что вы ожидаете.

Я предполагаю, что это то, что происходит, и я не могу изменить способ его вызова. Мой вопрос в том, есть ли в любом случае, чтобы получить экземпляр объекта его независимо от того, как он называется?

Ответ 1

Это обычная путаница с Javascript. Легко думать о них как о том, как ведут себя методы расширения на других языках, но в Javascript так легко изменить контекст this, что это часто делается случайно.

Итак:

MyClass.prototype.myFunction = function(args) 
{
    // You expect [this] to refer to an instance of MyClass
    this.somePropertyOfMyClass;
};

Затем вы можете вызвать это с помощью:

var x = new MyClass();
x.myFunction(args)

Однако способ, которым функция вызывается в Javascript, может изменить то, что this означает:

var y = somethingElse();
x.myFunction.call(y, args); // now in myFunction [this] refers to y

Скорее всего, многие библиотеки используют контекст this для цепочки и событий, что делает ошибки легкими. Например, в jQuery:

var $thing = $('.selector');
$thing.click(x.myFunction); // now in myFunction [this] refers to $thing

Вероятно, для человека, написавшего jQuery, очевидно, что вызов x.myFunction таким образом нарушит его. Они могут обходным путем (при условии, что они знают о реализации):

$thing.click(function() { x.myFunction(); }); 

Если вы хотите, чтобы ваш MyClass был устойчивым, чтобы его вызывали, как это, тогда не используйте prototype - вместо этого используйте свойство объекта:

function MyClass() {
    var self = this;
    // ...
    this.myFunction = function(args) 
    {
        // [self] will always refer to the current instance of MyClass
        self.somePropertyOfMyClass;
    };
}

Обратите внимание, что более современные браузерные Javascript-движки хороши, так как оптимизируют эти вызовы, поэтому я бы не использовал prototype так же, как оптимизацию, если вы уже не определили, что вам нужна дополнительная производительность.

Ответ 2

Предположительно, ссылка на функцию передается на какую-то другую функцию для вызова и что другая функция имеет значение:

function otherFunction(args, fn) {
    ...
    fn();
    ...
}

Чтобы этот метод получил this, ему нужно:

// Create a local variable referencing the `this` you want
var instance = this;

// Pass a function that has a closure to the variable
// and sets the appropriate this in the call
otherFunction(args, function(){myMethod.call(instance)})

Теперь this в myMethod будет использоваться instance. Обратите внимание: если вы измените значение instance после вызова otherFunction и до вызова метода myMethod получит это новое значение.

Вы можете справиться с этим, если это проблема.

О, вы также можете справиться с этим в конструкторе, предоставив каждому экземпляру собственный метод, который имеет закрытие экземпляра:

function MyObj(name) {
  var instance = this;
  instance.name = name;
  instance.getName = function() {
    return instance.name;
  }
}

var anObj = new MyObj('fred');

// Call as a method of anObj
alert(anObj.getName());  // fred 

// Pass method as a reference
var x = anObj.getName;

// Unqualified call
alert(x());  // fred    

Ответ 3

Одно исправление должно состоять в том, чтобы иметь экземпляр myFunction() для каждого экземпляра, т.е. создать его в конструкторе вместо его на прототипе, потому что тогда вы могли бы иметь ссылку на this, хранящуюся как локальная переменная в конструкторе:

function MyClass() {
    var self = this;
    this.myFunction = function(item, args) {
        // use self instead of this
        console.log(self);
    };
    // other constructor code here
}

Но, конечно, для создания функции для каждого экземпляра используется больше памяти, что может быть или не быть проблемой в зависимости от размера вашей функции. Компромисс может заключаться в том, чтобы поместить оболочку для вашей функции внутри конструктора и определить фактическую функцию на прототипе:

function MyClass() {
    var self = this;
    this.myFunction = function(item, args) {
        return self.wrappedMyFunction(item, args);
    };

    // your other constructor code here
}    
MyClass.prototype.wrappedMyFunction = function(item, args) 
{       
    console.log(this);
}

Ответ 4

Правило таково:

Когда this вызывается из функции, это относится к глобальному объекту (обычно Window).
Когда this вызывается из метода, он ссылается на объект-владелец (т.е. Текущий объект, в котором вы используете метод).

Пример:

function Tryme() {

    var that = this

    function func2() {
        console.log("'this' from 'func2':", this)
    }

    function func3() {
        console.log("'that' from 'func3':", that)
    }

    this.method1 = function () {
        console.log("'this' from 'method1':", this)
    }

    this.method2 = function () {
        func2()
    }

    this.method3 = function () {
        func3()
    }
}

myinstance = new Tryme()

myinstance.method1() // displays the object 'myinstance'
myinstance.method2() // displays the object 'Window'
myinstance.method3() // displays the object 'myinstance'

Что происходит в примере?

Когда вы вызываете .method1() и отображаете this, вы в настоящее время находитесь в методе объекта myinstance. Так что this будет относиться к самому объекту (т. myinstance).

При вызове .method2(), этот метод будет вызывать локальную функцию в пределах объекта myinstance называется func2(). Эта функция func2() является функцией, а не методом, так что this относится к глобальному объекту Window.

При вызове .method3(), этот метод будет вызывать локальную функцию в пределах объекта myinstance называется func3(). Эта функция func3() является функцией, а не методом, так что this относится к глобальному объекту Window (как в func2()). Но в func3(), мы отображая содержание, that, который является локальной переменной в пределах myinstance объекта. that это объект, который был инициализирован со значением this, когда this было со ссылкой на владельца объекта (то есть. myinstance). В JavaScript, если вы инициализируете object2 значением object1 (object2 = object1), то при изменении значения одного объекта значение другого объекта также изменится. Так that это всегда будет относиться к значению this которое является значением myinstance, даже когда значение this изменяется. Помните: this ключевое слово, которое относится к объекту, а не к переменной.

Что случилось в вашем случае?

Вы вызвали this из функции, так что this относится к глобальному объекту (Window).

@Keith предложил, что можно сделать, т.е. создание объекта, в котором вы будете создавать self (или that) объект, который сравняется по this инстансу вы заинтересованы в, то с помощью self объекта в функции (которая будет относиться к this объекту вы заинтересованы в),

Дополнительная информация

Здесь: https://www.w3schools.com/js/js_this.asp
Здесь: https://crockford.com/javascript/private.html