Блог Сергея Байдачного

Мой блог о технологиях

Archive for Август 2011

Толерантность в WordPress

leave a comment »

Только что сломался WordPress (или я довел свою машину до такого состояния). Выдает сообщение о том, что мой браузер устарел (для любых браузеров). Но радует то, что WordPress максимально лоялен к пользователю и предлагает обновить уже установленный (используемый в данный момент) на машине браузер, а не предлагает мне какое-то Г… или что-то в этом роде.

Вот:

clip_image002

или

clip_image004

И т. д.

Written by Sergiy Baydachnyy

30.08.2011 at 22:11

Опубликовано в Internet Explorer 10, Internet Explorer 9

Tagged with

Введение в Silverlight 5 (часть 2)

3 комментария

Повышение полномочий для приложений в браузере

В предыдущих версиях Silverlight, приложения, которые получали возможность работать вне браузера, могли претендовать на дополнительные (повышенные) привилегии. Это позволяло подобным приложениям получать доступ к диску, расширять изолированное хранилище без уведомления пользователя, реализовывать доступ к клавиатуре в полноэкранном режиме и многое другое. Между тем, многие разработчики желали бы иметь подобную функциональность и для приложений, работающих в браузере. Естественно речь идет о корпоративных приложениях, которые доступны только сотрудникам филиалов и при этом могут часто обновляться. В связи с этим в Silverlight 5 появилась возможность создавать приложения с повышенными полномочиями, работающими в окне браузера. Рассмотрим процедуру создания таких приложений.

Шаг 1. Приложение необходимо подписать сертификатом, что вполне можно сделать из оболочки Visual Studio в настройках проекта (вкладка Signing).

 

image

 

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

Шаг 2. Далее необходимо развернуть сертификат на машинах, где будет работать Ваше Silverlight-приложение. Если Вы хотите протестировать функциональность на рабочей машине, то это можно легко сделать, нажав кнопку More Details в диалоге выше, перейдя к параметрам сертификата:

 

image

Шаг 3. На следующем этапе нужно у всех клиентов разрешить запуск приложений в браузере с повышенными полномочиями. Это можно сделать, установив значение AllowElevatedTrustAppsInBrowser (DWORD) в 0х00000000 (disabled) или 0x00000001(enabled) в ключах HKEY_LOCAL_MACHINE\Software\Microsoft\Silverlight\ (для 32 бит) или HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Silverlight\ (для 64 бит).

Шаг 4. Необходимо внести изменения в конфигурационный файл таким образом, чтобы Ваше приложение при работе с браузером запрашивало дополнительные полномочия. Это легко можно сделать в настройках проекта, выбрав соответствующую галочку:

 

image

 

После этих действий, Visual Studio сгенерирует дополнительный конфигурационный файл следующего содержимого:

 

   1:  <InBrowserSettings>
   2:     <InBrowserSettings.SecuritySettings>
   3:        <SecuritySettings ElevatedPermissions="Required" />
   4:     </InBrowserSettings.SecuritySettings>
   5:  </InBrowserSettings>

 

Вот и все. Теперь Ваши приложения (подписанные сертификатом), будут запускаться в браузере с дополнительными полномочиями, включая полную поддержку клавиатуры в полноэкранном режиме, поддержку элементов WebBrowser и NotificationWindow и др.

Хочется отметить, что если Вы попробуете тестировать эту процедуру, запуская приложение через localhost, то первые три шага игнорируются. Поэтому не попадите в заблуждение: если на Вашей машине работает, то будет работать и на других.

Поддержка нескольких окон

Для приложений с повышенными привилегиями, работающих вне браузера, стало возможным отображать несколько немодальных окон, которые могут перемещаться за пределы родительского окна, а также интегрируются с панелью задач Windows 7.

Ниже приведен небольшой код, который, внутри конструктора главного окна, генерирует дочернее окно и делает его видимым:

 

   1:  public MainPage()
   2:  {
   3:  InitializeComponent();
   4:   
   5:     Window childWindow = new Window();
   6:     childWindow.Height = 400;
   7:     childWindow.Width = 600;
   8:     childWindow.Top = 30;
   9:     childWindow.Left = 30;
  10:     childWindow.Title = "Child Window";
  11:     childWindow.Visibility = Visibility.Visible;
  12:  }

 

В данном примере дочернее окно создается с размерами 600 на 400, при этом верхний левый его угол будет находит в позиции (30, 30), относительно экрана системы (не родительского окна). При этом обратите внимание на то, что созданное окно поддерживается панелью задач Windows 7:

 

image

 

Доступ к файловой системе для приложений с повышенными привилегиями

Технология Silverlight 4 поддерживала возможность приложениям с повышенными привилегиями получать доступ к файловой системе пользователя. Правда, доступ был ограничен лишь несколькими папками. Так, в режиме с повышенными привилегиями, приложения получают полный контроль к специальным папкам, ассоциированными с пользователем: MyDocuments, MyVideos, MyPictures, MyMusic. Пути к указанным папкам можно получить, используя статический класс Environment. При этом нужно отметить, что SilverLight-приложение работает только с папками, но не с библиотеками (Windows 7).

В Silverlight 5 появилась возможность взаимодействовать со всей файловой системой пользователя. Речь идет как о чтении файлов, так и о записи.

Written by Sergiy Baydachnyy

30.08.2011 at 15:59

Опубликовано в SilverLight

Tagged with

Зарисовки по JavaScript для .NET разработчиков: Конструкторы и наследование

2 комментария

В этой статье мне хотелось бы освятить различные варианты наследования в JavaScript. Но прежде чем сделать это, давайте рассмотрим некоторые особенности, которые доступны в функциях-конструкторах, чтобы закрыть вопросы, связанные с созданием объектов.

