Записки на лету

All posts in Статьи

Меня уже столько раз просили добавить скрипт на форму для запуска диалога или отчета с кнопки, что я решил разобраться с этой проблемой раз и навсегда.

Идея проста, написать общий скрипт для запуска диалога/отчета и дальше работать уже с только с CRM 2011 Visual Ribbon Editor, о котором я говорил ранее.

Итак, вот скрипт, в нем две функции, RunDialog() для запуска диалога и RunReport() для запуска отчета.

function RunDialog(dialogId, typeName, recordId) {
 /// <summary>Запускает диалог</summary>
 /// <param name="dialogId" type="String">GUID диалога (без скобок).</param>
 /// <param name="typeName" type="String">Имя сущности.</param>
 /// <param name="recordId" type="String">GUID сущности (без скобок).</param>
 var link = Mscrm.CrmUri.create("/cs/dialog/rundialog.aspx") + "?DialogId={" + dialogId + "}&EntityName=" + typeName + "&ObjectId=" + recordId;
 openStdDlg(link, null, 615, 480, false, false, null);
 window.location.reload(true);
 }
function RunReport(rdlName, reportGuid, entityGuid, entityType) {
 /// <summary>Determines the area of a circle that has the specified radius parameter.</summary>
 /// <param name="rdlName" type="String">Имя .rdl файла с отчетом.</param>
 /// <param name="reportGuid" type="String">GUID отчета (без скобок).</param>
 /// <param name="entityGuid" type="String">GUID сущности (без скобок).</param>
 /// <param name="entityType" type="String">Код сущности (без скобок).</param>
 var link = Mscrm.CrmUri.create("/crmreports/viewer/viewer.aspx") + "?action=run&context=records&helpID=" + rdlName + "&id={" + reportGuid + "}&records={" + entityGuid + "}&recordstype=" + entityType;
 openStdDlg(link, null, 800, 600, true, false, null);
 }

 

Сохраняем скрипт в CRM как веб-ресурс с названием new_ribbon, сохраняем и публикуем.

Предполагается, что у нас уже есть диалог и он активирован. Нам понадобится его Guid.

В моем случае мы добавляем кнопку на форму сущности Обращение (Incident).

Теперь запускаем Ribbon Editor и добавляем необходимую нам кнопку.

Переходим в раздел Action и жмем Add… -> JavaScriptFunction для добавления функции:

Вводим Function Name: RunDialog и Library: $webresource:new_ribbon
Теперь напротив только, что добавленной функции еще раз жмем Add… и добавляем следующие параметры:

  1. String Parameter AD119AC4-C589-4746-8AA2-AA74398B6C98
  2. String Parameter incident
  3. Crm Parameter -> FirstPrimaryItemId

Где 1 — Guid диалога, 2 — Имя схемы сущности и 3 — параметр, который является Guid-ом сущности с формы которой мы запускаем диалог.

Теперь жмем Save и, если мы все сделали правильно, наша кнопка будет вызывать диалог.
С отчетами все так же по аналогии.
И, что самое главное, теперь нам не надо никуда добавлять скрипты, на одну организацию достаточно одного скрипта. А это значит, что теперь этим могут заниматься Аналитики/Консультанты. 🙂


Если Вы заинтересованы в релизазах новых версий MS Dynamics CRM 2011, то я нашел способ подписаться на программу бета тестирования. Это даст доступ к превью 12-го накопительного пакета обновления, учебным видео, документации и т.д.

Для регистрации идем по ссылке  https://connect.microsoft.com/site687/InvitationUse.aspx?ProgramID=7905&InvitationID=pob-WJMP-TW3W на сайт Microsoft Connect. Если Вы еще не зарегистрированы на сайте, то это придется сделать.

После регистрации мы попадаем на главную страницу, где видим ссылку на на обязательный опрос «Microsoft Dynamics CRM Polaris and Orion Beta SLT and Registration Survey», который надо пройти чтобы получить доступ к бете.

После заполнения опроса жмем на Return to the connection home page.

