Каким будет Object.defineProperty в AS3?

Я архитектор с большим опытом работы с JavaScript, но в прошлом я немного работал с .NET и Java.

Однако я хотел приложить руку к ActionScript3, который, как мне обещали, очень связан с JavaScript.

В качестве стартового проекта я решил попробовать портировать на ActionScript3 одну из моих любимых утилит утверждения — should.js - это делает ваши тестовые коды действительно приятными для чтения.

Обновлено: 19 февраля 2013 г.

Я видел, что путаю свою абстрактную речь, поэтому я заменил часть поста конкретным вопросом. Вот полная картина:

Рассмотрим следующий код JavaScript:

Object.defineProperty(Object.prototype, 'should'
, { set: function(){}
  , get: 
    function(){
       return new Assertion(Object(this).valueOf());
    }
  , configurable: true
  , enumerable  : false
  }
);

Это часть реализации модуля JavaScript Should. Другая часть — это определение класса Assertion, который создан со значением и реализует широкий и удобный набор методов утверждения для этого значения. Методы вроде как

var o = Assertion(actualValue)
o.equals(expectedValue1)
o.moreThan(expectedValue2)
o.contains(expectedValue3)

и псевдонимы, чтобы сохранить английскую грамматику

var o = Assertion(actualValue)
o.equal(expectedValue1)
o.contain(expectedValue3)

и псевдонимы для ленивых снайперов, например

o.eql(expectedValue)
o.gt(expectedValue) //greater then
o.gte(...) //greater then or equal
//and so on... 

и некоторые соединители, которые просто возвращают this (это экземпляр Assertion, созданный с тестовым значением), например

o.be
o.and

Что это дает вам?

Тестовый код выглядит следующим образом:

var person = getPerson();
Should.exist(person); //that's a static call, and that's easy

//but these are a member calls:
person.should.have("name","age","address","friends");  
person.name.should.equal("John");
person.age
  .should
      .be.number()
  .and.be.between(20,30);

person.address
  .should
    .be.string().and
    .startWith("\d").and
    .endWith(" st.")
  //or even
    .and.match(/^[0-9]{1,9}\s+[A-Z][a-z0-9 ]* st\.$/);

person.friends
  .should
    .be.array().and
    .be.between(3,5).and
    .containOnlyType(String);

Разве это не прекрасно? это простой английский!

Можно спорить об эстетике отступов, о том, куда ставить and и нужны ли они вообще, но кроме этого — прочитать или написать это может любой: Однажды вы взяли атрибут «следует», который существует на всех объект, но не портит итерации карты — вы можете продолжать связывать все, что вам нужно, в отношении значения, с которого вы начали.

Он может иметь более изящные инструменты итерации, утилиты отражения, быть дополнен тестовыми функциями, относящимися к вашей объектной модели, и так далее и тому подобное, но давайте просто закончим первый шаг :)

Но для этого вам нужно, чтобы каждый объект в системе имел неперечисляемое смарт-свойство с именем should, которое в своей геттерной функции возвращает Assertion объект, созданный с this в качестве тестируемого значения.

(вы еще ничего не видели - подождите, чтобы увидеть красивые сообщения об отклонении, которые он выдает! Вкуснятина!! Так что да - я бы с радостью пожертвовал возможностью называть атрибут "следует"... и с радостью также откажусь от intelisense - по крайней мере, пока это простой английский)

Итак, в комментариях bfavaretto дал нам первый шаг — мы знаем, как предотвратить перечисление атрибута — отлично и спасибо!!

Теперь можем ли мы сделать это атрибутом-получателем, чья функция может получить доступ к this?

Когда я закончу, я собираюсь разместить его в каком-нибудь общедоступном репозитории под лицензией MIT, чтобы мы все могли повеселиться :)

Помочь кому-нибудь?


person Radagast the Brown    schedule 14.02.2013    source источник
comment
AS2 больше похож на Javascript, чем на AS3. Во всяком случае, я обнаружил, что AS3 больше похож на Java, чем на Javascript.   -  person Waleed Khan    schedule 14.02.2013
comment
Отлично, да, но есть ли аналог? Есть ли способ увеличить атрибуты объекта, которые не должны быть перечислены?   -  person Radagast the Brown    schedule 14.02.2013
comment
В частности, для перечисляемого атрибута существует Object.setPropertyIsEnumerable< /а>   -  person bfavaretto    schedule 14.02.2013
comment
Я не уверен, о чем вопрос на самом деле. Вы пытаетесь добавить неперечисляемые реквизиты в динамический объект, создать новый динамический объект с определенными свойствами или добавить что-то ко всем объектам через Object.prototype (в этом случае вы обречены, если не делаете какие-то странные вещи).   -  person Sophistifunk    schedule 15.02.2013


Ответы (3)


Ваш пример на самом деле верен на 90%, но определите его как actionscript, а не как javascript!

Вы по-прежнему можете определять прототипы в AS3, и они по-прежнему будут работать так же, как прототипы в AS2. Единственная разница в AS3 — это компилятор. AVM2 почему-то не отбрасывает прототипы в нативные классы (хотя пользовательские классы я не тестировал).

Трюк с прототипом: приведите класс к объекту.