Первое, на чем мне хотелось бы остановиться, это возвращаемое значение функции-конструктора. В языке С# и других, конструкторы не имеют возвращаемого значения и, создавая объект некоторого типа, Вы всегда получаете желаемое. В JavaScript функции-конструкторы, напротив, возвращают значение. В обычном случае это ссылка this, ссылающаяся на созданный объект. Последняя генерируется в начале работы конструктора и возвращается неявно (хотя можно вернуть и явно), но, если Вы решили изменить тип возвращаемого объекта, то это легко сделать, создав внутри что-то другое и вернуть объект, используя оператор return. Вот небольшой пример:

 

   1:  function Employee() {
   2:  return new Person();
   3:  }
   4:   
   5:  function Person() {
   6:  this.firstName = "Sergiy";
   7:  this.lastName = "Baydachnyy";
   8:  };
   9:   
  10:  var obj = new Employee();
  11:   
  12:  alert(obj.firstName);

 

Используя эту возможность, можно реализовать обработку ошибки, которая возникнет, если программист вызовет функцию-конструктор как обычную функцию (то есть без оператора new):

 

   1:  function Person() {
   2:  if (!(this instanceof Person)) {
   3:     return new Person();
   4:  }
   5:  this.firstName = "Sergiy";
   6:  this.lastName = "Baydachnyy";
   7:  }
   8:   
   9:  var obj = Person();
  10:   
  11:  alert(obj.firstName);

 

Теперь, когда мы окончательно разобрались с конструкторами, перейдем к возможным вариантам реализации наследования.

Вызов конструктора базового класса.

Мой любимый способ наследования, это вызов конструктора «базового класса» в конструкторе «производного класса». Иными словами, создавай объект с помощью функции-конструктора, Вы инициируете вызов другой (других) функции-конструктора, передавая текущий контекст. При этом инициализация последней выполняется без использования оператора new (так как он создает новый контекст), а прямым вызовом функции. В этом случае, создаваемый объект получает все данные и методы, объявленные в вызываемой функции-конструкторе. Посмотрим на примере:

 

   1:  function Person(fName, lName) {
   2:  this.firstName = fName;
   3:  this.lastName = lName;
   4:  this.Name = (function () { return this.firstName + " " + this.lastName });
   5:  }
   6:   
   7:  function Developer(fName, lName, lang) {
   8:  Person.call(this,fName, lName);
   9:  this.language = lang;
  10:  }
  11:   
  12:  var d = new Developer("Sergiy", "Baydachnyy", "C#");
  13:   
  14:  alert(d.Name());

 

Из кода видно, что мы используем метод call для установки контекста функции-конструктору. Аналогично можно было бы использовать и метод apply. Данный подход мне нравится тем, что позволяет получить все свойства и методы наследуемого объекта, с возможностью их вызова внутри самого конструктора.

Между тем у этого подхода есть несколько недостатков:

· Методы, которые объявляются в прототипах, никоим образом не попадают в производный объект. Иными словами в производный объект попадает все, что объявлено с помощью this;

· Определить связь с наследуемым объектом нет никакой возможности. Иными словами instanceof полностью работать не будет;

· Поскольку методы в JavaScript также являются объектами и занимают какую-то память, то эффективнее использовать ссылку на метод вместо его дублирования.

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

Использование прототипа.

Метод, основанный на использовании прототипов, состоит в том, что в свойство prototype производного объекта устанавливается ссылка на базовый объект (созданный с помощью new). В этом случае производный объект получает доступ ко всем полям базового объекта. При этом цепочка наследований может быть сколь угодно длинной.

 

   1:  function Person(fName, lName) {
   2:  this.firstName = fName;
   3:  this.lastName = lName;
   4:  this.Name = (function () { return this.firstName + " " + this.lastName });
   5:  }
   6:   
   7:  function Developer(lang, fName, lName) {
   8:  this.language = lang;
   9:  this.firstName = fName;
  10:  this.lastName = lName;
  11:  this.constructor = Developer;
  12:  }
  13:   
  14:  Developer.prototype = new Person();
  15:  var d = new Developer("C#", "Sergiy", "Baydachnyy");
  16:   
  17:  alert(d.Name());

 

Обратите внимание, что этот код не только устанавливает свойство prototype, но и изменяет свойство constructor, которое меняется по ходу установки prototype.

Этот подход также обладает рядом недостатков:

· Установку прототипа нужно выполнять вне функции-конструктора. Это означает, что мы не сможем воспользоваться методами базового объекта внутри конструктора производного;

· Поскольку при инициализации прототипа мы задаем шаблон для всех будущих объектов, нет смысла использовать параметры в конструкторе базового объекта. Данные нужно дублировать, в противном случае они будут общими для всех объектов. Мы можем даже написать код, который удаляет лишние поля данных из прототипа;

· Изменение прототипа скажется на всех объектах, включая производные;

· Код по конструированию объекта разбит на отдельные части в разных областях приложения, что меня, как ООП разработчика очень пугает;

Комбинация двух методов.

Часто, чтобы перенести данные и методы базового объекта в производный, сохранив при этом цепочку прототипов, прибегают к комбинации обоих методов. То есть, в конструкторе производного объекта вызывают функцию-конструктор базового (в текущем контексте), а далее устанавливают прототипы. Подобный подход нас практически избавляет от недостатков первого метода, но недостатки второго метода реализации наследования, остаются.

Наследование в ECMA Script 5

