Существуют ли законные варианты использования оператора «with» в JavaScript?


369

Комментарии Алана Шторма в ответ на мой ответ относительно withзаявления заставили меня задуматься. Я редко находил причину использовать эту особенность языка и никогда не задумывался над тем, как это может вызвать проблемы. Теперь мне интересно, как я мог бы эффективно использовать ее with, избегая при этом ее ловушек.

Где вы нашли это withзаявление полезным?


52
Я никогда не использую это. Легче жить без него, если я притворяюсь, что его не существует.
Носредна

6
Возможно, когда-то было много действительных применений для него. Но это спорный вопрос. ES5 Strict убран, withтак что больше ничего такого нет.
Томас Айлотт

27
Стоит отметить, что ES5 Strict все еще не является обязательным .
Shog9

5
Вместо удаления «с» в строгом ES5, не лучше ли было бы изменить стандарт так, чтобы, если переменная не была найдена, любое присвоение, сделанное внутри «с», связывалось с объектом аргумента?
JussiR

2
@JussiR: Возможно. Но проблема в том, что это может сломать вещи в старых браузерах.
Суни Расмуссен

Ответы:


520

Сегодня мне пришло в голову другое использование, поэтому я с энтузиазмом искал в Интернете и нашел упоминание об этом: определение переменных внутри блока .

Фон

JavaScript, несмотря на внешнее сходство с C и C ++, не включает переменные в блок, в котором они определены:

var name = "Joe";
if ( true )
{
   var name = "Jack";
}
// name now contains "Jack"

Объявление замыкания в цикле является распространенной задачей, где это может привести к ошибкам:

for (var i=0; i<3; ++i)
{
   var num = i;
   setTimeout(function() { alert(num); }, 10);
}

Поскольку цикл for не вводит новую область видимости, то же самое num- со значением 2- будет использоваться всеми тремя функциями.

Новая сфера: letиwith

С введением letоператора в ES6 становится легко вводить новую область, когда необходимо избежать этих проблем:

// variables introduced in this statement 
// are scoped to each iteration of the loop
for (let i=0; i<3; ++i)
{
   setTimeout(function() { alert(i); }, 10);
}

Или даже:

for (var i=0; i<3; ++i)
{
   // variables introduced in this statement 
   // are scoped to the block containing it.
   let num = i;
   setTimeout(function() { alert(num); }, 10);
}

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

for (var i=0; i<3; ++i)
{
   // object members introduced in this statement 
   // are scoped to the block following it.
   with ({num: i})
   {
      setTimeout(function() { alert(num); }, 10);
   }
}

Цикл теперь работает так, как задумано, создавая три отдельные переменные со значениями от 0 до 2. Обратите внимание, что переменные, объявленные в блоке, не ограничены им, в отличие от поведения блоков в C ++ (в C переменные должны быть объявлены в начале блок, так что в некотором роде это похоже). Это поведение на самом деле очень похоже на letблочный синтаксис, представленный в более ранних версиях браузеров Mozilla, но не получил широкого распространения в других местах.


15
Никогда не думал об использовании с литералом, кажется законным.
Мэтт Кантор

81
Это действительно очень мертвый. Я никогда не думал играть с областью JavaScript таким образом. Полностью расширены новые области для моего кодирования. Я бы хотел, чтобы я проголосовал 10 раз!
kizzx2

27
Для тех, кто все еще против, всегда можно использовать закрытие:for (var i = 0; i < 3; ++i) { setTimeout ((function () { var num = i; return function () { alert (num); }; }) (), 10);}
Томас Эдинг

4
На самом деле, проблема, указанная выше, появляется в большинстве браузеров, отличных от Mozilla (Chrome, Safari, Opera, IE).
Макс Шавабке

24
пусть поддержка заявление в IE действительно сохранить свой бекон прямо сейчас, я борюсь со своей совестью на том, следует ли использовать с вместо. Реальная проблема заключается в том , что даже с с как пусть , дополнительный уход еще должен быть принят из - за унаследованных свойств объекта на цепочке прототипов. Например, var toString = function () { return "Hello"; }; with ({"test":1}) { console.log(toString()); };. В рамках оператора with toString () является унаследованным свойством Object , поэтому явно определенная функция не вызывается. Тем не менее, отличный ответ :-)
Энди Э

161

Я использовал оператор with как простую форму импорта по областям. Допустим, у вас есть какой-то компоновщик разметки. Вместо того чтобы писать:

markupbuilder.div(
  markupbuilder.p('Hi! I am a paragraph!',
    markupbuilder.span('I am a span inside a paragraph')
  )
)

Вы могли бы вместо этого написать:

with(markupbuilder){
  div(
    p('Hi! I am a paragraph!',
      span('I am a span inside a paragraph')
    )
  )
}

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


5
Вот как я видел это используется в VB. (И единственное использование, которое я знал.)
Матин Улхак

2
Недостатком этого является то, что если вы ссылаетесь на переменную внутри блока with, находящегося за пределами объекта markupbuilder, движок js сначала будет искать его внутри markupbuilder, что снижает производительность.
Адам Томас

3
Это действительно помогает сократить код для тех, кто работает с путями холста.
Брайан МакКатчон

4
«С» версия этого кода буквально работает в 240 раз медленнее на моей машине, чем «не с» той же версии. Вот почему люди говорят, что нет законного использования для этого. Не потому, что он не может сделать код красивее в некоторых местах. Смотрите бенчмарк: jsfiddle.net/sc46eeyn
Джимбо Джонни

1
@McBrainy - это именно то место, где вы не должны использовать код, который работает на много медленнее (см. Комментарий, который я только что сделал над этим). Если вам нужны ярлыки для супер повторного кода, вы можете объявить их. Например, если вы используете context.bezierCurveToсто раз подряд, вы можете сказать, var bc2 = context.bezierCurveTo;а затем просто идти bc2(x,x,etc);каждый раз, когда вы хотите позвонить. Это довольно быстро и даже менее многословно, а withсупер медленно.
Джимбо Джонни

83

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

user = {};
someFunctionThatDoesStuffToUser(user);
someOtherFunction(user);

with(user){
    name = 'Bob';
    age  = 20;
}

Без тщательного изучения этих вызовов функций невозможно определить состояние вашей программы после выполнения этого кода. Если user.nameуже было установлено, то теперь будет Bob. Если он не был установлен, глобальный объект будет nameинициализирован или изменен на, Bobи userобъект останется без nameсвойства.

Баги случаются. Если вы используете вместе с вами, это в конечном итоге сделает это и увеличит шансы вашей программы не удастся. Хуже того, вы можете столкнуться с рабочим кодом, который устанавливает глобал в блоке with, либо намеренно, либо через автора, не знающего об этой причуде конструкции. Это очень похоже на столкновение с провалом на переключателе, вы не представляете, задумал ли автор это, и нет способа узнать, приведет ли «исправление» кода к регрессии.

Современные языки программирования переполнены функциями. Некоторые функции, после многих лет использования, оказались плохими, и их следует избегать. Javascript's withявляется одним из них.


18
Эта проблема появляется только тогда, когда вы присваиваете значения атрибуту объекта. Но что, если вы используете его только для чтения значений? Я утверждаю, что это нормально использовать в таком случае.
airportyh

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

12
При чтении значений существует четкое правило приоритета: атрибуты объекта проверяются перед переменными вне области действия. Это ничем не отличается от области видимости переменных в функциях. Реальная проблема с присваиванием и «с», насколько я понимаю, заключается в том, что происходит или нет назначение атрибута, зависит от того, существует ли атрибут на текущем объекте, о котором идет речь, который является свойством времени выполнения и не может быть легко выведен глядя на код.
airportyh 22.09.09

1
Я думаю, что вы можете быть там, Тоби. Мне достаточно проблемы записи, чтобы полностью отказаться от конструкции.
Алан Сторм

«Это очень похоже на столкновение с провалом на коммутаторе, вы понятия не имеете ...» - Итак, давайте тогда тоже запретим switch ()? ;-p
Sz.

66

Я на самом деле нашел это withзаявление невероятно полезным в последнее время. Эта техника никогда не приходила мне в голову, пока я не начал свой текущий проект - консоль командной строки, написанную на JavaScript. Я пытался эмулировать API-интерфейсы консоли Firebug / WebKit, где в консоль можно вводить специальные команды, но они не переопределяют никакие переменные в глобальной области видимости. Я думал об этом, пытаясь преодолеть проблему, о которой упоминал в комментариях к превосходному ответу Shog9 .

Чтобы достичь этого эффекта, я использовал два оператора with, чтобы «наслоить» область действия за глобальной областью действия:

with (consoleCommands) {
    with (window) {
        eval(expression); 
    }
}

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

Я был вдохновлен опубликовать этот ответ, когда, к моему удивлению, мне удалось найти ту же технику, что и в других местах - исходный код Chromium !

InjectedScript._evaluateOn = function(evalFunction, object, expression) {
    InjectedScript._ensureCommandLineAPIInstalled();
    // Surround the expression in with statements to inject our command line API so that
    // the window object properties still take more precedent than our API functions.
    expression = "with (window._inspectorCommandLineAPI) { with (window) { " + expression + " } }";
    return evalFunction.call(object, expression);
}

РЕДАКТИРОВАТЬ: Только что проверил источник Firebug, они соединяют 4 с заявлениями вместе для еще большего количества слоев. Сумасшедший!

const evalScript = "with (__win__.__scope__.vars) { with (__win__.__scope__.api) { with (__win__.__scope__.userVars) { with (__win__) {" +
    "try {" +
        "__win__.__scope__.callback(eval(__win__.__scope__.expr));" +
    "} catch (exc) {" +
        "__win__.__scope__.callback(exc, true);" +
    "}" +
"}}}}";

1
но беспокоюсь, что ecmascript5 мешает вам сделать это. Есть ли решение ecmascript 5?
kybernetikos

@ Адам: я не уверен в этом. ES5 только выдает ошибку для этого в строгом режиме, поэтому это не проблема, если у вас нет строгого режима, объявленного глобально. ES Harmony может представлять большую проблему, но это может быть исправлено с некоторыми более новыми вещами, такими как прокси.
Энди Э

@ AndyE извините, это не по теме, но доступна ли где-нибудь ваша консоль командной строки, написанная на JavaScript?
kybernetikos

@ Адам: нет, это не так. Все это было задумано как набор инструментов разработчика для Windows Desktop Gadgets, но я так и не закончил (хотя консоль работает очень хорошо). Я мог бы закончить это в какой-то момент, даже несмотря на то, что у WDG сейчас не очень светлое будущее.
Энди Э

3
Несколько недель назад мы перевели нашу консольную реализацию в Chrome с блока на магию символов, потому что блок заблокировал некоторые функции ES6 :)
Алексей Козятинский

54

Да, да и да. Существует очень законное использование. Смотреть:

with (document.getElementById("blah").style) {
    background = "black";
    color = "blue";
    border = "1px solid green";
}

В основном любые другие хуки DOM или CSS - фантастическое применение с. Это не значит, что «CloneNode» будет неопределенным и вернется к глобальной области видимости, если вы не пошли своим путем и решили сделать это возможным.

Скорость жалобы Крокфорда заключается в том, что с помощью создается новый контекст. Контексты, как правило, дороги. Согласен. Но если вы только что создали div и у вас нет какой-либо инфраструктуры для настройки вашего css и вам нужно вручную установить около 15 свойств CSS, тогда создание контекста, вероятно, будет дешевле, чем создание переменных и 15 разыменований:

var element = document.createElement("div"),
    elementStyle = element.style;

elementStyle.fontWeight = "bold";
elementStyle.fontSize = "1.5em";
elementStyle.color = "#55d";
elementStyle.marginLeft = "2px";

так далее...


5
+1, поскольку я также думаю, что есть много законных применений with. Тем не менее, в этом конкретном случае вы могли бы просто сделать:element.style.cssText="background: black ; color: blue ; border: 1px solid green"
GetFree

5
Вы можете достичь того же в одной строке , используя простой extendметод либо из JQuery или Underscore.js: $.extend(element.style, {fontWeight: 'bold', fontSize: '1.5em', color: '#55d', marginLeft: '2px'}).
Тревор Бернхам

9
@TrevorBurnham - Если вы предполагаете, что jQuery доступен, вы просто должны использовать его .css()метод ...
nnnnnn

4
Что именно мешает этим переменным перейти в глобальную область? Это просто потому, что все стили CSS всегда определены на всех элементах, или как?
mpen

1
@ Отметьте, да, они всегда определены, с пустыми или пустыми строками в качестве значений, если для свойства не существует пользовательского стиля
Esailija,

34

Вы можете определить небольшую вспомогательную функцию, чтобы обеспечить преимущества withбез двусмысленности:

var with_ = function (obj, func) { func (obj); };

with_ (object_name_here, function (_)
{
    _.a = "foo";
    _.b = "bar";
});

8
OMG, моя голова взорвалась! без двусмысленности? Должен проголосовать, человек!
Джаррод Диксон

@Jarrod: что в этом смешного ( is.gd/ktoZ )? Большинство всех, кто пользуется этим сайтом, умнее меня, поэтому простите меня, если я ошибаюсь, но это кажется плохой информацией.
ворон

