Mayank_Gupta_ Ответов: 5

Проблема определения области действия и закрытия Javascript


function Employee() {
  this.name = "Mayank";
  this.Age = 26;
  this.salary = 10000;
  var calculateBonus = function() {
    alert(this);
    return (this.salary + 1000);
  }
  calculateBonus();
}

var testObj = new Employee();


В этом коде, когда мы создаем объект "Employee" с помощью "new Employee ()", функция "calculateBonus" выполняется при создании объекта. Когда выполняется "calculateBonus", мы предупреждаем об этом ссылку, вывод предупреждения дает ссылку" окно". Так "это.зарплата" в "calculateBonus" функция привязывает зарплату к объекту window.

Почему функция "calculateBonus" выполняется в контексте "Windows", а не в контексте " Employee"

Что я уже пробовал:

Я пытаюсь выяснить, как решить проблему объема для этого вопроса. Пожалуйста, помогите мне понять эту концепцию.

5 Ответов

Рейтинг:
41

Richard Deeming

В Javascript, "this- это хитрая концепция. Многие статьи были написаны, пытаясь объяснить эти осложнения - например,:


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

Вы можете обойти это с помощью любого из них вызов[^] или применять[^] чтобы пройти в контексте:
function Employee() {
  this.name = "Mayank";
  this.Age = 26;
  this.salary = 10000;
  
  var calculateBonus = function() {
    alert(this);
    return (this.salary + 1000);
  };
  
  calculateBonus.call(this);
}

NB: То calculateBonus функция не обновляет объект Employee, и значение, возвращаемое функцией, нигде не используется. Вы, вероятно, захотите это исправить.


Sergey Alexandrovich Kryukov

Я получил ответ, но не читал статей, на которые вы ссылались. Мне не понравилось то, что я читал раньше на эту тему; это не так ясно и полезно.

В то же время «это» можно очень легко понять с помощью одного подхода, который психологически несколько сложен для многих, включая меня. Нужно избавиться от мысли о JavaScript как о «традиционной» ООП-системе с классами. Очень важно понимать, что программирование на JavaScript с использованием прототипов и конструкторов очень далеко от иллюзорного сходства с ООП: ни одна из этих концепций не аналогична классам или конструкторам экземпляров классов; в JavaScripts они ... намного проще. На самом деле все дело в объектах, а не в типах (концепция типа в JavaScript - это еще одна не аналогичная концепция; и прототипы - это просто прототипы объектов, а конструкторы - просто «нормальные» функции с «this».

Надеюсь, вам понравится мой другой ответ, Решение 2, точнее, еще одна ссылка на статью о "отвлекающих факторах". :-)

—СА

Sergey Alexandrovich Kryukov

Я просто добавил немного анализа ближе к ситуации вопрошающего, в дополнение к вашему ответу. Пожалуйста, посмотрите решение 3, особенно самый последний (несвязанный) пример кода — это довольно интересно.

...и решение 6. Я приписал вам использование .call, как я объяснил другие случаи передачи "этого".

—СА

Рейтинг:
28

Sergey Alexandrovich Kryukov

Пожалуйста, смотрите мой комментарий к решению 1. В дополнение к этому я хочу сказать:

Очень важно очистить разум от идей ООП. Это то, что называется "отвлечениями" в этой статье: Объекты JS: отвлекающие факторы.

Я серьезно советую учиться и хорошо разбираться.

Что касается вашего примера, то я серьезно советую вам использовать строгий режим. Это быстро очистит ваш ум от некоторых "отвлекающих факторов". Пожалуйста, смотрите: Строгий режим-JavaScript | MDN.

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

—СА


[no name]

Большое спасибо за предоставленное вами решение.

Но в случае, указанном выше, " alert (this) "возвращает объект" Window " при выполнении в нестрогой среде.

Я пытался оценить, как выполняется функция и как мы разрешаем область действия. Может вы объясните, пожалуйста, почему сфера "calculateBonus" это "окна" вместо сотрудника объекта

Sergey Alexandrovich Kryukov

Ваша идея о том, что "это" связано с областью действия, неверна. Это совершенно не имеет отношения к масштабу.

Она связана с понятием отношения между объектом, обладающим свойством, и объектом, используемым в качестве свойства. Обратите внимание, что 1) функция также является объектом, 2) и этот объект может быть свойством; в этом случае "это" фактически передается в качестве аргумента.

Если вы поймете это и отбросите свое ошибочное представление о сфере действия, то, вероятно, легко поймете и все остальное. Я описываю функционирование "этого" в решении 6.

—СА

Рейтинг:
26

Sergey Alexandrovich Kryukov

Надеюсь, вам понравилась ссылка, которую я предоставил в решении 2. А теперь давайте посмотрим, что происходит с вашим простым делом. Одним из возможных решений было бы следующее:

function Employee() {
  this.name = "Mayank";
  this.Age = 26;
  this.salary = 10000;
  // make alculateBonus a property:
  this.calculateBonus = function() {
    alert(this.name); // "Mayank"
    return (this.salary + 1000);
  }
  this.calculateBonus();
}

