Есть ли законное использование для JavaScript “с” оператором?

Что такое NullPointerException?

Хорошим местом для начала является JavaDocs . Они охватывают это:

Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  • Вызов метода экземпляра нулевого объекта.
  • Доступ или изменение поля нулевого объекта.
  • Выполнение длины null, как если бы это был массив.
  • Доступ или изменение слотов с нулевым значением, как если бы это был массив.
  • Бросать нуль, как если бы это было значение Throwable.

Приложения должны бросать экземпляры этого класса для указания других незаконных видов использования нулевого объекта.

blockquote>

Также, если вы попытаетесь использовать нулевую ссылку с synchronized, который также выдаст это исключение, за JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В противном случае, если значение выражения равно null, NullPointerException.
blockquote>

Как это исправить?

Итак, у вас есть NullPointerException. Как вы это исправите? Возьмем простой пример, который выдает NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Идентифицирует нулевые значения

. Первый шаг - точно определить , значения которого вызывают исключение . Для этого нам нужно выполнить некоторую отладку. Важно научиться читать stacktrace . Это покажет вам, где было выбрано исключение:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

Здесь мы видим, что исключение выбрано в строке 13 (в методе printString). Посмотрите на строку и проверьте, какие значения равны нулю, добавив протоколирующие операторы или используя отладчик . Мы обнаруживаем, что s имеет значение null, а вызов метода length на него вызывает исключение. Мы видим, что программа прекращает бросать исключение, когда s.length() удаляется из метода.

Трассировка, где эти значения взяты из

Затем проверьте, откуда это значение. Следуя вызовам метода, мы видим, что s передается с printString(name) в методе print(), а this.name - null.

Трассировка, где эти значения должны быть установлены

Где установлен this.name? В методе setName(String). С некоторой дополнительной отладкой мы видим, что этот метод вообще не вызывается. Если этот метод был вызван, обязательно проверьте порядок , что эти методы вызывают, а метод set не будет называться после методом печати. ​​

Этого достаточно, чтобы дать нам решение: добавить вызов printer.setName() перед вызовом printer.print().

Другие исправления

Переменная может иметь значение по умолчанию setName может помешать ему установить значение null):

private String name = "";

Либо метод print, либо printString может проверить значение null например:

printString((name == null) ? "" : name);

Или вы можете создать класс, чтобы name всегда имел ненулевое значение :

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

См. также:

Я все еще не могу найти проблему

Если вы попытались отладить проблему и до сих пор не имеете решения, вы можете отправить вопрос для получения дополнительной справки, но не забудьте включить то, что вы пробовали до сих пор. Как минимум, включите stacktrace в вопрос и отметьте важные номера строк в коде. Также попробуйте сначала упростить код (см. SSCCE ).

364
задан 7 revs, 5 users 45% 5 June 2018 в 20:29
поделиться

19 ответов

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

Фон

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

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

А новый объем: let и with

С введением let оператор в [1 113] 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 не универсально доступен, это использование остается ограниченным новейшими браузерами и разработчиками, готовыми использовать transpilers. Однако мы можем легко моделировать это поведение с помощью [1 110]:

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. Обратите внимание, что переменные объявили в [1 115], блок не ограничен по объему к нему, в отличие от поведения блоков в C++ (в C, переменные должны быть объявлены в начале блока, так способом это подобно). Это поведение на самом деле весьма схоже с let синтаксис блока представленный в более ранних версиях браузеров Mozilla, но не широко принятое в другом месте.

516
ответ дан Buzinas 23 November 2019 в 00:09
поделиться

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

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

2
ответ дан 17 of 26 23 November 2019 в 00:09
поделиться

Я думаю, что полноценность 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();
}

С другой стороны, можно было утверждать, что Вы нарушаете Закон Demeter, но, с другой стороны, возможно, нет. Я отступаю =).

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

2
ответ дан Tom 23 November 2019 в 00:09
поделиться

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

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

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

6
ответ дан angry person 23 November 2019 в 00:09
поделиться

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

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

3
ответ дан Allain Lalonde 23 November 2019 в 00:09
поделиться

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

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

, я могу записать:

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

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

With someObject
    .Foo = ''
End With

по сравнению с [1 114]

With someObject
    Foo = ''
End With

Первый someObject.Foo; последний Foo в объеме внешний someObject.

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

14
ответ дан Sören Kuklau 23 November 2019 в 00:09
поделиться

Я думаю, что очевидное использование как ярлык. Если Вы, например, инициализация объекта, Вы просто сохраняете ввод большого "Имени объекта". Отчасти как шепелявость,"со слотами", который позволяет Вам записать

(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", но все еще.

6
ответ дан Sarien 23 November 2019 в 00:09
поделиться

Едва кажется стоящим того, так как можно сделать следующее:

var o = incrediblyLongObjectNameThatNoOneWouldUse;
o.name = "Bob";
o.age = "50";
26
ответ дан Allain Lalonde 23 November 2019 в 00:09
поделиться

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

3
ответ дан Svend 23 November 2019 в 00:09
поделиться

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

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

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

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

Ошибки происходят. Если Вы будете использовать с [1 110], то Вы в конечном счете сделаете это и увеличите возможности, которые приведет к сбою Ваша программа. Хуже, можно встретиться с рабочим кодом, который устанавливает глобальное в с блоком, или сознательно или через автора, не знающего об этой причуде конструкции. Это много похоже на обнаружение, проваливаются на переключателе, Вы понятия не имеете, предназначил ли автор это и нет никакого способа знать, если "фиксация" кода представит регрессию.

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

81
ответ дан jakub.g 23 November 2019 в 00:09
поделиться

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

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

with_ (object_name_here, function (_)
{
    _.a = "foo";
    _.b = "bar";
});
34
ответ дан John Millikin 23 November 2019 в 00:09
поделиться

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

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";

и т. д.

54
ответ дан 23 November 2019 в 00:09
поделиться

I have been using the with statement as a simple form of scoped import. Let's say you have a markup builder of some sort. Rather than writing:

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

You could instead write:

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

For this use case, I am not doing any assignment, so I don't have the ambiguity problem associated with that.

159
ответ дан 23 November 2019 в 00:09
поделиться

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

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

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

18
ответ дан 23 November 2019 в 00:09
поделиться

Оператор 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 не оказывает отрицательного влияния на производительность, но является хорошим, поскольку уменьшает размер кода, что влияет на использование памяти на мобильных устройствах.

4
ответ дан 23 November 2019 в 00:09
поделиться

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

2
ответ дан 23 November 2019 в 00:09
поделиться

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

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

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

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
ответ дан 23 November 2019 в 00:09
поделиться

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

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

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, выберут второй.

7
ответ дан 23 November 2019 в 00:09
поделиться

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

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

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

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

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
ответ дан 23 November 2019 в 00:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: