Pull to refresh

Comments 88

Много раз встречал, что народ наплевательски относится к глобальному контексту и временные переменные утекают из разных мест.
Я, лично, всегда проверяю, а не появилось ли чего нового там, чего не должно быть. Пока вручную, но т.к. уже несколько раз приходилось перелапачивать исходники (~400 js файлов), в поисках места с косяком, то планирую это дело автоматизировать.
По-моему все IDE, с которыми я работал (Eclipse, IntelliJ IDEA, NetBeans), выводят предупреждение, если попытаться инициализировать переменную без var. Вообще было бы неплохо pre-commit хук поставить на контроль версий, чтобы не давало даже такую ересь коммитить.
и что даст хука? а если используется переменная, определенная в вызывающем контексте и тот контекст может быть далеко не window…
Intellij IDEA понимает scope и не ругается, когда используется переменная, определённая выше. И не нужен SoffeeScript.
IDEA так же отличает глобальную область видимости от scope функций. Другими словами, если Вы напишите в файле first.js:
var global = {}; // В глобальном контексте

или так:
window.global = {};

То в файле second.js не будет предупреждений при обращении к этой переменной (если он в том же проекте).

Либо JSLint, тут кому что больше нравится, инструментов хватает.
как минимум ide рисуют такие переменные другим цветом
А синтаксический сахар типа CoffeeScript не пробовали? Он должен избавлять от многих бед. В Rails 3.1 используется по умолчанию.
UFO landed and left these words here
Если кодить в WebStorm, то пропустить var практически невозможно. Переменные без var очень броско выделяются в коде: подчеркиваются голубым зигзагом, а их фон заливается примерно таким цветом #F0F0E7. Думаю, и другие редакторы такое умеют.
UFO landed and left these words here
А смысл?
Там и так очень четкая валидация всего чего можно.
А теперь что — WebStrorm will hurt your feelings?
UFO landed and left these words here
На самом деле, не всего. После webstorm'a jslint может найти ещё много интересных кусочков кода)
Сейчас вам, автор, напишут, что программисты никогда не должны ошибаться. И вообще что вы — лох и посмели посягнуть на святыню (node.js). А сам JS — великолепный язык, на котором прекрасно пишутся крупные приложения, просто вы не умеете его готовить.
Дык правда же. Пойду сейчас на ruby сделаю подобное и скажу, что Rails говно.
Вообще-то, JS — действительно великолепный язык. Его только подчистить от ошибок молодости надо (и этих ошибок не так много)
А можно огласить Ваш список? Хочу сверить со своим…
Исправить:
— По умолчанию глобальный контекст переменных
— Function-scope (хотелось бы block-scope)
— Semicolon insertion

Добавить:
— модульность

И уже можно пользоваться.

Что-то еще было, только ща котелок не варит уже :)
Мой шире… Еще заменить прототипное ООП на классовое и выкосить неявное приведение типов, а с ним все приколы с "==".
> Еще заменить прототипное ООП на классовое

А, ну я этого ожидал. почему-то всем в обязательном порядке хочется втянуть C++-style OOP за уши в любой язык.

Не нужно никакого «классового ООП» в JS.

> выкосить неявное приведение типов, а с ним все приколы с "==".

Да, согласен. Строгая типизация не помешала бы
> Не нужно никакого «классового ООП» в JS.

Можно спорить долго, но факт остаётся фактом: JS — единственный популярный язык с ООП на прототипах, да и в нем в конечном итоге все ваяют классы поверх прототипов а дальше по старинке.
> JS — единственный популярный язык с ООП на прототипах, да и в нем в конечном итоге все ваяют классы поверх прототипов а дальше по старинке.

Поэтому не надл натягивать на язык то, для чего он не создавался, а обучать программистов действовать не по старинке ;)
И реально кого-то удалось обучить не действовать по старинке?
Удавалось конечно. Какой это программист, если он не способен обучиться чему-то новому? ;)

Более того, большинство современных фреймворков для JS, даже те, которые натягивают способы работы «по старинке» совсем не чураются использовать prototype-based OOP, и их внутренности очень интересны для его изучения.
Я имел ввиду научить с целью не писать под себя очередную реализацию ООП на основе прототипов.
> Поэтому не надл натягивать на язык то, для чего он не создавался

