Вопрос

I found one interesting project on github which deals with pdf rendering in the browser.

I tried to read the code because I'm interested in this topic but I realized that my javascript knowledge is poor (insuficient).

There are constructs like:

var Obj = (function() {
    function constructor(type, value) {
    this.type = type;
    this.value = value;
    }

    constructor.prototype = {
    };

    var types = [
    "Bool", "Int", "Real", "String", "Name", "Null",
    "Array", "Dict", "Stream", "Ref",
    "Cmd", "Error", "EOF", "None"
    ];

    for (var i = 0; i < types.length; ++i) {
    var typeName = types[i];
    constructor[typeName] = i;
    constructor.prototype["is" + typeName] =
    (function (value) {
     return this.type == i &&
     (typeof value == "undefined" || value == this.value);
     });
    }

    constructor.prototype.lookup = function(key) {
      function lookup(key) {
        if (!(this.value.contains(key)))
          return Obj.nullObj;
        return this.value.get(key);
      }
    }

    Object.freeze(constructor.trueObj = new constructor(constructor.Bool, true));
    Object.freeze(constructor.falseObj = new constructor(constructor.Bool, false));
    Object.freeze(constructor.nullObj = new constructor(constructor.Null));
    Object.freeze(constructor.errorObj = new constructor(constructor.Error));
    Object.freeze(constructor.prototype);
    Object.freeze(constructor);

    return constructor;
})();

You can see more of them in the link above.

Could you please advise me some resources which to study to be able to understand the code in the project with ease and even better to contribute later to the project?

Это было полезно?

Решение

Working outside in, the first big concept here are anonymous funcitons.

var Obj = (function() { /* do work */})();

Simply, put, we are making an anonymous function, and then immediately execturing it, and assigning the return from the anonymous function to an variable named Obj.

Why would anyone want to do it?

In this case, it is used to create a private scope. Local variables in javascript are scoped to the function where they are defined. For example:

function test() {
    var a = 10;
}

// a is not defined here.

In the last example, a really only exists inside the scope of the function that defined it. Javascript is a little tricky this regard because by omitting the var keyword, you can define a global variable.

So in the example you've given, they are using this anonymous function to build a scope for some variables that will be used, but will ultimately want to be thrown away as soon as the function is finished executing.

Next:

var Obj = (function() {
    function constructor(type, value) {
        this.type = type;
        this.value = value;
    }

    // SNIP
})();

This creates a new function called constructor. Its important to note that javascript functions are first-class objects, which means they work a lot like any other object, and can be assigned to a variable. This function is scoped to the anonymous function. So trying to get constructor out side of the scope of its function, doesn't work. For example

var Obj = (function() {
    function constructor(type, value) {
        this.type = type;
        this.value = value;
    }

    // SNIP
})();

typeof(constructor) // <= undefined

So far, if you were to execute the snippits so far, then Obj would have been undefined. Now let's skip ahead a bit to end, and look at the return.

var Obj = (function() {
    function constructor(type, value) {
        this.type = type;
        this.value = value;
    }

    // SNIP

    return constructor;
})();

So when the anonymous function gets invoked, it returns the constructor. This function that gets passed back is assigned to Obj. This hands back the constructor, out of the local scope of the function, and assigns to the variable. You would then be able to invoke it

var Obj = (function() {
    function constructor(type, value) {
        this.type = type;
        this.value = value;
    }

    // SNIP

    return constructor;
})();

var o1 = new Obj("t", "v");
o1.type // <= "t"
o1.value // <= "v"

Next we've got an interesting line

var Obj = (function() {
    function constructor(type, value) {
    this.type = type;
    this.value = value;
    }

    constructor.prototype = { };

    // SNIP

    return constructor;
})();       

This sets the prototype for the constructor to an empty object. Explaining the details of prototypes is a bit of the scope of this post, but an over-simplication is that a prototype defines the instance methods that are available to an object that is created by a constructor. A single 'prototype' is shared between and will be used to define the methods that will be available to the objects that are constructed by calling new Obj().

Next we have a locally defined array

var types = [
    "Bool", "Int", "Real", "String", "Name", "Null",
    "Array", "Dict", "Stream", "Ref",
    "Cmd", "Error", "EOF", "None"
];

Remember because we are inside of a function, this variable is bound within the outer anonymous function's scope.

Next we loop through that array, and set up some stuff.

for (var i = 0; i < types.length; ++i) {
    var typeName = types[i];
    constructor[typeName] = i;
    constructor.prototype["is" + typeName] =
        (function (value) {
            return this.type == i &&
            (typeof value == "undefined" || value == this.value);
        });
}

Two things interesting happen here. First, it sets a 'static' property off of the constructor, and then creates a new function on the constructor's prototype. This function is called "is" + typeName. So we should generate a bunch of instance methods named stuff like: "isBool", "isInt", "isReal", and so on.

constructor.prototype.lookup = function(key) {
  function lookup(key) {
    if (!(this.value.contains(key)))
      return Obj.nullObj;
    return this.value.get(key);
  }
}

Next we define another instance method called lookup and does some work.

Lastly we create some static properties off of our constructor, and freeze them (so they can't be changed or extended)

Once its all said and done, Obj, should point to a constructor function, and we should be able to say things like:

var myType = new Obj("MyType",undefined);
myType.isBool(undefined) //instance method
Obj.Bool  // static property

Anyway, I hope that helped a little bit explaining a few of the concepts that were being used. The big take-away should be that a function can be used to control scope, and that functions are First-class functions, and can be handed around like variables. You can also reference a property off a object using dot notation (obj.property) or bracket notation (obj["property"]).

There are a bunch of more things to learn, and all of the book suggestions are solid in this thread. If it hasn't been mentioned I would also recommend Eloquent JavaSript by Haverbeke.

Другие советы

Douglas Crockford's book JavaScript: The Good Parts is a great place to gain an appreciation of the power in JavaScript, while steering you away from the ugly (or downright dangerous) parts. His website also has a collection of interesting articles about the language. I recommend both.

I find the blogs by Douglas Crockford(http://www.crockford.com/), John Resig(http://ejohn.org/blog/) useful for intermediate to advanced concepts. Looking at other people's code(just like what you are doing) can also be helpful. At the end of the day though nothing beats actually trying out the concept and for that the trusty Firebug console is still the best.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top