Существует еще один механизм наследования, о котором я не говорил, это наследование через прототип. В этом случае мы прототипу одного объекта присваиваем прототип другого объекта (иногда через создание промежуточной функции с пустым прототипом). А затем расширяем объект по своему усмотрению.

Мне этот метод не особо нравится, так как он также не позволяет красиво группировать код. В тоже время он существует и ECMA Script 5 определяет специальный метод у Object, который создает объект наследник через прототип:

 

   1:  function Person(fName, lName) {
   2:  this.firstName = fName;
   3:  this.lastName = lName;
   4:  Person.prototype.Name = (function () { return this.firstName + " " + this.lastName });
   5:  }
   6:   
   7:  var s = Object.create(new Person("Sergiy", "Baydachnyy"));
   8:   
   9:  //теперь можно расширять созданный объект

 

В завершении отмечу, что «тупое» копирование ссылок на методы базового объекта также является вариантом наследования. В зависимости от задачи, Вы легко можете комбинировать различные методы.

Вот, например, интересный код, который мне пришел в голову:

 

   1:  function Person(fName, lName) {
   2:  this.firstName = fName;
   3:  this.lastName = lName;
   4:  Person.prototype.Name = (function () { return this.firstName + " " + this.lastName });
   5:  }
   6:   
   7:  function Developer(lang, fName, lName) {
   8:   
   9:  function IDeveloper() {
  10:  }
  11:   
  12:  IDeveloper.prototype = new Person(fName,lName);
  13:  var that = new IDeveloper();
  14:  that.language = lang;
  15:  that.constructor = Developer;
  16:  return that;
  17:  }
  18:   
  19:  var d = new Developer("C#", "Sergiy", "Baydachnyy");

 

Этот код характерен тем, что не позволяет расширять прототипы за пределами конструктора, так как каждый раз создается новый объект типа IDeveloper, но с точки зрения ООП выглядит интересно. Наверняка его можно и улучшить.

Written by Sergiy Baydachnyy

29.08.2011 at 18:24

Опубликовано в Internet Explorer 10, Internet Explorer 9, JavaScript

Tagged with

Зарисовки по JavaScript для .NET разработчиков: определение типа, undefined и неявное приведение

leave a comment »

Из предыдущих статей Вы уже убедились, что для работы с переменными в JavaScript вовсе необязательно знать тип переменной. Достаточно объявить саму переменную с помощью ключевого слова var, а затем присвоить ей любой объект (включая функцию). Причем повторное присваивание можно выполнить в любом месте кода (естественно в области видимости), присвоив совершенно другой объект. Однако, часто возникает необходимость узнать тип переменной (объекта, на который ссылается переменная).

Если Вы посмотрите синтаксис JavaScript, то сможете увидеть возможность определения типа с помощью typeof. Реализовав небольшой пример можно прийти к выводу, что оно работает:

 

   1:  var n = 3;
   2:  var s = "Hello"
   3:   
   4:  //вернет number
   5:  alert(typeof n);
   6:   
   7:  //вернет string
   8:  alert(typeof s);

 

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

 

   1:  function Person() {
   2:     this.firstName = "Sergey";
   3:     this.lastName = "Baydachnyy";
   4:  };
   5:   
   6:  var obj = new Person();
   7:   
   8:  //вернет object
   9:  alert(typeof obj);

 

Сказать, что механизм отработал неправильно – нельзя (таки object), но нам от этого как-то не очень хорошо.

Поэтому, чтобы определить точный тип объекта, используется специальное свойство – constructor. Это свойство, в отличие от prototype, доступно через ссылку на наш объект, и содержит ссылку на объект-конструктор. Таким образом, можно использовать код ниже:

 

   1:  //возвращает true
   2:  alert(obj.constructor==Person);

 

Вместо constructor можно использовать оператор instanceof, который позволит определить, является ли объект экземпляром заданного типа, но это работает лишь тогда, когда у Вас нет сложной иерархии наследования. Так код ниже напечатает два сообщения TRUE, так как наш объект является как объектом типа Person так и объектом типа Object:

 

   1:  function Person() {
   2:     this.firstName = "Sergey";
   3:     this.lastName = "Baydachnyy";
   4:     this.Name = (function () { return this.firstName + " " + this.lastName; });
   5:  };
   6:   
   7:  Person.prototype.testF = (function () { return "testF"; });
   8:   
   9:  var obj = new Person();
  10:   
  11:  if (obj instanceof Object) {
  12:     alert("TRUE");
  13:  }
  14:   
  15:  if (obj instanceof Person) {
  16:     alert("TRUE");
  17:  }

 

Кроме типа объекта, очень часто необходимо выделить его свойства и методы. Чтобы перечислить все свойства и методы, доступные в объекте, достаточно использовать индексатор:

 

   1:  function Person() {
   2:     this.firstName = "Sergey";
   3:     this.lastName = "Baydachnyy";
   4:     this.Name = (function () { return this.firstName + " " + this.lastName; });
   5:  };
   6:   
   7:  Person.prototype.testF = (function () { return "testF"; });
   8:   
   9:  var obj = new Person();
  10:   
  11:  for (var i in obj) {
  12:     alert(obj[i]);
  13:  }

 

Код выше вернет два поля с данными и ссылки на два метода, один из которых метод нашего объекта, а второй – метод объекта-прототипа. Чтобы этого избежать и получить только объектные методы, используется специальная функция hasOwnProperty:

 

   1:  function Person() {
   2:     this.firstName = "Sergey";
   3:     this.lastName = "Baydachnyy";
   4:     this.Name = (function () { return this.firstName + " " + this.lastName; });
   5:  };
   6:   
   7:  Person.prototype.testF = (function () { return "testF"; });
   8:   
   9:  var obj = new Person();
  10:   
  11:  for (var i in obj) {
  12:     if (obj.hasOwnProperty(i)&&(typeof obj[i]=="function")) {
  13:        alert(obj[i]);
  14:     }
  15:  }

 