На следующей странице жмем Downloads слева.

И, наконец, мы попадаем на страницу со ссылками на нужную нам бету.

Удачи!


На днях попалась мне в руки официальное учебное пособие по разработке для MS Dynamics CRM 2011.

На сами курсы я не ходил но увидев у знакомого данную книгу сразу же выклянчил, т.к. получается, что это единственная книга по разработке для MS Dynamics CRM 2011, на русском уж точно. На английском, я думаю тоже, ибо все книжки по CRM, которые я читал или видел конкретно разработки не касаются вовсе или делают это вскользь.

Для проверки предлагаю ответить на вопросы предоставленные в конце каждой главы пособия.

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

Оказалось, что на русском я даже некоторые вопросы понять не мог. 🙁

Например, вопрос на английском выглядит так:

A custom workflow activity is written and a workflow rule is configured to call the activity whenever an Accout is created. When will the code actually run?

На Русском же так:

При каждом создании организации создается настраиваемое действие бизнес-процесса и настраивается правило бизнес-процесса для вызова этого действия.
Когда реально будет выполняться соответствующий код?

Я минут пять вообще пытался понять о чем это они?!

Прямо хочется письмо в Microsoft написать и излить им все мое негодование. Люди по этим пособиям учатся!

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

Теперь про само пособие. Состоит оно из 8 глав. Пересказывать книгу я не буду, остановлюсь только на тех моментах, которые меня заинтересовали.

Внимание!!!

Ниже пока краткое содержание, которое можно найти почти на каждом сайте центра обучения просто для информации.

Позже я напишу свои замечания по каждой главе.

Глава 1: Общие сведения о расширении

В этом модуле слушатели знакомятся с некоторыми базовыми понятиями Microsoft Dynamics CRM 2011, включая краткое описание платформы и функций xRM Application Framework. В модуле содержится обзор модели безопасности, бизнес-логики, бизнес-сущностей и компонентов доступа к данным. В конце модуля приведен список полезных навыков и ресурсов, помогающих в процессе разработки.

Глава 2: Общие операции платформы

В этом модуле рассматривается использование некоторых общих методов для обращения к системным и настраиваемым сущностям в Microsoft Dynamics CRM 2011. Он включает подробное описание этих методов и доступных в системе типов данных. Кроме того, модуль содержит пример приложения, демонстрирующий создание и обновление сущности Microsoft Dynamics CRM 2011, и полезную информацию об обработке ошибок.

Глава 3: Запросы данных и выполнение операций

В это модуле описываются различные способы обращения к базе данных Microsoft Dynamics CRM, включая QueryAttribute, QueryByExpression, LINQ, FetchXML, фильтрованные представления и OData. Кроме того, в модуле описан метод Execute и использованиеzationRequeganizationResponse в Microsoft Dynamics CRM. Наконец, в нем показано, как для взаимодействия с метаданными Microsoft Dynamics CRM использовать службу организации.

Глава 4: Реализация бизнес-процессов

В этом модуле описывается, как разрабатывать настраиваемые действия бизнес-процессов для использования в Microsoft Dynamics CRM. Функции Microsoft Dynamics CRM можно расширить с помощью настраиваемых действий бизнес-процессов, которые запускают код при выполнении настроенных правил бизнес-процессов или диалогов. Кроме того, в этом модуле описывается процесс отладки кода настраиваемых действий бизнес-процессов, а также использование декларативных бизнес-процессов в локальных развертываниях.

Глава 5: Подключаемые модули

В этом модуле содержится обзор подключаемых модулей. Подключаемые модули — это мощный механизм расширения функциональности событий всех сущностей Microsoft Dynamics CRM 2011. В модуле рассматривается использование подключаемых модулей, описывается инфраструктура обработки событий и объясняется применение событий с точки зрения изоляции, доверия и получения статистических данных. Кроме того, в модуле показано, как разрабатывать подключаемые модули, использовать олицетворение и классы сущностей, а также как регистрировать, развертывать и отлаживать подключаемые модули. Наконец, в модуле приводится обзор интеграции Windows Azure с Microsoft Dynamics CRM 2011.

Глава 6: Событийно-ориентированное программирование приложений

В этом модуле описывается использование встроенных функций приложений Microsoft Dynamics CRM. Кроме того, в нем демонстрируется поведение приложения Microsoft Dynamics CRM при загрузке или сохранении форм, изменении данных полей или состояния вкладок, а также при загрузке интернет-кадров. В Microsoft Dynamics CRM код обработчика событий можно прикреплять к пяти событиям.

Глава 7: Расширения клиента

В этом модуле описано, как добавлять настраиваемые кнопки, элементы меню и области навигации, чтобы включать собственные решения в Microsoft Dynamics CRM. Кроме того, в нем рассматриваются изменение области навигации приложения и настройка карты сайта и ленты.

Глава 8: Веб-ресурсы

В этом модуле описывается добавление пользовательского веб-содержимого (например, HTML, изображений, Silverlight, JavaScript) в формы CRM, что является распространенным методом расширения функциональности CRM-системы. В нем также приводится несколько примеров использования клиентских технологий, в том числе добавление настраиваемой проверки, гибридных веб-приложений и добавление новых приложений (например, приложений Silverlight с отображением данных Microsoft Dynamics CRM). Кроме того, в модуле описывается использование веб-ресурсов для хранения общих блоков кода или общих ресурсов, чтобы их можно было многократно использовать в веб-приложении Microsoft Dynamics CRM.


«Не удается выполнить программу из освобожденного сценария» («Can’t execute code from a freed script»)

Встретился вот с такой проблемой при работе с диалогом.
А конкретнее при использовании диалогового окна ServiceAppointment, которое должно вызываться по кнопке с нескольких сущностей я не смог получить даты.
Оказалось, что ссылочные типы переданные из диалога нельзя прочесть после закрытия диалога. И что самое удивительное, данные в дебаггере видно.
Большинство советов типа поменять браузер (это же MS Dymamics CRM) или передавать строки из диалога не работают, т.к. я использую диалоговое окно самой CRM и ничего с ним сделать не могу.
Решение оказалось простое.

Object.prototype.toString.call()

В моем случае с датой будет

Data.prototype.toString.call()

Следующая функция создает заметку.
Входящие параметры:
entity — EntityReference сущности к которой привязывается записка
subject — Заголовок заметки
text — Текст заметки
Функция GetOrganizationService() возвращает объект работы с данными в CRM 2011, была написана ранее, использует FetchXml (см. пример здесь Execute Fetch from JavaScript in CRM 2011).

function _createAnnotation(entity, subject, text) {
var orgService = GetOrganizationService();
var annotation = {};
annotation.Name = "annotation";
annotation._properties = [];
annotation._propertyTypes = [];
annotation._properties['objectid'] = entity;
annotation._propertyTypes['objectid'] = 'lookup';
annotation._properties['subject'] = subject;
annotation._propertyTypes['subject'] = 'string';
annotation._properties['notetext'] = text;
annotation._propertyTypes['notetext'] = 'string';
annotation._properties['isdocument'] = 'false';
annotation._propertyTypes['isdocument'] = 'boolean';
annotation._properties['mimetype'] = 'text/html';
annotation._propertyTypes['mimetype'] = 'string';
orgService.Create(annotation);
}

Здравствуйте!
В данном посте я буду описывать маленькие программки, которые мне помогают и облегчают разрабатывать под MS Dynamics CRM 2011.

Рибоны (Ribbons)

Для работы с рибонами я использую CRM 2011 Visual Ribbon Editor, который я нашел на Codeplex где, кстати, много всего полезного найти можно.

Выглядит очень наглядно и удобно.

crmvisualribboneditor