14
Но это просто дольше и сложнее понять, как это сделать: var _ = obj_name_here; _.a = "Foo"; _.b = "bar;
Rene Saarsoo

3
Рене: С этим вы выставите переменную "_" для внешней области видимости, что приведет к потенциальным ошибкам. Также будут отображаться любые временные переменные, используемые при расчете параметров объекта.
Джон Милликин

30
Вы получите больше ошибок из- with_за двойной мутной версии (function(_){ _.a="foo"; })(object_here);(стандартный способ имитации блоков в стиле c / java). Используйте это вместо этого.
мк.

25

Вряд ли это того стоит, так как вы можете делать следующее:

var o = incrediblyLongObjectNameThatNoOneWouldUse;
o.name = "Bob";
o.age = "50";

1
Это не имело никакого смысла для меня. Хотя я не специалист по JavaScript
JeroenEijkhof

@WmasterJ Для ясности, смотрите этот пост: yuiblog.com/blog/2006/04/11/with-statement-considered-harmful
Деннис

8
Длинные имена переменных не единственный вариант использования with.
Крис

18

Я никогда не пользуюсь, не вижу причин и не рекомендую это.

Проблема в withтом, что он предотвращает многочисленные лексические оптимизации , которые может выполнять реализация ECMAScript. Учитывая рост быстрых двигателей на базе JIT, эта проблема, вероятно, станет еще более важной в ближайшем будущем.

Может показаться, что это withпозволяет создавать более чистые конструкции (когда, скажем, вводится новая область вместо обычной обертки анонимной функции или замена многословного псевдонима), но это действительно того не стоит . Помимо снижения производительности, всегда существует опасность присвоения свойству неправильного объекта (когда свойство не найдено в объекте во введенной области видимости) и, возможно, ошибочно вводить глобальные переменные. IIRC, последняя проблема, которая побудила Крокфорда рекомендовать избегать with.


6
Бугимана производительности часто высовывают, почти так же часто, как глобальные вещи ... Всегда мне кажется странным, учитывая, что мы говорим о JavaScript . Вы могли бы предположить, что снижение производительности действительно драматично, чтобы заслуживать такого большого внимания, но ... Если у вас есть какие-то точные цифры о стоимости with(){}конструкций, подобных тем, которые приведены в других ответах здесь, в современных браузерах, я бы хотел увидеть их!
Shog9

6
Почему это странно в контексте Javascript? :) И да, это драматично. Подумайте об этом - реализация должна оценить выражение в скобках, преобразовать его в объект, вставить его в начало текущей цепочки областей действия, вычислить оператор внутри блока, а затем восстановить цепочку областей действия до нормального состояния. Это много работы. Это гораздо больше, чем простой поиск свойств, который можно превратить в высокооптимизированный низкоуровневый код. Вот очень простой тест, который я только что сделал (дайте мне знать, если обнаружите какие-либо ошибки), демонстрирующий разницу - gist.github.com/c36ea485926806020024
kangax

5
@kangax: Я родом из C ++, где многие программисты обычно зацикливаются на небольшой эффективности в своем коде, даже если на самом деле они не оказывают заметного влияния на производительность более крупной подпрограммы или программы. Мне кажется странным в контексте JavaScript, где такая большая часть производительности подпрограммы может зависеть от реализации виртуальной машины. Я видел несколько случаев, когда программисты JS избегали, скажем, анонимной функции из-за опасений по поводу стоимости установки, но, похоже, это исключение, а не правило, зарезервированное для очень чувствительных областей кода.
Shog9

5
Тем не менее, вы абсолютно правы в отношении стоимости with(){}: установка новой области действия с withогромными затратами для каждого браузера, который я тестировал. Вы бы хотели избежать этого в любом коде, который вызывается очень часто. Кроме того, Chrome продемонстрировал потрясающий успех для любого кода, выполняемого в with()области видимости. Интересно, что IE обладал лучшими характеристиками производительности для кода внутри with()блоков: с учетом стоимости установки with()обеспечивает самые быстрые средства доступа к членам в виртуальных машинах IE6 и IE8 (хотя эти виртуальные машины являются самыми медленными в целом). Хорошие вещи, спасибо ...
Shog9

5
FWIW: вот тот же набор тестов с учётом затрат на установку: jsbin.com/imidu/edit Доступ к переменным для with()почти в два раза медленнее в Chrome и более чем в два раза быстрее в IE ...!
Shog9

13

