Указатель «this» в Javascript во вложенной функции

97

У меня вопрос о том, как обрабатывается указатель «this» в сценарии вложенной функции.

Скажем, я вставил следующий пример кода на веб-страницу. Я получаю сообщение об ошибке при вызове вложенной функции «doSomeEffects ()». Я проверил в Firebug, и это показывает, что когда я нахожусь во вложенной функции, указатель this фактически указывает на глобальный объект «окно», чего я не ожидал. Я не должен понимать что-то правильно, потому что я думал, что, поскольку я объявил вложенную функцию внутри функции объекта, она должна иметь "локальную" область видимости по отношению к функции (т.е. указатель "this" будет относиться к самому объекту, как как это было в моем первом утверждении «если»).

Любые указатели (без каламбура) будут оценены.

var std_obj = {
  options : { rows: 0, cols: 0 },
  activeEffect : "none",
  displayMe : function() {

    // the 'this' pointer is referring to the std_obj
    if (this.activeEffect=="fade") { }

    var doSomeEffects = function() {

      // the 'this' pointer is referring to the window obj, why?
      if (this.activeEffect=="fade") { }

    }

    doSomeEffects();   
  }
};

std_obj.displayMe();
JoJoeDad
источник
В чем именно заключается ваш вопрос?
Sarfraz
2
При использовании внутри функции thisотносится к объекту, для которого функция вызывается.
около
9
То, что вы могли бы сделать во внешней области видимости, было бы чем-то вроде, var self = this;а затем ссылаться selfна внутреннюю функцию через закрытие.
Кай
1
doSomeEffectsне связан с каким-либо obj в частности, поэтому thisпредполагается, что это окно, мать всех элементов.
примитивно
3
@JoJoeDad Как мне дипломатично сказать это? Но ответ, данный ниже chuckj, безусловно, является ответом на ваш вопрос. Чтобы по-настоящему понять, что происходит, вы должны прочитать контекст выполнения , цепочку областей видимости и это ключевое слово . Судя по некоторым ответам, другие люди тоже должны их прочитать. Я стараюсь проповедовать хороший javascript. Вот почему я не тороплюсь, чтобы дать эти ссылки.
onefootswill

Ответы:

121

В JavaScript thisобъект действительно основан на том, как вы выполняете вызовы функций.

В общем, есть три способа настроить thisобъект:

  1. someThing.someFunction(arg1, arg2, argN)
  2. someFunction.call(someThing, arg1, arg2, argN)
  3. someFunction.apply(someThing, [arg1, arg2, argN])

Во всех приведенных выше примерах thisобъектом будет someThing. Вызов функции без ведущего родительского объекта обычно дает вам глобальный объект, который в большинстве браузеров означает windowобъект.

KylePDavis
источник
Я изменил код this.doSomeEffects();на ваш ответ, но он все равно не работает. Зачем?
Arashsoft
1
@Arashsoft thisв this.doSomeEffects()точках к std_obj. Как объяснено в ответе выше, если функция не имеет ссылки на объект, она thisдолжна быть объектом окна.
Шивы
45

Поскольку это один из самых популярных вопросов в своем роде, позвольте мне добавить после всех этих лет решение ES6 с использованием стрелочных функций:

var std_obj = {
  ...
  displayMe() {
    ...
    var doSomeEffects = () => {
                        ^^^^^^^    ARROW FUNCTION    
      // In an arrow function, the 'this' pointer is interpreted lexically,
      // so it will refer to the object as desired.
      if (this.activeEffect=="fade") { }
    };
    ...    
  }
};

источник
6
Вот и прогресс!
Джошуа Пинтер
Отличное, простое решение.
Джошуа Шлихтинг
Как я не знал последние 4 года, что это вещь.
xorinzor
32

thisне является частью области закрытия, его можно рассматривать как дополнительный параметр функции, привязанной к месту вызова. Если метод не вызывается как метод, глобальный объект передается как this. В браузере глобальный объект идентичен window. Например, рассмотрим следующую функцию,

function someFunction() {
}

и следующий объект,

var obj = { someFunction: someFunction };

Если вы вызываете функцию с использованием синтаксиса метода, например,

obj.someFunciton();

то thisобязательно obj.

Если вы вызываете someFunction () напрямую, например,

someFunction();

then thisпривязан к глобальному объекту, то есть window.

Наиболее распространенный способ - зафиксировать это в закрытии, например,

displayMe : function() {      

    // the 'this' pointer is referring to the std_obj      
    if (this.activeEffect=="fade") { }      
    var that = this;  
    var doSomeEffects = function() {      

      // the 'this' pointer is referring to global
      // that, however, refers to the outscope this
      if (that.activeEffect=="fade") { }      
    }      

    doSomeEffects();         
 }      
чакдж
источник
that = это идеально
Натер Уэббер
9

