in ECMAScript

The quiz

Recently some JavaScript quizzes appear then and there with the competitive titles wondering whether “you know JavaScript”? The recent quizzes were by kangax, Dmitry Baranovskiy and by Nicholas C. Zakas. All that quizzes are interesting, many questions though are mostly theoretical rather than practical.

And again just for fun, I suggest another ECMAScript quiz. I won’t use some boring things such as “a+++b”; but I will more sophisticated 😉 Some interesting questions are collected from newsgroups, mailing lists, forums and from my own.


Some features and difficulty level:

  • almost all questions are theoretical; some of them are directly related to ECMA-262-3 specification;
  • clear ECMAScript is used without non-standard features, augmentations of built-ins or any other extensions, though can be the case of user-defined global objects;
  • objectively, the test is hard, but should not be so if you will be attentive;
  • there are hard and easy questions;
  • there are questions only on attention;
  • if some questions seem to be hard, you can use console (that’s not a cheating, because some questions are hard).
  • what’s the goal, when and where will I need to use this “useless” stuff on practice? – Usually, you won’t. This test is mostly theoretical, you may pass it or may not – that’s your choice.

Good luck.

typeof typeof(null)










typeof foo == 'undefined'

and

typeof foo === 'undefined'




100['toString']['length']












var a = (1,5 - 1) * 2












var x = 10;
var foo = {
  x: 20,
  bar: function () {
    var x = 30;
    return this.x;
  }
};

console.log(
  foo.bar(),
  (foo.bar)(),
  (foo.bar = foo.bar)(),
  (foo.bar, foo.bar)()
);












function f(x, y) {
  x = 10;
  console.log(
    arguments[0],
    arguments[1]
  );
}

f();










var
  b = 10,
  c = (
    20,
    function (x) { return x + 100},
    function () { return arguments[0]}
  );

a = b + c
({x: 10}).x










1..z










({
  x: 10,
  foo: function () {
    function bar() {
      console.log(x);
      console.log(y);
      console.log(this.x);
    }
    with (this) {
      var x = 20;
      var y = 30;
      bar.call(this);
    }
  }
}).foo();












foreach (k in {a: 10, b: 20})
{
  // ...
}
















Write a Comment

Comment

