JavaScript'in “with” ifadesinin meşru kullanımları var mı?


369

Alan Storm'un withifadeyle ilgili cevabımın cevabı hakkındaki yorumları beni düşündürdü. Bu özel dil özelliğini kullanmak için nadiren bir neden buldum ve nasıl sorun yaratabileceği hakkında çok fazla düşünmemiştim. Şimdi, withtuzaklarından kaçarken nasıl etkili bir şekilde kullanabileceğimi merak ediyorum .

İfadeyi nerede withfaydalı buldunuz?


52
Asla kullanmam. Eğer yokmuş gibi yaparsam onsuz yaşamak daha kolaydır.
Nosredna

6
Bir zamanlar bunun için birçok geçerli kullanım olabilir. Ama tartışmalı. ES5 Strict kaldırıldı, withböylece artık böyle bir şey kalmadı.
Thomas Aylott

27
Burada ES5 Strict'in hala isteğe bağlı olduğunu belirtmek gerekir .
Shog9

5
Kesinlikle ES5'te 'ile' kaldırmak yerine, standardı değiştirmek daha iyi olmaz mıydı, böylece hiçbir değişken bulunamazsa, 'ile' içinde yapılan herhangi bir atama argüman nesnesine bağlanır mı?
JussiR

2
@JussiR: Muhtemelen. Ancak bunu yapmanın problemi, muhtemelen eski tarayıcılarda bazı şeyleri kıracağıdır.
Sune Rasmussen

Yanıtlar:


520

Bugün bana bir başka kullanım daha oldu, bu yüzden web'de heyecanla arama yaptım ve mevcut bir sözünü buldum: Blok Kapsamı içinde Değişkenleri Tanımlama .

Arka fon

JavaScript, C ve C ++ yüzeysel benzerliğine rağmen değişkenleri tanımlandıkları bloğa dahil etmez:

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

Döngüde bir kapatma bildirimi, bunun hatalara yol açabileceği yaygın bir görevdir:

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

For döngüsü yeni bir kapsam sunmadığından, aynı num- değeriyle 2- üç fonksiyon tarafından paylaşılacaktır.

Yeni bir kapsam: letvewith

Tanıtılmasıyla letiçinde açıklamaya ES6 , bu sorunlardan kaçınmak için gerektiğinde yeni kapsamını tanıtmak için kolay hale gelir:

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

Ya da:

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 evrensel olarak kullanılabilir olana kadar, bu kullanım en yeni tarayıcılarla ve transistör kullanmak isteyen geliştiricilerle sınırlı kalmaktadır. Ancak, bu davranışı aşağıdakileri kullanarak kolayca taklit edebiliriz 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);
   }
}

Döngü artık 0 ile 2 arasında değerlere sahip üç ayrı değişken oluşturarak amaçlandığı gibi çalışmaktadır . C ++ 'daki blokların davranışından farklı olarak, blok içinde bildirilen değişkenlerin ona dahil edilmediğini unutmayın (C'de, değişkenler, bir blok, yani bir şekilde benzer). Bu davranış aslında Mozilla tarayıcılarının önceki sürümlerinde sunulan bir letblok sözdizimine oldukça benzer , ancak başka yerlerde yaygın olarak kabul edilmemiştir.


15
Asla bir kelime ile kullanmayı düşünmedim, yasal görünüyor.
Matt Kantor

81
Bu gerçekten çok ölü. JavaScript'in kapsamıyla bu şekilde oynamayı hiç düşünmemiştim. Kodlamama tamamen yeni alanlar açıldı. Keşke 10 kez oy verebilseydim!
kizzx2

27
Hala karşı olanlar için, her zaman bir kapatma kullanılabilir:for (var i = 0; i < 3; ++i) { setTimeout ((function () { var num = i; return function () { alert (num); }; }) (), 10);}
Thomas Eding

4
Aslında, yukarıda bağlantılı sorun Mozilla olmayan çoğu tarayıcıda (Chrome, Safari, Opera, IE) görünür.
Max Shawabkeh

24
let IE deyimi desteği gerçekten şu anda benim pastırma kurtaracak, ben kullanmak olsun veya olmasın benim vicdanıyla mücadele ediyorum ile yerine. Asıl sorun bile sahip olmasıdır ile bir olarak let , ekstra bakım nedeniyle hala prototip zincirinde bir nesnenin kalıtsal özelliklerin alınması gerekmektedir. Örneğin var toString = function () { return "Hello"; }; with ({"test":1}) { console.log(toString()); };,. Kapsamında olan açıklamada, toString () bir kalıtsal bir özelliktir Nesne , bu nedenle açıkça tanımlanmış işlev çağrılmaz. Yine de büyük bir cevap, :-)
Andy E