Есть разница между переменными вложения и «это». «this» на самом деле определяется инициатором вызова функции, в то время как явные переменные остаются нетронутыми внутри блока объявления функции, известного как вложение. См. Пример ниже:

function myFirstObject(){
    var _this = this;
    this.name = "myFirstObject";
    this.getName = function(){
       console.log("_this.name = " + _this.name + " this.name = " + this.name);  
    }
}

function mySecondObject(){
    var _this = this;
    this.name = "mySecondObject";
    var firstObject = new myFirstObject();
    this.getName = firstObject.getName
}

var secondObject = new mySecondObject();
secondObject.getName();

вы можете попробовать это здесь: http://jsfiddle.net/kSTBy/

В вашей функции происходит «doSomeEffects ()», вызывается явно, это означает, что контекст или «это» функции - это окно. если «doSomeEffects» был методом прототипа, например this.doSomeEffects, скажем, «myObject», то myObject.doSomeEffects () приведет к тому, что «this» будет «myObject».

Шейн
источник
4
для ленивых и нетерпеливых, это демо-логи:_this.name = myFirstObject this.name = mySecondObject
ptim
9

Чтобы понять этот вопрос, попробуйте получить вывод для следующего фрагмента

var myObject = {
    foo: "bar",
    func: function() {
        var self = this;
        console.log("outer func:  this.foo = " + this.foo);
        console.log("outer func:  self.foo = " + self.foo);
        (function() {
            console.log("inner func:  this.foo = " + this.foo);
            console.log("inner func:  self.foo = " + self.foo);
        }());
    }
};
myObject.func();

Приведенный выше код выведет на консоль следующее:

outer func:  this.foo = bar
outer func:  self.foo = bar
inner func:  this.foo = undefined
inner func:  self.foo = bar

Во внешней функции this и self относятся к myObject, и поэтому оба могут правильно ссылаться на foo и обращаться к нему.

Однако во внутренней функции это больше не относится к myObject. В результате this.foo не определен во внутренней функции, тогда как ссылка на локальную переменную self остается в области видимости и доступна там. (До ECMA 5 this во внутренней функции будет относиться к глобальному объекту окна; тогда как в ECMA 5 this во внутренней функции будет неопределенным.)

ronakshah725
источник
1
Во внутренней функции thisссылается на объект окна (в среде браузера) или ГЛОБАЛЬНЫЙ объект (в среде node.js)
ранешу
2
Почему так случилось?
сету
@raneshu «использовать строго» и thisбольше не относится к окну
рофрол 08
3

Как объяснил Кайл, вы можете использовать callили applyдля указания thisв функции:

Вот эта концепция, примененная к вашему коду:

var std_obj = {
    options: {
        rows: 0,
        cols: 0
    },
    activeEffect: "none",
    displayMe: function() {

        // the 'this' pointer is referring to the std_obj
        if (this.activeEffect == "fade") {}

        var doSomeEffects = function() {
            // the 'this' pointer is referring to the window obj, why?
            if (this.activeEffect == "fade") {}
        }

        doSomeEffects.apply(this,[]);
    }
};

std_obj.displayMe();

JsFiddle

Хари Пачувеетил
источник
0

Я также получил предупреждение " Возможно недействительный доступ по ссылке к полю класса через это "

class MyListItem {
    constructor(labelPrm) {
        this._flagActive = false;
        this._myLabel = labelPrm;
        labelPrm.addEventListener('click', ()=>{ this.onDropdownListsElementClick();}, false);
    }

    get myLabel() {
        return this._myLabel
    }
    get flagActive(){
        return this._flagActive;
    }

    onDropdownListsElementClick(){console.log("Now'this'refers to the MyListItem itself")}}//end of the class
CodeToLife
источник
0

Поскольку об этом не упоминалось, я упомяну, что использование .bind()- это решение -


        doSomeEffects=doSomeEffect.bind(this);
        doSomeEffects();   
      }
    };

    std_obj.displayMe();

Вот более простой пример -

bad = { 
  name:'NAME', 
  z : function() { 
    function x() { console.log(this.name); }; 
    x() 
  } 
};
bad.z() // prints 'undefined'

good = { 
  name:'NAME', 
  z : function() { 
    function x() { console.log(this.name); }; 
    x=x.bind(this);
    x();
  } 
};
good.z() // prints 'NAME'

Верно, что использование стрелочной функции =>выглядит более гладко и легко для программиста. Однако следует иметь в виду, что лексическая область видимости, вероятно, потребует больше работы с точки зрения обработки и памяти для настройки и поддержки этой лексической области по сравнению с простым связыванием функции thisс указателем через .bind().

Частично преимущества разработки классов на JS заключались в предоставлении метода, позволяющего сделать их thisболее надежным и доступным, чтобы отказаться от функционального программирования и лексических областей видимости и, таким образом, сократить накладные расходы.

Из MDN

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

Крейг Хикс
источник