Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Lava.ClassManager.define('Lava.Animal', {
Extends: 'Lava.mixin.Observable',
name: null,
toys: [],
init: function(name) {
this.name = name;
},
takeToy: function(toy) {
this.toys.push(toy)
}
});
class Animal extends Observable {
constructor(name) {
this.name = name;
this.toys = toys;
}
takeToy(toy) {
this.toys.push(toy);
}
}
В FF на первом месте TypeScript, потом Native, потом ClassManager.
Vanilla.prototype.toString.call(this), когда я делал что-то подобное ;]Я думаю большая разница все-таки из-за того, что это prototype. Прочитать простое свойство это 1 mov, а прочитать prototype это куча кода
;;; @60: load-function-prototype. 0x45729a09 201 8b73ff mov esi,[ebx+0xff] 0x45729a0c 204 807e07b5 cmpb [esi+0x7],0xb5 0x45729a10 208 0f853006be11 jnz 0x5730a046 ;; deoptimization bailout 7 0x45729a16 214 f6460902 test_b [esi+0x9],0x2 0x45729a1a 218 751d jnz 249 (0x45729a39) 0x45729a1c 220 8b730f mov esi,[ebx+0xf] 0x45729a1f 223 81fea180002e cmp esi,0x2e0080a1 ;; object: 0x2e0080a1 <the hole> 0x45729a25 229 0f841b06be11 jz 0x5730a046 ;; deoptimization bailout 7 0x45729a2b 235 8b56ff mov edx,[esi+0xff] 0x45729a2e 238 807a0780 cmpb [edx+0x7],0x80 0x45729a32 242 7508 jnz 252 (0x45729a3c) 0x45729a34 244 8b760b mov esi,[esi+0xb] 0x45729a37 247 eb03 jmp 252 (0x45729a3c) 0x45729a39 249 8b760f mov esi,[esi+0xf]
Если вам кажется, что тут я считерил — то напишите свои тесты, быстрее все равно не будет.
- NativeParentClass.prototype.method.apply(this);
+ NativeParentClass.prototype.method.call(this);
NativeInstance.method разгоняется на Хроме в 20 раз см jsperf.com/liquidlava-class-system-performance/9f.apply(this) Crankshaft не распознает. Он всегда умел f.apply(this, arguments) распознавать и недавно его научили f.call(...). Если вызов метода родителя распознается — то все проинлайнивается и различные инварианты типа LoadFunctionPrototype выносятся LICMом за пределы самого горячего цикла и, как следствие, на результат бенчмарка влияют мало (см IR цикла, красная полоса — индикатор вложенности циклов). LoadFunctionPrototype похудел, он все еще больше чем одинокий mov, но меньше, чем то чудовище, которое раньше генерировалось.this.counter экземплярам потомков приходит из прототипа, из-за этого если создать два экземпляра и дергать у них по-очереди method, то в этом самом методе возникает нездоровый полиморфизм, который может скушать на этом микробенчмарке до 50% производительности.Function-конструктор.Lava method call (2) в моей версии.Base.prototype.method.call(this) и __.parent.call(this), где __ указывал на method в потомке, а __.parent на метод в базовом классе, т.е. Base.prototype.method. Причем все это было год назад, до того как V8 научился распознавать f.call. Из-за этого .call не проинлайнивался, и как следствие LICM не выносил LoadFunctionPrototype из цикла. Как следствие каждая лишняя инструкция была на счету и отражалась на результате микробенчмарка. (Плюс еще во втором случае на одно разыменование меньше, все складывается)В этом примере не будет работать оператор typeof, но на практике без него можно прекрасно обходиться. Я говорю про реальные приложения и задачи: если вам нужно отличать тип Animal от Cat — то это реальная задача, и она прекрасно решается. Но если вы хотите делать это оператором typeof — то извините, вам к другому доктору.
function Dog(name) {
this.name = name || "Тузик"; // Когда таких свойств у объекта много, вы так же пишите или по другому?
}
Lava.define('Lava.Something', {
// все это будет скопировано для каждого экземпляра класса
name: "Вася",
names: ["Коля", "Петя"],
number: 123
});
То, что абсолютно нормально в С++, бывает очень вредным в JavaScript. Я говорю это из своего опыта: если в классах есть приватные методы и переменные, то такие классы часто становятся неподдерживаемыми. Если вы хотите что-то изменить в таком куске кода — то иногда вам не остается ничего кроме как выбросить старый код и переписать всё с нуля.
protected. Без него код с классами и наследованием скатывается в одну из двух крайностей — либо все кишки торчат наружу, либо класс будет сложно расширить. Даже в ES6 такое не планируется, однако есть статья, где предлагается неплохой способ это реализовать. Могу перевести ее для хабра, если нужно.Приватные члены — это плохая практика.
Когда пишешь большое приложение на том же C++ — то почти все члены класса у вас будут именно защищенными, а не приватными, согласны?
Приватные члены — это, строго говоря, т.н. «сокрытие данных», а не инкапсуляция.
Плохой практикой ее можно назвать по двум причинам:
1) Если переборщить с приватными членами, мы бессмысленно затрудним наследование пользователю.
Нет, инкапсуляция — это объединение данных и поведение в одной сущности. Сокрытие данных, как видите, штука ортогональная.
In programming languages, encapsulation is used to refer to one of two related but distinct notions, and sometimes to the combination thereof:
A language mechanism for restricting access to some of the object's components.
A language construct that facilitates the bundling of data with the methods (or other functions) operating on that data.
Some programming language researchers and academics use the first meaning alone or in combination with the second as a distinguishing feature of object-oriented programming, while other programming languages which provide lexical closures view encapsulation as a feature of the language orthogonal to object orientation.
Вы говорите только про JS, или про любой ОО-язык?
что именно?
Если переборщить с приватными членами, мы бессмысленно затрудним наследование пользователю.
Во-первых, это сокрытие сложности, которое усилено компилятором.
А во-вторых, в некоторых языках/платформах private-члены вам вообще никак не видны (ну кроме прямого влезания в код).
Вот автор и считает, что это усиление компилятором излишне.
Без прямого влезания в код (чтение документации) можно скрыть и без private. Все зависит от используемого генератора документации.
в конкретно взятом js может быть невозможно реализовать истинное сокрытие информации
Еще есть API browsers
Если не лезть в исходники, то всегда все зависит от используемой системы.
Ну как бы Интерфейсы есть не в любом языке
каждый следующий программист, подключающийся к проекту, должен знать, что надо использовать менеджер
это уменьшение сложности?
Как и каждый программист, должен знать синтаксис ЯП.
но речь идет о таких ЯП, в которых нет из коробки.
Меньше необходимых знаний — меньше ошибок, как это ни удивительно.
а то, что вы получаете — не то же самое, что из коробки, а подобие
Ничуть. Такой же объем знаний, такой же объем ошибок (если все написано качественно).
На каком этапе обнаруживается ошибка
Не такой же.
Этого аргумента достаточно.
костыльная подделка, но зачем их вообще реализовывать руками
ваше сетование на сложность изучения публичного интерфейса класса
если присутствует группировка методов по модификаторам
И если класс предполагает уточнение своей реализации, т.е. не объявлен как final, то логично полагать, что потомки изменят и инварианты объектов.
Имхо пред и постусловия относятся к тем ограничениям, которые обязательно должны быть покрыты тестами. И этой защиты достаточно.
Можно и нужно исходить из того, что программист, наследующий от класса, должен понимать что и как реализовано в нём.
Как вы предлагаете устанавливать состояние объекта в производных классах?
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
}
}
});
return obj;
};
Cat.prototype.Animal$init = Animal.prototype.init, и, кажется, не сильно дороже. Вот такой вот:Cat.prototype.super = function(name, ...rest){
return Animal.prototype[name].apply(this, rest);
};
this.super('init'); всё же смотрится лучше, нежели this.Animal$init();.
Очень быстрые классы на JavaScript с красивым синтаксисом