161

Ben with ifadesi kapsam dahilinde içe aktarmanın basit bir biçimi olarak kullanıyorum. Diyelim ki bir çeşit biçimlendirici var. Yazmak yerine:

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

Bunun yerine şunu yazabilirsiniz:

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

Bu kullanım durumunda, herhangi bir ödev yapmıyorum, bu yüzden bununla ilgili belirsizlik problemim yok.


5
VB'de kullanıldığını böyle gördüm. (Ve bildiğim tek kullanım.)
Mateen Ulhaq

2
Bunun bir dezavantajı, markupbuilder nesnesinin dışında olan with bloğundaki bir değişkene başvurursanız, js motorunun önce zaten markupbuilder içinde araması ve performansı düşürmesi olacaktır.
Adam Thomas

3
Bu gerçekten tuval yolları ile çalışanlar için kodun azaltılmasına yardımcı olur.
Brian McCutchon

4
Bu kodun "ile" sürümü, kelimenin tam anlamıyla aynı "olmayan" sürümünden 240 kat daha yavaş çalışır . İşte bu yüzden insanlar bunun meşru bir kullanımı olmadığını söylüyor. Bazı noktalarda kodu daha güzel hale getiremediği için değil. Karşılaştırma ölçütü: jsfiddle.net/sc46eeyn
Jimbo Jonny

1
@McBrainy - Tam olarak çok daha yavaş çalışan kodu kullanmamanız gereken yer türüdür (bunun üzerinde yaptığım yoruma bakın). Süper tekrarlanan kod için kısayollara ihtiyacınız varsa bunları bildirebilirsiniz. Örneğin context.bezierCurveTo, yüz kere düz kullanıyorsanız , onu her aramak istediğinizde söyleyebilir var bc2 = context.bezierCurveTo;ve sonra gidebilirsiniz bc2(x,x,etc);. Bu withsüper hızlı olsa da, oldukça hızlı ve daha az ayrıntılı .
Jimbo Jonny

83

Önceki yorumlarımın da belirttiği withgibi, herhangi bir durumda ne kadar cazip olursa olsun güvenle kullanabileceğinizi düşünmüyorum . Sorun doğrudan burada ele alınmadığından tekrarlayacağım. Aşağıdaki kodu düşünün

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

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

Bu işlev çağrılarını dikkatle incelemeden, bu kod çalıştıktan sonra programınızın durumunun ne olacağını söylemenin bir yolu yoktur. Eğer user.namezaten kuruldu, artık olacak Bob. Ayarlanmadıysa, global namebaşlatılacak veya olarak değiştirilecek Bobve usernesne bir nameözellik olmadan kalacaktır .

Hatalar olur. Eğer kullanırsanız ile size en sonunda bunu ve program başarısız olur şansını artıracaktır. Daha da kötüsü, kasıtlı olarak veya yazar aracılığıyla yapının bu tuhaflığını bilmeden global ile blokta çalışan çalışma koduyla karşılaşabilirsiniz. Bu, bir anahtar üzerinde düşme ile karşılaşmak gibi, yazarın bunu amaçlayıp yapmadığına dair hiçbir fikriniz yok ve kodu "düzeltmenin" bir gerileme getirip getirmeyeceğini bilmenin bir yolu yok.

Modern programlama dilleri özelliklerle doludur. Yıllar süren kullanımdan sonra bazı özelliklerin kötü olduğu keşfedilmiştir ve bunlardan kaçınılmalıdır. Javascriptler withbunlardan biri.


18
Bu sorun, yalnızca nesnenin özniteliğine değerler atarken ortaya çıkar. Ama ya sadece değerleri okumak için kullanıyorsanız? Bu durumda kullanmanın uygun olduğunu iddia ediyorum.
airportyh

10
Aynı sorun Toby değerlerini okumak için de geçerlidir. Yukarıdaki kod snippet'inde, adın kullanıcı nesnesinde ayarlanıp ayarlanmadığını bilmediğinizden, genel adı mı yoksa kullanıcı adını mı okuduğunuzu bilemezsiniz.
Alan Storm