Visual Basic.NET имеет аналогичное Withутверждение. Один из наиболее распространенных способов, которыми я пользуюсь, - это быстро установить ряд свойств. Вместо:

someObject.Foo = ''
someObject.Bar = ''
someObject.Baz = ''

, Я могу написать:

With someObject
    .Foo = ''
    .Bar = ''
    .Baz = ''
End With

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

With someObject
    .Foo = ''
End With

против

With someObject
    Foo = ''
End With

Бывший есть someObject.Foo; последний находится Fooв поле зрения снаружи someObject .

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


2
Правда достаточно. Не отвечает на его вопрос, хотя. Так что это не по теме.
Аллен Лалонд

6
Это было и в моей голове. кто-то должен был сказать это . Почему JavaScript не может просто иметь точку.
Карсон Майерс

Согласен, точечная запись лучше, если бы JavaScript использовал ее. +1


7

Использование «с» может сделать ваш код более сухим.

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

var photo = document.getElementById('photo');
photo.style.position = 'absolute';
photo.style.left = '10px';
photo.style.top = '10px';

Вы можете высушить его до следующего:

with(document.getElementById('photo').style) {
  position = 'absolute';
  left = '10px';
  top = '10px';
}

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

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

Я предполагаю, что люди, которые любят Java или C #, выберут первый путь (object.member), а те, кто предпочитает Ruby или Python, выберут второй.


К сожалению, я не осознавал, что кто-то уже опубликовал этот же пример год назад. Помимо проблем с производительностью, «with» делает хороший СУХОЙ код за счет того, что его немного сложнее читать. Я думаю, что для совместной работы с другими разработчиками или большей частью производственного кода рекомендуется избегать ключевого слова «с». Но если вы работаете с программистами экспертного уровня и понимаете, как избежать потенциальной неэффективности, тогда непременно отправляйтесь в город с «с».
Иона

6

Я думаю, что очевидное использование в качестве ярлыка. Если вы, например, инициализируете объект, вы просто сохраняете набор «ObjectName». Вроде как "с-слоты" Лиспа, который позволяет вам писать

