Node.js: почему вызов hasOwnProperty отличается от global.hasOwnProperty?

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

Запустите консоль Node.js и:

> global.hasOwnProperty === hasOwnProperty
true

Тогда почему

> global.hasOwnProperty("x")
false

но

> hasOwnProperty("x")
TypeError: Cannot convert undefined or null to object
at hasOwnProperty (<anonymous>)
at repl:1:1
at sigintHandlersWrap (vm.js:22:35)
at sigintHandlersWrap (vm.js:96:12)
at ContextifyScript.Script.runInThisContext (vm.js:21:12)
at REPLServer.defaultEval (repl.js:313:29)
at bound (domain.js:280:14)
at REPLServer.runBound [as eval] (domain.js:293:12)
at REPLServer.<anonymous> (repl.js:513:10)
at emitOne (events.js:101:20)

?


person Alexander Mihailov    schedule 05.01.2017    source источник
comment
Лично мне не нравятся эти теоретические вопросы, которые не показывают, какую реальную проблему вы пытаетесь решить. В Javascript, а также в node.js нет стандартного определения глобально доступной функции с именем hasOwnProperty(), к которой можно получить доступ без какого-либо префикса, поэтому вам не следует его использовать. Если вы описываете проблему, которую на самом деле пытаетесь решить, мы можем лучше объяснить, как вы должны кодировать ее.   -  person jfriend00    schedule 05.01.2017
comment
@jfriend00 согласно mdn существует стандартное определение ecmascript для функции hasOwnProperty.   -  person Yerko Palma    schedule 05.01.2017
comment
@YerkoPalma Я полагаю, что он имеет в виду глобальный доступ, поскольку браузер и узел имеют разные концепции глобального объекта.   -  person Joseph    schedule 06.01.2017
comment
@YerkoPalma - мне известно о Object.prototype.hasOwnProperty(), который также будет доступен в объекте global в node.js, поскольку он является производным от Object. Я не знаю об отдельной версии (называемой без префикса), которая входит в какой-либо стандарт. Можете ли вы указать на какой-либо стандартный документ по этому поводу?   -  person jfriend00    schedule 06.01.2017
comment
Стоит отметить, что у меня это также проявляется в Chrome (конечно, с window вместо global). Наверняка существует существует спецификация, отображающая, как свойства глобального объекта становятся доступными в виде переменных верхнего уровня, верно? И тот, который указывает, наследуется ли глобальный объект от Object.prototype и каким образом? Я не удивлюсь, если Node не имеет такой спецификации, но WebIDL может иметь ее для браузера, если ее нет в ECMAScript. (@jfriend00)   -  person apsillers    schedule 06.01.2017
comment
Но не имеет значения, какой из них является глобальным объектом, дело в том, что это объект, поэтому он наследует функции прототипа объекта, включая функцию hasOwnProperty.   -  person Yerko Palma    schedule 06.01.2017
comment
То же самое сообщение об ошибке появится при выполнении Object.prototype.hasOwnProperty.call(null, 'x'), что может означать только то, что его прямой вызов приводит к тому, что this будет чем-то другим, а не глобальным объектом (что вы обычно ожидаете от напрямую вызываемых функций). В спецификации не упоминается, как определяется this. ecma-international.org/ecma-262/6.0 / но я могу ошибаться.   -  person Joseph    schedule 06.01.2017
comment
Я хочу сказать, что если вы хотите увидеть, есть ли свойство у объекта global, используйте global.hasOwnProperty(). Работает как чемпион. Проблема решена. Если вы пытаетесь сделать что-то еще, кроме проверки наличия свойства у глобального объекта, опишите эту проблему, чтобы мы могли посоветовать другое решение. Вызов метода без ссылки на объект, как в obj.method(), или использование .call() или .apply() вызывает проблемы и, вероятно, вызывает проблемы с this при выполнении hasOwnProperty(). Но, честно говоря, меня не очень интересует почему, потому что это неправильный способ кодирования.   -  person jfriend00    schedule 06.01.2017
comment
@JosephtheDreamer this определяется семантикой времени выполнения выражения вызова функции: ecma-international.org/ecma-262/6.0/#sec-function-call   -  person apsillers    schedule 06.01.2017
comment
извини @jfriend00 я не хотел тебя беспокоить. Если сделать то же самое в браузере, все работает нормально (нет разницы в вызове window.hasOwnProperty и hasOwnProperty). Многие пишут alert(), например, вместо window.alert() и это не вызывает комментариев типа стандартного определения нет... - просто работает. Но есть разница в node.js. И, возможно, вы правы - возможно, это какой-то странный побочный эффект. Я просто хотел знать ... если это недостаточно важно для этого сайта, я мог бы спросить где-нибудь еще. Спасибо, в любом случае!   -  person Alexander Mihailov    schedule 06.01.2017
comment
alert() — это просто глобальная функция. Это не метод, выполняющий операцию над объектом, к которому он присоединен. hasOwnProperty() — это метод, который необходимо вызывать в контексте определенного объекта, поскольку он работает с этим объектом. Если вы попытаетесь позвонить Object.prototype.hasOwnProperty("foo"), это тоже не сработает. n Должно быть Object.prototype.hasOwnProperty.call(someObj, "foo") или someObj.hasOwnProperty("foo"). То же самое с global.   -  person jfriend00    schedule 06.01.2017
comment
@jfriend00 Я не совсем с тобой согласен. 1) alert — это не глобальная функция, а метод объекта window. ссылка 2) как бы вы прокомментировали это, особенно раздел под названием Простой вызов   -  person Alexander Mihailov    schedule 06.01.2017
comment
@AlexanderMihailov Глобальные функции являются методами window. Вот как работает глобальная область видимости. На самом деле, я думаю (неправильное) понимание того, как работает глобальная область видимости, лежит в основе вашего замешательства. (Тем не менее, у меня есть небольшое идеологическое несогласие с jfriend00; я думаю, что это хороший вопрос и возможность узнать о некоторых низкоуровневых поведениях, определенных ECMAScript.)   -  person apsillers    schedule 06.01.2017
comment
@apsillers Хорошо, но разве hasOwnProperty не является также методом объекта окна? а так еще и глобальная функция (по Вашей логике?)   -  person Alexander Mihailov    schedule 06.01.2017
comment
@AlexanderMihailov Да, это так! Это глобальная функция, поскольку идентификатор hasOwnProperty глобально доступен из всех областей, и это так, потому что это свойство существует в window (путем наследования Object.prototype). Его можно вызвать, потому что это функция, но это не означает, что он имеет разумный результат, когда вызывается как голый hasOwnProperty(), конечно, как уже объяснял jfriend00.   -  person apsillers    schedule 06.01.2017
comment
@apsillers хорошо, в этот момент я мог только повторить свой вопрос. В браузере hasOwnProperty сопоставляется непосредственно с глобальной областью (window). В node.js это не так. Почему ? (Я имею в виду здесь: код отлично работает в браузере, если вы замените global на window)   -  person Alexander Mihailov    schedule 06.01.2017
comment
Вам не хватает hasOwnProperty(), для выполнения своей работы требуется значение this, и это значение this должно быть объектом, над которым вы пытаетесь работать. Таким образом, вызов hasOwnProperty("foo") работает только в том случае, если значение по умолчанию this при вызове функции оказывается таким, каким вам нужно. В строгом режиме этого НИКОГДА не будет, потому что this будет undefined. В нестрогом режиме вам может повезти. Но общий смысл заключается в том, что метод, который работает с объектом, ДОЛЖЕН вызываться в контексте ЭТОГО конкретного объекта, иначе он не сможет правильно выполнять свою работу.   -  person jfriend00    schedule 06.01.2017
comment
alert() не работает с объектом, поэтому его можно вызвать в любом случае, черт возьми, и он будет работать нормально. hasOwnProperty() должен вызываться в контексте конкретного объекта (если только вы не надеетесь, что система волшебным образом установит правильное this по умолчанию, что никогда не будет истинным в строгом режиме и никогда не должно использоваться в хорошем программировании на Javascript). Вы понимаете, что hasOwnProperty() — это метод объекта, и при вызове ему должна быть предоставлена ​​ссылка на объект для работы? Это ключ здесь.   -  person jfriend00    schedule 06.01.2017
comment
Когда вы просто вызываете hasOwnProperty(), как метод узнает, над каким объектом работать? Есть миллионы потенциальных объектов. Как он узнает, на каком из них вы просите посмотреть недвижимость? Если вы не полагаетесь на назначение нестрогого режима this объекту window в браузере (что является плохим поведением при кодировании), это никогда не сработает. Методы, работающие с объектом, должны вызываться с контекстом объекта, с которым вы хотите, чтобы они работали, иначе они не смогут выполнять свою работу должным образом.   -  person jfriend00    schedule 06.01.2017
comment
Я бы посоветовал вам прочитать Когда вы передаете "это" как аргумент для помощи в понимании того, что происходит с this в вызове функции. По сути, вы спрашиваете, почему неправильный код работает в некоторых средах. IMO, обучение здесь заключается в том, как писать правильный код, который работает во всех средах, а не в том, чтобы подробно разобраться, почему он работает в некоторых средах.   -  person jfriend00    schedule 06.01.2017
comment
alert() является глобальным. Просто так получилось, что в среде браузера (в основном по плохим историческим причинам) все глобальные переменные до ES6 являются свойствами объекта window. В ES6 глобальный объект, объявленный с помощью const или let, НЕ находится в объекте window, поскольку Javascript/браузеры пытаются исправить плохое поведение (глобальные объекты, объявленные с помощью var, все еще находятся в объекте window в ES6 для обратной совместимости). node.js не имеет такого же плохого поведения с глобальными переменными, и отчасти это связано с тем, что вы никогда не сможете запустить какой-либо код в истинном глобальном пространстве имен, поскольку весь код выполняется в контексте модуля.   -  person jfriend00    schedule 06.01.2017
comment
@jfriend00 в этой ссылке они объясняют, как this разрешается в случае простого вызова и точно так же работает в Mozilla FF. Они говорят, что поскольку следующий код не находится в строгом режиме и поскольку значение this не установлено вызовом, по умолчанию будет использоваться глобальный объект:. Так что кажется, что в строгом режиме вызов по умолчанию не к глобальному объекту, а к null (или undefined, я не знаю). И в этот момент я кое-что узнал, и я думаю, что это ответ. Спасибо, сэр!   -  person Alexander Mihailov    schedule 06.01.2017
comment
Имейте в виду, что MDN документирует поведение, которое Firefox и большинство других браузеров обычно используют. Глобальный объект — это одна вещь, которая обрабатывается в node.js по-разному, и вы не можете предполагать, что все действия браузера, связанные с глобальным объектом, одинаковы в node.js. Кроме того, пожалуйста, научитесь программировать в строгом режиме. Весь код строгого режима отлично работает в нестрогом режиме и намного безопаснее во ВСЕХ средах (поэтому вы должны использовать его). Если вы полагаетесь на какую-то автоматическую настройку нестрогого режима this, это ПЛОХОЙ код. Не делай этого. Немедленно измените весь свой код на строгий режим.   -  person jfriend00    schedule 06.01.2017