12
Değerleri okurken net bir öncelik kuralı vardır: nesne üzerindeki öznitelikler, kapsam dışındaki değişkenlerden önce kontrol edilir. Bu, işlevlerdeki kapsam belirleme değişkenlerinden farklı değildir. Atama ile ilgili 'asıl' ile ilgili asıl sorun, anladığım kadarıyla, öznitelik atamasının gerçekleşip gerçekleşmeyeceğinin, özniteliğin söz konusu geçerli nesne üzerinde var olup olmadığına bağlı olması, ki bu da bir runtime özelliği ve kolayca çıkarılamıyor. koda bakarak.
airportyh

1
Sanırım tam orada olabilirsin Toby. Yazma problemi, yapıdan tamamen çekilmem için yeterli.
Alan Storm

"Bu, bir şalterin üzerine düşmeyle karşılaşmak gibi, hiçbir fikriniz yok ..." - Öyleyse, switch'i () de yasaklayalım mı? ;-p
Sz.

66

Aslında bu withifadeyi son zamanlarda inanılmaz faydalı buldum . Şu anki projemi başlatana kadar bu teknik hiç aklıma gelmedi - JavaScript ile yazılmış bir komut satırı konsolu. Özel komutların konsola girilebildiği Firebug / WebKit konsol API'lerini taklit etmeye çalışıyordum, ancak global kapsamdaki herhangi bir değişkeni geçersiz kılmıyorlar. Shog9'un mükemmel cevabına yapılan yorumlarda bahsettiğim bir sorunun üstesinden gelmeye çalışırken bunu düşündüm .

Bu etkiyi elde etmek için, küresel kapsamın arkasındaki bir kapsamı "katmanlamak" için ifadeleri olan iki tane kullandım:

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

Bu teknikle ilgili en iyi şey, performans dezavantajlarının yanı sıra, withifadenin olağan korkularından muzdarip olmamasıdır , çünkü yine de küresel kapsamda değerlendiriyoruz - sözde kapsamımızın dışında değişkenlerin olma tehlikesi yok değiştirilmiş.

Sürprizime göre, başka bir yerde kullanılan aynı tekniği - Chromium kaynak kodu !

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

DÜZENLEME: Sadece Firebug kaynağını kontrol ettiler, daha da fazla katman için 4 ifadeleri bir araya getirdiler . Çılgın!

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
ama ecmascript5'in bunu yapmanıza engel olması endişe duyuyor. Bir ecmascript 5 çözümü var mı?
kybernetikos

@Adam: Bundan emin değilim. ES5 yalnızca katı modda bunun için bir hata atar, bu nedenle küresel olarak kesin mod bildirilmediyse acil bir sorun değildir. ES Harmony daha büyük bir sorun yaratabilir, ancak proxy gibi yeni bazı şeylerle düzeltilebilir.
Andy E

@AndyBu konu dışı olduğu için üzgünüm, ancak 'komut satırı konsolunuz JavaScript ile yazılmış' her yerde kullanılabilir mi?
kybernetikos

@Adam: hayır, değil. Her şey Windows Masaüstü Gadget'ları için bir dizi geliştirici araç olarak tasarlandı, ancak hiç bitirmedim (konsol çok iyi çalışıyor). WDG'lerin şu anda çok parlak bir geleceği olmasa da, bir noktada bitirebilirim.
Andy E

3
Birkaç hafta önce Chrome'daki konsol uygulamamızı blokla bazı sembol sihirine taşıdık çünkü blokla bazı ES6 özelliklerini engelledik :)
Alexey Kozyatinskiy

54

Evet, evet ve evet. Çok meşru bir kullanım var. İzlemek:

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

Temelde diğer herhangi bir DOM veya CSS kanca ile fantastik kullanımları vardır. "CloneNode" undefined olacak ve yolumdan çıkıp mümkün kılmaya karar vermedikçe küresel kapsama geri dönecek gibi değil.

Crockford'un hız şikayeti, ile yeni bir bağlamın yaratılmasıdır. Bağlamlar genellikle pahalıdır. Katılıyorum. Ancak yeni bir div oluşturduysanız ve css'inizi ayarlamak için elinizde bir çerçeve yoksa ve 15 veya daha fazla CSS özelliği el ile ayarlamanız gerekiyorsa, bir bağlam oluşturmak muhtemelen değişken oluşturma ve 15 dereferences'dan daha ucuz olacaktır:

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

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

vb...


5
+1 olarak da bence pek çok meşru kullanım var with. Ancak, bu özel durumda şunları yapabilirsiniz:element.style.cssText="background: black ; color: blue ; border: 1px solid green"
GetFree

