В JavaScript не производится проверка типов. Это даёт разработчика не только большие возможности, но и большие проблемы. Например, можно случайно присвоить объектной переменной строку и тем самым потерять все определённые ранее свойства объекта, например, объявим глобальную переменную:
var product = { code: 12, title: 'Remix Mini', cost: '4000' };
alert( product.title ); // 'Remix Mini' product = "Велосипед"; // забыли, что ранее уже была определена переменная с таким именем alert( product.title ); // undefined
В компилируемых языках уникальность глобальных определений проверяется на уровне компилятора, который не позволит создать два объекта или метода с одинаковыми именами (за некоторым исключением), а в JavaScript нам приходится самим следить за этим, в чём помогает подход к разработке от минимально доступной области видимости переменных. При данном подходе объекты, методы и свойства изначально создаются закрытыми (локальными). Для этого они должны находиться внутри функции и иметь локальное объявление. Практичность данного подхода очевидна - чем меньше происходит взаимодействия с внутренним кодом, тем меньше вероятность нарушить его работу. Пользователь должен взаимодействовать с внутренним кодом не напрямую, а посредством интерфейсных методов.
Рассмотрим стиль оформление кода из справки по DroidScript:
function OnStart() { //+++ внутренний код функции
lay = app.CreateLayout( "linear", "VCenter,FillXY" ); btn = app.CreateButton( "Press Me", 0.3, 0.1 ); btn.SetMargins( 0, 0.05, 0, 0 ); btn.SetOnTouch( btn_OnTouch );
lay.AddChild( btn ); app.AddLayout( lay );
//--- внутренний код функции } function btn_OnTouch()
{ //+++ внутренний код функции
btn.SetText( "Привет,Андроид!" );
//--- внутренний код функции }
В этом примере переменные lay и btn, для удобства обращения к ним за пределами функции OnStart, неявно объявлены глобальными. Такой подход - "закрыть доступ успею всегда, а для удобства разработки дам доступ ко всем переменным" - не рекомендуется использовать при разработке реальных приложений. "Потом" часто забывается и с увеличением объёма кода в нём увеличивается количество "висящих" глобальных переменных, полей и функций, случайное переопределение которых сразу приведёт к нестабильной работе кода, ошибкам и исключениям.. Исправим это, объявив все переменные локальными:
function OnStart() { //+++ внутренний код функции
var _lay = app.CreateLayout( "linear", "VCenter,FillXY" ); var _btn = app.CreateButton( "Press Me", 0.3, 0.1 ); _btn.SetMargins( 0, 0.05, 0, 0 ); _btn.SetOnTouch( btn_OnTouch ); _lay.AddChild( _btn ); app.AddLayout( _lay ); //--- внутренний код функции } function btn_OnTouch()
{ //+++ внутренний код функции
_btn.SetText( "Привет,Андроид!" ); // исключение, _btn не определено
//--- внутренний код функции }
Теперь за пределами функции OnStart невозможно изменить переменные и нажатие на кнопку приведёт к возникновению ошибки в приложении. Для удобства всем названиям локальных переменных присвоен префикс _, что в будущем позволит их легко различать.
В нашем примере код отображения виджетов, который реализован в функции OnStart, отделён от кода реакции на действие - функция btn_OnTouch, которая является глобальной. Для её локализации можно использовать анонимную функцию обратного вызова:
function OnStart() { var _lay = app.CreateLayout( "linear", "VCenter,FillXY" ); var _btn = app.CreateButton( "Press Me", 0.3, 0.1 ); _btn.SetMargins( 0, 0.05, 0, 0 ); _btn.SetOnTouch( function(){ app.ShowPopup( "Привет, Андроид!" ); }); _lay.AddChild( _btn ); app.AddLayout( _lay ); }
Мы избавились от глобальной функции, но теперь код отображения, данных и реакции на действие привязаны друг к другу. Насколько это хорошо или плохо будет зависеть от контекста использования. Программирование во многом напоминает шахматы, при игре в которые можно сделать сильный или слабый ход, пожертвовать одним ради получения преимущества в другом.
При разработке от минимальной области видимости код приложения представляет собой закрытые блоки. В процессе работы возникает необходимость обмена данными между ними, что можно реализовать при помощи глобального объекта:
var gl_widget = {}; function OnStart() { //+++ закрытый внутренний код var _lay = app.CreateLayout( "linear", "VCenter,FillXY" ); var _btn = app.CreateButton( "Press Me", 0.3, 0.1 ); _btn.SetMargins( 0, 0.05, 0, 0 ); _lay.AddChild( _btn ); app.AddLayout( _lay );
//--- закрытый внутренний код //+++ интерфейсная часть gl_widget.btnShowVersion = _btn;
//--- интерфейсная часть }
Интерфейсные объекты необходимо определять в самом конце функции, так как их изменение приведёт к изменению внутреннего объекта, присвоенного по ссылке.
var gl_widget = {}; function OnStart() { //+++ закрытый внутренний код var _lay = app.CreateLayout( "linear", "VCenter,FillXY" ); var _btn = app.CreateButton( "Press Me", 0.3, 0.1 ); gl_widget.btnShowVersion = _btn; _btn.SetMargins( 0, 0.05, 0, 0 ); _lay.AddChild( _btn ); app.AddLayout( _lay ); //--- закрытый внутренний код //+++ интерфейсная часть gl_widget.btnShowVersion.name = 12; // теперь и _btn.name = 12 alert(_btn.name); // 12 //--- интерфейсная часть }
Префикс gl_ в именах глобальных переменных позволит отличать их от остальных переменных.
Интерфейсный объект связан с внутренним объектом по ссылке, о чём нужно помнить. Это позволяет новые свойства и обработчики событий присваивать как внутреннему объекту, так и интерфейсному, но лучше следовать правилу, при котором интерфейсному объекту присваиваются только обработчики событий, а всё остальное добавлять исходному объекту.
Глобальная переменная gl_widget является свойством глобального объекта, который в браузерной среде доступен под именем window:
window.gl_widget; или window[ 'gl_widget' ];
Такое обращение позволяет избавиться от глобальной переменной gl_widget. Для справки, в среде Node.js глобальный объект доступен под именем global.
Глобальный объект window используется часто. Например, при помощи него можно узнать имена всех существующих в коде общебоступных переменных и функции.