Все так на полном серьёзе считают прототипы обдуманным решением. Интересно, если на начальную версию языка понадобилось 10 дней, то сколько времени было потрачено на предварительное обдумывание и проектирование?
То, что на начальную версию языка пондобилось 10 дней, ничего не говорит о качестве обдумывания и проектирования — это раз. Вообще-то, ошибок в языке достаточно мало. Можно посмотреть, что об этом говорит сам автор языка, например.

Во-вторых — JS — это, по сути, Self+Scheme с C-like синтаксисом и это очень хорошо. То, что вы не представляете себе другого ООП, кроме C++ного, не значит, что этот ООП надо натягивать на все языки ;)

Ну и в третьих, Harmony добавит достаточно много вкусного сахара и попытается исправить некоторые из ошибок молодости.
> ничего не говорит о качестве обдумывания и проектирования — это раз

… и тайком протащить туда его спасительные фишки: first class functions (замыкания сделал позже, но они были частью плана сразу) и прототипы (примерно как в языке Self).
Цитата как бы намекает на широту и продолжительность обсуждения этого элемента дизайна языка Javascript.
Ни на что она не намекает. То, что автор пошел против желания руководства, никак не указывает на то, что он не думал перед тем, как вводить эту функциональность.

В итоге JS — самый популярныя функциональный язык программирования в мире, но, как всегда
Действительно, зачем создавать продукт здесь и сейчас, зачем думать о бизнес задачах, зачем захватывать не занятые пока еще рынки? Зачем думать о рынке труда и текущих знаниях большинства программистов? Нет! Лучше писать код, не поднимая головы, и не интересуясь ничем больше. И вместо основ экономики, например, выучивать как делать ООП на прототипах.
единственный популярный язык с ООП на прототипах


Не единственный, на вскидку:

* Ruby (появился раньше, чем js)
* ActionScript 3 (близкий родственничек)

У этих двух реализация наследования сделана на порядок удобнее (т.е. я бы сказал, она там есть из коробки). Это на мой взгляд, конечно. Всегда найдутся фанаты десятка различных сторонних реализаций наследования в чистом js (и ни одного стандартного способа в одну понятную всем строку, господа, ни одного!).
В Ruby не prototype-based ООП (емнип)

ActionScript — это не близкий родственник, а младший брат, выросший из Javascript -> Ecmascript
В Ruby очень интересная реализация, я привык ее считать прототипориентированной, но возможно, я ошибаюсь.

В двух строчках описать сложно, но попробую:
1) В руби нет понятия чисто-класс как шаблон (как в C++), в Руби все классы точно такие объекты, как и объекты.

2) Method lookup использует цепочки «прототипов», очень приближенно говоря, у каждого объекта есть пару цепочек, указывающих на предков, к примеру цепочки class и superclass. Кратко можно глянуть в документации www.ruby-doc.org/core-1.9.3/Class.html#method-i-superclass

Это позволяет расширять классы как угодно, во всех старых инстансах класса будут доступны новые методы. Чем не прототипы?

3) Есть возможность клонировать объекты, а так же вы вольны:
* создавать новые объекты, выдергивая уже готовые методы из существующих (Через Method#unbind)
* создавать классы в рантайме, например используя некую функцию
* расширять только конкретные объекты, не трогая остальные объекты этого класса.
и т.д.
* пройти всю цепочку предков и что-то сделать с каждым.

Ну есть хочется вообще чего-то особо извращенного, типа смены class у созданного инстанса, то можно попробовать www.ruby-doc.org/stdlib-1.9.3/libdoc/delegate/rdoc/Delegator.html#method-c-new

это не близкий родственник, а младший брат, выросший из Javascript -> Ecmascript


Ну младший брат это и есть близкий родственник, как я думаю :) Мне кажется, в AS3 есть интересные решения (смешать Java и JS и не взбалтывать).
То, что вы описываете у Ruby — это метаобъекты, а не прототипное наследование. В первом случае у нас есть чертежи объектов — метаклассы, и есть изготовленные по чертежам объекты. Во втором случае любой объект можно превратить в шаблон класса. Разница как между промышленной сборкой и размножением живых организмов ;)
Хм, вы правы, спасибо за комментарий, буду читать в эту сторону, я как-то обычно этот момент упускал.

Не могли бы вы проиллюстрировать второй случай? Мне кажется, что функция-конструктор в js тоже может быть рассмотрена, как метаобъект :)
function MyClass() {/*…*/}
MyClass.answer = 42

