Perfection kills

Exploring Javascript by example

Archives Posts

Understanding delete

January 10th, 2010 by kangax
  1. Theory

  2. Firebug confusion

  3. Browsers compliance

  4. IE bugs
  5. Misconceptions
  6. `delete` and host objects
  7. ES5 strict mode
  8. Summary

A couple of weeks ago, I had a chance to glance through Stoyan Stefanov’s Object-Oriented Javascript. The book had an exceptionally high rating on Amazon (12 reviews with 5 stars), so I was curious to see if it was something worth recommending. I started reading through chapter on functions, and really enjoyed the way things were explained there; the flow of examples was structured in such nice, progressive way, it seemed even beginners would grasp it easily. However, almost immediately I stumbled upon an interesting misconception present throughout the entire chapter — deleting functions. There were some other mistakes (such as the difference between function declarations and function expressions), but we aren’t going to talk about them now.

The book claims that “function is treated as a normal variable—it can be copied to a different variable and even deleted.”. Following that explanation, there is this example:

  >>> var sum = function(a, b) {return a + b;} 
  >>> var add = sum; 
  >>> delete sum
  true
  >>> typeof sum;
  "undefined"

Ignoring a couple of missing semicolons, can you see what’s wrong with this snippet? The problem, of course, is that deleting sum variable should not be successful; delete statement should not evaluate to true and typeof sum should not result in “undefined”. All because it’s not possible to delete variables in Javascript. At least not when declared in such way.

So what’s going on in this example? Is it a typo? A diversion? Probably not. This whole snippet is actually a real output from the Firebug console, which Stoyan must have been using for quick testing. It’s almost as if Firebug follows some other rules of deletion. It is Firebug that has led Stoyan astray! So what is really going on here?

To answer this question, we need to understand how delete operator works in Javascript: what exactly can and cannot be deleted and why. Today I’ll try to explain this in details. We’ll take a look at Firebug’s “weird” behavior and realize that it’s not all that weird; we’ll delve into what’s going on behind the scenes when declaring variables, functions, assigning properties and deleting them; we’ll look at browsers’ compliance and some of the most notorious bugs; we’ll also talk about strict mode of 5th edition of ECMAScript, and how it changes delete operator behavior.

I’ll be using Javascript and ECMAScript interchangeable to really mean ECMAScript (unless explicitly talking about Mozilla’s JavaScript™ implementation).

Unsurprisingly, explanations of delete on the web are rather scarce. MDC article is probably the most comprehensive resource, but unfortunately misses few interesting details about the subject; Curiously, one of these forgotten things is the cause of Firebug’s tricky behavior. MSDN reference is practically useless.

Theory

So why is it that we can delete object properties:

  var o = { x: 1 }; 
  delete o.x; // true
  o.x; // undefined

but not variables, declared like this:

  var x = 1; 
  delete x; // false
  x; // 1

or functions, declared like this:

  function x(){}
  delete x; // false
  typeof x; // "function"

Note that delete only returns false when a property can not be deleted.

To understand this, we need to first grasp such concepts as variable instantiation and property attributes — something that’s unfortunately rarely covered in books on Javascript. I’ll try go over these very concisely in the next few paragraphs. It’s not hard to understand them at all! If you don’t care about why things work the way they work, feel free to skip this chapter.

Type of code

There are 3 types of executable code in ECMAScript: Global code, Function code and Eval code. These types are somewhat self-descriptive, but here’s a short overview:

  1. When a source text is treated as a Program, it is executed in a global scope, and is considered a Global code. In a browser environment, content of SCRIPT elements is usually parsed as a Program, and is therefore evaluated as a Global code.
  2. Anything that’s executed directly within a function is, quite obviously, considered a Function code. In browsers, content of event attributes (e.g. <p onclick="...">) is usually parsed and treated as a Function code.
  3. Finally, text that’s supplied to a built-in eval function is parsed as Eval code. We will soon see why this type is special.

Execution context

When ECMAScript code executes, it always happens within certain execution context. Execution context is a somewhat abstract entity, which helps understand how scope and variable instantiation works. For each of three types of executable code, there’s an execution context. When a function is executed, it is said that control enters execution context for Function code; when Global code executes, control enters execution context for Global code, and so on.

As you can see, execution contexts can logically form a stack. First there might be Global code with its own execution context; that code might call a function, with its own execution context; that function could call another function, and so on and so forth. Even if function is calling itself recursively, a new execition context is being entered with every invocation.

Activation object / Variable object

Every execution context has a so-called Variable Object associated with it. Similarly to execution context, Variable object is an abstract entity, a mechanism to describe variable instantiation. Now, the interesing part is that variables and functions declared in a source text are actually added as properties of this Variable object.

When control enters execution context for Global code, a Global object is used as a Variable object. This is precisely why variables or functions declared globally become properties of a Global object:

  /* remember that `this` refers to global object when in global scope */
  var GLOBAL_OBJECT = this;
 
  var foo = 1;
  GLOBAL_OBJECT.foo; // 1
  foo === GLOBAL_OBJECT.foo; // true
 
  function bar(){}
  typeof GLOBAL_OBJECT.bar; // "function"
  GLOBAL_OBJECT.bar === bar; // true

Ok, so global variables become properties of Global object, but what happens with local variables — those declared in Function code? The behavior is actually very similar: they become properties of Variable object. The only difference is that when in Function code, a Variable object is not a Global object, but a so-called Activation object. Activation object is created every time execution context for Function code is entered.

