Question: nodejs: why calling hasOwnProperty ist different from global.hasOwnProperty?

Question

nodejs: why calling hasOwnProperty ist different from global.hasOwnProperty?

Answers 4
Added at 2017-01-05 21:01
Tags
Question

Maybe this is a newbie question, but I cannot find or think out the explanation. Start the Node.js console.

> global.hasOwnProperty === hasOwnProperty
true

Then why

> global.hasOwnProperty("x")
false

but

> 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)
Answers to

nodejs: why calling hasOwnProperty ist different from global.hasOwnProperty?

nr: #1 dodano: 2017-01-05 23:01

The solution: The node.js works in strict mode and

in strict mode, if this was not defined by the execution context, it remains undefined.

(this is the case when I called hasOwnProperty directly)

More info here (in the section Simple Call)

nr: #2 dodano: 2017-01-05 23:01

The main point you need to understand here is that the value of this within a function execution changes depending on how the function is called. The two relevant ways here are:

  • When a function is called as a property of an object (e.g., foo.bar()) then this is set to the owning object

  • When a function is invoked as a "bare" function (e.g., bar()), then the this value is either

    • the global object, if the function has non-strict-mode code, or
    • undefined, if the function has strict-mode code

The purpose of the hasOwnProperty function is to test if some object (supplied as the this value) has a property with a particular name (supplied as a function argument). foo.hasOwnProperty("baz") uses a this value of foo and tests if the this value has a property named baz.

When you call hasOwnProperty("baz"), the standalone identifier hasOwnProperty refers to the globally-accessible value from window.hasOwnProperty, but the invocation is of the form bar(...), not of the form foo.bar(...). Therefore, the second rule, above, for supplying a this value applies.

It appears that Node's hasOwnProperty method is in strict-mode, so hasOwnProperty("baz") is supplied undefined as its this value. It is not sensible to ask undefined if it has any property, so this invocation produces an error.

By contrast, it appears that Firefox's hasOwnProperty method is non-strict, so it gets the global object as this. That makes the result of the calls window.hasOwnproperty(...) and hasOwnproperty(...) identical, because they both get a this equal to window.

nr: #3 dodano: 2017-01-05 23:01

Collecting all my comments into an answer, now that I have time to write a proper answer and the question is finally more clear to me.

The issue here is that hasOwnProperty() is a method of an object and it's entire function is to operate on properties of that object. As such, it only works if, when it is called, it is given a proper object context. Normally written methods expect that object context to arrive in the value of this when the method is invoked.

In most cases in Javascript (except for functions defined with the arrow syntax), the value of this is determined by how a method is called. The usual and most common way to invoke a method on the appropriate object is with:

obj.method()

This will cause Javascript to set this to obj when it called method().

If you do something like this:

var myFunction = obj.method;

And, then you call that method with no object reference as in:

var myFunction = obj.method;
myFunction();

Then, the object reference in obj is lost and is not given to the method in any way. The Javascript interpreter will pick a default value for this. In strict mode, this will be set to undefined and any method that attempts to use that value, expecting it to be an object reference, will fail. In non-strict mode, a browser will set this to point to "some default value". In a browser, that is the window object. So, if you were trying to use the method on the window object, lo and behold, it would happen to work. I consider this somewhat of an accident, not good code.

IMO, the moral of the story is that any method that expects to be associated with an object should be called with an explicit object reference. Then, that removes all confusion, removes all difference between strict mode and non-strict mode and removes all difference between a browser and node.js.

So why does this happen:

hasOwnProperty("x")

TypeError: Cannot convert undefined or null to object

If you're trying to call hasOwnProperty() to test a property of the global object in node.js, then call the method with the context of the global object as in:

global.hasOwnProperty("a")

That will work everywhere and is considered good and proper Javascript code. Calling it without the proper object context causes the this value to be set to a default value. In node.js, that default value will not necessarily be the global object. But, in all cases, you should NEVER rely on what that default value will be. Program correctly by always specifying the desired object reference and your code will work fine everywhere.


FYI, there are more ways to control what this is passed a function than just obj.method(). You can read about the other ways here in this other answer. They include things such as .call(), .apply(), arrow functions (in ES6), etc...

nr: #4 dodano: 2017-01-06 02:01

When you use hasOwnProperty("x"),

  1. hasOwnProperty is an identifier. See runtime semantics
  2. The identifier is resolved using ResolveBinding, which calls GetIdentifierReference
  3. Possibly after some recursion, this produces the reference

    { base: globalEnvRec, referencedName: "hasOwnProperty", strict: strictFlag }
    
  4. Then you call it. See runtime semantics

  5. It gets the function using GetValue, like this:
    1. Calls the GetBindingValue for global environment records
    2. Assuming there is no declared binding, it will call GetBindingValue for the object record
    3. That will call Get with the binding object
  6. Since the base of the reference is an environment record, the thisValue is obtained from WithBaseObject for global environment records, which always returns undefined.
  7. Finally, the call is evaluated by EvaluateDirectCall, which uses that thisValue set to undefined.

When you use globalObj.hasOwnProperty("x"),

  1. Identifier resolution happens for globalObj, let's say it gets the global object.
  2. The property accessor is evaluated. See runtime semantics
  3. It returns the reference

    { base: globalObj, referencedName: "hasOwnProperty", strict: strictFlag }
    
  4. Then you call it. See runtime semantics
  5. It gets the function using GetValue. Since it's a property reference and the base is an object, the [[Get]] internal method is used
  6. Since the reference is a property reference, the thisValue is obtained from GetThisValue, which returns the base (the global object).
  7. Finally, the call is evaluated by EvaluateDirectCall, which uses that thisValue set to the global object.

Now what's important is how the function will treat the this value.

By default, [[Call]] uses OrdinaryCallBindThis which in sloppy mode transforms a null or undefined thisArgument to the global object. This does not happen if the function is defined in strict mode.

Finally, the definition of Object.prototype.hasOwnProperty uses ToObject on the this value. That will throw if it's null or undefined.

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

So, is hasOwnProperty defined in strict mode? Well, for built-in function objects,

For each built-in function, when invoked with [[Call]], the [[Call]] thisArgument provides the this value, the [[Call]] argumentsList provides the named parameters, and the NewTarget value is undefined

So the this value is passed as is, without converting null and undefined to the global object. Like in strict functions.

As a corollary, if you want to use hasOwnProperty directly without specifying the global object as the base, you could just redefine it with a sloppy-mode function. I don't recommend this, it's just for fun.

(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

Or a getter approach:

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

However, the ECMASCript standard does not ensure that the global object will inherit from Object.prototype.

From InitializeHostDefinedRealm, it's completely implementation-dependent.

If the host requires use of an exotic object to serve as realm's global object, let global be such an object created in an implementation defined manner.

So in general you should not use globalObj.hasOwnProperty nor hasOwnProperty. For some implementation it may be OK (e.g. for web browsers this is enforced by W3C and WHATWG standards), but for others it might completely fail.

Even if it's OK for your implementation, it's still bad. hasOwnProperty could be shadowed. For example, I just said on the web window inherits from Object.prototype, but the global polluter is closer in the prototypical chain, and there could be an element with id="hasOwnProperty"!!

Instead, I recommend one of these:

Object.prototype.hasOwnProperty.call(globalObj, prop)
Object.getOwnPropertyDescriptor(globalObj, prop) !== undefined
Source Show
◀ Wstecz