Ответы (4)


Проблема здесь в том, что hasOwnProperty() — это метод объекта, и вся его функция заключается в работе со свойствами этого объекта. Таким образом, он работает только в том случае, если при его вызове ему задан правильный контекст объекта. Обычно это письменные методы, ожидающие, что контекст объекта должен получить значение this при вызове метода.

В большинстве случаев в JavaScript (за исключением функций, определенных с помощью стрелочного синтаксиса) значение this определяется тем, как вызывается метод. Обычный и наиболее распространенный способ вызвать метод для соответствующего объекта:

obj.method()

Это приведет к тому, что JavaScript установит this в obj при вызове method().

Если вы сделаете что-то вроде этого:

var myFunction = obj.method;

И затем вы вызываете этот метод без ссылки на объект, как в:

var myFunction = obj.method;
myFunction();

Тогда ссылка на объект в obj теряется и никак не передается методу. Интерпретатор JavaScript выберет значение по умолчанию для this.

В строгом режиме this будет установлено на undefined, и любой метод, который попытается использовать это значение, ожидая, что это ссылка на объект, потерпит неудачу.

В нестрогом режиме браузер установит this так, чтобы он указывал на некоторое значение по умолчанию. В браузере это объект window. Итак, если бы вы попытались использовать этот метод для объекта окна, о чудо, он бы сработал. Я считаю это своего рода случайностью, а не хорошим кодом.