Not only do variables and functions declared within Function code become properties of Activation object; this also happens with each of function arguments (under names corresponding to formal parameters) and a special Arguments object (under arguments name). Note that Activation object is an internal mechanism and is never really accessible by program code.

  (function(foo){
 
    var bar = 2;
    function baz(){}
 
    /*
    In abstract terms,
 
    Special `arguments` object becomes a property of containing function's Activation object: 
      ACTIVATION_OBJECT.arguments; // Arguments object
 
    ...as well as argument `foo`:
      ACTIVATION_OBJECT.foo; // 1
 
    ...as well as variable `bar`:
      ACTIVATION_OBJECT.bar; // 2
 
    ...as well as function declared locally:
      typeof ACTIVATION_OBJECT.baz; // "function"
    */
 
  })(1);

Finally, variables declared within Eval code are created as properties of calling context’s Variable object. Eval code simply uses Variable object of the execution context that it’s being called within:

  var GLOBAL_OBJECT = this;
 
  /* `foo` is created as a property of calling context Variable object,
      which in this case is a Global object */
 
  eval('var foo = 1;');
  GLOBAL_OBJECT.foo; // 1
 
  (function(){
 
    /* `bar` is created as a property of calling context Variable object,
      which in this case is an Activation object of containing function */
 
    eval('var bar = 1;');
 
    /* 
      In abstract terms, 
      ACTIVATION_OBJECT.bar; // 1
    */
 
  })();

Property attributes

We are almost there. Now that it’s clear what happens with variables (they become properties), the only remaining concept to understand is property attributes. Every property can have zero or more attributes from the following set — ReadOnly, DontEnum, DontDelete and Internal. You can think of them as flags — an attribute can either exist on a property or not. For the purposes of today’s discussion, we are only interested in DontDelete.

When declared variables and functions become properties of a Variable object — either Activation object (for Function code), or Global object (for Global code), these properties are created with DontDelete attribute. However, any explicit (or implicit) property assignment creates property without DontDelete attribute. And this is essentialy why we can delete some properties, but not others:

  var GLOBAL_OBJECT = this;
 
  /*  `foo` is a property of a Global object.
      It is created via variable declaration and so has DontDelete attribute.
      This is why it can not be deleted. */
 
  var foo = 1;
  delete foo; // false
  typeof foo; // "number"
 
  /*  `bar` is a property of a Global object.
      It is created via function declaration and so has DontDelete attribute.
      This is why it can not be deleted either. */
 
  function bar(){}
  delete bar; // false
  typeof bar; // "function"
 
  /*  `baz` is also a property of a Global object.
      However, it is created via property assignment and so has no DontDelete attribute.
      This is why it can be deleted. */
 
  GLOBAL_OBJECT.baz = 'blah';
  delete GLOBAL_OBJECT.baz; // true
  typeof GLOBAL_OBJECT.baz; // "undefined"

Built-ins and DontDelete

So this is what it’s all about: a special attribute on a property that controls whether this property can be deleted or not. Note that some of the properties of built-in objects are specified to have DontDelete, and so can not be deleted. Special arguments variable (or, as we know now, a property of Activation object) has DontDelete. length property of any function instance has DontDelete as well:

  (function(){
 
    /* can't delete `arguments`, since it has DontDelete */
 
    delete arguments; // false
    typeof arguments; // "object"
 
    /* can't delete function's `length`; it also has DontDelete */
 
    function f(){}
    delete f.length; // false
    typeof f.length; // "number"
 
  })();

Properties corresponding to function arguments are created with DontDelete as well, and so can not be deleted either:

  (function(foo, bar){
 
    delete foo; // false
    foo; // 1
 
    delete bar; // false
    bar; // 'blah'
 
  })(1, 'blah');

Undeclared assignments

As you might remember, undeclared assignment creates a property on a global object. That is unless that property is found somewhere in the scope chain before global object. And now that we know the difference between property assignment and variable declaration — latter one sets DontDelete, whereas former one doesn’t — it should be clear why undeclared assignment creates a deletable property:

  var GLOBAL_OBJECT = this;
 
  /* create global property via variable declaration; property has DontDelete */
  var foo = 1;
 
  /* create global property via undeclared assignment; property has no DontDelete */
  bar = 2;
 
  delete foo; // false
  typeof foo; // "number"
 
  delete bar; // true
  typeof bar; // "undefined"

Note that it is during property creation that attributes are determined (i.e. none are set). Later assignments don’t modify attributes of existing property. It’s important to understand this distinction.

  /* `foo` is created as a property with DontDelete */
  function foo(){}
 
  /* Later assignments do not modify attributes. DontDelete is still there! */
  foo = 1;
  delete foo; // false
  typeof foo; // "number"
 
  /* But assigning to a property that doesn't exist, 
     creates that property with empty attributes (and so without DontDelete) */
 
  this.bar = 1;
  delete bar; // true
  typeof bar; // "undefined"

Firebug confusion

So what happens in Firebug? Why is it that variables declared in console can be deleted, contrary to what we have just learned? Well, as I said before, Eval code has a special behavior when it comes to variable declaration. Variables declared within Eval code are actually created as properties without DontDelete:

  eval('var foo = 1;');
  foo; // 1
  delete foo; // true
  typeof foo; // "undefined"