5
Aynı şeyi extendjQuery veya Underscore.js: 'den basit yöntemi kullanarak bir satırda elde edebilirsiniz $.extend(element.style, {fontWeight: 'bold', fontSize: '1.5em', color: '#55d', marginLeft: '2px'}).
Trevor Burnham

9
@TrevorBurnham - jQuery'nin kullanılabilir olduğunu varsayacaksanız, sadece .css()yöntemini kullanırsınız ...
nnnnnn

4
Bu değişkenlerin küresel kapsama girmesini tam olarak engelleyen nedir? Bunun nedeni, tüm CSS stillerinin her zaman tüm öğelerde tanımlanmış olması ya da ne olması?
mpen

1
@Mark evet, her zaman bir özellik için özel bir stil yoksa null veya boş dizelerle değer olarak tanımlanır
Esailija

34

withBelirsizlik olmadan fayda sağlamak için küçük bir yardımcı işlev tanımlayabilirsiniz :

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

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

8
Aman tanrım, başım patladı! belirsizlik olmadan ? Buna oy vermeliyim dostum!
Jarrod Dixon

@Jarrod: bununla ilgili komik olan ne ( is.gd/ktoZ )? Bu siteyi kullananların çoğu benden daha akıllı, bu yüzden yanılıyorsam beni affet, ama bu kötü bilgi gibi görünüyor.
kuzgun

14
Ama bunu yapmanın yolunu anlamak artık daha uzun ve zor: var _ = obj_name_here; _.a = "fan"; _.b = "bar;
Rene Saarsoo

3
Rene: Bununla, "_" değişkenini dış kapsama maruz bırakarak potansiyel hatalara neden olacaksınız. Ayrıca nesne parametrelerini hesaplamada kullanılan geçici değişkenleri de ortaya koyacaktır.
John Millikin

30
with_Çamurlu iki katına çıkmış sürümünden (function(_){ _.a="foo"; })(object_here);(c / java tarzı blokları simüle etmenin standart yolu) daha fazla hata elde edersiniz . Bunun yerine bunu kullanın.
mk.

25

Aşağıdakileri yapabileceğiniz için buna değmez gibi görünüyor:

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

1
Bu benim için bir anlam ifade etmiyordu. JavaScript konusunda uzman
olmasam da

@WmasterJ Açıklık için bu gönderiye
Dennis

8
Uzun değişken adları tek kullanımlık durum değildir with.
Chris

18

Hiç kullanmıyorum, bir neden görmüyorum ve tavsiye etmiyorum.

Sorun withtam o sayısız sözcük optimizasyonlar engelleyen bir ECMAScript uygulaması gerçekleştirebilirsiniz. Hızlı JIT tabanlı motorların yükselişi göz önüne alındığında, bu sorun yakın gelecekte daha da önemli hale gelecektir.

Daha withtemiz yapılara izin verebilir gibi görünebilir (örneğin, ortak bir anonim işlev sarıcı yerine yeni bir kapsam sunarken veya ayrıntılı örtüşme yerine), ancak buna değmez . Azalan performansın yanı sıra, her zaman yanlış bir nesnenin bir özelliğine (enjekte edilen kapsamdaki bir nesnede özellik bulunmadığında) atanma ve belki de küresel değişkenleri yanlış sokma tehlikesi vardır. IIRC, ikinci mesele Crockford'u kaçınmak için motive eden konudur with.


6
Performanslı bogeyman sık sık, neredeyse küresel şey kadar sık ​​sık boğulur ... Bahsettiğimiz JavaScript olduğu göz önüne alındığında, bana her zaman tuhaf geliyor . Performans vuruşunun bu kadar çok dikkat çekmek için gerçekten dramatik olduğunu varsayıyorsunuz , ancak ... with(){}Diğer yanıtlarda verilenler gibi yapıların maliyetinde, modern tarayıcılarda zor rakamlar varsa , görmek isterim onları!
Shog9

6
Javascript bağlamında neden garip? :) Ve evet, dramatik. Bir düşünün - bir uygulamanın parantez içindeki bir ifadeyi değerlendirmesi, nesneye dönüştürmesi, geçerli kapsam zincirinin önüne yerleştirmesi, bloğun içindeki ifadeyi değerlendirmesi ve ardından kapsam zincirini normale döndürmesi gerekir. Bu çok iş. Yüksek düzeyde optimize edilmiş düşük seviyeli bir koda dönüştürülebilen basit bir özellik aramasından çok daha fazlası. İşte farkımı
kangax