ИМО, мораль этой истории заключается в том, что любой метод, который должен быть связан с объектом, должен вызываться с явной ссылкой на объект. Затем это устраняет всю путаницу, устраняет все различия между строгим и нестрогим режимами и устраняет все различия между браузером и Node.js.

Итак, почему это происходит:

имеет собственное свойство (x)

TypeError: невозможно преобразовать undefined или null в объект

Если вы пытаетесь вызвать hasOwnProperty() для проверки свойства глобального объекта в node.js, вызовите метод с контекстом объекта global, как показано ниже:

global.hasOwnProperty("a")

Это будет работать везде и считается хорошим и правильным кодом Javascript. Вызов его без надлежащего контекста объекта приводит к тому, что значение this устанавливается на значение по умолчанию. В node.js это значение по умолчанию не обязательно будет глобальным объектом. Но во всех случаях вы НИКОГДА не должны полагаться на то, каким будет это значение по умолчанию. Программируйте правильно, всегда указывая нужную ссылку на объект, и ваш код везде будет работать нормально.


К вашему сведению, есть больше способов контролировать, что this передается функции, чем просто obj.method(). Вы можете прочитать о других способах здесь, в этом другом ответе. Они включают в себя такие вещи, как .call(), .apply(), стрелочные функции (в ES6) и т. д.