В коде выше мы не только выдаем только методы нашего объекта, исключив свойства и методы прототипа.

Поговорив о типе объектов, хотелось бы остановиться на вопросах преобразования типов. Дело в том, что JavaScript пытается выполнить неявное приведение типов везде, где это возможно. В том же C# конструкция типа int i=0; if (i) {…} являлась бы ошибочной, так как переменная типа int не может быть преобразована к типу bool. В JavaScript же реализовано множество неявных преобразований. Поэтому, чтобы избежать возможной неоднозначности, тут используют сразу два оператора сравнений, это == и === (аналогично != и !==). Первый оператор позволяет выполнить неявное преобразование, а второй – нет. Поэтому, всем разработчикам на .NET рекомендую использовать === и !==, чтобы не получить ошибки, которые в .NET были бы невозможны.

Напоследок отмечу, что если вы пытаетесь получить доступ к свойству или методу, которые не определены (или были удалены) в объекте, то получаете специальное значение undefined. Поэтому, если Вы хотите узнать, существует ли метод или свойство внутри объекта, используйте сравнение с этим значением:

 

   1:  if (obj.val === undefined) {
   2:     alert("Undefined");
   3:  }

Written by Sergiy Baydachnyy

29.08.2011 at 07:24

Опубликовано в JavaScript

Tagged with

Зарисовки по JavaScript для .NET разработчиков: Любовь к JSON

with one comment

Не знаю как Вам, но мне приходилось писать множество веб-служб для внутреннего использования другими частями приложения. И всегда, когда приходилось принимать решение о том, в каком виде возвращать данные, я выбирал XML. Что может быть лучше супер-универсального протокола SOAP, получившего значительное расширения в WCF? И что может быть проще сериализации и десериализации на основе XML в .NET Framework?

Вот только нужна ли универсальность SOAP и избыточность XML при реализации внутренних служб. Ведь формат передаваемых данных четко специфицирован. Кроме того, что делать разработчику, который программирует клиент, используя JavaScript. Конечно, можно предложить ему набор оберток (сгенерированных внешней утилитой), выполняющих черновую работу или заставить писать клиент только на ASP.NET и использовать ScriptManager. Но есть и другой вариант, состоящий в поддержке Вашими службами протокола JSON.

Ранее, читая о поддержке JSON в ASP.NET или Bing службах, я не придавал этому особого значения, но когда я начал писать приложения на JavaScript, то тут же потерял желание обрабатывать XML.

Прежде чем перейти к описанию работы с JSON, посмотрим на еще один способ создания объекта в JavaScript, это литералы. Вот пример создания небольшого объекта:

 

   1:  var obj1 = {
   2:   
   3:  firstName: "Sergey",
   4:  lastName: "Baydachnyy",
   5:   
   6:  Name: (function () {
   7:  return this.firstName + " " + this.lastName;
   8:  })
   9:   
  10:  };
  11:   
  12:  alert(obj1.Name());

 

Тут мы добавили два поля с данными и один метод. Естественно, что сейчас нас интересуют только данные, но так Вы получите более полное представление о JavaScript. Создав объект таким образом, Вы можете без труда добавлять свойства и методы к этому объекту в любом месте когда (как я писал в первой статье).

Из кода видно, что литерал позволяет создавать объект с помощью литерала в формате имя поля : значение. А вот теперь вернемся к JSON или к JavaScript Object Notation. Уже по одному названию понятно, что данные с помощью JSON передаются в аналогичном формате и практически готовы для создания объекта. «Практически» вкралось не случайно, так как данные могут содержать дополнительную мета информацию и множество лишних кавычек (в формате JSON имя также заключается в кавычки, что не обязательно для литерала).

Чтобы подготовить данные к преобразованию в объект, достаточно использовать специальный объект JSON, содержащий два метода: parse и stringify. Первый метод преобразует JSON данные в объект, а второй – наоборот:

 

   1:  var jsonData = 
   2:     '{"firstName":"Sergiy","lastName":"Baydachnyy"}';
   3:   
   4:  var obj1 = JSON.parse(jsonData);
   5:   
   6:  alert(obj1.firstName);

 

Таким образом, обрабатывать JSON данные в JavaScript достаточно просто. Что касается объекта JSON, то сегодня он поддерживается всеми браузерами, включая Internet Explorer 9, поэтому различные альтернативы уже можно и не использовать (в сети есть масса примеров с eval и др. – не все они сейчас актуальны).

Written by Sergiy Baydachnyy

26.08.2011 at 16:57

Зарисовки по JavaScript для .NET разработчиков: Больше строгости

leave a comment »

В предыдущей статье я написал, почему меня заинтересовал JavaScript, а также постарался раскрыть объектно-ориентированную сторону этого языка. Сейчас мне бы хотелось немного углубиться в детали реализации и начну я с мелочей, которые могут привести к большому количеству ошибок.

Как я писал ранее, JavaScript позволяет объявить переменную с помощью ключевого слова var и область видимости переменной задается функцией или глобальным объектом. Но помимо переменных в JavaScript присутствуют свойства объектов. Чтобы объявить свойство, достаточно записать имя объекта (или использовать par1в функции-конструкторе), а затем, через точку, имя свойства, которому присвоить некоторое значение:

 

   1:  p.i=3;

 