5
@kangax: Birçok programcının kodlarındaki küçük verimlilikleri takıntı haline getirmenin, daha büyük rutin veya programın performansı üzerinde gözle görülür bir etkisi olmasa bile, geleneksel bir C ++ arka planından geliyorum. Bir rutin performansının büyük bir bölümünün VM uygulamasına bağlı olabileceği JavaScript bağlamında bana garip geliyor. JS programcılarının kurulum maliyeti konusundaki endişeler nedeniyle anonim bir işlevden kaçınacağı birkaç örnek gördüm, ancak bu çok hassas kod alanları için ayrılmış kural değil istisna gibi görünüyor.
Shog9

5
Bununla birlikte, maliyeti konusunda kesinlikle haklısınız with(){}: yeni bir kapsam oluşturmak with, test ettiğim her tarayıcıda çok pahalı. Çok sık denilen herhangi bir kodda bundan kaçınmak istersiniz. Buna ek olarak, Chrome bir with()kapsamda yürütülen herhangi bir kod için dramatik bir hit sergiledi . İlginç bir şekilde IE, with()bloklar içindeki kod için en iyi performans özelliklerine sahipti : kurulum maliyetini with()hesaba katarak, IE6 ve IE8 VM'lerde üye erişiminin en hızlı yolunu sağlar (bu VM'ler genel olarak en yavaş olanıdır). İyi şeyler, teşekkürler ...
Shog9

5
FWIW: Kurulum maliyetleri hesaba katıldığında aynı testler dizisi: jsbin.com/imidu/edit Değişken erişim with(), Chrome'da neredeyse daha yavaş ve IE'de iki kat daha hızlıdır ...!
Şok9

13

Visual Basic.NET benzer bir Withifadeye sahiptir. Bunu kullanmanın daha yaygın yollarından biri, bir dizi özelliği hızlı bir şekilde ayarlamaktır. Onun yerine:

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

, Yazabilirim:

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

Bu sadece tembellik meselesi değil. Ayrıca çok daha okunabilir bir kod oluşturur. Ve JavaScript'ten farklı olarak, ifadeden etkilenen her şeyin bir .(nokta) ile ön ekini yapmanız gerektiğinden belirsizliğe maruz kalmaz . Yani, aşağıdaki ikisi açıkça farklıdır:

With someObject
    .Foo = ''
End With

vs.

With someObject
    Foo = ''
End With

Birincisi someObject.Foo; ikincisi dışarıdakiFoo kapsamdadır . someObject

Belirsizlik riski çok yüksek olduğundan, JavaScript'in ayrım eksikliğinin Visual Basic'in varyantından çok daha az kullanışlı olduğunu düşünüyorum. Bunun dışında, withdaha iyi okunabilirlik için hala güçlü bir fikir.


2
Yeterince doğru. Yine de sorusuna cevap vermiyor. Yani konu dışı.
Allain Lalonde

6
Bu da aklımdan geçiyordu. Birisi söylemek zorundaydı . Neden JavaScript sadece noktaya sahip olamaz?
Carson Myers

Kabul ediyorum, nokta gösterimi üstündür, JavaScript'in kullanılmasını ister. +1


7

"İle" kullanmak kodunuzu daha kuru hale getirebilir.

Aşağıdaki kodu göz önünde bulundurun:

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

Aşağıdakilere kurutabilirsiniz:

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

Sanırım bu, okunabilirlik veya ifade etme tercihiniz olup olmadığına bağlıdır.

İlk örnek daha okunaklı ve muhtemelen çoğu kod için önerilir. Ama çoğu kod zaten oldukça uysal. İkincisi biraz daha belirsizdir, ancak kod boyutunu ve gereksiz değişkenleri azaltmak için dilin etkileyici doğasını kullanır.

Java veya C # gibi kişilerin ilk yolu (object.member) seçeceğini ve Ruby veya Python'u tercih edenlerin ikincisini seçeceğini hayal ediyorum.


Maalesef, bir yıl önce aynı örneği zaten gönderdiğini fark etmedim. Performans sorunları bir yana, "ile" biraz daha zor olma pahasına güzel DRY kodu yapar. Diğer geliştiricilerle veya çoğu üretim koduyla işbirliği için "with" anahtar kelimesinden kaçınmanın iyi bir uygulama olduğunu düşünüyorum. Ancak, uzman düzeyinde programcılar ile çalışıyorsanız ve potansiyel verimsizlikleri nasıl önleyeceğinizi anlıyorsanız, elbette "ile" şehre gidin.
Jonah

6

Bence bariz kullanım bir kısayol. Örneğin, bir nesneyi başlatırsanız, çok sayıda "NesneAdı" yazmanız yeterlidir. Bir çeşit lisp'in "yuvaları" gibi

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

ki bu yazı ile aynı

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

Bunun neden bir kısayol olduğu daha açıktır ve diliniz "Objectname.foo" 'ya izin veriyor ancak yine de.


1
lisp kodunu görmek harika! Ben javascript "ile" açıkça bir dil olarak düzeni kökleri esinlenerek düşünüyorum, ama ne yazık ki bir javascript soru LISP göndermek için downvoted.
Fire Crow

1
Temel prensip üzerine oylama. 'ile' olağanüstü güçlü bir yapıdır. Ancak çoğu JS çalışanı kapanmaları anlamıyor ve JS'nin üzerine gülünç derecede karmaşık Java sınıfı kalıtım sistemleri yazıyor - bu yüzden 'ile' sunduğu metaprogramlama potansiyelinin nasıl farkında olabilirler?
jared

Tabii ki with-slotshangi yuvaları kullandığınızı belirtmenizi gerektirirken oysa withçalışma zamanında bağlı olan yuvaları kullanır.
Samuel Edwin Ward

6

Delphi ile deneyime sahip, bunu kullanarak söyleyebilirim ile muhtemelen güvenliğini doğrulamak için statik kod analizi erişimi olan JavaScript asgarileştirir algoritması çeşit tarafından gerçekleştirilen bir son çare boyutu optimizasyonu, olmalıdır.

Eğer with ifadesinin liberal kullanımı ile karşılaşabilirsiniz kapsam sorunları a ** bir kraliyet ağrısı olabilir ve kimsenin kodunda neler olduğunu anlamak için bir hata ayıklama oturumu yaşamak istemem. , yalnızca hedeflediğiniz genel veya dış kapsam değişkeniniz yerine bir nesne üyesi veya yanlış yerel değişken yakaladığını bulmak için.

VB ile bu kapsam belirleme belirsizliği giderecek noktalar ihtiyacı var, ancak Delphi ki açıklamada, iyidir ile deyimi bir hairtrigger ile dolu bir silah gibi, ve javascript biri aynı uyarıyı gerektirecek yeterli benzerlik sanki bana bakıyor.


5
Deyimli javascript Delphi olandan daha kötüdür. Delphi ile, object.member gösterimi kadar hızlı (daha hızlı değilse) gerçekleştirir. Javascript, ile eşleşen üyeleri kontrol etmek için kapsam yürümek zorunda, böylece her zaman object.member gösterimi daha yavaş yapar.
Martijn

5

İle kullanılması önerilmez ve ECMAScript 5 katı modda yasaktır. Önerilen alternatif, özelliklerini geçici bir değişkene erişmek istediğiniz nesneyi atamaktır.

Kaynak: Mozilla.org


4

With ifadesi kod boyutunu azaltmak veya özel sınıf üyeleri için kullanılabilir, örnek:

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

Kapsamı değiştirmek isterseniz, çalışma zamanında manipüle edebileceğiniz kendi global kapsamınız için gerekli olan with-ifadesi çok kullanışlıdır. Sabitleri veya genellikle "toUpper", "toLower" veya "isNumber", "clipNumber" gibi bazı yardımcı işlevleri koyabilirsiniz.

Sık sık okuduğum kötü performans hakkında: Bir fonksiyonun kapsamının performans üzerinde herhangi bir etkisi olmayacaktır, aslında FF'mde kapsamlı bir fonksiyon, kapatılmamış olandan daha hızlı çalışır:

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

Bu nedenle, yukarıda belirtilen şekilde kullanılan with-ifadesinin performans üzerinde olumsuz bir etkisi yoktur, ancak kod boyutunu azalttığı için iyi bir olanı, mobil cihazlarda bellek kullanımını etkiler.


3

İle birlikte kullanmak, her şey artık arama için ekstra bir kapsamda sarıldığından, birçok uygulamada kodunuzu yavaşlatır. JavaScript ile birlikte kullanmanın meşru bir nedeni yoktur.