person jfriend00    schedule 05.01.2017
comment
@alexandermihailov - я собрал свои комментарии в ответ. - person jfriend00; 06.01.2017
comment
Думаю суть ответа в том, как this по умолчанию становится undefined или global(window) в режиме strict\non-strict. Детали, которые вы описываете, выходят за рамки этого, но они действительно хороши и будут действительно полезны для других. Спасибо за обсуждение! - person Alexander Mihailov; 06.01.2017

Обновление от 16 января 2017 г.: Node.js не работает в strict mode до тех пор, пока он не будет задан явным образом. Тем не менее, есть разница между Node.js в non strict mode и Firefox (в Firefox простой вызов hasOwnProperty работает без исключения). Я буду искать дальше и обновлю этот ответ, когда/если найду результат.


Решение: способ Node.js работает в strict mode и

в strict mode, если this не был определен контекстом выполнения, он остается undefined.

(Это тот случай, когда я напрямую вызывал hasOwnProperty.)

Дополнительная информация находится здесь (в разделе Простой вызов< /эм>).

person Alexander Mihailov    schedule 05.01.2017

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

  • Когда функция вызывается как свойство объекта (например, foo.bar()), тогда this устанавливается на объект-владелец

  • Когда функция вызывается как голая функция (например, bar()), то значение this либо

    • the global object, if the function has non-strict-mode code, or
    • undefined, если функция имеет код строгого режима

Цель функции hasOwnProperty состоит в том, чтобы проверить, имеет ли некоторый объект (предоставленный как значение this) свойство с определенным именем (предоставленное в качестве аргумента функции). foo.hasOwnProperty("baz") использует значение this, равное foo, и проверяет, имеет ли значение this свойство с именем baz.

Когда вы вызываете hasOwnProperty("baz"), автономный идентификатор hasOwnProperty ссылается на глобально доступное значение из window.hasOwnProperty, но вызов имеет форму bar(...), а не форму foo.bar(...). Следовательно, применяется второе правило, приведенное выше, для предоставления значения this.

Похоже, что метод Node.js hasOwnProperty находится в строгом режиме, поэтому hasOwnProperty("baz") предоставляется undefined в качестве значения this. Не имеет смысла спрашивать undefined, есть ли у него какое-либо свойство, поэтому этот вызов выдает ошибку.

Напротив, похоже, что метод hasOwnProperty Firefox не является строгим, поэтому он получает глобальный объект как this. Это делает результат вызовов window.hasOwnproperty(...) и hasOwnproperty(...) идентичным, потому что они оба получают this равным window.

person apsillers    schedule 05.01.2017
comment
Единственное, чего я не мог понять, так это почему вызов hasOwnProperty сам по себе не приводит к глобальному объекту по умолчанию. strict против non-strict ответили на мой вопрос. Я понимаю остальное, но это поможет кому-то еще наверняка. Обсуждение помогло мне прийти к решению. Спасибо! - person Alexander Mihailov; 06.01.2017