Свойства можно объявить и при создании объекта с помощью литерала, а также расширяя прототип объекта. В конечном итоге это сейчас не важно. Сконцентрируемся на том, можно ли добавить свойство к глобальному объекту? Можно, и для этого можно использовать слово this в контексте глобального объекта, объект window или же просто записать имя свойства и присвоить ему значение:

 

   1:  par1 = 5;
   2:   
   3:  alert(window.par1);

 

Код выше замечательно работает в любом месте приложения, объявляя свойство глобального объекта par1, а затем печатая его содержимое на экран.

А теперь представим себе код, в котором мы объявляем переменную с некоторым именем, а ниже, меняем значение этой переменной, но допускаем опечатку в имени (например, я все время вместо window пишу windows):

 

(function test() {

var val1 = 10;

. . . . .

vall1 = 11;

})();

 

В данном коде не произойдет никакой ошибки. По мнению JavaScript мы определяем переменную val1, а затем определяем свойство глобального объекта vall1. Как долго Вы будете искать подобные ошибки, и нужна ли Вам такая не строгость при объявлении переменных и свойств? Возможно, стоило бы исключить подобные неоднозначности из языка. Так вот, при разработке версии ECMA Script 5 об этом подумали и решили ввести специальную возможность, исключающую неоднозначности и старые возможности языка из Вашего кода – это специальный режим strict. В будущем планируется, что подобный режим будет работать по умолчанию, но поскольку реализаций ECMA Script 5 сейчас практически нигде нет, а старых библиотек громадное количество, Вы сами можете решать, где включать strict режим, а где нет.

Процедура следующая: при разработке собственного кода, в области видимости Ваших функций, вы указываете текстовую строку use strict”;, которая включает strict режим в тех браузерах, где он поддерживается. В остальных браузерах JavaScript сталкивается со строковым литералом, который не приносит особого вреда. На следующем этапе Вы тестируете Ваше приложение в браузерах, поддерживающих strict, и избавляетесь от «плохого» кода (поскольку получаете ошибки во время выполнения там, где раньше все хорошо работало в условиях неоднозначности). Таким образом, если мы изменим код нашей функции, используя strict, то должны получить сообщение об ошибке:

 

   1:  (function test() {
   2:   
   3:  "use strict";
   4:   
   5:  var val1 = 10;
   6:   
   7:  . . . . .
   8:   
   9:  vall1 = 11;
  10:   
  11:  })();

 

Осталось найти браузер, который уже поддерживает какие-то возможности ECMA Script 5. Поскольку во время написания этого поста я ехал в поезде, то пришлось пробовать те браузеры, которые у меня стояли на машине: Internet Explorer 9, Opera 11.11, Internet Explorer 10 Platform Preview. (Opera у меня стоит только для демонстрации своей несостоятельности). Конечно, я бросился проверять данный код в Opera. И был удивлен тем, что там он замечательно работает и не выдает ошибку. Аналогичная реакция была и в IE 9. Но вот Internet Explorer 10 меня порадовал:

 

image

 

Вот она ошибочка. И время на ее поиски совсем не потрачено.

И стало мне хорошо и приятно за наш Internet Explorer 10.

Но вернемся к JavaScript. Существует еще один механизм определения «плохого» кода. Это специальная утилита, которую можно найти по адресу http://jslint.com/. Если наш код скопировать в окно на этом сайте, то мы получим аналогичную ошибку. Именно поэтому, тестирование кода в режиме strict, а также использование утилиты JSLint, позволит Вам писать хороший код без ошибок.

Конечно, копировать Ваш код из окна Visual Studio дело неблагодарное (учитывая поток изменений), поэтому рекомендую всем скачать соответствующий плагин для Visual Studio 2010 с сайта http://jslint4vs2010.codeplex.com/. И будет Вам счастье:

 

image

Written by Sergiy Baydachnyy

26.08.2011 at 14:05

Опубликовано в Internet Explorer 10, JavaScript

Tagged with ,

Зарисовки по JavaScript для .NET разработчиков: Объектно-ориентированное программирование

4 комментария

Немного мыслей

Недавно Виктор Шатохин пообещал много денег за .NET разработчика, знающего JavaScript. Я мысленно стал перебирать всех знакомых разработчиков с хорошим знанием .NET, и пришел к выводу, что они не соответствуют второму критерию (JavaScript). Не любим мы это дело. Язык кажется для нас примитивным и не стоящим внимания настоящего ООП программиста. А если добавить отсутствие хороших редакторов, проблемы с несовместимостью возможностей различных окружений JavaScript и невозможность защитить свою интеллектуальную собственность (ведь Ваш код легко можно скопировать), то тут же хочется плеваться от отвращения.

На самом деле многие тезисы, которые я изложил выше, могут быть и не верны, но именно такое понимание обычно формируется у .NET разработчиков. И это понимание я неоднократно высказывал. А ведь программировать на JavaScript мне практически и не довелось (как и многим из нас). Самое интересное то, что когда я высказывал свое мнение по этому поводу, никто даже не пытался мне сказать что-то противоположное. А на вопрос «кто любит JavaScript» обычно поднималось одна-две руки из зала.

Откуда же берется понимание о «недостойности» JavaScript? Я попробовал проанализировать эту проблему и пришел к выводу, что мнение формируется только на основе того кода, который мы видим на существующих сайтах. Кода, который, может быть и не всегда, написан верно. Я даже возьму на себя смелость заявить, что в большинстве случаев – совсем не верно. Но этот код формирует мнение.