Вот так?)
Я имею в виду:
function Test () { ... }
Test.prototype = любой_объект;


Так рассматривать, наверное можно, но в вашем комментарии выше вы писали про «наоборот» — метаклассы как прототипы, а не прототипы как метаклассы.
> Ruby

И где там прототипы?

> ActionScript 3

Так они же еще в версии 2 вроде перешли на классы, нет?
Не совсем. В 2 и тем более 3 версии прототипы — не более чем implementation details. Это к тому, что сахар — это факультативное удобство — (хочешь — используй, хочешь — нет), а классы в AS2 и AS3 — это более-менее обязательное требование.
В AS2 классы были реализованы поверх прототипного наследования и при компиляции код неявно преобразовывался из class-based в prototype-based.
О чём, кстати, упоминается в статье по вами приведённой ссылке:

It’s important to understand that the underlying inheritance mechanism did not change between ActionScript 1.0 and ActionScript 2.0. ActionScript 2.0 merely added a new syntax for defining classes. The prototype chain works the same way in both versions of the language.


Для AS3 была написана новая виртуальная машина, названная AVM2 и там уже реализовано честное class-based ООП. Хотя и оставлена опциональная возможность динамической декларации полей и методов.
Угу, только почему-то prototype оттуда никуда не делся. А почему? А потому что ActionScript 3.0 — это, цитирую, «Completely conforming implementation of the ECMAScript fourth edition draft specification».
В защиту JS хотел бы сказать, что он позволяет написать свою реализацию примесей и наследования, с которой было бы удобно работать, хороший пример, на мой взгляд, в AtomJS: github.com/theshock/atomjs/blob/master/Docs/Class/Class.md

Опять же, это означает, что нужно тащить дополнительные соглашения в код. Сравните:

Ruby
class GameObject; end
module Jump; end
module Run; end

class DuckFromMario < GameObject
   include Run, Jump, Fire
end

duck1 = DuckFromMario.new


JS + SomeFramework
 var Run = SomeFramework.createClass({
     __constructor__: function(){}
 });
 var Jump = SomeFramework.createClass({
     __constructor__: function(){}
 });
 var Fire = ...

var GameObject = SomeFramework.createClass({
 __constructor__: function(name) {},
 
});

var DuckFromMario = SomeFramework.createClass({
 __constructor__: function(name) {}, 

 __extends__: GameObject,
__include__: [Run, Jump, Fire],
 ....
});

var someDuck = new DuckFromMario("someDuck")


И начинается. А что во втором примере будет, если будет вызов
var someDuck = DuckFromMario() 
, подумал ли автор фреймворка об этом?