Для работы с рибонами я также использую Ribbon Browser, он только показывает, но, работает быстро и показывает все, что есть на рибоне, что очень полезно если надо что-то хакнуть на стандартных кнопках, табах, групп и т.д.
Есть еще Ribbon Editor for Microsoft Dynamics CRM 2011 но он не визуальный и, к тому же, не совсем стабильно работает, что мне, конечно, не нравится.

Сайтмап (SiteMap)

Для работы с навигационной панелью я использую SiteMap Editor for Microsoft Dynamics CRM 2011.
SiteMap editor screenshot
Все просто понятно и удобно.


В этой статье я буду писать о своем опыте использования JavaScript в MS Dynamics CRM 2011.

Работа с полями

Во-первых, чтобы скрипт работал на форме его необходимо добавить в веб-ресурс и добавить этот веб-ресурс непосредственно на форму.

Получить значение текстового поля «telephone»

Xrm.Page.data.entity.attributes.get("telephone1").getValue();

Сохранить значение «123» в текстовое поле «telephone»

Xrm.Page.data.entity.attributes.get("telephone1").setValue("123");

Получить значение пиклиста «address1_addresstypecode»

Xrm.Page.data.entity.attributes.get("address1_addresstypecode").getValue(); //Цифровое значение
Xrm.Page.data.entity.attributes.get("address1_addresstypecode").getText(); //Текстовое значение

Имейте в виду, что если значение пиклиста не выбрано, то getValue()/getText() вернет «undefined».
Установить значение 2 пиклиста «address1_addresstypecode»

Xrm.Page.data.entity.attributes.get("address1_addresstypecode").setValue(2);

Установить значение поля с датой «birthdate» на сегодня

var today = new Date();
Xrm.Page.data.entity.attributes.get("birthdate").setValue(today);

Для выражения Xrm.Page.data.entity.attributes.get() есть сокращенный вариант Xrm.Page.getAttribute().
Если поле помечено на форме, что оно доступно только для чтения (ReadOnly) чтобы его изменить необходимо выставить ему SubmitMode = Always.

Xrm.Page.getAttribute("fieldName").setSubmitMode("always");

Отключить (Disable) поле «name»

Xrm.Page.ui.controls.get("name").setDisabled(true);

Скрыть (Hide) поле «name»

Xrm.Page.ui.controls.get("name").setVisible(false);

Скрыть (Hide) секцию в табе

Xrm.Page.ui.tabs.get("tabname").sections.get("sectionname").setVisible(false);

Работа с формой

Сохранить форму

Xrm.Page.data.entity.save();

Сохранить и закрыть форму

Xrm.Page.data.entity.save("saveandclose");

Остановить сохранение формы
Вешаемся на событие формы OnSave.
Обязательно надо поставить галочку напротив «Pass execution context as first parameter». Без этого не сработает.

function formOnSave(executionObj)
{
var shouldSave = true;
if (shouldSave) {
alert("Unable to save because of some reason or the other.");
executionObj.getEventArgs().preventDefault();
}
}

Закрыть форму
Пользователю будет показано напоминание если он не сохранил изменения.

Xrm.Page.ui.close();

Открыть форму создания
entityType — имя сущности (entity logical name).

Xrm.Utility.openEntityForm(entityType);

Открыть форму создания с заполненными полями
parameters — объект с параметрами,
entityType — имя сущности (entity logical name).

var entityType = "account";
var parameters = {};
// Заполняем лукап "accountid"
parameters["accountid"] = "b053a39a-041a-4356-acef-ddf00182762b";
parameters["accountidname"] = "Имя организации"
// Следующий параметр только для поля типа Клиент, где надо указать Контакт это или Организация
parameters["accountidtype"] = "account";
// Заполняем текстовое поле "name"
parameters["name"] = "Test";
// Заполняем пиклист "address1_addresstypecode"
parameters["address1_addresstypecode"] = "3";
// Заполняем булево поле Да/Нет  "donotemail"
parameters["donotemail"] = "1";
Xrm.Utility.openEntityForm(entityType, null, parameters);

Подробнее здесь Set Field Values Using Parameters Passed to a Form.
Открыть форму существующей сущности
id — GUID экземпляра сущности,
entityType — имя сущности (entity logical name).

