Чи є законне використання для JavaScript оператора "з"?


369

Зауваження Алана Шторма у відповідь на мою відповідь щодо withзаяви твердили мене. Я рідко знаходив причину використовувати саме цю мовну функцію, і ніколи не задумувався над тим, як це може спричинити проблеми. Тепер мені цікаво, як я можу ефективно використовувати це with, уникаючи його підводних каменів.

Де ви вважаєте withзаяву корисною?


52
Я ніколи цим не користуюся. Жити без неї легше, якщо робити вигляд, що її немає.
Носредна

6
Можливо, колись було багато дійсних застосувань для цього. Але це суперечки. ES5 Strict видалено, withщоб більше не було подібного.
Томас Айлотт

27
Варто зазначити, що ES5 Strict все ще не є обов'язковим .
Shog9

5
Замість того, щоб видаляти "з" в ES5 строго, чи не було б краще змінити стандарт так, що якщо не буде знайдена змінна, будь-яке призначення, зроблене всередині "з", пов'язане з об'єктом аргументу?
JussiR

2
@JussiR: Напевно. Але проблема в цьому полягає в тому, що це, можливо, зламає речі в старих браузерах.
Sune Rasmussen

Відповіді:


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 ++ (у С змінні повинні бути оголошені на початку блок, тож у чомусь подібний). Така поведінка насправді досить схожа на 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. (І про єдине використання, про яке я знав.)
Mateen Ulhaq

2
Мінусом цього було б те, що якщо ви посилаєтеся на змінну в блоці з, який знаходиться поза об’єктом markupbuilder, js-движок спочатку шукатиме її всередині markupbuilder, знижуючи продуктивність.
Адам Томас

3
Це дійсно допомагає скоротити код для тих, хто працює з полотнами.
Брайан Маккотчон

4
Версія цього коду "з" буквально працює на моїй машині в 240 разів повільніше, ніж версія "не з". Чому люди кажуть, що для цього немає законного використання. Не тому, що він не може зробити код гарнішим у деяких місцях. Дивіться орієнтир: jsfiddle.net/sc46eeyn
Jonny

1
@McBrainy - Це саме той тип місця, де ви не повинні використовувати код, який працює з безліччю повільніше (див. Коментар, який я щойно зробив вище цього). Якщо вам потрібні ярлики для супер повторного коду, ви можете їх оголосити. Наприклад, якщо використовувати context.bezierCurveToсто разів прямо, ви можете сказати, var bc2 = context.bezierCurveTo;а потім просто переходити bc2(x,x,etc);кожен раз, коли ви хочете зателефонувати. Це досить швидко і навіть менш багатослівно, тоді як withдуже повільно.
Jimbo Jonny

83

Як було зазначено в моїх попередніх коментарях, я не думаю, що ви можете користуватися withбезпечно, незалежно від того, наскільки спокусливим це може бути в будь-якій ситуації. Оскільки питання тут не висвітлено безпосередньо, я повторю його. Розглянемо наступний код

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

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

Без ретельного дослідження цих функціональних викликів неможливо сказати, яке буде стан вашої програми після запуску цього коду. Якщо user.nameвже було встановлено, то тепер буде Bob. Якщо він не був встановлений, глобальний nameбуде ініціалізований або змінений, Bobа userоб'єкт залишиться без nameвластивості.

Помилки трапляються. Якщо ви користуєтесь з вами, з часом це зробите і збільшите шанси на те, що ваша програма вийде з ладу. Гірше, що ви можете зіткнутися з робочим кодом, який встановлює глобальний блок з блоком, навмисно або через автора, не знаючи про цю вигадку конструкції. Це дуже схоже на те, що ви стикаєтесь з провалом на комутаторі, ви не маєте уявлення, чи автор цього задумав, і немає ніякого способу дізнатися, чи "фіксація" коду введе регресію.

Сучасні мови програмування забиті повнофункціональними можливостями. Деякі особливості після багатьох років використання виявляються поганими, і їх слід уникати. Javascript- withце один з них.