var DuckFromMario = SomeFramework.createClass({
 __constructor__: function(name) {}, 
//а что будет, если конструктор будет без параметра? А как же родитель?
// а что будет если конструктора не будет вообще?


И, внезапно, все подобные проблемы сваливаются на голову разработчика (и пользователей) фреймворка, а не на рантайм языка, как должно было бы по хорошему.
> И, внезапно, все подобные проблемы сваливаются на голову разработчика (и пользователей) фреймворка, а не на рантайм языка, как должно было бы по хорошему.

Потому что не надо натягивать C++-like OOP на язык с совершенно другим OOP.
Советую посмотреть видео с YUIConf 2011 «From one, many; from many, one — class inheritance and composition in YUI, Luke Smith», когда его выложат в YUI theater.
murr правильно говорит, было бы лучше еслиб всё это было в языке, а не фрэймворках. И все кто это делает отлично разбираются в жаваскриптовом ООП, и к счастью не только в нём.
> было бы лучше еслиб всё это было в языке, а не фрэймворках

Что было в языке? Повторю еще раз. Не надо натягивать C++-like OOP на язык с совершенно другим OOP.
> хороший пример, на мой взгляд, в AtomJS: github.com/theshock/atomjs/blob/master/Docs/Class/Class.md

Отлично, еще один пример в мою копилку.

> И, внезапно, все подобные проблемы сваливаются на голову разработчика (и пользователей) фреймворка, а не на рантайм языка, как должно было бы по хорошему.

Именно.
Ага. Добавляйте. И вспомните наш разговор. Это просто удобный сахар для повторяющихся действий в прототипно-ориентированном программировании. Не больше, не меньше.
И начинается. А что во втором примере будет, если будет вызов
var someDuck = DuckFromMario()
, подумал ли автор фреймворка об этом?

Между прочим, это очень интересная возможность. Как разница между
var n = new Number( '15' );
и
var n = Number( '15' );


//а что будет, если конструктор будет без параметра? А как же родитель?

И что?
Lua, и если он не популярен в вебе то это не значит что он не популярен.
UFO landed and left these words here
Дык это первое, что проверяешь, если возникают «необъяснимые» ошибки — глобальные (пропущен var) и статические (объявленные через прототип) переменные.
UFO landed and left these words here
Буквально сегодня такое же нашел у себя. Но это еще только писалось, так что никакого вреда
? Неужто то, что я пропустил один var делает меня ущербным?

— Что может быть хуже, чем откусить яблоко и обнаружить там червяка?
— Откусить яблоко и обнаружить там полчервяка!

А по-моему такие цейтноты очень интересны. Концентрация 200%!
Есть отличная штука, которая помогает отловить неопознанные глобальные переменные – detect-global, но она не сгодится для server-side в текущей реализации.
Недавно начал работать с Javascript'ом. Пишу в строго настроенной WebStorm по TDD-методологии, без 'use strict' не обходится ни одна функция, весь код перепроверяю через jslint. Для меня это пока минимальный набор методов, позволяющих хоть какой-то быть уверенным в своем js-коде. Поленился написать тесты — код ломается, поленился проверить jslint'ом — код ломается… это ужас какой-то! :)
UFO landed and left these words here
Я бы назвал его недоделанным. Но при определенной дисциплине на нем можно писать работающий код.
У него есть плюс, который в значительной степени перекрывает все эти минусы: на нем можно написать приложение, которое будет работать на большинстве платформ (с помощью браузера). Для меня это оказалось очень удобным. :)
TDD и статический анализ у нас теперь как БСДМ проходят?
Интересное кино!
UFO landed and left these words here
Да, перед тем, как начинать реализовывать новую фичу или фиксить баги я разрабатываю тесты для как можно более полного покрытия. Иногда бывает под сотню тестов для очередной feature, для багов обычно меньше.

Если в результате доработок должны появиться новые методы, используемые в тестах, то перед написанием тестов создаются эти методы с пустыми телами. Позднее добавляется реализация.

Как результат — после того, как все тесты начинают проходить, я могу быть уверен, что задача выполнена.

На мой взгляд БСДМ — это попытка фиксить баги/добавлять функционал без предварительно написанных тестов.
UFO landed and left these words here
В том числе именно поэтому Гугл ваяет Dart на замену JS.

Зачем Гугл ваяет Дарт, не знает, похоже, даже сам Гугл
За что люблю Python — прострелить в нём ногу гораздо сложнее, обычно.

Всё-таки есть что-то нехорошее в том, что глобальные переменные делать легче, чем локальные.
Поглядел MelonCard. Какой-то неимоверный треш. На любые данные сообщает, что нашел меня на тысячах сайтов.
Я как-то полночи отлаживал программу на C, которая вела себя примерно так же — половина данных правильная, половина нет. Оказалось, что под буфер из double-ов я выделял память как n*sizeof(char).
Дурацкую неотлавливаемую ошибку лёгким движением руки можно сделать в любом языке.
При современных IDE, которые все подчеркивают/подсвечивают/выделяет рамкой, подобные ошибки делать надо уметь.
С другой стороны, если писать код в 3-4 часа ночи — то подобные ошибки могут быть у любого профи.
С третьей стороны, нехватка тестов дала о себе знать, вот если бы было нагрузочное тестирование… Если я правильно все понял, то проблема проявлялась когда несколько пользователей одновременно совершали запросы, а юнит-тесты все проверки наверника выполняли в однопользователском режиме.
UFO landed and left these words here
думаю запрет использования глобального контекста (объявление переменных без var) должно ложится на сам интерпретатор/компилятор.
например в JScript.NET в параметрах компилятора существует опция fast, которая и так включена по-умолчанию.
так для кода
initial = extractVariables(req.body);

выдалась бы ошибка
error JS1135: Variable 'initial' has not been declared

причем во время компиляции, а в продакшене и при выполнении приложения
Сочувствую, отладка асинхронного кода и впрямь бывает болью в заднице.
«нужно было использовать CoffeeScript» мне захотелось написать сразу после прочтения заголовка :)
Удачи вам с проектом :)
Sign up to leave a comment.

Articles