and, similarly, when called within Function code:

  (function(){
 
    eval('var foo = 1;');
    foo; // 1
    delete foo; // true
    typeof foo; // "undefined"
 
  })();

And this is the gist of Firebug’s abnormal behavior. All the text in console seems to be parsed and executed as Eval code, not as a Global or Function one. Obviously, any declared variables end up as properties without DontDelete, and so can be easily deleted. Be aware of these differences between regular Global code and Firebug console.

Deleting variables via eval

This interesting eval behavior, coupled with another aspect of ECMAScript can technically allow us to delete non-deletable properties. The thing about function declarations is that they can overwrite same-named variables in the same execution context:

  function x(){ }
  var x;
  typeof x; // "function"

Note how function declaration takes precedence and overwrites same-named variable (or, in other words, same property of Variable object). This is because function declarations are instantiated after variable declarations, and are allowed to overwrite them. Not only does function declaration replaces previous value of a property, it also replaces that property attributes. If we declare function via eval, that function should also replace that property’s attributes with its own. And since variables declared from within eval create properties without DontDelete, instantiating this new function should essentially remove existing DontDelete attribute from the property in question, making that property deletable (and of course changing its value to reference newly created function).

  var x = 1;
 
  /* Can't delete, `x` has DontDelete */
 
  delete x; // false
  typeof x; // "number"
 
  eval('function x(){}');
 
  /* `x` property now references function, and should have no DontDelete */
 
  typeof x; // "function"
  delete x; // should be `true`
  typeof x; // should be "undefined"

Unfortunately, this kind of spoofing doesn’t work in any implementation I tried. I might be missing something here, or this behavior might simply be too obscure for implementors to pay attention to.

Browsers compliance

Knowing how things work in theory is useful, but practical implications are paramount. Do browsers follow standards when it comes to variable/property creation/deletion? For the most part, yes.

I wrote a simple test suite to check compliance of delete operator with Global code, Function code and Eval code. Test suite checks both — return value of delete operator, and whether properties are deleted (or not) as they are supposed to. delete return value is not as important as its actual results. It’s not very crucial if delete returns true instead of false, but it’s important that properties with DontDelete are not deleted and vice versa.

Modern browsers are generally pretty compliant. Besides this eval peculiarity I mentioned earlier, the following browsers pass test suite fully: Opera 7.54+, Firefox 1.0+, Safari 3.1.2+, Chrome 4+.

Safari 2.x and 3.0.4 have problems with deleting function arguments; those properties seem to be created without DontDelete, so it is possible to delete them. Safari 2.x has even more problems — deleting non-reference (e.g. delete 1) throws error; function declarations create deletable properties (but, strangely, not variable declarations); variable declarations in eval become non-deletable (but not function declarations).

Similar to Safari, Konqueror (3.5, but not 4.3) throws error when deleting non-reference (e.g. delete 1) and erroneously makes function arguments deletable.

Gecko DontDelete bug

Gecko 1.8.x browsers — Firefox 2.x, Camino 1.x, Seamonkey 1.x, etc. — exhibit an interesting bug where explicitly assigning to a property can remove its DontDelete attribite, even if that property was created via variable or function declaration:

    function foo(){}
    delete foo; // false (as expected)
    typeof foo; // "function" (as expected)
 
    /* now assign to a property explicitly */
 
    this.foo = 1; // erroneously clears DontDelete attribute
    delete foo; // true
    typeof foo; // "undefined"
 
    /* note that this doesn't happen when assigning property implicitly */
 
    function bar(){}
    bar = 1;
    delete bar; // false
    typeof bar; // "number" (although assignment replaced property)

Surprisingly, Internet Explorer 5.5 – 8 passes test suite fully except that deleting non-reference (e.g. delete 1) throws error (just like in older Safari). But there are actually more serious bugs in IE, that are not immediately apparent. These bugs are related to Global object.

IE bugs

The entire chapter just for bugs in Internet Explorer? How unexpected!

In IE (at least, 6-8), the following expression throws error (when evaluated in Global code):

    this.x = 1;
    delete x; // TypeError: Object doesn't support this action

and this one as well, but different exception, just to make things interesting:

    var x = 1;
    delete this.x; // TypeError: Cannot delete 'this.x'

It’s as if variable declarations in Global code do not create properties on Global object in IE. Creating property via assignment (this.x = 1) and then deleting it via delete x throws error. Creating property via declaration (var x = 1) and then deleting it via delete this.x throws another error.

But that’s not all. Creating property via explicit assignment actually always throws error on deletion. Not only is there an error, but created property appears to have DontDelete set on it, which of course it shouldn’t have:

    this.x = 1;
 
    delete this.x; // TypeError: Object doesn't support this action
    typeof x; // "number" (still exists, wasn't deleted as it should have been!)
 
    delete x; // TypeError: Object doesn't support this action
    typeof x; // "number" (wasn't deleted again)

Now, contrary to what one would think, undeclared assignments (those that should create a property on global object) do create deletable properties in IE:

    x = 1;
    delete x; // true
    typeof x; // "undefined"

But if you try to delete such property by referecing it via this in Global code (delete this.x), a familiar error pops up:

    x = 1;
    delete this.x; // TypeError: Cannot delete 'this.x'

If we were to generalize this behavior, it would appear that delete this.x from within Global code never succeeds. When property in question is created via explicit assignment (this.x = 1), delete throws one error; when property is created via undeclared assignment (x = 1) or via declaration (var x = 1), delete throws another error.

