Javascript "цей" вказівник у межах вкладеної функції


94

У мене виникає питання, як трактується "цей" покажчик у сценарії вкладених функцій.

Скажіть, я вставляю наступний зразок коду на веб-сторінку. Я отримую помилку, коли я називаю вкладену функцію "doSomeEffects ()". Я перевірив у Firebug, і це вказує, що коли я перебуваю в цій вкладеній функції, покажчик "цей" насправді вказує на глобальний "віконний" об'єкт - чого я не очікував. Я не повинен щось правильно розуміти, тому що я думав, коли я оголосив вкладену функцію у межах функції об'єкта, вона повинна мати "локальну" область по відношенню до функції (тобто вказівник "цей" посилався б на сам об'єкт, як як це в моєму першому висловленні "якщо").

Будь-які вказівки (каламбур не призначений) будуть вдячні.

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();

Яке саме ваше запитання?
Сарфраз

2
При використанні всередині функції, thisпосилається на об'єкт, для якого викликається функція.
приблизно синій

8
Те, що ви можете зробити у зовнішній області дії, це щось подібне, var self = this;а потім звернутися до selfвнутрішньої функції через закриття.
Кай

1
doSomeEffectsне асоціюється з яким-небудь об'єктом, зокрема, тому thisвважається вікном, матір'ю всіх елементів.
приблизно

3
@JoJoeDad Як я можу дипломатично це сказати? Але відповідь, яку дав chuckj нижче, є безумовно відповіддю на ваше запитання. Щоб по-справжньому зрозуміти, що відбувається, вам слід прочитати контекст виконання , ланцюжок обсягу та це ключове слово . І як виглядають деякі відповіді тут, інші люди також повинні їх прочитати. Я намагаюся евангелізувати хороший JavaScript. Ось чому я забираю час, щоб надати ці посилання.
onefootswill

Відповіді:


120

У 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об'єкт.


Я змінив код на this.doSomeEffects();основі вашої відповіді, але все одно він не працює. Чому?
Arashsoft

1
@Arashsoft thisв this.doSomeEffects()точках до std_obj. Як пояснено у відповіді вище, якщо функція не має посилання на об'єкт, то це thisповинен бути об'єктом вікна.
Шиви

38

Оскільки це, мабуть, одне з найбільш актуальних питань такого типу, дозвольте мені додати після всіх цих років рішення 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") { }
    };
    ...    
  }
};

5
Зараз це прогрес!
Джошуа Пінтер,

Відмінне, просте, рішення.
Джошуа Шліхтінг,

32

thisне є частиною області закриття, його можна розглядати як додатковий параметр до функції, яка прив’язана до місця виклику. Якщо метод не викликається як метод, тоді глобальний об'єкт передається як this. У браузері глобальний об'єкт ідентичний window. Наприклад, розглянемо такий функціонал,

function someFunction() {
}

і наступний об'єкт,

var obj = { someFunction: someFunction };

Якщо ви викликаєте функцію, використовуючи синтаксис методу, такий як,

obj.someFunciton();

то thisприв’язаний до obj.

Якщо ви безпосередньо викликаєте someFunction (), наприклад,

someFunction();

тоді 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 = це ідеально
Nather Webber

10

Існує різниця між змінними корпусу та "цим". "this" фактично визначається засобом, що викликає функцію, тоді як явні змінні залишаються незмінними всередині блоку оголошення функції, відомого як enclosure. Дивіться приклад нижче:

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

У зовнішній функції і це, і само посилання посилаються на myObject, і тому обидва можуть належним чином посилатися та отримувати доступ до foo.

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


1
У внутрішній функції thisпосилається на об’єкт вікна (у середовищі браузера) або об’єкт GLOBAL (у середовищі node.js)
raneshu

2
Чому це відбувається?
постановка

@raneshu "використовувати строгий" і thisбільше не посилається на вікно
rofrol

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

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

Міркування щодо продуктивності Нерозумно створювати функції в межах інших функцій, якщо закриття не потрібне для певного завдання, оскільки це негативно вплине на продуктивність сценарію як з точки зору швидкості обробки, так і споживання пам'яті.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.