ECMAScript
сегодня и в будущем.

     Дмитрий Сошников
 http://dmitrysoshnikov.com
ES5 : Новый API объектов
Два вида свойств:


Обычные data-свойства
 (явная ассоциация имени и значения)


Accessor-свойства (геттеры/сеттеры)
 (неявная ассоциация имени с accessor-функциями)
Работа со свойствами
Object
 // работа с атрибутами
 .defineProperty
Работа со свойствами
Object
 // работа с атрибутами
 .defineProperty

 // сразу несколько свойств
 .defineProperties
Работа со свойствами
Object
 // работа с атрибутами
 .defineProperty

 // сразу несколько свойств
 .defineProperties

 // анализ атрибутов (дескриптор)
 .getOwnPropertyDescriptor
Работа со свойствами
Object
 // работа с атрибутами             // статика
 .defineProperty                    .preventExtensions / .isExtensible


 // сразу несколько свойств         // «опечатывание»
 .defineProperties                  .seal / .isSealed


 // анализ атрибутов (дескриптор)   // «заморозка»
 .getOwnPropertyDescriptor          .freeze / .isFrozen


 // список свойств                  // наследование; прототипы
 .keys                              .getPrototypeOf
 .getOwnPropertyNames               .create
Дескриптор data-свойства
{
    value: 10, // значение
    writable: true, // только чтение?
    enumerable: false, // перечислимо в for-in?
    configurable: true // можно удалить?
}
Data-свойство
var foo = Object.defineProperty({}, “x” {
    value: 10,
    writable: true,
    enumerable: false,
    configurable: true
});
Data-свойство
var foo = Object.defineProperty(,-, “x” ,
    value: 10,
                                              По умолчанию все атрибуты : false
    writable: true,
    enumerable: false,
                             Object.defineProperty(foo, “MAX_SIZE” {
    configurable: true
                                 value: 20,
});
                                 writable: false, // default
                                 enumerable: false, // default
                                 configurable: false // default
                             });
Data-свойство
   var foo = Object.defineProperty(,-, “x” ,
       value: 10,
                                                 По умолчанию все атрибуты : false
       writable: true,
       enumerable: false,
                                Object.defineProperty(foo, “MAX_SIZE” {
       configurable: true                                                              ES3
                                    value: 20,
   });
                                    writable: false,
                                    enumerable: false,
                                    configurable: false    foo.z = 30;
                                });                        Object.defineProperty(foo, “z” ,
                                                             value: 30,
                                                             writable: true,
                                                             enumerable: true,
                                                             configurable: true
Создание свойства присваиванием -- атрибуты : true });
enumerable: false
ES3                          ES5
Object.prototype.x = 10;      Object.defineProperty(
                                 Object.prototype, “x”, {
var foo = {y: 20};
                                   value: 10
                                 }
                              );

for (var k in foo) {          for (var k in foo) {
  console.log(k); // y, x       console.log(k); // y
}                             }
Дескриптор accessor-свойства
{
    get: function () {...}, // чтение значения
    set: function (v) {...}, // установка значения
    enumerable: false, // перечислимо в for-in?
    configurable: true // можно удалить?
}
Accessor-свойство
Object.defineProperty(foo, “z” {
    get: function () {
       return this.x + this.y;
    },
    set: undefined,
    enumerable: false,
    configurable: false
});
console.log(foo.z); // 30
foo.z = 40; // false, только геттер
console.log(foo.z); // не изменилось - 30
Декларативное объявление
             геттера/сеттера

var foo = {
   x: 10,
   y: 20,
   get sum() {
      return this.x + this.y;
   }
};

foo.sum; // 30
Список свойств объекта

// только enumerable-свойства
Object.keys(foo); // *“y”, “z”+