5
Erken optimizasyon. Eğer sayıları çıtırtı sürece "yavaş" iddia etmeyin; herhangi bir ek yük muhtemelen hem modern hem de eski js impls üzerinde önemsizdir.
mk.

2
Sizden başka geliştiriciler için neyin sorun olmayabileceğine bağlı olarak sonucunuza kesinlikle katılmıyorum.
Dave Van den Eynde

4
@mk: ok, burada sizin için sayı çıtırtısı: 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;ortalama 2500 verirken 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;, ortalama 750 verir, 3 kattan daha yavaş kullanırken bunu yapar.
yorick

3
Bunu gördüğümde bunları Chrome 23'te konsolda çalıştırdım. Aldığım sonuçlar withkod için 1138 ve olmadan 903 idi. Sıkı bir döngüde bile bu küçük farkla, performans konusunda endişelenmeden önce kodlama basitliğine ve duruma göre yeniden düzenleme kolaylığına dayalı bir seçim yapacağım.
Plynx

3

Ben with-deyimi bir şablon dili JavaScript dönüştürürken kullanışlı gelebilir düşünüyorum. Örnek için JST içinde temel2 ama bunu daha sık gördük.

Bunu açıklama olmadan programlayabileceğine katılıyorum. Ancak herhangi bir sorun yaratmadığı için meşru bir kullanımdır.


3

Göreceli olarak karmaşık bir ortamda çalışan kodu bir kaba koymak için iyidir: Ben "pencere" için yerel bir bağlanma yapmak için kullanın ve böyle bir web tarayıcısı için amaçlanan kod çalıştırmak için kullanın.


3

Nesne gerçek kullanımı ilginç, bir kapatma kullanmak için bir açılan yerine gibi ilginç olduğunu düşünüyorum

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

veya kapanışa eşdeğer ifade içeren

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

Ben gerçek risk yanlışlıkla with deyiminin bir parçası olmayan değişkenleri minimize olduğunu düşünüyorum, bu yüzden nesne değişmezi ile geçmesini seviyorum, tam olarak kodunda eklenen bağlamda ne olacağını görebilirsiniz.


3

Bu belirsizliğin bir kısmını şu withifadeyle ortadan kaldıran bir "birleştirme" işlevi oluşturdum :

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

Bunu benzer şekilde kullanabilirim with, ancak etkilemeyi düşünmediğim herhangi bir kapsamı etkilemeyeceğini biliyorum.

Kullanımı:

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

Bazı kısa kod parçaları için, ben gibi trigonometrik fonksiyonlar kullanmak istiyorum sin, cosderece modda yerine radyan modunda vb. Bu amaçla bir AngularDegreenesne kullanıyorum:

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

Sonra trigonometrik fonksiyonları bir withblokta başka bir dil gürültüsü olmadan derece modunda kullanabilirim :

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

Bunun anlamı: Bir nesneyi, doğrudan erişim için sınırlı bir kod bölgesinde etkinleştirdiğim bir işlevler koleksiyonu olarak kullanıyorum. Bunu faydalı buluyorum.


withdeyimi bu şekilde kullanmak hoş bir fikir değildir , sadece kodun okunmasını zorlaştırır, çünkü hangi işlevin küresel olduğunu ve hangi işlevin nesne kapsamında çağrıldığını bilmiyorsanız, bu nedenle herhangi bir işlev tarafından herhangi bir şekilde tanımlanmamışsa Nesne kapsamı daha sonra küresel isim alanında erişmeye çalışacaktır
Saket Patel

Kapsam belirleme sorununun farkında olarak, bunu hala yararlı buluyorum. Kodu okuyan bir matematikçi, yukarıdaki formülün küresel trigonometride "birbirini takip eden 4 parçanın yasası" nın bir uygulaması olduğunu doğrudan görmek istiyor. Katı alternatif formülü gizler: 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;aynı sonucu verir, ancak korku budur.
rplantiko

@rplantiko Akılda tutulması gereken şey, çoğu insanın kendisiyle rahat hissetmediğidir. Kod yazmazsanız, hiç kimsenin dokunmayacağı bir şey. Ayrıca, size withgüdük olacağını birkaç kullanım gösterebilirim eminim .
Juan Mendes

2

Bence yararlılığı withkodunuzun ne kadar iyi yazılmış olduğuna bağlı olabilir. Örneğin, şöyle görünen bir kod yazıyorsanız:

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

withbunu yaparak kodun okunabilirliğini artıracağını iddia edebilirsiniz :

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

