Каков порядок оценки области действия в ColdFusion при установке переменной? - PullRequest
8 голосов
/ 10 февраля 2012

Порядок оценки объема хорошо известен / задокументирован, когда использует переменные .Однако я не могу найти никакой информации о порядке оценки области при установке переменной.

Можно предположить, что это тот же список, но, как показано здесь, есть несколько предостережений:

<cfset qryChain = queryNew("id,next")>
<!--- add some data so the cfloop kicks in --->
<cfloop query="qryChain">
    <cfset Next = StructNew()>
    <cfset Next.id = qryChain.next>
</cfloop>

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

Поскольку cfset s находятся внутри цикла запроса, элемент 4 порядка оценки области должен использоваться для обоих.Вместо этого Next оценивается как Variables.Next (элемент 6), а затем Next.id оценивается как Variables.qryChain.next.id (элемент 4) и завершается неудачей.

Это где-нибудь задокументировано?Это просто пункты 1-6 из списка «использования» выше с несколькими оговорками?Являются ли эти предупреждения преднамеренными или ошибками?Какие еще есть предостережения?

Ответы [ 3 ]

5 голосов
/ 10 февраля 2012

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

В вашем первом примере:

<cfset qryChain = queryNew("id,next")>
    <!--- add some data so the cfloop kicks in --->
<cfloop query="qryChain">
    <cfset Next = StructNew()>
    <cfset Next.id = qryChain.next>
</cfloop>

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

Проблема в следующей строке. Когда вы пытаетесь получить доступ к переменной Next, чтобы установить в ней новый ключ, ColdFusion сначала находит переменную Next, которая существует в результате запроса, потому что во время зацикливания запроса область запросов (на самом деле не является областью, но работает как этот случай) имеет более высокий приоритет, чем область действия переменных. Эта переменная не содержит структуру, поэтому вы получите ошибку о том, как вы на нее ссылаетесь.

Поиск в области происходит, но это не во время настройки, а во время доступа для установки.

Вот рабочий пример, чтобы продемонстрировать это.

<cfset qryChain = queryNew("id,next")>
<cfset queryAddRow(qryChain, 3) />
<cfdump var="#qryChain#">
<!--- add some data so the cfloop kicks in --->
<cfloop query="qryChain">
    <cfset Next = StructNew()>
    <cfdump var="#variables#">
    <cfset Next.id = qryChain.next>
    <cfdump var="#qryChain#">

</cfloop>

в этом примере я показываю, что после того, как вы создадите следующую переменную, она существует в области видимости переменных, но когда вы сразу попытаетесь ввести ключ в нее, не ограничивая доступ, вы получите вместо этого переменную NEXT из текущей записи в запросе. Это происходит только потому, что в запросе есть запись с именем столбца, которое совпадает с переменной, которую вы пытаетесь использовать.

Так почему же ColdFusion не пытается установить StructNew () в область запросов (псевдо-область)? Запросом нельзя манипулировать с помощью точечной нотации. Опять же, это не совсем сфера. Так что в этом смысле он доступен только для чтения и пропускается через Чтобы манипулировать набором результатов запроса в CF, вы должны использовать область действия функций VARIABLES. Поскольку переменные с незаданной областью всегда помещаются в область VARIABLES. Однако в строке, где вы пытаетесь установить идентификатор, в фоновом режиме происходит еще одна фаза, где сначала необходимо получить доступ к переменной с возможностью чтения, а затем попытаться выполнить набор. В этом случае он НАХОДИТСЯ переменную NEXT в запросе, потому что сначала будет произведен поиск области, чтобы определить, существует ли в NEXT значение для установки этого ключа, а затем, когда вы попытаетесь что-то установить для него, произойдет ошибка.

Что касается вашего второго набора примеров, это ожидаемое поведение и его легко объяснить.