А ведь я уже попадал в такую ловушку, еще во время моей учебы в университете, на факультете была очень популярна среда программирования Delphi. Используя эту среду, приложения мог разрабатывать любой (извините) идиот. Ведь в те далекие времена Visual Studio не предлагал визуальную среду разработки, а .NET Framework еще не существовал. Поэтому Delphi был отличной альтернативой для студентов, не сумевших освоить ООП, но владеющий мышкой. Приложения, которые сдавались, удивляли своей «правильностью». Данные часто хранились прямо в элементах управления, а о каких-то подходах ООП нельзя было и говорить. Естественно, в те далекие времена я писал код на С++, а Delphi вызывала лишь улыбку. Немного позже я узнал, что на Delphi можно писать отличные приложения и имеется множество коммерческих продуктов. Вот только сначала нужно получить знания по продукту, а потом приступать к разработке.

Именно поэтому в JavaScript я решил разобраться более детально, и был сильно удивлен. Так, еще три дня назад, я был уверен в том, что этот язык не заслуживает моего внимания, а сегодня я уверен в противоположном. JavaScript можно использовать для написания серьезных приложений. Другое дело, что для формирования альтернативного мнения мне пришлось прочитать четыре различных книги, посвященных JavaScript. Попутно я обнаружил, что ни одна книга не описывает все возможности языка, а иногда та или иная книга представляет какой-то из шаблонов (не всегда хороший), как единственно возможный. Но, пользуясь тем, что я все же ООП программист, мне удалось понять JavaScript настолько, чтобы сделать вывод: JavaScript – объектно-ориентированный язык программирования.

Мне еще предстоит разобраться с взаимодействием JavaScript с тем или иным окружение, с возможностями различных библиотек, а также определить список того, что из ECMA 5 уже поддерживается в IE9/10 и других браузерах, но это уже технические детали, а ниже я постараюсь изложить свою позицию, которая подтолкнула меня к изучению дополнительного материала по этому языку.

Отступление. Я вовсе не собираюсь претендовать на позицию, о которой говорил Виктор и имею другие побуждения в основе своих действий. Поэтому, если Вы тот самый разработчик, то пишите ВикторуJ

Зачем

Прежде чем перейти к ООП возможностям JavaScript давайте ответим на вопрос «Зачем?». Ведь сегодня JavaScript преимущественно используется для клиентских сценариев, где окружением выступает браузер. На самом деле JavaScript не зависит от платформы и не привязан к окружению. Иными словами, окружение «браузер» может заменять любое другое окружение, например «веб-сервер» или «операционная система». И сегодня уже есть множество разработок, позволяющих запускать JavaScript на серверной стороне. В то же время отсутствие привязки JavaScript к определенному производителю средств разработки, делает этот язык практически неуязвимым, ведь стратегия у отдельной компании могут меняться, а, следовательно, меняться и технологии.

Если же говорить о сегодняшних .NET и JavaScript разработчиках, то последних вряд ли можно убедить перейти на .NET, да и хлопотно это (много нового нужно учить). А вот .NET разработчику можно без труда освоить JavaScript, зная идеологию ООП. При этом у .NET разработчика больше шансов использовать «правильный» JavaScript, так как способ мышления давно уже сложился.

Этим я никоим образом не хочу обидеть JavaScript разработчиков и хочу отметить, что некоторые вещи (верстка, подходы при дизайне веб-интерфейсов и др.) .NET разработчикам не под силу. Но при разработке сложных приложений ООП разработчик имеет преимущество. А если к знаниям .NET прибавить и знание JavaScript, то это только повышает стоимость (с точки зрения зарплаты) такого специалиста. Поэтому ответ на вопрос «Зачем?» — больше возможностей, а, следовательно, большая заработная плата. (Если бы я знал такого разработчика, то уже бы смог заработатьJ)

Именно поэтому я решил окунуться в JavaScript и взглянуть на него с позиции .NET разработчика, чтобы попытаться понять, правильно ли я размышляю, так как совсем недавно я был не прав (опять же по моему мнению).

Переменные и функции

Изучая JavaScript я начал вспоминать С++, где вполне уживались классы, объекты этих классов и глобальные функции с переменными. В JavaScript похожая история, так тут имеют место глобальные переменные и функции. Причем код такого типа очень распространен в Веб (по крайней мере, на украинских веб-сайтах). Вот Вам и создание переменной, и определение функции, и вызов функции:

 

   1:  var val1 = "Hello";
   2:   
   3:  function SayHello() {
   4:     alert(val1);
   5:  }
   6:   
   7:  SayHello();

 

Какая гадость.

На самом деле, если мы рассматриваем даже такой примитивный пример, то уже тут мы работаем сразу с двумя объектами, это функция, а также глобальный объект. Последний служит пристанищем для всех глобальных переменных и функций. При этом (по крайней мере в браузере), доступ к этому глобальному объекту можно получить, используя ссылку window:

 

   1:  var val1 = "Hello";
   2:   
   3:  function SayHello() {
   4:     alert(window.val1);
   5:     //или
   6:     alert(window["val1"]);
   7:  }
   8:   
   9:  SayHello();

 

Обратите внимание, что выше я не очепятался, функция также является объектом. Причем именно на этом факте строится все ООП в JavaScript. Более того, в JavaScript все, кроме чисел, строк, логического типа (но для них есть обертки) является объектами.

Но вернемся к переменным, так как правила определения переменных нам понадобятся ниже. Как видно из кода выше, переменные определяются с помощью ключевого слова var. Если Вы определяете переменную внутри функции, то у нее область видимости вся функция, в противном случае это глобальная область видимости. Обращаю Ваше внимание на то, что если Вы что-то определяете без var, то оно попадает в глобальную область. Я не случайно написал «что-то» и «оно», поскольку в данном случае Вы объявили бы не переменную, а свойство – свойство глобального объекта, о чем мы поговорим в следующем блоке.