Xrm.Utility.openEntityForm(entityType, id);

Определить тип формы
Значения тип форм (Form type codes):
Create — 1, Update — 2, Read Only — 3, Disabled — 4, Bulk Edit — 6.

Xrm.Page.ui.getFormType();

Получить GUID текущей записи

Xrm.Page.data.entity.getId();

Получить GUID текущего пользователя

Xrm.Page.context.getUserId();

Получить LCID текущего пользователя и организации

Xrm.Page.context.getUserLcid(); //For User
Xrm.Page.context.getOrgLcid(); //For Organization

Список всех LCID здесь.

Получить URL сервера CRM

Xrm.Page.context.getServerUrl();

С выходом 12-го роллапа функция устарела и теперь рекоммендуется делать так

Xrm.Page.context.getClientUrl();

а чтобы работало всегда то так

var ORG_URL = (typeof(Xrm.Page.context.getClientUrl) == "function") ? Xrm.Page.context.getClientUrl() : Xrm.Page.context.getServerUrl();

Получить имя организации

Xrm.Page.context.getOrgUniqueName();

Лукапы

Получить GUID значение лукапа «contactid»

Xrm.Page.data.entity.attributes.get("contactid").getValue()[0].id;

Получить текстовое значение лукапа «contactid»

Xrm.Page.data.entity.attributes.get("contactid").getValue()[0].name;

Функция, которая заполняет лукап
Параметры:
fieldName — название поля,
id — GUID экземпляра сущности,
name — название сущности, которое будет показано в лукапе,
entityType — имя сущности (entity logical name)

// Set the value of a lookup field
function SetLookupValue(fieldName, id, name, entityType) {
if (fieldName != null) {
var lookupValue = [];
lookupValue[0] = {};
lookupValue[0].id = id;
lookupValue[0].name = name;
lookupValue[0].entityType = entityType;
Xrm.Page.getAttribute(fieldName).setValue(lookupValue);
}
}

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

Тоже самое можно сделать с помощью JSON в одну строку, что выглядит, правда уже не так красиво.

Xrm.Page.data.entity.attributes.get(fieldName).setValue([{ id: id, name: name, entityType: entityType}]);

Открыть диалоговое окно
В CRM 2011 есть обертка для window.showModalDialog(), функция называется openStdDlg().

openStdDlg(sPath, oArgs, iWidth, iHeight, bResizable, bModeless, sCustomWinParams);

где:
sPath — ссылка на окно,
oArgs — объект с параметрами,
iWidth — ширина окна,
iHeight — высота окна,
bResizable — можно ли изменять размер окна,
bModeless — ,
sCustomWinParams — пользовательские параметры, передаваемые в окно.

Открыть форму сущности
Следующий пример открывает форму существующей сущности incident с GUID = 47c2bb7b-ba62-4afb-9b10-1a24c7a2799e в новом окне.

window.open(Xrm.Page.context.getServerUrl() +
"/main.aspx?etn=incident&pagetype=entityrecord&id=" +
encodeURIComponent("{47c2bb7b-ba62-4afb-9b10-1a24c7a2799e}"),
"_blank",
"location=no,menubar=no,status=no,toolbar=no",
false);

Обновить рибон
Обновляет только состояние Enable/Disable Rule;

Xrm.Page.ui.refreshRibbon();

Статья еще не закончена и, скорее всего, вряд ли будет, так как я буду ее пополнять по мере расширения моего собственного опыта.
Ждите обновлений…


Как оказалось, задача весьма проста и решается одной строкой.

WhoAmIResponse whoAmI = (WhoAmIResponse)Service.Execute(new WhoAmIRequest());

В результате у объекта whoAmI будет 3 св-ва типа Guid OrganizationId, BusinessUnitId и UserId.
Единственное, что может еще понадобиться это
using Microsoft.Crm.Sdk.Messages;


Правильное понимание false значений