// все родные свойства
Object. getOwnPropertyNames(foo); // *“x”, “y”, “z”+
Фиксация объектов
Статика: запрещает расширение объекта
                                                                  var foo = {
   Object.isExtensible(Object.preventExtensions(foo)); // false     x: 10
   foo.bar = 100; // false                                        };
   console.log(“bar” in foo); // false
Фиксация объектов
Статика: запрещает расширение объекта
                                                                  var foo = {
   Object.isExtensible(Object.preventExtensions(foo)); // false     x: 10
   foo.bar = 100; // false                                        };
   console.log(“bar” in foo); // false

Опечатывание (seal): + устанавливает всем свойствам configurable == false
   Object.isSealed(Object.seal(foo)); // true
   Object.getOwnPropertyDescriptor(foo, “x”).configurable; // false
Фиксация объектов
Статика: запрещает расширение объекта
                                                                  var foo = {
   Object.isExtensible(Object.preventExtensions(foo)); // false     x: 10
   foo.bar = 100; // false                                        };
   console.log(“bar” in foo); // false

Опечатывание (seal): + устанавливает всем свойствам configurable == false
   Object.isSealed(Object.seal(foo)); // true
   Object.getOwnPropertyDescriptor(foo, “x”).configurable; // false

Заморозка (freeze): + устанавливает всем свойствам writable == false
   Object.isFrozen(Object. freeze(foo)); // true
   foo.y = 100; // false
   console.log(foo.y); // не изменилось, 20
Получение прототипа объекта


    Object.getPrototypeOf(foo)
               ===
     Object.prototype; // true
Прототипное наследование
     Object.create(proto, [properties])
// объект-родитель
var foo = {
   x: 10,
   y: 20,
   z: 30
};
Прототипное наследование
       Object.create(proto, [properties])
// объект-родитель            // наследуем bar от foo
var foo = {                   var bar = Object.create(foo, {
   x: 10,                        q: {
   y: 20,                          value: 40
   z: 30                         }
};                            };


console.log(bar.x, bar.y, bar.z, bar.q); // 10, 20, 30, 40
Обычные hash-таблицы без
      прототипа


  var foo = Object.create(null);
  alert(foo); // ?
Обычные hash-таблицы без
      прототипа


    var foo = Object.create(null);
    alert(foo); // error

Нет метода toString. Объект foo
пустой и ни от кого не наследует.
Будущее > ES6
let : переменные в блоках
Блочная область видимости
// ES3

if (false) {
   var a = 10;
}

alert(a); // ?
Блочная область видимости
// ES3
var a; // = undefined;
if (false) {                        Все переменные создаются до
                                  запуска кода – при входе в контекст.
   a = 10;
                                     Так называемое «поднятие»
}                                       (hoisting) переменных.


alert(a); // undefined

See: http://dmitrysoshnikov.com/notes/note-4-two-words-about-hoisting/
Блочная область видимости
// ES6 Harmony

if (false) {
   let a = 10;
}

alert(a); // ReferenceError
Блочная область видимости
// ES3, ES5


var data = [];                  data[0](); // ?
                                data[1](); // ?
for (var k = 0; k < 3; k++) {   data[2](); // ?
  data[k] = function () {
     alert(k);
  };
}
Блочная область видимости
// ES3, ES5


var data = [];              data[0](); // 3
var k;                      data[1](); // 3
for (k = 0; k < 3; k++) {   data[2](); // 3
  data[k] = function () {
     alert(k);
  };
}
let : блочная область видимости
ES3, ES5                        ES6
for (var k = 0; k < 3; k++) {    for (let k = 0; k < 3; k++) {
                                   let x = k;
  (function (x) {
                                   data[x] = function () {
     data[x] = function () {
                                      alert(x);
        alert(x);
                                   };
     };                          }
  })(k);
}

data[0](); // 0                 data[0](); // 0
Деструктуризация
или «нестогий pattern-matching»
Деструктуризация : массивы

// для массивов
let [x, y] = [10, 20, 30]; // нестрогий matching

console.log(x, y); // 10, 20
Деструктуризация: объекты

// для объектов
let foo = {value: 100, data: {x: 10, y: 20}};

let {value: v, data: {x: x, y: y}} = foo;

console.log(v, x, y); // 100, 10, 20
Деструктуризация
          параметров функции

function foo({a: a, b: [x, y]}) {
  return a + x * y;
}

let data = {a: 1, b: [2, 3]};

foo(data); // 7
Деструктуризация:
              обмен переменных

// обмен двух переменных без третьей?

let x = 10;
let y = 20;

[x, y] = [y, x]; // легко
Замена arguments:
 ”rest” и ”spread”
Объект arguments
// ES3, ES5

function foo(name, /* rest */) {
  var rest = [].slice.call(arguments, 1);
  var squares = rest.map(function (x) { return x * x});
  return squares;
}

foo(“squares”, 1, 2, 3); // [1, 4, 9]
Прощай, arguments
// ES3, ES5

function foo(name, /* rest */) {
  var rest = [].slice.call(arguments, 1); // сложно
  var squares = rest.map(function (x) { return x * x});
  return squares;
}

foo(“squares”, 1, 2, 3); // *1, 4, 9+
Привет, ”rest”
// ES6 aka Harmony

function foo(name, …rest) { // настоящий массив
  var squares = rest.map(function (x) { return x * x});
  return squares;
}

foo(“squares”, 1, 2, 3); // [1, 4, 9]
А также ”spread”
// ES6 aka Harmony

function bar(x, y, z) {
  return x + y * z;
}

let args = [1, 2, 3];

bar(…args); // 7

bar.apply(null, args); // или так, 7
”spread” в pattern-matching
// ES6 aka Harmony

let args = [“data”, 1, 2, 3];
let [name, ...values] = args;

console.log(name); // “data”
console.log(values); // [1, 2, 3]
Сокращенные нотации
Сокращения в
             деструктуризации

// полная нотация
let {x: x, y: y, z: z} = {x: 10, y: 20, z: 30};

// сокращенная нотация
let {x, y, z} = {x: 10, y: 20, z: 30};
Короткий синтаксис функций.
            #-функции
// обычные функции
[1, 2, 3].map(function (x) { return x * x; }); // [1, 4, 9]

// #-функции
[1, 2, 3].map(#(x) { x * x }); // [1, 4, 9]

Синтаксически:
   • необязательный return;
   • # вместо function
Семантика #-функций
// обычная функция                  Динамически
let object = {                    связываемый this

   start: function () {
      setTimeout(function () { this.continue(); }, 500);
   },
   continue: function () { ... }
};                                    Решения:
                                    var that = this;
object.start(); // error
                                      .bind(this)
Семантика #-функций
// #-функции                     this автоматом связан
let object = {                с лексическим контекстом

   start: function () {
      setTimeout(#{ this.continue(); }, 500);
   },
   continue: function () { ... }
};
object.start(); // ok
Семантика #-функций
// #-функции                     Динамический this
let object = {                     в #-функциях

   start: # (this) {
      setTimeout(#{ this.continue(); }, 500);
   },
   continue: # (this) { ... }
};
object.start(); // ok
Proxy объекты : мета уровень
Proxy-объекты
/* handler – обработчик мета-уровня
 * proto – прототип прокси-объекта */
Proxy.create(handler, [proto])

/* handler – мета-обработчик
 * call – проксирование вызова
 * construct – проксирование конструирования */
Proxy.createFunction(handler, [call, [construct]])

See: http://wiki.ecmascript.org/doku.php?id=harmony:proxies
Proxy-объекты
 // original object       // proxied object
 let foo = {              let pFoo = Proxy.create({
    x: 10,                   get: function (rcvr, name) {
    y: 20                       console.log(“get: ”, name);
 };                             return foo[name];
                             },
                             set: function (rcvr, name, value) {
Перехват чтения                 console.log(“set: ”, name, value);
   свойства
                                foo[name] = value;
                             }
Перехват записи           }, Object.getPrototypeOf(foo));
   свойства
Proxy-объекты
                               // proxied object
Мета-обработчик                let pFoo = Proxy.create({
                                  get: function (rcvr, name) {
// перехват чтения                   console.log(“get: ”, name);
pFoo.x; // get: x, 10                return foo[name];
                                  },
// перехват записи                set: function (rcvr, name, value) {
pFoo.x = 100; // set: x, 100         console.log(“set: ”, name, value);
                                     foo[name] = value;
// отображается на оригинале      }
foo.x; // 100                  }, Object.getPrototypeOf(foo));
Callable Proxy-объекты
// original object              // proxied object
let foo = {x: 10, y: 20};       let pFoo = Proxy.createFunction({
                                   get: function (rcvr, name) {
function callTrap() {                 console.log(“get: ”, name);
  console.log(“call”);                return foo[name];
}                                  },
                                   set: function (rcvr, name, value) {
function constructTrap() {
                                      console.log(“set: ”, name, value);
  console.log(“construct”);
                                      foo[name] = value;
}
                                   }
                                }, callTrap, constructTrap);
pFoo(10, 20); // “call”
new pFoo(100); // “construct”
                                                             Перехват
                                   Перехват вызова
                                                         конструирования
Proxy : примеры
// логгеры (на чтение и запись)
Proxy.create(logHandler(object));

// множественное наследование (делегирующие примеси)
Proxy.create(mixin(obj1, obj2));

// noSuchMethod
Proxy.create(object, noSuchMethod)

// Массивы с отрицательными индексами (как в Python)
let a = Array.new([1, 2, 3]);
console.log(a[-1]); // 3
a[-1] = 10; console.log(a); // [1, 2, 10]

See: https://github.com/DmitrySoshnikov/es-laboratory/tree/master/examples
Система модулей
Модули в ES3, ES5
var Library = (function (global) {
   /* save original */
   var originalLibrary = global.Library;
   function noConflict() {
      global.Library = originalLibrary;    1.   Создать локальный скоп
   }                                       2.   Функция восстановления
   /* implementation */                    3.   Имплементация
                                           4.   Публичный интерфейс
   function query() { ... }
   /* exports, public API */
   return {
      noConflict: noConflict,
      query: query
   };
})(this);
Модули в ES3, ES5
var Library = (function (global) {
   /* save original */
   var originalLibrary = global.Library;
   function noConflict() {
      global.Library = originalLibrary;    1.   Создать локальный скоп
   }                                       2.   Функция восстановления
   /* implementation */                    3.   Имплементация
                                           4.   Публичный интерфейс
   function query() { ... }
   /* exports, public API */
                                               Слишком много
   return {                                синтаксического «шума».
      noConflict: noConflict,                   Нужен «сахар».
      query: query
   };
})(this);
Модули в ES6
module Library {
  export function query(s) { ... }
  export function ajax(...args) { ... }
}

import Library.*; // импортировать все

import Library.{query, ajax: xhr}; // импортировать только нужное

query(“#my-element”).hide();

xhr(“/books/store”, {
   onSuccess: # (response) { ... }
})
Модули в ES6
module Widgets {

    var collection= [ ... ]; // приватное свойство
    function registerWidget(name, ...params) { ... } // приватное ?

    // внутренний приватный модуль
    module Register { ... }
                                                В дополнение:
    // вложенный публичный модуль
    export module Panel {                    // ошибка, нельзя присвоить export’у
       export function create() { ... }      Widgets.Panel = false;
    }                                        // ошибка, нет такого export’а
    // множественный export                  let bar = Widget.Window
    export { register: registerWidget }
}

let panel = Widgets.Panel.create({title: “Options”});
Внешние модули в ES6
// на файловой системе
module $ = “./library/selector.js”;

// глобально, из сети; сами определяем имя модуля
module CanvasLib = “http:// ... /js-modules/canvas.js”;

// используем напрямую
let rect = new CanvasLib.Rectangle({width: 30, height: 40, shadow: true});

// или импортируем нужные объекты
import CanvasLib.{Triangle, rotate};

rotate(-30, new Triangle($.query(...params)));
Спасибо за внимание


    Дмитрий Сошников

dmitry.soshnikov@gmail.com
  http://dmitrysoshnikov.com

      @DmitrySoshnikov

AddConf. Дмитрий Сошников - Будущее ECMAScript

  • 1.
    ECMAScript сегодня и вбудущем. Дмитрий Сошников http://dmitrysoshnikov.com
  • 2.
    ES5 : НовыйAPI объектов
  • 3.
    Два вида свойств: Обычныеdata-свойства (явная ассоциация имени и значения) Accessor-свойства (геттеры/сеттеры) (неявная ассоциация имени с accessor-функциями)
  • 4.
    Работа со свойствами Object // работа с атрибутами .defineProperty
  • 5.
    Работа со свойствами Object // работа с атрибутами .defineProperty // сразу несколько свойств .defineProperties
  • 6.
    Работа со свойствами Object // работа с атрибутами .defineProperty // сразу несколько свойств .defineProperties // анализ атрибутов (дескриптор) .getOwnPropertyDescriptor
  • 7.
    Работа со свойствами Object // работа с атрибутами // статика .defineProperty .preventExtensions / .isExtensible // сразу несколько свойств // «опечатывание» .defineProperties .seal / .isSealed // анализ атрибутов (дескриптор) // «заморозка» .getOwnPropertyDescriptor .freeze / .isFrozen // список свойств // наследование; прототипы .keys .getPrototypeOf .getOwnPropertyNames .create
  • 8.
    Дескриптор data-свойства { value: 10, // значение writable: true, // только чтение? enumerable: false, // перечислимо в for-in? configurable: true // можно удалить? }
  • 9.
    Data-свойство var foo =Object.defineProperty({}, “x” { value: 10, writable: true, enumerable: false, configurable: true });
  • 10.
    Data-свойство var foo =Object.defineProperty(,-, “x” , value: 10, По умолчанию все атрибуты : false writable: true, enumerable: false, Object.defineProperty(foo, “MAX_SIZE” { configurable: true value: 20, }); writable: false, // default enumerable: false, // default configurable: false // default });
  • 11.
    Data-свойство var foo = Object.defineProperty(,-, “x” , value: 10, По умолчанию все атрибуты : false writable: true, enumerable: false, Object.defineProperty(foo, “MAX_SIZE” { configurable: true ES3 value: 20, }); writable: false, enumerable: false, configurable: false foo.z = 30; }); Object.defineProperty(foo, “z” , value: 30, writable: true, enumerable: true, configurable: true Создание свойства присваиванием -- атрибуты : true });
  • 12.
    enumerable: false ES3 ES5 Object.prototype.x = 10; Object.defineProperty( Object.prototype, “x”, { var foo = {y: 20}; value: 10 } ); for (var k in foo) { for (var k in foo) { console.log(k); // y, x console.log(k); // y } }
  • 13.
    Дескриптор accessor-свойства { get: function () {...}, // чтение значения set: function (v) {...}, // установка значения enumerable: false, // перечислимо в for-in? configurable: true // можно удалить? }
  • 14.
    Accessor-свойство Object.defineProperty(foo, “z” { get: function () { return this.x + this.y; }, set: undefined, enumerable: false, configurable: false }); console.log(foo.z); // 30 foo.z = 40; // false, только геттер console.log(foo.z); // не изменилось - 30
  • 15.
    Декларативное объявление геттера/сеттера var foo = { x: 10, y: 20, get sum() { return this.x + this.y; } }; foo.sum; // 30
  • 16.
    Список свойств объекта //только enumerable-свойства Object.keys(foo); // *“y”, “z”+ // все родные свойства Object. getOwnPropertyNames(foo); // *“x”, “y”, “z”+
  • 17.
    Фиксация объектов Статика: запрещаетрасширение объекта var foo = { Object.isExtensible(Object.preventExtensions(foo)); // false x: 10 foo.bar = 100; // false }; console.log(“bar” in foo); // false
  • 18.
    Фиксация объектов Статика: запрещаетрасширение объекта var foo = { Object.isExtensible(Object.preventExtensions(foo)); // false x: 10 foo.bar = 100; // false }; console.log(“bar” in foo); // false Опечатывание (seal): + устанавливает всем свойствам configurable == false Object.isSealed(Object.seal(foo)); // true Object.getOwnPropertyDescriptor(foo, “x”).configurable; // false
  • 19.
    Фиксация объектов Статика: запрещаетрасширение объекта var foo = { Object.isExtensible(Object.preventExtensions(foo)); // false x: 10 foo.bar = 100; // false }; console.log(“bar” in foo); // false Опечатывание (seal): + устанавливает всем свойствам configurable == false Object.isSealed(Object.seal(foo)); // true Object.getOwnPropertyDescriptor(foo, “x”).configurable; // false Заморозка (freeze): + устанавливает всем свойствам writable == false Object.isFrozen(Object. freeze(foo)); // true foo.y = 100; // false console.log(foo.y); // не изменилось, 20
  • 20.
    Получение прототипа объекта Object.getPrototypeOf(foo) === Object.prototype; // true
  • 21.
    Прототипное наследование Object.create(proto, [properties]) // объект-родитель var foo = { x: 10, y: 20, z: 30 };
  • 22.
    Прототипное наследование Object.create(proto, [properties]) // объект-родитель // наследуем bar от foo var foo = { var bar = Object.create(foo, { x: 10, q: { y: 20, value: 40 z: 30 } }; }; console.log(bar.x, bar.y, bar.z, bar.q); // 10, 20, 30, 40
  • 23.
    Обычные hash-таблицы без прототипа var foo = Object.create(null); alert(foo); // ?
  • 24.
    Обычные hash-таблицы без прототипа var foo = Object.create(null); alert(foo); // error Нет метода toString. Объект foo пустой и ни от кого не наследует.
  • 25.
  • 26.
  • 27.
    Блочная область видимости //ES3 if (false) { var a = 10; } alert(a); // ?
  • 28.
    Блочная область видимости //ES3 var a; // = undefined; if (false) { Все переменные создаются до запуска кода – при входе в контекст. a = 10; Так называемое «поднятие» } (hoisting) переменных. alert(a); // undefined See: http://dmitrysoshnikov.com/notes/note-4-two-words-about-hoisting/
  • 29.
    Блочная область видимости //ES6 Harmony if (false) { let a = 10; } alert(a); // ReferenceError
  • 30.
    Блочная область видимости //ES3, ES5 var data = []; data[0](); // ? data[1](); // ? for (var k = 0; k < 3; k++) { data[2](); // ? data[k] = function () { alert(k); }; }
  • 31.
    Блочная область видимости //ES3, ES5 var data = []; data[0](); // 3 var k; data[1](); // 3 for (k = 0; k < 3; k++) { data[2](); // 3 data[k] = function () { alert(k); }; }
  • 32.
    let : блочнаяобласть видимости ES3, ES5 ES6 for (var k = 0; k < 3; k++) { for (let k = 0; k < 3; k++) { let x = k; (function (x) { data[x] = function () { data[x] = function () { alert(x); alert(x); }; }; } })(k); } data[0](); // 0 data[0](); // 0
  • 33.
  • 34.
    Деструктуризация : массивы //для массивов let [x, y] = [10, 20, 30]; // нестрогий matching console.log(x, y); // 10, 20
  • 35.
    Деструктуризация: объекты // дляобъектов let foo = {value: 100, data: {x: 10, y: 20}}; let {value: v, data: {x: x, y: y}} = foo; console.log(v, x, y); // 100, 10, 20
  • 36.
    Деструктуризация параметров функции function foo({a: a, b: [x, y]}) { return a + x * y; } let data = {a: 1, b: [2, 3]}; foo(data); // 7
  • 37.
    Деструктуризация: обмен переменных // обмен двух переменных без третьей? let x = 10; let y = 20; [x, y] = [y, x]; // легко
  • 38.
  • 39.
    Объект arguments // ES3,ES5 function foo(name, /* rest */) { var rest = [].slice.call(arguments, 1); var squares = rest.map(function (x) { return x * x}); return squares; } foo(“squares”, 1, 2, 3); // [1, 4, 9]
  • 40.
    Прощай, arguments // ES3,ES5 function foo(name, /* rest */) { var rest = [].slice.call(arguments, 1); // сложно var squares = rest.map(function (x) { return x * x}); return squares; } foo(“squares”, 1, 2, 3); // *1, 4, 9+
  • 41.
    Привет, ”rest” // ES6aka Harmony function foo(name, …rest) { // настоящий массив var squares = rest.map(function (x) { return x * x}); return squares; } foo(“squares”, 1, 2, 3); // [1, 4, 9]
  • 42.
    А также ”spread” //ES6 aka Harmony function bar(x, y, z) { return x + y * z; } let args = [1, 2, 3]; bar(…args); // 7 bar.apply(null, args); // или так, 7
  • 43.
    ”spread” в pattern-matching //ES6 aka Harmony let args = [“data”, 1, 2, 3]; let [name, ...values] = args; console.log(name); // “data” console.log(values); // [1, 2, 3]
  • 44.
  • 45.
    Сокращения в деструктуризации // полная нотация let {x: x, y: y, z: z} = {x: 10, y: 20, z: 30}; // сокращенная нотация let {x, y, z} = {x: 10, y: 20, z: 30};
  • 46.
    Короткий синтаксис функций. #-функции // обычные функции [1, 2, 3].map(function (x) { return x * x; }); // [1, 4, 9] // #-функции [1, 2, 3].map(#(x) { x * x }); // [1, 4, 9] Синтаксически: • необязательный return; • # вместо function
  • 47.
    Семантика #-функций // обычнаяфункция Динамически let object = { связываемый this start: function () { setTimeout(function () { this.continue(); }, 500); }, continue: function () { ... } }; Решения: var that = this; object.start(); // error .bind(this)
  • 48.
    Семантика #-функций // #-функции this автоматом связан let object = { с лексическим контекстом start: function () { setTimeout(#{ this.continue(); }, 500); }, continue: function () { ... } }; object.start(); // ok
  • 49.
    Семантика #-функций // #-функции Динамический this let object = { в #-функциях start: # (this) { setTimeout(#{ this.continue(); }, 500); }, continue: # (this) { ... } }; object.start(); // ok
  • 50.
    Proxy объекты :мета уровень
  • 51.
    Proxy-объекты /* handler –обработчик мета-уровня * proto – прототип прокси-объекта */ Proxy.create(handler, [proto]) /* handler – мета-обработчик * call – проксирование вызова * construct – проксирование конструирования */ Proxy.createFunction(handler, [call, [construct]]) See: http://wiki.ecmascript.org/doku.php?id=harmony:proxies
  • 52.
    Proxy-объекты // originalobject // proxied object let foo = { let pFoo = Proxy.create({ x: 10, get: function (rcvr, name) { y: 20 console.log(“get: ”, name); }; return foo[name]; }, set: function (rcvr, name, value) { Перехват чтения console.log(“set: ”, name, value); свойства foo[name] = value; } Перехват записи }, Object.getPrototypeOf(foo)); свойства
  • 53.
    Proxy-объекты // proxied object Мета-обработчик let pFoo = Proxy.create({ get: function (rcvr, name) { // перехват чтения console.log(“get: ”, name); pFoo.x; // get: x, 10 return foo[name]; }, // перехват записи set: function (rcvr, name, value) { pFoo.x = 100; // set: x, 100 console.log(“set: ”, name, value); foo[name] = value; // отображается на оригинале } foo.x; // 100 }, Object.getPrototypeOf(foo));
  • 54.
    Callable Proxy-объекты // originalobject // proxied object let foo = {x: 10, y: 20}; let pFoo = Proxy.createFunction({ get: function (rcvr, name) { function callTrap() { console.log(“get: ”, name); console.log(“call”); return foo[name]; } }, set: function (rcvr, name, value) { function constructTrap() { console.log(“set: ”, name, value); console.log(“construct”); foo[name] = value; } } }, callTrap, constructTrap); pFoo(10, 20); // “call” new pFoo(100); // “construct” Перехват Перехват вызова конструирования
  • 55.
    Proxy : примеры //логгеры (на чтение и запись) Proxy.create(logHandler(object)); // множественное наследование (делегирующие примеси) Proxy.create(mixin(obj1, obj2)); // noSuchMethod Proxy.create(object, noSuchMethod) // Массивы с отрицательными индексами (как в Python) let a = Array.new([1, 2, 3]); console.log(a[-1]); // 3 a[-1] = 10; console.log(a); // [1, 2, 10] See: https://github.com/DmitrySoshnikov/es-laboratory/tree/master/examples
  • 56.
  • 57.
    Модули в ES3,ES5 var Library = (function (global) { /* save original */ var originalLibrary = global.Library; function noConflict() { global.Library = originalLibrary; 1. Создать локальный скоп } 2. Функция восстановления /* implementation */ 3. Имплементация 4. Публичный интерфейс function query() { ... } /* exports, public API */ return { noConflict: noConflict, query: query }; })(this);
  • 58.
    Модули в ES3,ES5 var Library = (function (global) { /* save original */ var originalLibrary = global.Library; function noConflict() { global.Library = originalLibrary; 1. Создать локальный скоп } 2. Функция восстановления /* implementation */ 3. Имплементация 4. Публичный интерфейс function query() { ... } /* exports, public API */ Слишком много return { синтаксического «шума». noConflict: noConflict, Нужен «сахар». query: query }; })(this);
  • 59.
    Модули в ES6 moduleLibrary { export function query(s) { ... } export function ajax(...args) { ... } } import Library.*; // импортировать все import Library.{query, ajax: xhr}; // импортировать только нужное query(“#my-element”).hide(); xhr(“/books/store”, { onSuccess: # (response) { ... } })
  • 60.
    Модули в ES6 moduleWidgets { var collection= [ ... ]; // приватное свойство function registerWidget(name, ...params) { ... } // приватное ? // внутренний приватный модуль module Register { ... } В дополнение: // вложенный публичный модуль export module Panel { // ошибка, нельзя присвоить export’у export function create() { ... } Widgets.Panel = false; } // ошибка, нет такого export’а // множественный export let bar = Widget.Window export { register: registerWidget } } let panel = Widgets.Panel.create({title: “Options”});
  • 61.
    Внешние модули вES6 // на файловой системе module $ = “./library/selector.js”; // глобально, из сети; сами определяем имя модуля module CanvasLib = “http:// ... /js-modules/canvas.js”; // используем напрямую let rect = new CanvasLib.Rectangle({width: 30, height: 40, shadow: true}); // или импортируем нужные объекты import CanvasLib.{Triangle, rotate}; rotate(-30, new Triangle($.query(...params)));
  • 62.
    Спасибо за внимание Дмитрий Сошников dmitry.soshnikov@gmail.com http://dmitrysoshnikov.com @DmitrySoshnikov