Нужно обратить внимание на то, что в JavaScript нет области видимости внутри блока, то есть переменную можно использовать внутри всей функции сразу после инициализации, независимо от блоков. Дурацкий пример, но работает:

 

   1:  function SayHello(i) {
   2:   
   3:  if (i>3)
   4:  {
   5:     var sHello="Hello";
   6:  }
   7:  alert(sHello);
   8:  }
   9:   
  10:  SayHello(4);

 

Переменной легко можно присваивать ссылку на функцию и использовать ее в дальнейшем:

 

   1:  var f1 = (function SayHello(i) {
   2:     if (i > 3) {
   3:     var sHello = "Hello";
   4:     }
   5:     alert(sHello);
   6:  });
   7:   
   8:  //или
   9:   
  10:  var f2 = f1;
  11:   
  12:  f1(4);
  13:  f2(5);

 

Следовательно, функции можно легко передавать как параметры другим функциям.

Наконец, если говорить о параметрах, то параметры никто не проверяет. Вы можете передавать в функцию любое количество параметров, а затем получать доступ к массиву их параметров, определяя их типы и количество. Таким образом, Вы можете создать «эмуляцию» перегрузки функций, определив несколько реализаций с разным количеством параметров, внутри одной функции.

Наконец, если у Вас есть вложенная функция, то она может легко использовать переменные и параметры родительской функции. Подобная функциональность называется замыканием и нам она очень пригодится при реализации некоторых механизмов ООП. Вот пример:

 

   1:  function generate(initialVal) {
   2:     return (function (mulVal) {
   3:        return initialVal * mulVal;
   4:     });
   5:  }
   6:   
   7:  var res = generate(2);
   8:  alert(res(2)); //4
   9:  alert(res(4)); //8

 

Последний пример немного интереснее? Как Вы видите, мы возвращаем ссылку на вложенную функцию, которая использует параметр, передаваемый родительской функции. Дальше мы рассмотрим еще несколько примеров и все станет ясно.

Рассмотрев несколько примеров с функциями, перейдем к ООП.

Создание объектов и инкапсуляция

Основными понятиями в ООП являются классы и объекты. Так вот, в JavaScript классов нет. Вы конструируете готовые объекты. Сказать, что это не правильно я не могу, так как в жизни именно так все и происходит: «Вы конструируете объект, а иногда прототип, из составных частей». Просто так, по описанию, ничего не происходит. Это только Бог сотворил Землю из хаоса. Видимо он использовал С++. Ну а мы, смертные,…

Так вот, чтобы создать примитивный (почти пустой) объект, можно воспользоваться следующим синтаксисом:

 

   1:  var obj1 = {};
   2:   
   3:  //или
   4:   
   5:  var obj2 = new Object();

 

Далее объект можно наполнить содержимым (функциями и свойствами). Это можно сделать в любой момент времени, расширяя объект по своему усмотрению:

 

   1:  var obj1 = {};
   2:   
   3:  obj1.val1 = "Hello";
   4:   
   5:  obj1.SayHello = (function () { return this.val1; });
   6:   
   7:  alert(obj1.SayHello());

 

Обратите внимание, что val1 является свойством объекта (данными). При этом, чтобы получить доступ к этим данным из функции объекта, необходимо использовать ключевое слово this, которое указывает на контекст объекта. Если не задать контекст, то свойство будет добавлено к глобальному объекту (это также произойдет, если использовать this в глобальной функции).

Добавив свойство или функцию (теперь уже метод) к объекту, их можно удалить в любой момент:

 

   1:  delete obj1.val1;
   2:   
   3:  delete obj1.SayHello;

 

Причем это также логично. Представьте себе, что Вы создали класс «Человек», наклепали конкретные объекты, а потом появилось устройство «мобильный телефон» и Вы хотите его добавить в класс. А ведь уже поздно, поезд уехал. Между прочим завтра, может понадобится убрать свойство «мобильный телефон» и сразу вживить чип в голову (то есть изрядно покопаться в объектах). В С++ Вы можете пытаться сделать универсальный мега-класс, предусматривающий все возможности. Но последнее (предусмотреть) не всегда получается. А в JavaScript раз и готово.

Все это здорово до тех пор, пока у нас создаются разные объекты. А что же делать, если мы решили заселить Землю людьми. Нужно ли нам дублировать код каждый раз? И вот тут на помощь приходят функции. Как я писал выше, функции также представляют собой объекты. Вот пример функции, к которой мы добавили свойство и другую функцию:

 

   1:  function testF() {
   2:   
   3:  }
   4:   
   5:  testF.val1 = "Hello";
   6:   
   7:  testF.SayHello = (function () { return "Hello" });
   8:   
   9:  alert(testF.SayHello());

 

Особенность функции состоит в том, что к ней можно применить оператор new, который позволит создать новый объект по образу и подобию функции. Таким образом получается, что если мы определим функцию Human и наполним ее кодом, добавляющим к новому объекту данные и другие функции (методы), то получим функцию-конструктор. Вот оно, ООП!!! Рассмотрим пример:

 

   1:  function Human(firstName, lastName) {
   2:   
   3:     this.getFirstName = (function () { return firstName; });
   4:     this.getLastName = (function () { return lastName; });
   5:   
   6:     this.Age = 33;
   7:   
   8:     this.Name = (function () { return this.getFirstName() + " " + this.getLastName(); });
   9:   
  10:     Human.CopyHuman = (function (h) { return new Human(h.getFirstName(), h.getLastName()); });
  11:  }
  12:   
  13:  var h = new Human("Sergiy", "Baydachnyy");
  14:   
  15:  alert(h.Name());
  16:   
  17:  var hCopy = Human.CopyHuman(h);
  18:   
  19:  alert(hCopy.Name());

 