В отличие от языка C# в JavaScript имеется много значений кроме false, которые также расцениваются как false. Они называются False-y values и очень важно их понимать.

Эти значения это:

  1. false
  2. null
  3. undefined
  4. «»
  5. 0
  6. NaN (not a number)

Все остальные значения, включая «0», «false», и много других странных комбинаций рассматриваются как true.

Устанавливайте значения по умолчанию правильно

Обычно мы проверяем переменную на null перед использованием во избежание исключения «Object reference not set to an instance of an object».

Следующий пример типичен для языка C#.

Никогда не делайте так в JavaScript.

 var someString = "";
 if (someString != null && someString.length > 0) {
 someString = "Default value";
 }

Да и в C# тоже так лучше не делать. Для этих целей есть специальная функция.

 var someString = "";
 if (!string.IsNullOrEmpty(someString)) {
 someString = "Default value";
 }

Учитывая false-y значения упомянутые выше, в JavaScript следует делать так:

 if (someString) {
 someString = "Default value";
 }

В данном случае выполняется проверка на значения undefined, null, и empty string.

Еще один вариант у разработчиков C# может выглядеть так:

someString = someString ? someString : "Default value";

Он чуть получше но, все равно не верен.

Некоторые могут попытаться сделать даже так:

someString = someString ?? "Default value";

Что не поддерживается в JavaScript вообще.

В JavaScript, как ни странно, это должно выглядеть так:

someString = someString || "Default value";

Вывод: Если нам надо проверить переменную является-ли она undefined, null и т.д. или имеет значение.

Все, что нам надо сделать это проверить саму переменную.

Правильно используйте операторы сравнения

Операторы == и != в языке JavaScript попытаются сначала привести сравниваемые значения к одному типу перед сравнением, что при ведет к неожиданным результатам.

Т.е. следующие выражения возвращают true

console.log(0 == '') //Returns true
console.log(0 == '0') //Returns true
console.log(false == '0') //Returns true
console.log(null == undefined) //Returns true
console.log(0 == ' \t\r\n') //Returns true

Используйте операторы === и !== т.к. они так же делают проверку и на тип.

Т.е. следующие выражения возвращают false:

console.log(0 === '') //Returns false
console.log(0 === '0') //Returns false
console.log(false === '0') //Returns false
console.log(null === undefined) //Returns false
console.log(0 === ' \t\r\n') //Returns false

Объявляйте объекты и массивы правильно

Рабочее, но неправильное объявление

var person = new Object(),
keys = new Array();

Ключевое слово new было добавлено в JavaScript, от части, чтобы походить на классические языки, но на самом деле программистов оно больше запутывает чем помогает. В JavaScript есть более соответствующие языку способы объявить объекты и массивы, которые и следует использовать.

var person = {},
keys = [];
var person = {
    firstName: "Vasily",
    lastName: "Pupkin",
    fullName: function () {
        return this.firstName + " " + this.lastName;
    }
},
keys = ["123", "676", "242"];

Объявляйте все Ваши переменные используя «literal notation» вместо оператора new.

Определенно не следует использовать оператор new с переменными типа Boolean, Number и String или с функциями.

Используйте оператор new только когда Вы создаете объект и Вы хотите, чтобы он использовал свой конструктор.

Использование циклов

Цикл for…in в JavaScript не гарантирует порядок полученных элементов, так же Вы можете встретить ряд других проблем.

Например, объявим массив.

var myArray = [], name;
myArray[5] = "test";
console.log(myArray.length);

покажет 6, что, в принципе, вполне предсказуемо.

Создадим цикл…

for(var i = 0, length = myArray.length; i < length; i++){
    console.log(i, myArray[i]);
}

Результатом цикла будет:

//0, undefined
//1, undefined
//2, undefined
//3, undefined
//4, undefined
//5, test

Может возникнуть, так же, другая проблема. Предположим, что кто-то или что-то, скажем какая-нибудь библиотека, которую мы подключили, изменила прототип массива следующим образом.