(with-slots (foo bar) objectname
   "some code that accesses foo and bar"

что то же самое, что писать

"some code that accesses (slot-value objectname 'foo) and (slot-value objectname 'bar)""

Более понятно, почему это ярлык, чем когда ваш язык разрешает «Objectname.foo», но все же.


1
Приятно видеть код LISP! Я думаю, что «с» в javascript, очевидно, навеяно корнями его схемы как языка, но, увы, вам отказывают в голосовании за размещение LISP по вопросу javascript.
Fire Crow

1
Проголосование по основному принципу. «с» - феноменально мощная конструкция. Но большинство людей JS не понимают замыканий и пишут смехотворно сложные системы наследования классов Java поверх JS - так как они могли знать о потенциале метапрограммирования, который предлагает «с»?
Джаред

Конечно, with-slotsвам нужно указать, какие слоты вы используете, тогда как withбудете использовать любые слоты, которые будут связаны во время выполнения.
Сэмюэль Эдвин Уорд

6

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

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

VB с утверждением лучше, так как ему нужны точки для устранения неоднозначности, но Delphi с утверждением - это заряженное ружье с hairtrigger, и мне кажется, что javascript один достаточно похож, чтобы оправдать то же предупреждение.


5
Javascript с утверждением хуже Delphi. В Delphi, с выполняет так же быстро (если не быстрее), чем нотация object.member. В javascript функция with должна проходить область видимости для проверки соответствия членов, таким образом всегда делая ее медленнее, чем нотация object.member.
Мартейн

5

Использование с не рекомендуется, и запрещено в строгом режиме ECMAScript 5. Рекомендуемая альтернатива - назначить объект, свойства которого вы хотите получить доступ к временной переменной.

Источник: Mozilla.org


4

Оператор with может использоваться для уменьшения размера кода или для закрытых членов класса, например:

// demo class framework
var Class= function(name, o) {
   var c=function(){};
   if( o.hasOwnProperty("constructor") ) {
       c= o.constructor;
   }
   delete o["constructor"];
   delete o["prototype"];
   c.prototype= {};
   for( var k in o ) c.prototype[k]= o[k];
   c.scope= Class.scope;
   c.scope.Class= c;
   c.Name= name;
   return c;
}
Class.newScope= function() {
    Class.scope= {};
    Class.scope.Scope= Class.scope;
    return Class.scope;
}

// create a new class
with( Class.newScope() ) {
   window.Foo= Class("Foo",{
      test: function() {
          alert( Class.Name );
      }
   });
}
(new Foo()).test();

Оператор with очень полезен, если вы хотите изменить область, что необходимо для того, чтобы иметь собственную глобальную область, которой вы можете манипулировать во время выполнения. Вы можете использовать константы или некоторые часто используемые вспомогательные функции, такие как, например, «toUpper», «toLower» или «isNumber», «clipNumber» aso ..

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

var o={x: 5},r, fnRAW= function(a,b){ return a*b; }, fnScoped, s, e, i;
with( o ) {
    fnScoped= function(a,b){ return a*b; };
}

s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
    r+= fnRAW(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );

s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
    r+= fnScoped(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );

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


3

Использование with также делает ваш код медленным во многих реализациях, так как теперь все упаковано в дополнительную область для поиска. Там нет законных оснований для использования с в JavaScript.


5
Преждевременная оптимизация. Не говорите «медленнее», если вы не сократите числа; любые накладные расходы, вероятно, тривиальны как для современных, так и для древних импл.
мк.

2
Я категорически не согласен с вашим выводом, основанным на том, что не может быть проблемой для разработчиков, кроме вас.
Дэйв Ван ден Эйнде

4
@mk: хорошо, число здесь для вас: var obj={a:0,b:0,c:0};var d=+new Date;with(obj){for(var i=0;i<1000000;++i){a+=1;b+=1;c+=1}}+new Date-d;дает в среднем 2500, в то время как var obj={a:0,b:0,c:0};var d=+new Date;for(var i=0;i<1000000;++i){obj.a+=1;obj.b+=1;obj.c+=1}+new Date-d;в среднем 750, что делает использование более 3 раз медленнее.
Йорик

3
Просто запустил их в Chrome 23 в консоли, когда я увидел это. Результаты, которые я получил, были 1138 для withкода и 903 без. С этой крошечной разницей даже в тесном цикле я бы сделал выбор, основываясь на простоте кодирования и простоте рефакторинга в каждом конкретном случае, прежде чем беспокоиться о производительности.
Plynx

3

Я думаю, что оператор with может пригодиться при преобразовании языка шаблонов в JavaScript. Например JST в base2 , но я видел это чаще.

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


3

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


3

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

for(var i = nodes.length; i--;)
{
       // info is namespaced in a closure the click handler can access!
       (function(info)
       {           
            nodes[i].onclick = function(){ showStuff(info) };
       })(data[i]);
}

или с оператором, эквивалентным замыканию

for(var i = nodes.length; i--;)
{
       // info is namespaced in a closure the click handler can access!
       with({info: data[i]})
       {           
            nodes[i].onclick = function(){ showStuff(info) };
       }        
}

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


3

Я создал функцию «слияния», которая устраняет эту двусмысленность с помощью withвыражения:

if (typeof Object.merge !== 'function') {
    Object.merge = function (o1, o2) { // Function to merge all of the properties from one object into another
        for(var i in o2) { o1[i] = o2[i]; }
        return o1;
    };
}

Я могу использовать его аналогично with , но я могу знать, что это не повлияет на область, на которую я не собираюсь влиять.

Применение:

var eDiv = document.createElement("div");
var eHeader = Object.merge(eDiv.cloneNode(false), {className: "header", onclick: function(){ alert("Click!"); }});
function NewObj() {
    Object.merge(this, {size: 4096, initDate: new Date()});
}

3

Для некоторых коротких фрагментов кода я хотел бы использовать тригонометрические функции, такие как sinи cosт. Д., В режиме степени, а не в режиме излучения. Для этого я использую AngularDegreeобъект:

AngularDegree = new function() {
this.CONV = Math.PI / 180;
this.sin = function(x) { return Math.sin( x * this.CONV ) };
this.cos = function(x) { return Math.cos( x * this.CONV ) };
this.tan = function(x) { return Math.tan( x * this.CONV ) };
this.asin = function(x) { return Math.asin( x ) / this.CONV };
this.acos = function(x) { return Math.acos( x ) / this.CONV };
this.atan = function(x) { return Math.atan( x ) / this.CONV };
this.atan2 = function(x,y) { return Math.atan2(x,y) / this.CONV };
};

Затем я могу использовать тригонометрические функции в режиме степени без дальнейшего языкового шума в withблоке:

function getAzimut(pol,pos) {
  ...
  var d = pos.lon - pol.lon;
  with(AngularDegree) {
    var z = atan2( sin(d), cos(pol.lat)*tan(pos.lat) - sin(pol.lat)*cos(d) );
    return z;
    }
  }

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


не очень хорошая идея использовать withоператор таким образом, он просто затрудняет чтение кода, потому что вы не знаете, какая функция является глобальной и какая функция вызывается в области действия объекта, поэтому, если какая-либо функция каким-либо образом не определена в тогда объект будет пытаться получить к нему доступ в глобальном пространстве имен
Saket Patel

Осознавая проблему с ограничениями, я все же нахожу это полезным. Математик, читающий код, хочет прямо увидеть, что приведенная выше формула является применением «закона четырех последовательных частей» в сферической тригонометрии. Строгая альтернатива запутывает формулу: z = Math.atan2( Math.sin(d * Math.PI / 180), Math.cos( pol.lat * Math.PI / 180) * Math.tan( pos.lat * Math.PI / 180 ) - Math.sin( pol.lat * Math.PI / 180 ) * Math.cos( d * Math.PI / 180) ) * 180 / Math.PI;даст тот же результат, но это ужас.
rplantiko

@rplantiko Имейте в виду, что большинству людей это неудобно. Так что, если вы не пишете код, который никто больше не коснется. Кроме того, я почти уверен, что смогу показать вам пару примеров того with, как это вас озадачило.
Хуан Мендес

2

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

var sHeader = object.data.header.toString();
var sContent = object.data.content.toString();
var sFooter = object.data.footer.toString();

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

var sHeader = null, sContent = null, sFooter = null;
with(object.data) {
    sHeader = header.toString();
    sContent = content.toString();
    sFooter = content.toString();
}

И наоборот, можно утверждать, что вы нарушаете закон Деметры , но, опять же, возможно, нет. Я отвлекся =).