18
Ця проблема виникає лише тоді, коли ви присвоюєте значення атрибуту об'єкта. Але що робити, якщо ви використовуєте його лише для читання значень? Я стверджую, що в цьому випадку це нормально використовувати.
аеропорт

10
Ця ж проблема стосується і читання значень Toby. У наведеному вище фрагменті коду ви не знаєте, чи вказано ім'я на об’єкт користувача, тому ви не знаєте, чи читаєте ви глобальне ім'я чи ім'я користувача.
Алан Шторм

12
Зі значеннями зчитування існує чітке правило пріоритету: атрибути на об'єкті перевіряються перед змінними за межами області. Це нічим не відрізняється від змінних, які визначаються функціями. Справжня проблема із призначенням та «з», як я це розумію, полягає в тому, що від того, чи відбувається присвоєння атрибуту, залежить від того, існує атрибут на поточному об'єкті, про який йдеться, який є властивістю виконання та його неможливо легко визначити дивлячись на код.
аеропорт

1
Я думаю, що ти можеш бути там, Тобі. Проблеми з написанням мені достатньо, щоб повністю ухилятися від конструкції.
Алан Шторм

"Це дуже схоже на те, що ви стикаєтесь з провалюванням на комутаторі, ви не маєте уявлення ..." - Тоді давайте також заборонимо перемикатися ()? ;-p
Sz.

66

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

Щоб досягти цього ефекту, я використав два з твердженнями, щоб "прошаркувати" область, що стоїть за загальною сферою:

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);
}

EDIT: Просто перевірили джерело 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

@Adam: Я не впевнений у цьому. ES5 видає помилку лише в суворому режимі, тому це не є негайною проблемою, якщо у вас немає жорсткого режиму, оголошеного в усьому світі. ES Harmony може створити більшу проблему, але це може бути виправлено за допомогою деяких нових матеріалів, таких як проксі.
Енді Е

@AndyE Вибачте, це поза темою, але чи доступна ваша консоль командного рядка, написана на JavaScript?
kybernetikos

@Adam: ні, це не так. Вся справа мала бути набором інструментів для розробників гаджетів для робочого столу Windows, але я ніколи цього не закінчив (хоча консоль працює дуже добре). Я можу закінчити це в якийсь момент, навіть незважаючи на те, що у 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 Dixon

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

14
Але це просто довше і важче зрозуміти спосіб виконання: var _ = obj_name_here; _.a = "foo"; _.b = "бар;
Рене Саарсоо

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
кенгакс

5
@kangax: Я походжу з C ++, де традиційним для багатьох програмістів є одержимість невеликої ефективності свого коду, навіть коли вони насправді не відчутно впливають на ефективність більшої програми або програми. Мені це здається дивним у контексті JavaScript, де така значна частина продуктивності програми може залежати від реалізації VM. Я бачив декілька випадків, коли програмісти 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

vs.

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, обрали б другий.


Уупс, я не зрозумів, що хтось уже розмістив цей самий приклад рік тому. Питання щодо продуктивності в бік "з" робить гарний код DRY за рахунок того, що він трохи складніше читати. Я думаю, що для співпраці з іншими розробниками або більшості виробничого коду добре застосовувати ключове слово "з". Але якщо ви працюєте з програмістами на експертному рівні і розумієте, як уникнути потенційної неефективності, то будь-якими шляхами вирушайте до міста зі «з»
Йона

6

Я думаю, що очевидне використання - це ярлик. Якщо ви, наприклад, ініціалізуєте об'єкт, ви просто заощадите, набравши багато "ObjectName". На кшталт "s-slots", який дає змогу писати Lisp