Tersine, Demeter Yasasını ihlal ettiğiniz iddia edilebilir , ancak yine de belki değil. Ben kazıyorum =).

Her şeyden önce, Douglas Crockford önerir biliyoruz değil kullanarak with. Burada ilgili blog yayınına withve alternatiflerine göz atmanızı tavsiye ediyorum .


Yanıtınız için teşekkürler, Tom. Crockford'un tavsiyesini okudum ve mantıklı olsa da şimdiye kadar devam ediyor. Ben {} ile gerçek gücünün kapsamı manipüle etmek için kullanılabileceği şekilde - doekman tarafından dolaylı olarak dokunulduğu fikrine geliyorum ...
Şok9

2

İle kullanmak sadece object.member yazmaktan daha okunabilir olduğunu gerçekten görmüyorum. Daha az okunabilir olduğunu sanmıyorum, ama daha okunabilir olduğunu da sanmıyorum.

Lassevk'in dediği gibi, kesinlikle çok açık "object.member" sözdizimini kullanmaktan çok daha fazla hataya açık olacağını görebiliyorum.


1

W3schools http://www.w3schools.com/js/js_js_form_validation.asp adresindeki bir formun doğrulanmasını, 'e-posta' adında bir girdi bulmak için nesne formunun "taranması" gereken yerde görmelisiniz.

Ama ben herhangi bir form alan adı veya adından bağımsız olarak, tüm alanları boş değil doğrulamak HERHANGİ bir form almak için değiştirdim. Sadece metin alanlarını test ettim.

Ancak with () işleri kolaylaştırdı. İşte kod:

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'in Coco çatalı bir withanahtar kelimeye sahiptir, ancak yalnızca this( @CoffeeScript / Coco'daki gibi yazılabilir ) blok içindeki hedef nesneye ayarlar . Bu, belirsizliği ortadan kaldırır ve ES5'in katı mod uyumluluğunu sağlar:

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

0

İşte bunun için iyi bir kullanım with: bir Nesne Değişmezine, bu Nesnede depolanan değerlere dayalı yeni öğeler ekleme. İşte bugün kullandığım bir örnek:

Kullanılabilecek bir dizi olası karo vardı (açıklıklar üst, alt, sol veya sağa bakacak şekilde) ve oyunun başında her zaman yerleştirilecek ve kilitlenecek karoların bir listesini eklemenin hızlı bir yolunu istedim. . types.tbrListedeki her tür için yazmaya devam etmek istemedim , bu yüzden yeni kullandım 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

Requir.js kullanırken açıklığı açıkça yönetmek zorunda kalmamak için ile kullanabilirsiniz:

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

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

Requijs.declare uygulaması:

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

Andy E'nin Shog9'un cevabının yorumlarında işaret ettiği gibi, bu potansiyel olarak beklenmedik davranış, withbir nesne değişmeziyle kullanıldığında ortaya çıkar :

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

Beklenmedik davranış zaten birwith .

Bu tekniği gerçekten hala kullanmak istiyorsanız, en azından boş prototipli bir nesne kullanın.

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

Ancak bu yalnızca ES5 + 'da çalışır. Ayrıca kullanmayın with.


0

Ben kullanıcıların uygulama bölümlerinin davranışını değiştirmek için kod yüklemek için izin verecek bir proje üzerinde çalışıyorum. Bu senaryoda, withkodlarını ile uğraşmak istiyorum kapsam dışında bir şey değiştirmesini engellemek için bir cümle kullanıyorum . Bunu yapmak için kullandığım kodun (basitleştirilmiş) kısmı:

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

Bu kod, (bir şekilde) kullanıcı tanımlı kodun, window ne de bir kapatma yoluyla yerel değişkenlerimden herhangi .

Sadece bilge bir kelime olarak, ben hala küresel kapsama erişmek için diğer gizli görgü kullanmadığından emin olmak için kullanıcı tarafından gönderilen kod üzerinde statik kod kontrolleri yapmak zorunda. Örneğin, aşağıdaki kullanıcı tanımlı kod aşağıdakilere doğrudan erişim sağlar window:

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


0

Benim

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
}

indirgenir

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

Çok düşük kaliteli koda güvenebilir misiniz? Hayır, bunun kesinlikle okunamaz hale getirildiğini görüyoruz. Bu örnek inkar edilemez bir şekilde okunabilirliği doğru alıyorsam with-deyimine gerek olmadığını kanıtlar;)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.