delete x, on the other hand, only throws error when property in question is created via explicit assignment — this.x = 1. If a property is created via declaration (var x = 1), deletion simply never occurs and delete correctly returns false. If a property is created via undeclared assignment (x = 1), deletion works as expected.

I was pondering about this issue back in September, and Garrett Smith suggested that in IE “The global variable object is implemented as a JScript object, and the global object is implemented by the host. Garrett used Eric Lippert’s blog entry as a reference.
We can somewhat confirm this theory by performing few tests. Note how this and window seem to reference same object (if we can believe === operator), but Variable object (the one on which function is declared) is different from whatever this references.

    /* in Global code */
    function getBase(){ return this; }
 
    getBase() === this.getBase(); // false
    this.getBase() === this.getBase(); // true
    window.getBase() === this.getBase(); // true
    window.getBase() === getBase(); // false

Misconceptions

The beauty of understanding why things work the way they work can not be underestimated. I’ve seen few misconceptions on the web related to misunderstanding of delete operator. For example, there’s this answer on Stackoverflow (with surprisingly high rating), confidently explaining how “delete is supposed to be no-op when target isn’t an object property”. Now that we understand the core of delete behavior, it becomes pretty clear that this answer is rather inaccurate. delete doesn’t differentiate between variables and properties (in fact, for delete, those are all References) and really only cares about DontDelete attribute (and property existence).

It’s also interesting to see how misconceptions bounce off of each other, where in the very same thread someone first suggests to just delete variable (which won’t work unless it’s declared from within eval), and another person provides a wrong correction how it’s possible to delete variables in Global code but not in Function one.

Be careful with Javascript explanations on the web, and ideally, always seek to understand the core of the issue ;)

`delete` and host objects

An algorithm for delete is specified roughtly like this:

  1. If operand is not a reference, return true
  2. If object has no direct property with such name, return true (where, as we now know, object can be Activation object or Global object)
  3. If property exists but has DontDelete, return false
  4. Otherwise, remove property and return true

However, behavior of delete operator with host objects can be rather unpredictable. And there’s actually nothing wrong with that: host objects are allowed (by specification) to implement any kind of behavior for operations such as read (internal [[Get]] method), write (internal [[Put]] method) or delete (internal [[Delete]] method), among few others. This allowance for custom [[Delete]] behavior is what makes host objects so chaotic.

We’ve already seen some IE oddities, where deleting certain objects (which are apparently implemented as host objects) throws errors. Some versions of Firefox throw when trying to delete window.location. You can’t trust return values of delete either, when it comes to host objects; take a look at what happens in Firefox:

    /* "alert" is a direct property of `window` (if we were to believe `hasOwnProperty`) */
    window.hasOwnProperty('alert'); // true
 
    delete window.alert; // true
    typeof window.alert; // "function"

Deleting window.alert returns true, even though there’s nothing about this property that should lead to such result. It resolves to a reference (so can’t return true on the first step). It’s a direct property of a window object (so can’t return true on a second step). The only way delete could return true is after reaching step 4 and actually deleting a property. Yet, property is never deleted.

The moral of the story is to never trust host objects.

ES5 strict mode

So what does strict mode of ECMAScript 5th edition bring to the table? Few restrictions are being introduced. SyntaxError is now thrown when expression in delete operator is a direct reference to a variable, function argument or function identifier. In addition, if property has internal [[Configurable]] == false, a TypeError is thrown:

  (function(foo){
 
    "use strict"; // enable strict mode within this function
 
    var bar;
    function baz(){}
 
    delete foo; // SyntaxError (when deleting argument)
    delete bar; // SyntaxError (when deleting variable)
    delete baz; // SyntaxError (when deleting variable created with function declaration)
 
    /* `length` of function instances has { [[Configurable]] : false } */
 
    delete (function(){}).length; // TypeError
 
  })();

In addition, deleting undeclared variable (or in other words, unresolved Referece) throws SyntaxError as well:

    "use strict";
    delete i_dont_exist; // SyntaxError

This is somewhat similar to the way undeclared assignment in strict mode behaves (except that ReferenceError is thrown instead of a SyntaxError):

    "use strict";
    i_dont_exist = 1; // ReferenceError

As you now understand, all these restrictions somewhat make sense, given how much confusion deleting variables, function declarations and arguments causes. Instead of silently ignoring deletion, strict mode takes more agressive and descriptive measures.

Summary

This post turned out to be quite lengthy, so I’m not going to talk about things like removing array items with delete and what the implications of it are. You can always refer to MDC article for that particular explanation (or read specs and experiment yourself).

Here’s a short summary of how deletion works in Javascript:

  • Variables and function declarations are properties of either Activation or Global objects.
  • Properties have attributes, one of which — DontDelete — is responsible for whether a property can be deleted.
  • Variable and function declarations in Global and Function code always create properties with DontDelete.
  • Function arguments are also properties of Activation object and are created with DontDelete.
  • Variable and function declarations in Eval code always create properties without DontDelete.
  • New properties are always created with empty attributes (and so without DontDelete).
  • Host objects are allowed to react to deletion however they want.

If you’d like to get more familiar with things described here, please refer to ECMA-262 3rd edition specification.

I hope you enjoyed this overview and learned something new. Any questions, suggestions and corrections are as always welcomed.

Archives Posts

Professional Javascript. Review.

October 28th, 2009 by kangax