(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.
Вогнева ворона

1
Оголошення за основним принципом. "з" - феноменально потужна конструкція. Але більшість людей з JS не розуміють закриттів і пишуть смішно складні системи успадкування класу Java на вершині JS - так як вони могли бути обізнані про потенціал метапрограмування, який пропонує «з»?
Джаред

Звичайно, with-slotsпотрібно вказати, які слоти ви використовуєте, тоді як withбуде використовуватися будь-які слоти, пов'язані під час виконання.
Семюель Едвін Уорд

6

Маючи досвід роботи з Delphi, я б сказав, що використовуючи допомогою має бути оптимізацією розміру в останню чергу, можливо, виконаним якимось алгоритмом мінімізації javascript з доступом до аналізу статичного коду для перевірки його безпеки.

Проблеми з визначенням масштабів, з якими ви можете зіткнутися при ліберальному використанні с оператора заявою, можуть бути королівськими болями в **, і я не хотів би, щоб хто-небудь переживав сесію налагодження, щоб зрозуміти, що він у вашому коді відбувається. , лише щоб дізнатись, що він захопив об’єкт або неправильну локальну змінну замість вашої глобальної чи зовнішньої змінної області, яку ви задумали.

VB з твердженням кращий, оскільки йому потрібні крапки для розмежування масштабування , але Delphi з заявою - це завантажений пістолет із зачісками для волосся, і мені здається, що як javascript є таким же схожим, щоб гарантувати те саме попередження.


5
Javascript із заявою гірший, ніж у Delphi. У Delphi, функція виконує так само швидко (якщо не швидше), ніж позначення object.member. У javascript, with має пройти область, щоб перевірити відповідність членів, таким чином, це завжди робить його повільніше, ніж позначення object.member.
Martijn

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();

Оператор-оператор дуже корисний, якщо ви хочете змінити область, що необхідно для створення вашої власної глобальної області, якою ви можете маніпулювати під час виконання. Ви можете помістити на нього константи або певні допоміжні функції, які часто використовуються, наприклад, "toUpper", "toLower" або "isNumber", "clipNumber" і т.д.

Про погану ефективність, яку я часто читав: Оцінка функції не матиме жодного впливу на продуктивність, адже в моєму 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" );

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


3

Використання з також робить ваш код повільнішим у багатьох реалізаціях, оскільки тепер усе перетворюється на додатковий простір для пошуку. Немає законних причин для використання в JavaScript.


5
Передчасна оптимізація. Не вимагайте "повільніше", якщо ви не розчарували цифри; будь-які накладні витрати, ймовірно, тривіальні як для сучасних, так і для древніх js impls.
мк.

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

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

Я згоден, можна програмувати це без заяви. Але оскільки це не створює проблем, це законне використання.


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її альтернатив тут .


Дякую за відповідь, Томе. Я прочитав рекомендацію Крокфорда, і хоча це має сенс, він іде лише до цього часу. Я підіймаюсь до ідеї - дотикман якої торкнувся опосередковано, - що реальна сила з {} полягає в тому, як це можна використовувати для маніпулювання сферою ...
Shog9

2

Я просто не бачу, як користуватися функцією "С" - це читабельніше, ніж просто вводити object.member. Я не думаю, що він є менш читабельним, але я не думаю, що він ще і легкий для читання.

Як сказав Lassevk, я точно можу побачити, як використання з би було більш схильним до помилок, ніж просто використання дуже явного синтаксису "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

CoffeeScript в Coco вилка має withключове слово, але це просто набори this(також доступні для запису , як @в CoffeeScript / Coco) до цільового об'єкту в межах блоку. Це усуває неоднозначність і домагається суворого дотримання режиму ES5:

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

0

Ось корисне використання для with: додавання нових елементів у літературу об’єкта на основі значень, збережених у цьому об’єкті. Ось приклад, який я щойно використовував сьогодні:

У мене був набір можливих плиток (з отворами, зверненими вгорі, внизу, ліворуч або праворуч), які можна було використати, і я хотів швидкий спосіб додати список плиток, які завжди будуть розміщені та заблоковані на початку гри . Я не хотів продовжувати друкувати 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

Ви можете використовувати з, щоб уникнути необхідності явного керування артієм при використанні Requ.js:

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

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

Реалізація Requjs.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.