Ну посмотрите, чем не класс (особенно для C# разработчиков, где весь код находится внутри класса)? Тут мы с Вами объявили функцию Human, где, использовав замыкание, создали два метода для чтения переменных, передаваемых функции при инициализации объекта (О!), затем объявили поле с данным, почему-то присвоив ему значение 33, создали еще один метод, который использовал уже созданные нами функции. В завершении всего, мы создали «статическую» функцию, которая предназначена для копирования объектов и может быть доступна через имя нашей функции-конструктора («класса»).

При этом к созданным объектам можно добавить новые данные и методы (или удалить существующие).

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

На самом деле вариант выше не идеален. Дело в том, что используя this, мы добавили все наши методы к каждому объекту, сожрав какое-то количество памяти. В JavaScript можно поступить более экономно, используя специальное свойство prototype. Последнее является ссылкой на объект-прототип для всех порожденных объектов типа Human и позволяет сделать наши функции общими для всех объектов:

 

   1:  function Human(firstName, lastName) {
   2:     this.getFirstName = (function () { return firstName; });
   3:     this.getLastName = (function () { return lastName; });
   4:   
   5:     Human.prototype.Age = 33;
   6:   
   7:     Human.prototype.Name = (function () { return this.getFirstName() + " " + this.getLastName(); });
   8:   
   9:     Human.CopyHuman = (function (h) { return new Human(h.getFirstName(), h.getLastName()); });
  10:  }

 

В данном случае, если Вы обращаетесь к функции Name, то JavaScript пытается найти ее в объекте, а если ее там нет, то пробегается по цепочке прототипов. Но в эти игры нужно очень осторожно играться с замыканием, где каждая функция замкнута на данные конкретного объекта. Поэтому прототип я использовал только для Name.

Прототип можно использовать, чтобы добавить функции после определения конструктора. Фактически расширить все объекты, порождаемые от прототипа, можно в любом месте приложения. Например, ввело правительство Украины идентификационный код, а мы его раз и ко всем объектам одной строкой:

Human.prototype.inn = 0;

Хочешь узнать реальный код, приходи в налоговую и получи справкуJ Все как в реальной жизни! А С++ так не может.

Многие разработчики определяют функцию-конструктор пустым, а затем записывают код, который добавляет методы к прототипу. Я не считаю такой подход правильным, так как он выпадает из привычного мне ООП стиля (все определяем в классе). Код получается разбросанный по разным местам, нельзя использовать замыкание (private свойства в классе), а также могут возникнуть проблемы с наследованием. Но JavaScript позволяет все. Тут скорее язык можно упрекнуть за излишние возможности, но не за недостаток.

Наследование и полиморфизм

Перейдем теперь к вопросам наследования и полиморфизма.

Поскольку в JavaScript нет четкой типизации ссылок, то язык по умолчанию поддерживает полиморфизм (чем-то похоже на Java). А вот наследование, это отдельная история. Наследование не просто присутствует в JavaScript, но и может быть реализовано несколькими способами (молчу про версию ECMA 5, где есть специальные методы в Object).

Первый способ работает, если в базовом классе все данные и методы добавлены через ключевое слово this (без прототипа). В этом случае в производном классе можно вызвать конструктор базового (именно вызвать), используя один из двух специальных методов call или apply (они отличаются лишь набором параметров). Особенностью этих методов есть то, что они позволяют передать конструктору базового класса, контекст объекта, создаваемого от производного, что позволит тут же создать все методы и данные базового конструктора в производном объекте:

 

   1:  function Human(firstName, lastName) {
   2:     this.getFirstName = (function () { return firstName; });
   3:     this.getLastName = (function () { return lastName; });
   4:     
   5:     this.Age = 33;
   6:   
   7:     this.Name = (function () { return this.getFirstName() + " " + this.getLastName(); });
   8:   
   9:     Human.CopyHuman = (function (h) { return new Human(h.getFirstName(), h.getLastName()); });
  10:  }
  11:   
  12:  function Women(firstName, lastName, colorOfHead) {
  13:     Human.call(this, firstName, lastName);
  14:   
  15:     this.color = colorOfHead;
  16:  }
  17:   
  18:  var w = new Women("Jessica", "Alba", "Brune");
  19:   
  20:  alert(w.Name());

 

Именно подход выше соответствует стандартной практике наследования в С++ или С#.

Еще один метод, это использование свойства прототип производного класса. В данном подходе свойству prototype производного «класса» присваивают объект базового «класса». Но это необходимо делать уже после описания всей функции-конструктора, что означает невозможность использовать методы базового «класса» внутри производного. Последнее требует создание пустого конструктора с определением данных и методов вне конструктора (где-то там в коде, что я не люблю).

Заключение

Поскольку я пишу уже девятую страницу текста, то пора закругляться. Поэтому я не буду показывать все подходы. Если будет время, то в других постах я постараюсь освятить каждую тему (и другие) более детально. Целью же этого поста было желание дать Вам понять, что JavaScript является полноценным ООП языком и при этом очень интересным. Обратите на него внимание. Причем начинайте с самого языка, а уже потом с каких-то библиотек (типа JQuery), которые привязаны к каким-то окружениям.

Рекомендуемая литература:

Стоян Стефанов – JavaScript. Шаблоны

Дэвид Флэнаган – JavaScript. Подробное руководство.

Джон Рейсинг – JavaScript: Профессиональные приемы программирования

Written by Sergiy Baydachnyy

26.08.2011 at 08:32

Опубликовано в .NET Development, JavaScript

Tagged with