Few months ago, I promised Nicholas Zakas to review 2nd edition of his “Professional Javascript for Web Developers” (published by Wrox). Nicholas was kind enough to send me a copy of a book, but the lack of time at that moment didn’t let me dive into reviewing it right away. Last week I have decided to finally get together and fulfill a long-standing promise.

Here it goes:

What I liked

  • Technically correct most of the time (many other books, for example, have much more errors and misconceptions).
  • Follows specs and standards closely, uses proper terminology.
  • Covers great variety of topics.
  • Promotes good practices, both — javascript and general programming ones.

What I didn’t like

  • Often oversimplified. Covers only about 85% of core language.
  • Browsers behavior and bugs are often talked about without specifying actual versions (*)
  • Uses unnecessary browser sniffing.
  • Information about ECMAScript 5 is heavily outdated.

(*) Because of this, some statements turn out to be plain wrong. For example, there is — “When defining an object via object literal notation, the Object constructor is never actually called (except in Firefox).”. Yet, this only happens in earlier versions of Firefox (<= 2, IIRC). Then there’s — “The children collection is supported in all browsers except Firefox.” — however, Firefox 3.5 does in fact have working children. Unfortunately, there are few more of such misleading assertions.

General nitpicks

  • Not all examples work in Safari 2.x (although book claims they do)
  • Many methods are not written defensively (e.g. assuming that certain methods/properties exist before accessing them)
  • Use of new Array() instead of [] (probably for clarity)
  • Mix of HTML and XHTML (ideally, it would be nice to drop XHTML for well known reasons)
  • Variables are sometimes initialized with (redundant) null

Verdict

The book is a solid foundation for anyone serious about Javascript. Even though not perfect, it is probably one of the best ones on the market at the moment; The quality of so many other books is simply horrendous. I wish “Professional Javascript” would cover things in more details and employed defensive techniques, or even better — had defensive mindset. However, the fact that it doesn’t only leaves room for future improvement. I would rate it as 7/10.

If anyone is interested in more detailed ramblings review, here it is — all broken down by chapters. Enjoy.

Chapter 2

Chapter 2 is about Javascript in HTML. It covers script and noscript elements, document modes and the way scripts are embedded into web pages. It’s nice to see good practices being promoted here: not using html comments in scripts, preferring external scripts to inline ones as well as script elements positioning in the document for best performance. One glaring mistake in this chapter is mention of “</script>” terminating script element, and how “</scr” + “ipt>” should be used instead. This is not really true. What terminates script element in HTML4.01 is actually “</”; and to work around it, it’s usually recommended to break tags as “<” + “/script>” or “<\/script>”.

When explaining CDATA markers, I wish author mentioned that it is mime-type that affects document nature. Prepending document with XHTML doctype — contrary to a popular myth — doesn’t make document true XHTML. Serving it with application/xhtml+xml does. This is important because CDATA markers are now being blindly injected all over the web into documents served as HTML (but with XHTML doctype).

Chapter 3

Chapter 3 gives a brief, yet in-depth overview of language basics. It’s a pretty friendly introductory chapter. At times, it feels like a couple more things could be added to explanations. Section on number conversion, for example, mentions Number and parseInt but misses unary plus (+) operator. Same goes for string conversion — so commonly used something + '' solution is missing.

Some things in this chapter sound questionable. For example, it was strange to hear that “Logically, a null value is an empty object pointer”, that “The value undefined is a derivative of null” and that var o = new Object; //legal, but not recommended. I also didn’t agree with the assertion that ”[…] ECMAScript’s data types have dynamic aspects that make other data types unnecessary” — proper hash table would certainly be a useful addition to a language (since prototypal nature of objects in ECMAScript makes it a rather non-trivial task). There’s an insignificant mistake about undeclared variable — “Only one operation can be performed on an undeclared variable: you can call typeof on it.” — is not technically true; delete operator will not throw ReferenceError either when given undeclared identifier.

Another thing I wish author gave more attention to is the notion of native vs. host objects. Instead, there’s only a very brief section about IE host objects — “The Internet Explorer (IE) implementation of JavaScript has a slightly different approach to JavaScript objects. In IE, only developer-defined objects inherit from Object. All Browser Object Model (BOM) and Document Object Model (DOM) objects are represented differently and so may not have all of the properties and methods of Object.” Unfortunately, IE is not the only browser whose environment has “misbehaving” host objects. It would be nice to see proper explanation of true nature of host objects and caution about how unpredictable they really are (and, more importantly, are allowed to be, by specification).

Chapter 4

Chapter 4 presents simple and clear explanation of variables, as well as so-often-crucial difference between objects and primitives. It’s nice to see argument passing (a source of so many confusions) explained correctly and thoroughly. Another pleasant surprise is Activation/Variable objects being referenced by their original names (contrary to what so many other authors like to do). Undeclared assignments are recommended against, however there’s no mention of related IE bugs, nor is there an explanation of the difference between variable declaration in global context and undeclared assignments (which might not be very important, after all). Explanation of garbage collection is a nice ending of the chapter.

On the down side, some things are only touched upon briefly and some are plain incorrect.

Note on typeof operator is overly generic and misleading — ”[…] When used on a regular expression, typeof incorrectly returns “function” as well.” typeof does in fact return “function” for regex objects but only in some environments — definitely not everywhere. The reason this happens is because in those environments regex objects actually implement internal [[Call]] method. By specification, native objects implementing [[Call]] should return “function” when applied typeof to, so implementations that do this are actually very much conforming.