Например: если вы создаете:

Array.prototype.random = function():void{}

Затем создайте объект:

var myProtoArray:Array = новый массив;

произойдет 2 вещи:

myProtoArray.random() //ОШИБКА - это не удастся, AVM2 не сопоставил прототип с массивом

но

Объект (myProtoArray).random () // РАБОТАЕТ

random() был приведен к классу Object, а затем сопоставлен с Array - я понятия не имею, почему!

Надеюсь, это поможет, ура.

person Reshape Media    schedule 14.02.2013
comment
Если это будет проблемой, мы можем добавить атрибут «следует» к прототипам String, Number, Date, Boolean и т. д. так же, как мы добавляем его в Object.prototype (где все остальные наследуют Object). Проблема заключается в том, чтобы сделать это не повторяющийся атрибут с функцией получения - person Radagast the Brown; 19.02.2013

Признаюсь, я не слишком хорошо знаком с тем, как работает Javascript, но если я понимаю defineProperties используется правильно, это диктат времени выполнения не только того, каким должно быть свойство, но и связанного с ним пространства имен, к которому оно принадлежит (или, по крайней мере, того, что AS3 считает пространством имен).

Свойства класса либо предопределены и изменяются только с помощью пользовательских функций get() set(), либо динамические. После компиляции их пространство имен не может быть изменено (насколько мне известно), поэтому любое нечастное свойство неявно перечислимо и может быть изменено независимо от того, написали ли вы геттер/сеттер (то есть: foo.a = value). Согласно Adobe...

Создаваемые вами свойства являются перечисляемыми, но встроенные свойства, как правило, не подлежат перечислению.

При этом вы можете получить полный список свойств класса, используя describeType. Таким образом можно получить довольно исчерпывающий объем информации, и я подозреваю, что он подойдет вам, если вы хотите портировать пример воссоздания Mozilla defineProperties. Ниже приведен пример вывода только значений свойств.

function showProps(obj:*):void {
    var desc:XML= describeType(obj);

    // public vars
    for each (var n:XML in desc.variable){
        trace(n.@name + ": " + obj[n.@name]);
    }
    // getters
    for each (n in desc.accessor){
        try {
            trace(n.@name + ": " + obj[n.@name]);
        } catch (error:Error) {
            trace("Unable to read write-only property.");
        }
    }
}

Я надеюсь, что это поможет, но я уверен, что не совсем понимаю, чего вы пытаетесь достичь. Если бы вы могли уточнить, это было бы оценено.

person Atriace    schedule 14.02.2013
comment
Информативно на деле, но не в направлении... :P Я обновил свой пост: добавлена ​​более подробная информация - person Radagast the Brown; 19.02.2013

Хорошо, ребята, всем спасибо за помощь, 22+ я дам резюме для людей, которые интересуются исходным вопросом, а после этого - покажу вам результат своих стараний.

Челлендж состоял из двух частей:

1 – предотвратить перечисление расширенного (=добавленного во время выполнения) свойства

К первой части - спасибо @bfavaretto, прокомментировал уровень вопроса - Object.setPropertyIsEnumerable - отлично справился.

2 – заставить расширенное свойство работать с функцией-получателем с доступом к this, чтобы оно могло использовать его в конструкторе возвращаемого значения.

Об этой второй части - В основном - я не смог найти способ увеличить (= добавить) метод получения свойства к прототипу и заставить его работать с экземплярами, которые пользуются его API через дерево наследования.

Во всяком случае, в этих пределах - вот результат:

https://github.com/osher/should.as

Не точное портирование из-за различий в платформах, и у меня все еще есть некоторые методы, чтобы догнать оригинальный should.js (например, методы тестирования HTTP), но достаточно близкие. Основное отличие состоит в том, что вместо

var o:Object = 
    { name : "Radagast"
    , color: "Brown"
    }
o.should.have.properties("name","color")
    .and.have.property("name","Radagast");
o.name.should.not.equal("Palandoo");
o.color.should.equal("Brown");

Вы должны пойти

o.should().have.properties("name","color")
       and.have.property("name","Radagast");
o.name.should().not.equal("Palandoo");
o.color.should().equal("Brown");

(скобки - геттер невозможен - поэтому атрибут должен быть методом, и вы должны вызывать его самостоятельно)

Теперь, если вы застряли и вам нужна помощь от intellisense, вы должны сделать это:

var should:tdd.Should = o.color.should();
should. <ctrl+space>

какой-то вид снимает жало, но заглянуть в интелисенс - это помогает

Важный

Еще одна вещь — вы должны заставить статический конструктор Should выполняться как можно быстрее, например, я делаю это здесь:

[Suite]
[RunWith("org.flexunit.runners.Suite")]
public class my_awsome_test_suite
{
            //forces the static constructor of tdd.Should
    import tdd.Should;
    private static var s:Should = new Should(); 

    public var c1:testCase1;
    public var c2:testCase2;
    public var c3:testCase3;
    public var c4:testCase4;
    }

Возможно, позже я добавлю соответствующий файл README.md и другие классные функции-члены в tdd.Should.

Веселиться

person Radagast the Brown    schedule 05.03.2013