44 Comments

  1. Good stuff.

    I messed up #9, thinking that `x` in `x, y, this.x` has value of `20`, not `undefined`. Then realized where the mistake was (`bar`’s VO is followed by `foo`’s VO, not the object that `with` injected into the scope; lexical scoping keeps biting me from time to time :-)).

    Minor nitpicks: question #2 is debatable. I kind of understand what you mean by “[…] algorithms of the following checks completely equivalent”, but if we were to be precise, ‘undefined’ == ‘undefined’ and ‘undefined’ === ‘undefined’ go through different algorithms. Former one goes through “The Abstract Equality Comparison Algorithm” (11.9.3), whereas latter one — through “The Strict Equality Comparison Algorithm” (11.9.6). Both go through steps 1,2,3,4 and 11, but step 1 in 11.9.3 is technically not the same as step 1 in 11.9.6. Therefore, I can say that algorithms are different.

    Finally, just as I mentioned on Zakas blog (http://www.nczonline.net/blog/2010/02/16/my-javascript-quiz/#comment-4568), it’s better to stay away from host objects (e.g. `alert`) as their unspecified nature makes questions ambiguous.

  2. You’ve got 5 mistakes, in questions: #2, #5, #7, #9, #10.

    Снова 50/50 =(
    В последнем совсем ступил и посмотрел не туда.

  3. Объясните пожалуйста 5 вопрос, почему так, можно со ссылками на спецификацию

  4. Allright, I’ll bite… #2: two distinct algorithms are applied (but in effect they do the same thing, yes). #3: very nice, totally forgot about the radix parameter. #5: I do believe this should be 20,10,10,10, not 20,20,10,10 (the accepted solution). I had #6 wrong, but the result is just a comma, not “undefined”, when you alert it. #9 is a “with” scoping problem i never get right and #10 totally got me 🙂 (5/5)

  5. Good questions. All of these are related with specification, and there are no host objects. Would be great if, there site with quiz like yours and @kangax. People can improve their knowledge about ECMA-262 3 and perhaps 5 editions.

    Regarding semicolon insertions:

    var x = true //Single line comment
    false;

    a) SyntaxError
    b) Transform to `;false;`

    var x = true /* Multi line comment 
    Multi line comment*/ false;

    a) Syntax error
    b) Transform to `;false;`

  6. @qFox, #5, (foo.bar)() is exactly the same as foo.bar():

    11.1.6 The Grouping Operator
    The production PrimaryExpression : ( Expression ) is evaluated as follows:

    1. Evaluate Expression. This may be of type Reference.
    2. Return Result(1).

  7. Again regarding ASI. I see in ECMA-262 5, there still rules for automatic semicolon. And strict mode doesn’t make any sense about this. That is one of the “features”, which is total misconceptions in language. I can’t see any reasons to skip semicolon. I always put explicitly semicolon. Semicolon insertion is job for developer, not for Tokenizator of the language. Automatic insertion is bad design. That design is developed for people, which don’t give a damn about, where actually need semicolon. If developer doesn’t know where need be put semicolon, he should be read specification of the language, not rules for automatic semicolon insertion. Of course ES5 cannot remove automatic insertion because will break backward compatible. But i am wonder, why in strict mode, there still rules for ASI?

    Regards for good question, and will be great if you post explanations about correct behavior.

    Perhaps will be good if you create your own site in which you can post your articles, quiz’s and other things about ES. There you can build content on different languages, and for readers will be easy to understand your suggestions and articles. ;~)

  8. update:

    Question #4 is changed from:

    4. What’s the result of:

    try {
      throw function () {
        console.log(this);
      };
    } catch (e) {
      e();
    }
    • SyntaxError
    • Catch-object with “e” property
    • TypeError
    • global
    • function () {console.log(this);}

    The correct answer was set to “global”. But this question is very ambiguous. By ES3 it should be “Catch-object with “e” property” although only some implementation conform to it (spec doesn’t say directly about this, though I myself was wondering once why this value in implementations is global but not catch-value) – you can see conforming implementations e.g. in Opera 10.10, or in current version of Rhino – they will return catch-object in this case.

    But in ES5 this was recognized as a bug, and this value in this case should be set to global. So from the ES5 viewpoint the answer was correct. And if I directly mentioned in disclaimer that questions are for ES3, for do not make this question ambiguous, I decided to change this question on current (provided by @DmitryKorobkin):

    4. What’s the result of:

    var a = (1,5 - 1) * 2
    • 0.999999999
    • 1
    • 0.5
    • 8
    • -0.5
    • 4

    Thanks to @ohunt

    Dmitry.

  9. Thanks all who already passed the test. I necessarily will answer all questions but a bit latter (other may want to pass it too). You sure also can write your explanations to each other – it’s welcomed 😉

  10. But they can use special object for augment Scope Chain during evaluation of `catch` statement. Object which in specification called activation in:

    | 11.2.3 Function Calls
    | 7. If Result(6) is an activation object, Result(7) is null.
    | Otherwise, Result(7) is the same as Result(6)

    Of course here that object isn’t “Activation Object” in context of “10 Execution Contexts”, but behavior is reasonable. You can use “NFE” for observing same behavior.

    (function f() {
        window.alert(this);
        if ((f.recFlag = !f.recFlag))
        {
            f();
        }
    })();

    In second invokation of `f’, `this’ value should reffer object created on:

    | 1. Create a new object as if by the expression newObject().
    | 2. Add Result(1) to the front of the scope chain.

  11. Great and very sophisticated test, this one totally had me down 😉
    Thanks, I learned a lot.

  12. В мене питання щодо тесту #6.
    ECMA-262, 3rd Edition, section 10.1.8:
    For each non-negative integer, arg, less than the value of the length property, a property is created with name ToString(arg) and property attributes { DontEnum }. The initial value of this property is the value of the corresponding actual parameter supplied by the caller. The first actual parameter value corresponds to arg = 0, the second to arg = 1, and so on. In the case when arg is less than the number of formal parameters for the Function object, this property shares its value with the corresponding property of the activation object. This means that changing this property changes the corresponding property of the activation object and vice versa.

    Тобто, на скільки я розумію, грубо кажучи, зміни x і arguments[0] мали б синхронізуватися. Власне, в хромі так і відбувається. В фаєрфоксі – ні. Хто правий і чи не мала б бути там відповідь “10, undefined”?

  13. @Alex Gromov

    Объясните пожалуйста 5 вопрос, почему так

    Моё недавнее объяснение в одном из топиков на Хабре: http://habrahabr.ru/blogs/javascript/84381/#comment_2519403

    На форуме http://javascript.ru/forum/, вопрос изначально появился от Zeroglif (там же есть несколько объяснений): http://javascript.ru/forum/misc/5724-secrets-javascript-ninja.html#post34116

    можно со ссылками на спецификацию

    ECMA-262-3, 11.2.3 Вызовы функций – http://javascript.ru/ecma/part11#a-11.2.3 (важные пункты 6-8).

  14. @qFox

    #5: I do believe this should be 20,10,10,10, not 20,20,10,10 (the accepted solution)

    As already was mentioned bellow, the grouping operator doesn’t call GetValue, so after its work there’s still RefereceType value. Two last call GetValue, so there’re already functions but not value of ReferenceType, which means: this value = null => global.

    I had #6 wrong, but the result is just a comma, not “undefined”

    Yes, there was alert([…]) used, I’ve changed it to console.log(…) which should show undefined.

    Dmitry.

  15. @Asen Bozhilov

    Yes, the behavior with second call of NFE in this case is also debatable. I guess implementations handle this case somehow specially, they know that if base object is determinate as that special object storing optional name of FE, then also null => global should be used for the this value. But that’s just the guess, I don’t see in spec explanation of it.

    Also we know that some implementations create this identifier binding right in the activation object (e.g. Rhino, but the similar behavior we saw in newer versions of Firefox).

    But regarding exactly catch clause, ECMA-262-5 really has clarification of this ambiguous “bug” (feature):

    From “Annex D (informative). Corrections and Clarifications in the 5th Edition with Possible 3rd Edition Compatibility Impact” of ECMA-262-5:

    12.4: In Edition 3, an object is created, as if by new Object()to serve as the scope for resolving the name of the exception parameter passed to a catch clause of a try statement. If the actual exception object is a function and it is called from within the catch clause, the scope object will be passed as the this value of the call. The body of the function can then define new properties on its this value and those property names become visible identifiers bindings within the scope of the catch clause after the function returns. In Edition 5, when an exception parameter is called as a function, undefined is passed as the this value.

    Dmitry.

  16. @kunik

    Seems, you can’t write in Russian, but understand English. So, sorry, I can’t write in your suggested language, and will answer in English.

    From ECMA-262-3 10.1.8, bullet 2:

    A property is created with name length and property attributes { DontEnum }. The initial value of this property is the number of actual parameter values supplied by the caller.

    function foo(x) {
      x = 42;
      console.log(arguments.length); // 0
      console.log(arguments[0]); // undefined
      return arguments[0];
    }
    
    // number of actual
    // parameter values is 0
    f(); // undefined

    So, in this case (no parameter passed) this property (arguments[0]) doesn’t share its value with the corresponding property of the activation object. And that’s why Chrome is wrong.

    Dmitry.

  17. @Asen Bozhilov

    Would be great if, there site with quiz like yours and @kangax

    Yeah, I’m thinking on it, maybe will be soon.

    Regarding semicolon insertions:

    var x = true //Single line comment
    false;

    a) SyntaxError
    b) Transform to `;false;`

    var x = true /* Multi line comment 
    Multi line comment*/ false;

    a) Syntax error
    b) Transform to `;false;`

    This questions are very interesting, thanks, Asen. For now I know at least two non-conformant implementations – IE (JScript) and Chrome (V8).

    About the first case (ECMA-262-3, 7.4 Comments):

    However, the LineTerminator at the end of the line is not considered to be part of the single-line comment; it is recognised separately by the lexical rammar and becomes part of the stream of input elements for the syntactic grammar. This point is very important, because it implies that the presence or absence of single-line comments does not affect the process of automatic semicolon insertion (7.9)

    So the answer is (b) – Transform to `;false;`, as still there’s LineTerminator.

    About the second case (ECMA-262-3, 7.4 Comments):

    Comments behave like white space and are discarded except that, if a MultiLineComment contains a line terminator character, then the entire comment is considered to be a LineTerminator for purposes of parsing by the syntactic grammar

    So the answer is also (b) – Transform to `;false;`, as again we have one LineTerminator. But IE and Chrome parse this case wrong and have SyntaxError.

    Dmitry.

  18. update:

    @joseanpg has provided a good explanation of the quiz (you can find it on his twitter page). Although, question #10 became debatable. In disclaimer there was said:

    clear ECMAScript is used without non-standard features, augmentations of built-ins or any other extensions;

    which if to be fair can make answer on question #10 wrong. So I’ve changed this point in disclaimer to:

    clear ECMAScript is used without non-standard features, augmentations of built-ins or any other extensions, though can be the case of user-defined global objects;

    although, simple global function – is an augmentation of the global object, and the global object – is a built-in object. And to keep this question more interesting (rather than just “Always ReferenceError”), I’ve clarified disclaimer point; but the answer of joseanpg fairly (if there’s no any user-defined functions) is correct too.

    The main goal of this question is about automatic semicolon insertion (7.9, ECMA-262-3) and position of the open curly block’s bracket; “foreach” and “k” play minor role here. In contrast with:

    // always SyntaxError
    foreach (k in {a: 10, b: 20}) {
      // ...
    }

    which always will provide SyntaxError and thus will help to avoid error in logic of the program if user-defined function “foreach” exists and open curly block’s bracket is set on the new line:

    // ReferenceError or possibly no error
    // if "k" and "foreach" function exist
    foreach (k in {a: 10, b: 20})
    {
      // ...
    }

    as it will be transformed to:

    // function call
    foreach (k in {a: 10, b: 20})
    // just a block
    ;{
      // ...
    }

    Dmitry.

  19. @kangax

    I messed up #9, thinking that `x` in `x, y, this.x` has value of `20`, not `undefined`. Then realized where the mistake was (`bar`’s VO is followed by `foo`’s VO, not the object that `with` injected into the scope; lexical scoping keeps biting me from time to time ).

    Yes, that’s it, [[Scope]] of `bar’ is already set; firstly this question appeared on es-discuss mailing list, there is also my explanation: https://mail.mozilla.org/pipermail/es-discuss/2010-February/010791.html

    Minor nitpicks: question #2 is debatable. I kind of understand what you mean by “[…] algorithms of the following checks completely equivalent”, but if we were to be precise, ‘undefined’ == ‘undefined’ and ‘undefined’ === ‘undefined’ go through different algorithms. Former one goes through “The Abstract Equality Comparison Algorithm” (11.9.3), whereas latter one — through “The Strict Equality Comparison Algorithm” (11.9.6). Both go through steps 1,2,3,4 and 11, but step 1 in 11.9.3 is technically not the same as step 1 in 11.9.6. Therefore, I can say that algorithms are different.

    Well, yeah, the main goal of this question is in the same types of operands and which imperative actions are taken applying algorithms. As types are the same, step 1, can be omitted to make this question more interesting than just “No, different algorithms (names) are used”.

    The goal is to show that there’s no need to write (how we can see in many frameworks) strict equality with typeof operator and string operand.

    About debatable – I think it will be better to form (more) correctly question title. Any suggestions?

    it’s better to stay away from host objects (e.g. `alert`) as their unspecified nature makes questions ambiguous

    Yep, true, I changed it to console.log, which is also, btw, host 😉

    Dmitry.

  20. @Asen Bozhilov

    That is one of the “features”, which is total misconceptions in language. I can’t see any reasons to skip semicolon. I always put explicitly semicolon. Semicolon insertion is job for developer, not for Tokenizator of the language. Automatic insertion is bad design.

    Well, actually automatic semicolon insertion there’s in many languages (though, maybe it’s named differently), e.g. in Ruby, QML declarative, other. You can write (in Ruby):

    def test
      10
    end

    or

    def test; 10; end

    the same result.

    Yes, that’s true, that ECMAScript has some ambiguous (from the human logic viewpoint) features regarding to ASI (if not to know and not to understand how this mechanism works).

    But in general, this idea can be not so bad. The question is in implementation and ES has some ambiguous cases in this place. That’s it.

    What about me – I always put semicolon explicitly in ES. And about ES5 – yep, they could force put semicolon explicitly in “strict mode”, but as I already told in some forums – I believe “strict mode” feature itself as an error of language design. Be more exact – the error is not the “strict” technique as itself, but in distinguishing “strict” and “non-strict” modes, which will cause many useless holy wars and debates. Some will talk that’s professional code is only in strict – but that’s completely wrong.

    By the truth, I think that ES has a bit overloaded syntax, I think it should omit some redundant C-style features, which is actually done in version 1.8 of SpiderMonkey – as you know there open curly brackets and return keyword in some cases are optional – the same can be with semicolon – maybe that’s why they do not force put it explicitly even in strict mode.

    Dmitry.

  21. I enjoyed the test, and did find it difficult. It’s really interesting though to see how the correct answers differ from output in a Javascript console.

  22. I got a few wrong, and after going through them in a js console I now understand why they do what they do (yay!) but I don’t get #10 even if I go through it and change things around. Can you explain what’s going on here so that I can understand JavaScript better?

  23. @Pomax, congrats, understanding 9 of them is a very good result. About #10 — it can be a function call (“foreach” is not a reserved word and since it’s followed by (), it can be treated as a function call; if such a user-defined function exists, it’s not an error, otherwise — the ReferenceError). Detailed answer is here.

  24. I have got some questions about #3. Why does 1.z got “syntax error” when 1..z is legal?

  25. I didn’t understand the #7 and #9 questions. Can you please explain? I seen the comment #1 by kangax , but didn’t get exactly what’s happening in #9.

  26. You’ve got 10 mistakes, in questions: #1, #2, #3, #4, #5, #6, #7, #8, #9, #10

    great!

  27. @msankhala

    On the #7: the result of the comma operator is always the last expression. So c is the function which returns its first argument.

    Then, since c is the function, the code:

    a = b + c
    ({x: 10}).x

    is the same (because semicolon is not inserted at the end of the first line) as:

    a = b + c({x: 10}).x // 20

    The #9 is probably the hardest question and relates to static scope rules of JS and semantics of with statement. It requires longer explanation which can be found in Chapter 4. Scope chain and also in this quiz’s solutions.

  28. Thank a lot Dmitry for such a nice explanation.

  29. You’ve got 2 mistakes, in questions: #5, #7.
    I still need to learn a lot 🙂

  30. @Dmitry, please help check my understanding.

    To #9 case,

    ({
      x: 10,
      foo: function () {
        function bar() {
          console.log(x);
          console.log(y);
          console.log(this.x);
        }
        with (this) {
          var x = 20;
          var y = 30;
          bar.call(this);
        }
      }
    }).foo();

    this in with is refer to the most outer object, let’s call it O, which including {x:10, foo: }.

    AO of O = {
      foo: <reference to function>, // FD is ahead of variable
      x: 10,
      globalContext.VO
    };

    On entering the foo context

    AO of foo = {
      bar: <reference to bar>,
      x: undefined,
      y: undefined,
      AO of O
    };

    On entering the with context, with_Object will added in to scope chain.

    AO of with_Object = {
      AO of O, // because <code>this</code> is the O itself
      AO of foo
    };
    // full form of AO of with_Object
    AO of with_Object = {
      foo: <reference to function>, // FD is ahead of variable
      x: 10,  // O.x
      globalContext.VO,
      bar: <reference to bar>,
      x: undefined,  // local one
      y: undefined,  // local one
      AO of O
    };

    So x =20; will update O.x to 20, and local x: undefined is unchanged, y = 30; will update y: undefined.

    Consequently, we can get AO of bar

    AO of bar = {
      x: undefined,  // local one
      y: 30  // local one
    };

    And this in console.log(this.x) will resolved to O, so it will output 20 (which is O.x).

    So the final result is undefined, 30, 20.

    I think some of my representation is not clear and accurate. Please correct me, thanks.

    P.S. Dmitry, I do not know whether AO I list are correct or not? I am not sure VOs of the code, is VO of bar is {globalContext.VO}? Could you please list all AO and VO corresponding to the code?

    Regards.

  31. @Hong you have correct answer and the line of thinking, however, you’re confused the terminology a bit.

    AO can only be for functions. So “AO of O” is not correct terminology, although, it contains correct data in your description.

    Also, AO contains only local vars. If a function refers non-local variable (or how we say, free variable), this variable is found in the scope chain of the running context. And the scope chain is formed as: AO + fn.[[Scope]].

    Also contexts are created only by functions (plus eval and the global context), so the “On entering the with context” is also not correct terminology.

    But other than that, your analysis is correct 😉

    Please take a look at these answers: http://joseanpg.net/jslab/quiz/soshnikov/answers.html

  32. @Dmitry, thanks for your comment.

    But other than that, your analysis is correct 😉

    Other than that, seems no other useful info;), maybe I need to re-read your articles.

  33. Здравствуйте, Дмитрий!

    Спасибо за замечательный тест. У меня есть вопрос, на который пока не могу найти ответа, возможно его можно было бы включить в подобный тест. Почему:
    1 < 2 2 > 1 // false

    Предположение о том, что имеет значение приоритет операторов не помогает мне ответить на этот вопрос.

    Спасибо.

  34. 1 < 2 < 3 // true
    3 > 2 > 1 // false
  35. @Ярослав,

    Это эквивалентно

    ((1 < 2) < 3); // true
    

    Т.к.

    true < 3; // true
    

    Поскольку Number(true) — это 1, а 1 меньше 3.

    Соответсвенно,

    ((3 > 2) > 1); // false
    

    Т.к.

    true > 1; // false
    
    1 > 1; // false
    
  36. Дмитрий, скажите, пожалуйста, почему

    false == " "; // true?

    Ведь преобразование численное. false – это 0. Не пустая строка это true, а значит 1.

    И правильно ли я понял, typeof typeof(null)?
    Сначала вычисляется typeof(null) //"object"
    Затем typeof "object" // "string"

  37. @Александр

    Не пустая строка это true, а значит 1.

    Строка, содержащая пробельные символы — это 0 в JS.

    Number(" "); // 0
    Number("\n\t "); // 0
    

    С typeof — да, все верно поняли.

  38. Nice quiz,
    #9 Like many people I like it the most : )
    #6 Just for my sense of completion I would add a little twist

    function f(x, y) {
      x = 11;
      y = 21
      console.log(
        arguments[0],
        arguments[1]
      );
    }
    f(10);