最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Inconsistent behavior in JavaScript's conditional checking - Stack Overflow

programmeradmin2浏览0评论

I want to build an object obj1 with property obj2, which is another object. To avoid redeclaring obj1 and obj2, I use the following code:

if (!obj1) obj1 = {};
if (!obj1.obj2) obj1.obj2 = {};
// code to use obj1

Assume, obj1 and obj1.obj2 aren't defined yet, the code causes the browser to report the error "obj1 is not defined".

If I change the code to:

if (typeof obj1==='undefined') obj1 = {};
if (!obj1.obj2) obj1.obj2 = {};
// code to use obj1

Then there's no error, while I think it should report "obj2 is not defined". I'm puzzled as to why the JavaScript treats the short-hand falsy check of a reference and a property differently. Can anyone shed a light on that?

I want to build an object obj1 with property obj2, which is another object. To avoid redeclaring obj1 and obj2, I use the following code:

if (!obj1) obj1 = {};
if (!obj1.obj2) obj1.obj2 = {};
// code to use obj1

Assume, obj1 and obj1.obj2 aren't defined yet, the code causes the browser to report the error "obj1 is not defined".

If I change the code to:

if (typeof obj1==='undefined') obj1 = {};
if (!obj1.obj2) obj1.obj2 = {};
// code to use obj1

Then there's no error, while I think it should report "obj2 is not defined". I'm puzzled as to why the JavaScript treats the short-hand falsy check of a reference and a property differently. Can anyone shed a light on that?

Share Improve this question asked Nov 30, 2010 at 11:04 BuuBuu 50.4k5 gold badges69 silver badges85 bronze badges 3
  • 1 You can do if(!window.obj1). – sje397 Commented Nov 30, 2010 at 11:08
  • obj1 is a type, obj2 is a property. – leppie Commented Nov 30, 2010 at 11:10
  • 1 offtopic: it's not that nice, it's a simple "scope of variables" question; ontopic: a variable cannot be checked for value(first sample) before it is declared, second code sample checks for existence, and as a best practice do declare your variables you are using do not let the interpreter to make the declarations for you, you can declare your "global" variables at the beginning of the js file and after that use them. – rbhro Commented Nov 30, 2010 at 12:11
Add a ment  | 

4 Answers 4

Reset to default 6

If you would do:

if (!window.obj1) window.obj1 = {};
if (!obj1.obj2) obj1.obj2 = {};

You will find the code works as you expect.

obj1 isn't even a reference when you check it's existance; it's nothing. It doesn't exist because you haven't declared it (neither have you initialized it).

var obj1;

if (!obj1) obj1 = {};
if (!obj1.obj2) obj1.obj2 = {};

This will also work because you've declared the existance of obj1; you just haven't initialized it.

All properties of an object that haven't been set hold the value undefined; which is why it responds to your short hand !obj1.obj2

var obj1 = {};
obj1.a === undefined // true;

Variables however, must be defined before you can access them.

The "obj1" reference is not declared at all, as a result you get an error.

Use the following syntax for such kind of check:

var obj1 = obj1 || {};

By the way:

if (typeof obj1==='undefined') obj1 = {};

does not help if obj1 == null.

Don't declare global variables (without var). And I strongly remend you to read the JavaScript: The Definitive Guide, 5th Edition. You may skip some chapters but pay your attention to Chapters 3, 7, 8, 9. They must be read and understood.

I think the latter code boils down to §8.7.1 and §8.12.3 in ECMAScript 5:

8.7.1 GetValue

  1. If Type(V) is not Reference, return V.
  2. Let base be the result of calling GetBase(V). If
  3. IsUnresolvableReference(V), throw a ReferenceError exception.
  4. If IsPropertyReference(V), then

a. If HasPrimitiveBase(V) is false, then let get be the [[Get]] internal method of base, otherwise let get be the special [[Get]] internal method defined below.

b. Return the result of calling the get internal method using base as its this value, and passing GetReferencedName(V) for the argument.

5. Else, base must be an environment record.

a. Return the result of calling the GetBindingValue (see 10.2.1) concrete method of base passing GetReferencedName(V) and IsStrictReference(V) as arguments.

8.12.3 [[Get]]

When the [[Get]] internal method of O is called with property name P, the following steps are taken:

8. Let desc be the result of calling the [[GetProperty]] internal method of O with property name P.

9. If desc is undefined, return undefined.

10 If IsDataDescriptor(desc) is true, return desc.[[Value]].

11 Otherwise, IsAccessorDescriptor(desc) must be true so, let getter be desc.[[Get]].

12. If getter is undefined, return undefined.

13. Return the result calling the [[Call]] internal method of getter providing O as the this value and providing no arguments.

As you can see, it only throws a ReferenceError here for IsUnresolvableReference. That applies if the base (obj1) is undefined. There is a list of places where ReferenceError is thrown in §15.11.6.3.

I tried to reconstruct it using chrome JScript console

If you add a watch expression to both obj1 and obj1.obj2 before your two lines both are of course not defined:

obj1: ReferenceError: obj1 is not defined

obj1.obj2: ReferenceError: obj1 is not defined

As soon as you go over this line if (typeof obj1==='undefined') obj1 = {}; all of a sudden they turn into

obj1: Object

obj1.obj2: undefined

So, you cannot check the first with ! because it is not undefined

Hope this helps

发布评论

评论列表(0)

  1. 暂无评论