Статьи

Будущий JavaScript, сегодня: Google Traceur

ECMAScript — это язык, работающий как зонтик для JavaScript, Internet Explorer JScript и Flash ActionScript. Инновации в браузерах сегодня связаны с их поддержкой функций HTML5 и CSS3, а также с внедрением новых версий ECMAScript.

Все браузеры поддерживают ECMAScript 3, который вышел в 1999 году и практически определяет JavaScript, каким мы его знаем сегодня. ECMAScript 5 (изначально 3.1) в настоящее время принят и поддерживается в таких браузерах , как Internet Explorer 9, Firefox 4, Safari 5 и Chrome . Он предоставляет свойства объекта первого класса с определением метаданных, позволяя, например, указать, можно ли вообще удалить или изменить одно из них.

Мы можем ожидать, что в ближайшие один или два года мы сможем использовать функции ECMAScript 5 в качестве базового языка JavaScript, ориентируясь на старые браузеры.

Чтобы еще больше заглянуть в будущее, нам следует ориентироваться на ECMAScript Harmony (пока без номера версии), который будет следующей спецификацией. Его разработка идет полным ходом, но многие функции настолько интересны, что попробовать их стоит хлопот.

Но как поэкспериментировать с ECMAScript Harmony? Инструментом, который мы рассмотрим в этой статье, является Google Traceur , компилятор, который на лету преобразует JavaScript в будущем.

механика

Хотя CoffeeScript улучшает современный JavaScript, предоставляя другой язык с той же семантикой, Traceur работает над будущими функциями. Он позволяет вам опробовать функции из ECMAScript Harmony и по-прежнему компилировать в JavaScript для запуска во всех браузерах.

Альтернативой является выполнение шага компиляции в автономном режиме или даже в браузере; Поскольку компилятор написан на самом JavaScript, он может работать практически в любой среде.

Все, что вам нужно сделать, чтобы использовать Traceur, — это записать свой код в тег <script type = «text / traceur»> и включить еще один <script> для компилятора, размещенного на общедоступном сервере.
С JIT-компиляцией производительность невелика, поэтому будьте осторожны, прежде чем писать настоящий код с помощью этого инструмента.

Примеры

Давайте углубимся в функции, которые предложит ECMAScript Harmony (если они будут подтверждены в окончательной версии спецификации) и которые Traceur позволит вам опробовать сегодня.

Я взял некоторые примеры из вики Traceur и максимально сократил их для вашего удобства, затем свернул их в один файл, чтобы вы могли запустить его в своем браузере. Загрузите этот файл .html с открытой консолью Firebug или Chrome, чтобы увидеть Traceur и Harmony в действии даже при загрузке из локальной файловой системы.

<!DOCTYPE html>
<html>
  <head>
    <title>Hello, World!</title>
    <script src="http://traceur-compiler.googlecode.com/svn/branches/v0.10/src/traceur.js"
        type="text/javascript"></script>
    <script src="http://traceur-compiler.googlecode.com/svn/branches/v0.10/src/bootstrap.js"
        type="text/javascript"></script>
  </head>
  <body>
    <script type="text/traceur">
    /**
     * Classes, finally.
     * http://wiki.ecmascript.org/doku.php?id=strawman:obj_initialiser_class_abstraction
     */
    class Greeter {
        // the constructor
        new(message) {
          this.message = message;
        }

        greet() {
          let element = document.querySelector('#message');
          element.innerHTML = this.message;
        }
    };
    
    let greeter = new Greeter('Hello, world!');
    greeter.greet();

    /**
     * let statements provide a block scope for variables, smaller than the
     * function or global scopes we often use.
     * Inside the block, variables shadow the outer scoped ones (if they exist)
     * with the same name.
     */
    let ordinal = '3rd';
    for (let i=4; i<10; i++) {
        let ordinal = i + 'th';
        // ...
    }
    console.log(ordinal); // 3rd
    

    /**
     * Traits are a mechanism for code reuse which lets you share method 
     * definitions between classes. They're also included in future versions 
     * of PHP and present in Ruby with the name of mixins.
     */
    trait ComparableTrait {
        // abstract methods: must be defined by the including class
        requires lessThan;
        requires equals;

        function lessThanOrEquals(other) {
            return this.lessThan(other) || this.equals(other);
        }
    }

    class Interval {
        // you can include multiple traits
        mixin ComparableTrait;

        new(min, max) {
            this.start = min;
            this.end = max;
            this.size = max - min - 1;
        }
        function lessThan(ival) {
            return this.end <= ival.start;
        }
        function equals(ival) {
            return this.start == ival.start && this.end == ival.end;
        }
    }

    var i1 = new Interval(0, 5);
    var i2 = new Interval(7, 12);
    // both methods from the class and its traits are available
    console.log(i1 + ' <= ' + i2 + ': ' + i1.lessThanOrEquals(i2));        // true
    console.log(i1 + ' <= ' + i1 + ': ' + i1.lessThanOrEquals(i1)); // true
    console.log(i2 + ' <= ' + i1 + ': ' + i2.lessThanOrEquals(i1)); // false

    /**
     * Modules as first-class citizens would be very handy in integrating
     * different libraries, vendor code with our own...
     * NOT YET IMPLEMENTED in Traceur 0.10: you will get an exception 
     * if you uncomment this.
    module Profile {
        export var firstName = 'David';
        export var lastName = 'Belle';
        export var year = 1973;
    }

    module ProfileView {
        import Profile.{firstName, lastName, year};

        function setHeader(element) {
            element.textContent = firstName + ' ' + lastName;
        }
    }
    ProfileView.setHeader(document.querySelector('#name'));
     */

    /**
     * Destructuring allows multiple initialization based on pattern matching,
     * similarly to PHP list(). It's however more powerful being also recursive.
     */
    var [a, [b], c, d] = ['hello', [', ', 'junk'], ['world']];
    console.log(a + b + c); // hello, world

    /**
     * Default parameters do not need further explanation.
     */
    function hello(who='world') {
        console.log('Hello! Hello, ' + who);
    }
    hello(); // Hello! Hello, world
    hello('Giorgio'); // Hello! Hello, Giorgio
    /**
     * The interesting thing is that the default value may be any expression,
     * not just a constant.
     */
    function name() { return 'Giorgio' }
    function goodbye(who = name()) {
        console.log('Goodbye ' + who);
    }
    goodbye(); // Goodbye, Giorgio
    </script>
    <h1 id="message"></h1>
    <div id="name"></div>
  </body>
</html>

Есть еще несколько возможностей для изучения, таких как поддержка итераторов или еще один способ предоставления параметров функциям (с переменным числом); однако, я чувствую, что это те, которые будут иметь наибольшее влияние.

Конечная цель Traceur — дать людям возможность опробовать новые функции и принять участие в процессе спецификации, чтобы, когда выйдет следующий ECMAScript, он уже был подвергнут тщательному анализу со стороны множества разработчиков: от итерации доходит до совершенства. Счастливого взлома с Гармонией!