Прежде всего, знайте, что Дуглас Крокфорд рекомендует не использовать with. Я призываю вас проверить его пост в блоге относительно withего альтернатив здесь .


Спасибо за ответ, Том. Я прочитал рекомендацию Крокфорда, и хотя она имеет смысл, она заходит так далеко. Я подхожу к мысли - косвенно затронутой doekman - что настоящая сила с {} заключается в том, как ее можно использовать для манипулирования сферой ...
Shog9

2

Я просто не понимаю, как использовать with легче для чтения, чем просто набирать object.member. Я не думаю, что это менее читабельно, но я не думаю, что это также более читабельно.

Как сказал lassevk, я определенно вижу, что использование with будет более подвержено ошибкам, чем просто использование очень явного синтаксиса "object.member".


1

Вы должны увидеть подтверждение формы в javascript в W3schools http://www.w3schools.com/js/js_form_validation.asp, где форма объекта «сканируется», чтобы найти вход с именем «email»

Но я изменил его, чтобы получить из ЛЮБОЙ формы все поля проверки как не пустые, независимо от имени или количества поля в форме. Ну, я проверял только текстовые поля.

Но с помощью () все стало проще. Вот код:

function validate_required(field)
{
with (field)
  {
  if (value==null||value=="")
    {
    alert('All fields are mandtory');return false;
    }
  else
    {
    return true;
    }
  }
}

function validate_form(thisform)
{
with (thisform)
  {
    for(fiie in elements){
        if (validate_required(elements[fiie])==false){
            elements[fiie].focus();
            elements[fiie].style.border='1px solid red';
            return false;
        } else {elements[fiie].style.border='1px solid #7F9DB9';}
    }

  }
  return false;
}

1

В Coco- вилке CoffeeScript есть withключевое слово, но оно просто устанавливает this(также доступный для записи, как @в CoffeeScript / Coco) целевой объект в блоке. Это устраняет неоднозначность и обеспечивает строгое соблюдение режима ES5:

with long.object.reference
  @a = 'foo'
  bar = @b

0

Вот хорошее использование для with : добавления новых элементов в Object Literal на основе значений, хранящихся в этом Object. Вот пример, который я только что использовал сегодня:

У меня был набор возможных плиток (с отверстиями вверху, внизу, влево или вправо), которые можно было использовать, и я хотел быстро добавить список плиток, которые всегда будут помещаться и блокироваться в начале игры. , Я не хотел продолжать печатать types.tbrдля каждого типа в списке, поэтому я просто использовал with.