В первом примере вы изменяете свою переменную (которая помещает ее в локальную область видимости). Затем вы устанавливаете значение этой переменной. Когда вы устанавливаете значение в переменную (не ограничивая его), ColdFusion проверяет, существует ли эта переменная где-либо еще (таким образом, она выполняет поиск по области, опять-таки, к этому моменту выполняется доступ, а не устанавливается), она находит его в локальной области. и затем установите значение там. После этого вы снова устанавливаете значение, на этот раз правильно определяете его, поэтому поиск не производится.

Во втором примере вы не изменяете переменную, когда она изначально установлена, она нигде не существует, и поэтому она установлена ​​в область видимости переменных. Если бы он уже существовал в локальной области видимости, то ColdFusion нашел бы его и установил там (как в первом примере), но, поскольку он не существовал, и он не был изменен, он был установлен в область видимости переменных.

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

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

3 голосов
/ 09 марта 2012

Оценка области во время назначения

Мне известны два различных метода оценки области при создании переменных в ColdFusion.Я не проверял все возможные экземпляры, но именно так он и должен работать.

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

Второй экземпляр использует первые 6 областей в , оценивая переменные с незаданной областью , а затем в случае неудачитакже создайте переменную в области действия переменных.Это используется cfset и любыми другими тегами, которые создают переменные, такие как cfhttp с атрибутом result и атрибутом cfsavecontent s variable.

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

Подъем

Хотя ColdFusion был разработан дляКопировать JavaScript многими способами (в частности, cfscript) есть небольшое отклонение, которое я не видел задокументированным.Что касается функций (как сценария, так и тега), JavaScript использует hoisting, тогда как ColdFusion - нет.

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

До CF9 ключевое слово var нужно было использовать в верхней части функции, что по существу исключает необходимость в подъеме.Это отличается от JavaScript, где var может использоваться в любом месте функции и использует подъем.С CF9 ColdFusion принял философию «объявить где угодно», но не реализовал подъем.

В обоих следующих примерах JavaScript будет иметь дело только с одной областью действия, при этом x будет локальной функцией.

<!--- sets variables in 1 scope --->
<cfscript>
    var x = 0;
    x = 1;
    local.x = 7;
</cfscript>

По сравнению с:

<!--- sets variables in 2 scopes --->
<cfscript>
    x = 1;
    var x = 0;
    local.x = 7;
</cfscript>

Чтобы избежать потенциальных ловушек, вызванных отсутствием подъема, вы можете либо var только в верхней части функции, либо делать то же, что и многие до CF9 иобъявите структуру var в верхней части функции и добавьте к ней все переменные (помните, чтобы не называть ее локальной).Например,

<cfset var localVars = StructNew()>
<cfset localVars.x = 7>
<cfset localVars.y = 1>

var против local

В функциях var является гражданином второго сорта в области действия local.Если вы попытаетесь установить для локальной переменной то же имя, что и для аргумента, используя var, вы получите сообщение об ошибке, в котором будет сказано: Используйте local, чтобы определить локальную переменную с тем же именем.* предположительно эквивалентный.

Больше

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

0 голосов
/ 10 февраля 2012

Вы получаете эту ошибку, предположительно (так как это ошибка, которую я получил):

You have attempted to dereference a scalar variable of type class coldfusion.sql.QueryColumn as a structure with members.

Это потому, что когда вы находитесь в циклическом цикле запроса, область запроса имеет приоритет над переменнымиобласть при оценке переменной без области.Однако, когда вы назначаете переменные без области (либо в контексте запроса cfloop, либо нет), вы используете область действия variables.Поэтому, когда вы присваиваете

<cfset Next = StructNew()>

, вы устанавливаете переменную «Next» в область действия variables..Однако, когда вы пытаетесь оценить Next (со ссылкой на его ключ, .id), например, так:

<cfset Next.id = qryChain.next>

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

Итак, чтобы попытаться ответить на ваш главный вопрос - когда вы присваиваете переменную без указания области, CF поместит переменную в область действия variables,Когда вы пытаетесь вычислить переменную без заданной области действия, она будет использовать нормальные правила приоритета области действия , чтобы найти совпадение (в данном случае это будет найдено в области действия query).

...