Array.prototype.someVariable = "Some Variable";
for(name in myArray){
    console.log(name, myArray[name]);
}

Результатом цикла будет:

//5, test
//someVariable, Some Variable

Вместо цикла for…in следует использовать for по крайней мере с массивами.

При использовании for…in с объектами желательно делать проверку на наличие свойства как показано ниже что бы исключить из результата неожиданные объекты наследованные от прототипа.

for(name in myArray){
    if(object.hasOwnProperty(name)){
        //Do something
    }
}

Вывод: Используйте цикл for для массивов и for…in с проверкой методом hasOwnProperty() для объектов.

Правильно используйте скобки

Нижеследующий код работает неправильно.

При клике мышкой на любую ссылку Вы получите «You’ve clicked 10»

var unorderedList = $("ul");
for(var i =0; i < 10; i++){
    $(""), {
        id: i,
        text: "Link " + i,
        click: function() {
        console.log("You've clicked " + i);
        }
    }).appendTo(unorderedList);
}

Исправленный код.

var unorderedList = $("ul"), i;
for(i = 0; i < 10; i++){
    $(""), {
        id: i,
        text: "Link " + i,
        click: function(index) {
            return function(){
                console.log("You've clicked " + index);
            }
        }
    }).appendTo(unorderedList);
}

Область видимости

Область видимости определяется не фигурными скобками а функцией.

Рассмотрим нижеследующий код.

function eat() {
    var food = "bacon",
    isHungry = true;
    if (isHungry) {
        var timeToWait = 10;
        alert("Waiting " + timeToWait + " minutes");
        chew();
    }
    function chew() {
        var bodyPart = "mouth";
    }
    alert("After waiting " + timeToWait + " minutes I am eating " +
    food + " with my " + bodyPart);
}

Разработчик C# посмотрев на код, представленный выше, скорее всего подумает, что переменная timeToWait доступна только в рамках выражения if (isHungry){}, но это не так.

Переменная timeToWait доступна везде внутри функции eat() что значит, что и функция chew() тоже имеет доступ к переменной.

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

Вы можете получить проблемы если в Вашем коде встретится похожая конструкция.

Подъем переменных

В JavaScript все переменные внутри функции находятся в одном поле видимости. Внутри функции JavaScript сначала ищет все переменные и поднимает их объявления в начало функции и инициализирует как undefined.

Присвоение Ваших значений остается там, где Вы это сделали в коде.

Это называется про поднятие переменных (variable hoisting).

Рассмотрим пример.

function sayHello(firstName, middleName, lastName) {
    var fullName = firstName + " " + middleName + " " + lastName;
    if (fullName === "Carlos Rey Norris") {
        var nickName = "Chuck";
        console.log("Hello " + nickName + " " + fullName.split(" ")[2]);
    }
}
sayHello("Carlos", "Rey", "Norris"); //Hello Chuck Norris

JavaScript «За кадром» берет переменные fullName и nickName и объявляет их в начале функции. Их присвоения остаются на своем месте, но их объявления переносятся.

Получается примерно так.

function sayHello(firstName, middleName, lastName) {
    //Hoists all the variable declarations to the top
    //of the function and sets to undefined
    var fullName = undefined,
    nickName = undefined;
    //fullName assignment remains in original position
    fullName = firstName + " " + middleName + " " + lastName;
    if (fullName === "Carlos Rey Norris") {
        //nickName assigned remains in original position
        nickName = "Chuck";
        console.log("Hello " + nickName + " " + fullName.split(" ")[2]);
    }
}
sayHello("Carlos", "Rey", "Norris"); //Hello Chuck Norris

Незнание этого может привести к ошибкам в таких случаях как следующий.

for(var i = 0; i < 10; ++i){
    for(var i = 0; i < 5; ++i){
        alert("Hello");
    }
}

Это будет бесконечный цикл.

Статья написана на основе статьи How Good C# Habits can Encourage Bad JavaScript Habits by Elijah Manor

Перевод и редакция Григорий Жуков