Tile.types = (function(t,l,b,r) {
  function j(a) { return a.join(' '); }
  // all possible types
  var types = { 
    br:  j(  [b,r]),
    lbr: j([l,b,r]),
    lb:  j([l,b]  ),  
    tbr: j([t,b,r]),
    tbl: j([t,b,l]),
    tlr: j([t,l,r]),
    tr:  j([t,r]  ),  
    tl:  j([t,l]  ),  
    locked: []
  };  
  // store starting (base/locked) tiles in types.locked
  with( types ) { locked = [ 
    br,  lbr, lbr, lb, 
    tbr, tbr, lbr, tbl,
    tbr, tlr, tbl, tbl,
    tr,  tlr, tlr, tl
  ] } 
  return types;
})("top","left","bottom","right");

0

Вы можете использовать с, чтобы избежать необходимости явно управлять арностью при использовании require.js:

var modules = requirejs.declare([{
    'App' : 'app/app'
}]);

require(modules.paths(), function() { with (modules.resolve(arguments)) {
    App.run();
}});

Реализация requirejs.declare:

requirejs.declare = function(dependencyPairs) {
    var pair;
    var dependencyKeys = [];
    var dependencyValues = [];

    for (var i=0, n=dependencyPairs.length; i<n; i++) {
        pair = dependencyPairs[i];
        for (var key in dependencyPairs[i]) {
            dependencyKeys.push(key);
            dependencyValues.push(pair[key]);
            break;
        }
    };

    return {
        paths : function() {
            return dependencyValues;
        },

        resolve : function(args) {
            var modules = {};
            for (var i=0, n=args.length; i<n; i++) {
                modules[dependencyKeys[i]] = args[i];
            }
            return modules;
        }
    }   
}

0

Как отметил Энди Э. в комментариях к ответу Shog9, это потенциально неожиданное поведение возникает при использовании withс литералом объекта:

for (var i = 0; i < 3; i++) {
  function toString() {
    return 'a';
  }
  with ({num: i}) {
    setTimeout(function() { console.log(num); }, 10);
    console.log(toString()); // prints "[object Object]"
  }
}

Не то неожиданное поведение уже не было отличительной чертой with.

Если вы все еще хотите использовать эту технику, по крайней мере, используйте объект с нулевым прототипом.

function scope(o) {
  var ret = Object.create(null);
  if (typeof o !== 'object') return ret;
  Object.keys(o).forEach(function (key) {
    ret[key] = o[key];
  });
  return ret;
}

for (var i = 0; i < 3; i++) {
  function toString() {
    return 'a';
  }
  with (scope({num: i})) {
    setTimeout(function() { console.log(num); }, 10);
    console.log(toString()); // prints "a"
  }
}

Но это будет работать только в ES5 +. Также не используйте with.


0

Я работаю над проектом, который позволит пользователям загружать код для изменения поведения частей приложения. В этом сценарии я использовал withпредложение, чтобы их код не изменял что-либо вне области, с которой я хочу, чтобы они возились. (Упрощенная) часть кода, которую я использую для этого:

// this code is only executed once
var localScope = {
    build: undefined,

    // this is where all of the values I want to hide go; the list is rather long
    window: undefined,
    console: undefined,
    ...
};
with(localScope) {
    build = function(userCode) {
        eval('var builtFunction = function(options) {' + userCode + '}');
        return builtFunction;
    }
}
var build = localScope.build;
delete localScope.build;

// this is how I use the build method
var userCode = 'return "Hello, World!";';
var userFunction = build(userCode);

Этот код (в некоторой степени) гарантирует, что определенный пользователем код не имеет доступа ни к каким объектам глобальной области действия, таким как windowни к одной из моих локальных переменных, через замыкание.

Как слово для мудрых, мне все еще нужно выполнить статические проверки кода для кода, представленного пользователем, чтобы убедиться, что они не используют другие хитрые способы доступа к глобальной области видимости. Например, следующий определенный пользователем код получает прямой доступ к window:

test = function() {
     return this.window
};
return test();


0

мой

switch(e.type) {
    case gapi.drive.realtime.ErrorType.TOKEN_REFRESH_REQUIRED: blah
    case gapi.drive.realtime.ErrorType.CLIENT_ERROR: blah
    case gapi.drive.realtime.ErrorType.NOT_FOUND: blah
}

сводится к

with(gapi.drive.realtime.ErrorType) {switch(e.type) {
    case TOKEN_REFRESH_REQUIRED: blah
    case CLIENT_ERROR: blah
    case NOT_FOUND: blah
}}

Можно ли доверять такому некачественному коду? Нет, мы видим, что это было сделано абсолютно нечитаемым. Этот пример неоспоримо доказывает, что нет никакой необходимости с-заявления, если я беру читаемость право;)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.