Предположим, что вы используете строгий режим во всех случаях. Нестрогое поведение слишком запутанно, чтобы использовать его во время разработки. В вашем исходном коде, alert показал бы window объект, который явно ошибочен (или, эти устаревшие неправильные решения!) и трудно распознать, что это такое. Я выходной this.name только для того, чтобы показать некоторые доказательства того, что это правильное "это". (В стиле изречений кролика из "Винни-Пуха" Милна: "это может быть по-другому!". :-))

Конечно, в этом конкретном фрагменте кода, делая calculateBonus это было бы совершенно непрактично, потому что это не то, что вы хотели сделать; вы, вероятно, не хотели раскрывать эту функцию пользователям объекта. Я почти уверен, что вы знали практическое решение; только было бы бесполезно раскрывать "тайну" "этого". Так, на всякий случай:
function Employee() {
  this.name = "Mayank";
  this.Age = 26;
  this.salary = 10000;
  calculateBonus = function(owner) { // not a property!
    alert(owner.name);
    return (owner.salary + 1000);
  }
  calculateBonus(this);
}

Теперь, скажем,вы хотите использовать закрытие. Отлично! На самом деле это гораздо лучшая идея, чем использование свойств. Я надеюсь, что решение будет очевидным:
function Employee() {
  this.name = "Mayank";
  this.Age = 26;
  this.salary = 10000;
  var name = this.name;
  var salary = this.salary;
  var calculateBonus = function() { // use closure
    alert(name);
    return (salary + 1000);
  }
  calculateBonus();
}

Давайте сделаем еще один шаг. Зачем нам вообще нужен "calculateBonus"? Избавьтесь от него:
function Employee() {
  this.name = "Mayank";
  this.Age = 26;
  this.salary = 10000;
  var name = this.name;
  var salary = this.salary;
  (function() { // use closure
    alert(name);
    return (salary + 1000);
  })();
}

Гораздо лучше, не так ли? Пожалуйста, смотрите Pluralitas non est ponenda sine necessitate, смотреть также:
Бритва Оккама-Википедия, свободная энциклопедия,
Немедленно вызываемое выражение функции — Википедия, свободная энциклопедия.

Еще один вариант, сочетающий в себе некоторые из вышеперечисленных:
function Employee() {
  this.name = "Mayank";
  this.Age = 26;
  this.salary = 10000;
  var owner = this;
  (function() { // use closure
    alert(owner.name);
    return (owner.salary + 1000);
  })();
}




Теперь я хочу представить вам еще одну захватывающую особенность строгого режима. Это может помочь вам, устраняя ложную свободу использования конструкторов. Попробовать это:
function ConstructorInFact() {
	this.A = 13;
	return 14;
}

var a = new ConstructorInFact();
var b = ConstructorInFact();

Я надеюсь, что вы видите, как этот код глуп. Если это используется в качестве конструктора (a), return 14 не использовать. Если это не вызов конструктора (b), "это" бессмысленно. Однако все это будет работать в нестрогом режиме. Некоторые говорят, что это хорошо, но он не обнаружит глупости этого кода. Переключитесь в строгий режим и для первой попытки закомментируйте последнюю строку (bИ все же это сработает.

Раскомментируйте последнюю строку. В строке want проблема будет обнаружена. Сюрприз: в строке "этот.А =...": "это не определено". Обратите внимание, что проблема существует только в том случае, если есть последняя строка.

Погодите-ка! Не на JavaScript переводчика? Нет, это не чистый интерпретатор. Сценарий сначала лексически анализируется. Еще один сюрприз? :-)

—СА


[no name]

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

Большое спасибо....

Но я также хотел бы знать, почему приведенный выше код предупреждает меня с помощью объекта "окно", а не объекта Employee

Sergey Alexandrovich Kryukov

Потому что если не строгий режим. Это просто странное устаревшее поведение. В строгом режиме это всегда "неопределенный" объект.
Разве не ясно, почему оно не определено? Почему это должно быть что-то другое? "это" - скрытый параметр функции, который в данном случае отсутствует.
—СА

Sergey Alexandrovich Kryukov

Я подробно ответил на этот вопрос в решении 6. Пожалуйста, смотрите. Надеюсь, вы также примете его формально.
—СА

[no name]

Я предполагаю, что когда вызывается "новый сотрудник ()", он делает следующие вещи:

1. Создайте для него отдельный блок памяти.
2. Присвоение объекту свойств
3. назначьте объекту "это"
4. Возвращение "этот"

Я предполагаю, что код "new Employee ()" вызывается из глобальной области видимости. В то время как глобальная область выполняет указанные шаги, она сталкивается с функцией "calculateBonus". Теперь функция выполняется в глобальном контексте, так как новый объект все еще не создан.

Поэтому, когда эта функция выполняется из глобальной области видимости, контекст выполнения функции становится "глобальным", и поэтому" alert(this) "дает объект" window".

Пожалуйста, скажите мне, правильно ли мое предположение или нет. Или есть какая-то проблема в понимании разрешения области действия этой функции.

С уважением,
Майянк Гупта

Sergey Alexandrovich Kryukov

Неправда. Нет такого понятия, как"назначить это". "Это" и "объект" - одно и то же. Возвращение "этого" есть... ну, не совсем точно. Чтобы проверить, напишите в конце "return 3". Вы увидите, что экземпляр по-прежнему возвращается через new Employee(); то есть существует отдельный механизм возврата результата построения. Существует два способа тоже вызов функции f(): ВАР а = F() и VAR а = новый F(). Во втором случае возврат игнорируется и берется вновь построенный объект. "это" - это только имя объекта, переданного функции. В вашем случае ничего не передается.
—СА

[no name]

Спасибо за другой выход для решения той же проблемы.

Помогло прояснение моих сомнений..

Рейтинг:
20

Sergey Alexandrovich Kryukov

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

Большое спасибо....

Но я также хотел бы знать, почему приведенный выше код предупреждает меня с помощью объекта "окно", а не объекта Employee
Пожалуйста, посмотрите мой комментарий к этому вопросу. Я думаю, что это очевидно.

Я уже объяснял, что объект Window - это странный артефакт действия устаревшего и необоснованного поведения нестрогого режима JavaScript. Вы, наверное, понимаете, что обратная совместимость для JavaScript очень серьезна. Это единственный язык, поддерживаемый всеми браузерами. Вот почему был создан строгий режим — см. Решение 2 и ссылку. В строгом режиме объект "this"это "undefined" объект. Кстати, это особый уникальный предопределенный объект специального типа "предопределенный" (а не "объект"). В строгом режиме, "this"также может быть ссылка на текущий объект окна, но только тогда, когда это строго так, когда функция действительно является свойством этого объекта, но чтобы понять это, нам нужно будет обсудить это позже.

С этого момента давайте обсудим только те случаи, когда используется строгий режим, для строгости. :-)
Ключевое слово "this" используется в качестве аргумента функции, используемой внутри реализации этой функции, и ничего больше. Итак, вам нужно понять, что "это" - это ключевое слово используется для доступа к фактическому аргументу функции. Это специальный аргумент функции, передаваемый функции неявно, а не явно, то есть он никогда не появляется в формальном список аргументов Но этот аргумент может быть или не быть фактически передан. Он передается только в некоторых случаях:

[РЕДАКТИРОВАТЬ]

Последний пункт вышеприведенного списка следует понимать "динамически". На самом деле не имеет значения, используется ли какой-то объект функции в качестве свойства, имеет значение только то, вызывается ли функция через это свойство или нет. Сравнивать:
var object = [];
object.property = "some property";
object.someFunctionProperty = function() {
	alert(this.property);
}

// will alert "some property":
object.someFunctionProperty();

// but
var f = object.someFunctionProperty;
// will alert "undefined":
f();   

[КОНЕЦ РЕДАКТИРОВАНИЯ]

Как всегда с JavaScript, если вы используете какое-то неопределенное имя объекта, скажем, присваивание какой-то переменной, эта переменная начинает ссылаться undefined Это именно то, что происходит, если this на самом деле он вообще не прошел. В вашем случае функция является локальным объектом функции, то есть переменной стека. Ни один из механизмов, которые могли бы пройти this применяется.

Наконец, есть один предельный случай this объект: используйте его в скрипте верхнего уровня. Допустим, у вас вообще нет никаких функций на всей странице. Что такое this- Это легко попробовать. Он будет ссылаться на текущий объект окна как в строгом, так и в нестрогом режимах. Он говорит вам, что скрипт верхнего уровня на самом деле всегда является функцией; среда выполнения JavaScript передала текущий объект window как this аргумент к нему.

—СА


[no name]

Спасибо за решение. Было очень приятно учиться на тех решениях, которые мы предлагали.

Просто я пытался понять, как браузер ведет себя без строгого режима.

[no name]

Если вы не возражаете, могу ли я спросить, Можно ли считать "решение 5" полуважительным аргументом для вышеупомянутой проблемы, которую я указал, даже если учесть, что она ведет себя странно в браузере без строгого режима.

Рейтинг:
1

Mayank_Gupta_

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

Большое спасибо....

Но я также хотел бы знать, почему приведенный выше код предупреждает меня с помощью объекта "окно", а не объекта Employee.

Я предполагаю, что когда вызывается "новый сотрудник ()", он делает следующие вещи:

1. Создайте для него отдельный блок памяти.
2. Присвоение объекту свойств
3. Назначение этого объекта
4. Вернуть

Таким образом, код "new Employee()" вызывается из глобальной области видимости. В то время как глобальная область выполняет указанные шаги, она сталкивается с функцией "calculateBonus". Теперь функция выполняется в глобальном контексте, так как новый объект все еще не создан.

Поэтому, когда эта функция выполняется из глобальной области видимости, контекст выполнения функции становится "глобальным", и поэтому" alert(this) "дает объект" window".

Пожалуйста, скажите мне, правильно ли мое предположение или нет. Или есть какая-то проблема в понимании разрешения области действия этой функции.

С уважением,
Майянк Гупта