Когда вы используете hasOwnProperty("x"),

  1. hasOwnProperty – это идентификатор. См. семантику среды выполнения.

  2. Идентификатор разрешается с помощью ResolveBinding, который вызывает GetIdentifierReference

  3. Возможно, после некоторой рекурсии это дает ссылку

    { base: globalEnvRec, referencedName: "hasOwnProperty", strict: strictFlag }
    
  4. Тогда вы называете это. См. семантику среды выполнения.

  5. Он получает функцию, используя GetValue, например:

    1. Calls the GetBindingValue for global environment records
    2. Предполагая, что привязка не объявлена, он вызовет GetBindingValue для записи объекта
    3. Это вызовет Get с объектом привязки
  6. Поскольку основой ссылки является запись среды, thisValue получается из WithBaseObject для глобальных записей среды, который всегда возвращает undefined.

  7. Наконец, вызов оценивается EvaluateDirectCall, который использует что thisValue установлен на undefined.

Когда вы используете globalObj.hasOwnProperty("x"),

  1. Разрешение идентификатора происходит для globalObj. Допустим, он получает глобальный объект.

  2. Метод доступа к свойству оценивается. См. семантику среды выполнения.

  3. Он возвращает ссылку

    { base: globalObj, referencedName: "hasOwnProperty", strict: strictFlag }
    
  4. Тогда вы называете это. См. семантику среды выполнения.

  5. Он получает функцию, используя GetValue. Поскольку это ссылка на свойство, а базой является объект, [[Get]] используется внутренний метод

  6. Поскольку ссылка является ссылкой на свойство, thisValue получается из GetThisValue, который возвращает базу (глобальный объект).

  7. Наконец, вызов оценивается EvaluateDirectCall, который использует что thisValue установлен на глобальный объект.

Теперь важно, как функция будет обрабатывать значение this.

По умолчанию [[ Call]] использует OrdinaryCallBindThis, который в небрежный режим преобразует null или undefined thisArgument в глобальный объект. Этого не происходит, если функция определена в строгом режиме.

Наконец, в определении Object.prototype.hasOwnProperty используется ToObject для значения this. Будет выброшено, если это null или undefined.

console.log(function(){ return this }() === window);
console.log(function(){ "use strict"; return this }() === undefined);

Итак, hasOwnProperty определено в строгом режиме? Что ж, для объектов встроенных функций< / а>,

Для каждой встроенной функции при вызове с [[Call]] [[Call]] thisArgument предоставляет значение this, [[Call]] argumentsList предоставляет именованные параметры, а значение NewTarget не определено.

Таким образом, значение this передается как есть, без преобразования null и undefined в глобальный объект. Как в строгих функциях.

Как следствие, если вы хотите использовать hasOwnProperty напрямую, не указывая глобальный объект в качестве основы, вы можете просто переопределить его с помощью функции небрежного режима. Я не рекомендую это; это просто для удовольствия.

(function() {
  var has = Object.prototype.hasOwnProperty;
  Object.prototype.hasOwnProperty = function() {
    return has.apply(this, arguments);
  };
})();
console.log(hasOwnProperty('hello')); // false
console.log(hasOwnProperty('window')); // true

Или геттерный подход:

Object.defineProperty(window, 'hasOwnProperty', {get: function() {
  return Object.prototype.hasOwnProperty.bind(this);
}});
console.log(hasOwnProperty('hello')); // false
console.log(hasOwnProperty('window')); // true

Однако стандарт ECMASCript не гарантирует, что глобальный объект будет наследоваться от Object.prototype.

От InitializeHostDefinedRealm это полностью зависит от реализации.

Если хост требует использования экзотического объекта в качестве области глобальный объект, пусть global< /em> быть таким объектом, созданным способом, определенным реализацией.

Так что вообще вы не должны использовать globalObj.hasOwnProperty или hasOwnProperty. Для некоторых реализаций это может быть нормально (например, для веб-браузеров это обеспечивается W3C и < href="https://en.wikipedia.org/wiki/WHATWG" rel="nofollow noreferrer">WHATWG), но для других это может совершенно не сработать.

Даже если это нормально для вашей реализации, это все равно плохо. hasOwnProperty может быть затенен. Например, я только что сказал в Интернете, что window наследуется от Object.prototype, но глобальный загрязнитель находится ближе в прототипной цепочке, и может быть элемент с id="hasOwnProperty"!!

Вместо этого я рекомендую один из них:

Object.prototype.hasOwnProperty.call(globalObj, prop)
Object.getOwnPropertyDescriptor(globalObj, prop) !== undefined
person Oriol    schedule 06.01.2017