Another incorrect statement is — “Even though there are only two types of execution contexts […]”. Third overlooked type of execution context is, of course, eval one. Since eval context is not mentioned, its semantics are not mentioned either (however, eval code is being discussed later when talking about global eval method).

Finally, author talks about scope augmentation and how both with and catch affect execution context — “Certain statements cause a temporary addition to the front of the scope chain that is later removed after code execution. There are two times when this occurs […]”. What’s unfortunately missing here is mention of named function expressions which result in scope augmentation as well. That is a third — perfectly valid — scenario of scope augmentation.

Chapter 5

Chapter 5 is about reference types — Object, Array, Date, RegExp, etc. As always, each type is discussed in great detail and with plenty of corresponding examples. It’s an excellent overview of these language types. The chapter also emphasizes what to avoid, such as trailing commas after last value in array and object literals. However, there are few ambiguous statements and, as with other parts of the book, some things seem to be a bit oversimplified.

As an example, author says — “There are two ways to create an instance of Object”. What goes unnoticed is that those are two explicit ways to create an object, and that there’s also an implicit one via function contsructor, e.g.: new (function(){ this.foo = 'bar'; });.

A slight ambiguity is encountered when talking about object literals — “In this example, the left curly brace ({) signifies the beginning of an object literal because it occurs after an assignment operator (in any other context, the left curly brace indicates the beginning of a block statement).” This phrase can be understood as if braces constitute object literal only when it is on the right hand side of assignment expression (as in this case). It would be better if instead author mentioned that braces constitute object literal when in expression context (and block — when in statement context); few examples of expression and statement contexts would be helpful too.

There’s no mention of number literals being valid production in property names of object literals (i.e. — { 5: ‘foo’ }); It was surprising not to see the discussion of dot property access vs bracket-based one (“[” and “]”) — something that I find to be rather important. One notable difference, of course, is that with bracket accessor it is possible to access properties whose names are not valid identifiers, such as “class” or “foo-bar”. When discussing arrays and their methods, it would be nice to see a mention of Array#sort not guaranteed to be stable.

I found section on function expressions vs. function declarations to be very short and simplistic. Although author rightfully describes difference between two in terms of when functions are instantiated (declaration — during variable declaration phase, expression — during actual execution), there are many interesting things that are left untouched: named function expressions and their semantics, use of function declarations in statements and how it should be avoided, infamous NFE bugs in JScript and why naming functions could be beneficial for profiling and debugging. Finally, there’s a rather vague and overgeneralized statement about Safari not understanding named function expressions.

I didn’t like that this value was being called scope, as scope can be misinterpreted with what’s otherwise known as execution context. I would rather see this being called simply as “this value”.

Unfortunately, there was also no mention of JScript bugs with Number.protoype.toFixed and String.prototype.split with regex and capturing groups.

Another misleading assertion was — “Though ECMA-262 doesn’t indicate a way to access the Global object directly […]”. It’s not clear why Mr. Zakas didn’t provide and explain rather ubiquitous way of accessing Global object from within any context — (function(){ return this; })(); — especially crucial in non-browser environments, which might not have global window property.

Chapter 6

Chapter 6 describes OOP in Javascript. It was nice to see author talk about constructors rather than classes, compare various ways of creating reusable objects and explain popular conventions, such as capitalizing constructors. I was a bit disappointed to hear a somewhat vague — ”[…] the this object always points to the Global object (window in web browsers) when a function is called in the global scope.”. Even though technically correct, in my opinion, it would be better to say that function’s this value references Global object when function is called as a function, no matter from which scope — global or local.

The rest of the chapter delves into OOP in great detail, and is sprinkled generously with very helpful and descriptive diagrams. Speaking of prototypal inheritance, Zakas attributes a popular pattern of cloning to Douglas Crockford who wrote an article on this object in 2006. However, I’m not sure why there’s no attribution to Lasse Reichstein Nielsen who presented it 3 years earlier than Crockford, in 2003.

Chapter 7

Chapter 7 is about more complex aspects of ECMAScript — anonymous functions, recursion, closures, private variables and other related goodness.

Unfortunately, it starts somewhat badly by making few erroneous statements about function expressions. First, it says that “function expressions create anonymous functions” (not necessarily). Then it asserts that function(arg0, arg1, arg2){ } is a valid code (not always; it is only “valid” in expression context — such as in assignment or return statements; when in Global code or in function body, this is, of course, a SyntaxError).

Nevertheless, chapter goes on to explain closures quite thoroughly, paying attention to all of the details of how Activation objects are created and initialized, how they constitute scope chain and how this all relates to closures. A minor nitpick is that self-executing anonymous function in few examples are not wrapped with parenthesis; it would be nice if author mentioned this “wrapping” as a good practice and explained why not wrapping functions leads to confusion when scanning through large functions.

The rest of the chapter is informative and in-depth, but unfortunately presents yet another harmful example towards the end — when simulating private variables it uses undeclared assignment to define variable as global one and does so on purpose! This is especially bizarre, given that a bit earlier there’s a clear recommendation to avoid such “pattern”. Undeclared assignment could be easily avoided there, of course.

Chapter 8

Browser Object Model is the topic of this chapter. It talks about window as well as its properties/methods, location, navigator and other “global” members provided by browsers. I have nothing but positive comments here. The only nitpick is with getQueryStringArgs abstraction, which suffers from object being used as a hash table (and have keys “conflict” with Object.prototype.* members).

Chapter 9

This chapter talks about client detection. Fortunately, author explains that capability/feature detection is a more robust approach to browser scripting. However, I feel that some things could be mentioned in more details and under a different angle. For example, there’s no recommendation to test standard methods before proprietary ones, when possible. Neither is there an explanation of typeof being more reliable way of testing host objects presence and/or capabilities. A couple of feature test examples are both rather trivial; I was expecting to see more involved ones. There is however a rather entertaining coverage of userAgent string history and a comprehensive method of parsing that string. Overall, I wish there was more emphasis on the dangers and fragile nature of browser sniffing in this part of the book.

Chapter 10

Chapter 10 describes Document Object Model gradually and throughly. It focuses on properties and semantics of all Node types, and how to work with them. Relationships between nodes are presented very clearly through graphs.

I’m not a fan of convertToArray solution presented there, as it uses rather slow and unnecessary try-catch at run time instead of testing for IE quirk once. It’s also strange to hear rather bold statements such as — “The Element type constructor and prototype are accessible in script in all browsers, including IE as of version 8.” This is far from being true: at least Safari <=2 and Opera <8 don’t provide these “constructors”. It’s very possible that mobile browsers do not expose them either.

Quite shockingly, an example of document.createElement specifics in IE is presented with blatant and completely unnecessary browser sniff. It was ironic to see this “solution” after previous chapter discussed how feature detection should be preferred to browser sniffing whenever possible.

When talking about children there is no mention of bugs in older Safari. contains section, on the other hand, warns about Safari 2.x bugs, but spoils the moment by suggesting to sniff for browser (something that’s, again, unnecessary). An implementation of contains that follows unfortunately turns sniffing advise into reality.

An implementation of getInnerText fortunately tests for features, but fails to mention differences between textContent and innerText.

Implementations of loadScriptString and loadStyleString that follow are excellent examples of proper abstractions, where features are tested and are tested in correct order (however, it would be nice to see alternative example which would avoid run-time try-catch in favor of single load-time test).

Chapter 11

This covers DOM Level 2 and 3; Styles, Traversal and Range modules. Everything presented here is informative and thought-through. Minor nitpick is vague statements about browser bugs and corresponding browser versions. There’s also a warning about floatcssFloat mapping, but nothing about forhtmlFor one.

I liked that most of cross-browser abstractions use feature detection in correct order. One notable example is getBoundingClientRect abstraction which actually shows non-trivial feature test for the first time. The only downside to that solution is that it uses (often slow-to-create) arguments.callee instead of a special variable in a closure — to store temporary offset value.

Chapter ends with a comprehensive overview of Ranges — both DOM and proprietary IE ones.

Chapter 12

This one talks about Events. Capturing, bubbling phases and event flow are illustrated nicely with diagrams. There are sections on DOM Level 0 and Level 2 event handlers, discussing both — DOM and IE event models. Other topics include event object, simulating events and memory considerations.

Unfortunately, scope chain augmentation of intrinsic event handlers is not mentioned. Event abstraction utility is rather simple. When describing events order for dblclick, there’s no mention of IE deviations; instead, these events are claimed to be fired in exact order (presented there) — something that’s rather misleading. Nothing is being said about Opera lack of contextmenu event and when talking about event delegation, there’s no mention of focus/blur events.

It was very nice to see “beforeshow” / “beforehide” events discussed, together with effect of “unload” event handler on page caching. Event model specifics of less traditional devices/clients, such as Wii and iPhone, were touched upon as well.

“Removing event handlers” section is a little disappointing. It makes it sound as if Internet Explorer always leaks memory as pages are navigated, suggesting “unload” event as a remedy. Instead, it would be useful to see actual leak pattern, and explanation of event handlers forming circular references through closures. It would also make sense to mention that attaching event handlers “properly” allows to avoid both — unload cleanup (and so disabled bfcache) and leaks.

Chapter 13

Scripting forms is about… forms, and everything related: from basic things like submitting them to more advanced like rich text editing and serialization. As always, there’s plenty of good recommendations, suggesting to avoid things like form resetting, accessing forms directly on a document, accessing form controls directly on forms, etc.

One rather important aspect I found missing here is the problem of unsafe names in forms. Another insignificant nitpick is that getSelectedText abstraction doesn’t feature test selectionStart on form control, before accessing it (it’s missing in Safari 2.0, for example). On a similar note, an example demonstrating tab forwarding is accessing a text field that might not exist.

This chapter presents a pretty solid function for form serialization, but it’s unfortunately a bit off and incorrectly serializes controls without names.

Chapter 14

This chapter dives into error handling in Javascript, as well as everything that has to do with debugging, including overview of debugging tools on various browsers. It’s an in-depth look at a great variety of error-handling aspects of cross-browser scripting.

Unfortunately, when talking about try-catch, author doesn’t mention rather important IE deviations. When describing EvalError, he doesn’t explain in which cases it might actually occur — something that specification makes very clear — “if value of the eval property is used in any way other than a direct call (that is, other than by the explicit use of its name as an Identifier which is the MemberExpression in a CallExpression), or if the eval property is assigned to, an EvalError exception may be thrown.”. In fact, EvalError can be easily observed in, say, Firefox 3.5 where using eval in a new expression — new eval() — leads to exactly this type of exception.

Another potential source of confusion is with checking value for Array type in one of the examples. It’s being done via instanceof operator and without explanation of why it might not work and what to do instead (note that iframe issue is mentioned later, in Chapter 20, however [[Class]]-based testing is not mentioned even there).

Chapters 15 & 16

— are about XML in Javascript. One discusses XML DOM, XPath and XSLT support in browsers. The other one continues on the similar subject, delving into ECMAScript for XML (E4X). Unfortunately, I’m not very familiar with these aspects of browser scripting, so can’t provide any useful feedback here. Overall, this section of a book looks like a solid foundation for anyone willing to master XML in Javascript.

Chapter 17

— is about Ajax. It talks about XHR objects, Cross-Domain requests, JSON, and Security — explaining each of those from the ground up. It was nice to see non-standard extensions mentioned — XHR’s timeout and ‘progress’ events, Microsoft’s XDomainRequest, etc. One minor thing I didn’t like is that createXHR abstraction used (often expensive) arguments.callee instead of storing variable in a closure.

A slightly confusing explanation is given in eval section where phrases such as “evaluate as data format”, “statement without name”, and “it identifies a value rather than a statement” don’t make much sense; It looks like by “named statement” author really meant “labeled statement”, and by “identifies as a value” — “parsed as expression”.

Chapter 18

— is named “Advanced Techniques” and covers quite few interesting topics — scope-safe constructors, lazy function loading, binding and currying. It also explains timers quite extensively.

Strangely, a pattern of forking function declarations is not covered when talking about lazy-loading functions. That pattern is tremendously useful and is somewhat ubiquitous by now; I’m surprised it’s missing here. bind and curry abstractions are very simple (unoptimized), apparently only demonstrating general ideas behind them.

It would be nice to see more robust event abstraction: with error handling, so that integrity of event handlers is not compromised when one of them errors out. Drag and drop example is presented in a nice encapsulated way – using module pattern, however there is a slight mistake of using indexOf to check if element has certain class.

Chapter 19

— explains everything related to Client-Side storage: cookies, best practices on using them and some of the restrictions. A pretty solid cookie utility is presented there as well. Other things discussed are Internet Explorer user data and DOM Storage.

Unfortunately, it also describes StorageItem interface, which doesn’t exist in Web Storage specs any longer. Some of the localStorage bugs and quirks are not mentioned (quite obviously, since they didn’t even exist at the time of the writing). There’s no recommendation to prefer getItem/setItem to plain property accessors, since latter ones are rather destructive and are not generally safe. Compare:

  localStorage['clear'] = 'foo';
  localStorage.clear; // 'foo', not a `clear` method
 
  // vs.
  localStorage.setItem('clear', 'foo');
  localStorage.getItem('clear'); // 'foo'
 
  // the following is currently true in Firefox, but not WebKit (see: https://bugs.webkit.org/show_bug.cgi?id=30996)
  localStorage.clear; // `clear` method

Chapter 20

— is about Best Practices. I almost fully agree with everything said here. The chapter is full of valuable advice, both — Javascript and general ones. Not only does it tell you what to do, but also explains why. This is important.

There were moments when I didn’t share same vision as Mr. Zakas, such as the one where he recommends to use comments in places where there are large amounts of code; I believe in a different approach — breaking code into smaller, more understandable chunks, rather than adding comments on top. Another oddity was with example demonstrating decoupling of application logic and event handlers. Ironically, validateValue would both — validate and perform destructive action, changing visibility of an element. This was very unintuitive and is something I would avoid.

When talking about performance, it wasn’t clear why variable access is considered to take O(1) operations, and property lookup — O(N). Both of these have to go through some jumps and hops internally — variable is resolved via scope chain, whereas property — via prototype chain (and only if not found directly on an object). It’s understandable that property lookups are slightly slower, since besides actual property lookup, there’s also an initial “base” variable resolution, but where O(1) and O(N) are coming from was a mystery.

Chapter 21

This chapter talks about Upcoming APIs, namely Selectors API and those from HTML5. Describing specifications that are in a state of draft is a risky business, which author rightfully mentions upfront. This section of a book is a nice cursory overview of most of the upcoming goodness — querySelectorAll, getElementsByClassName, classList, data attributes, cross-document messaging, media elements, canvas, offline support, history management, and others.

Chapter 22

The finale of the book is The Evolution of JavaScript. It first covers wide range of extensions to JavaScript, from 1.5 to 1.8 — constants, accessors, array extras, array and string generics, block-level scope with let, generators and iterators, as well as few others. The only minor nit here is that examples of array extras had functions with unused parameters.

Unfortunately, a lot of information about ECMAScript 5 here is outdated. It’s even called by its previous name — ECMAScript 3.1.

  • [[Flexible]] attribute used throughout the examples is now called [[Configurable]]
  • [[Getter]] and [[Setter]] attributes are called [[Get]] and [[Set]] accordingly
  • [[Const]] attribute no longer exists
  • Object.clone no longer exists
  • Object.keys doesn’t accept second (sort) argument
  • Function objects don’t have name or parameters properties
  • arguments object is not an instance of Array
  • Decimals do not exist
  • “use subset cautious”; doesn’t exist and is replaced with Use Strict Directive — “use strict”;

There’s also a slight mistake in one of the examples when accessing getPrototypeOf as an instance method, rather than as static one of an Object.

Filed under review having 4 Comments »