language-features Существуют ли законные применения для оператора JavaScript с инструкцией?




15 Answers

Я использую оператор 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')
    )
  )
}

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

javascript language-features with-statement

Комментарии Алана Шторма в ответ на мой ответ в связи 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);" +
    "}" +
"}}}}";



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

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

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



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

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

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




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




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

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

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




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

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




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




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

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

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




Для некоторых коротких фрагментов кода я хотел бы использовать тригонометрические функции типа 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;
    }
  }

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




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

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




Вы должны увидеть валидацию формы в 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;
}



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

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



Как заметил Энди Е в комментариях ответа 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.




мой

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

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




Related