Apakah ada kegunaan yang sah untuk pernyataan "dengan" JavaScript?

369

Komentar Alan Storm sebagai jawaban atas jawaban saya tentangwith pernyataan itu membuat saya berpikir. Saya jarang menemukan alasan untuk menggunakan fitur bahasa khusus ini, dan tidak pernah memikirkan bagaimana hal itu dapat menyebabkan masalah. Sekarang, saya ingin tahu bagaimana saya bisa memanfaatkannya secara efektif with, sambil menghindari perangkapnya.

Di mana Anda menemukan withpernyataan itu berguna?

Shog9
sumber
52
Saya tidak pernah menggunakannya. Lebih mudah hidup tanpanya jika saya berpura-pura tidak ada.
Nosredna
6
Mungkin pernah ada banyak kegunaan yang valid untuk itu. Tapi ini bisa diperdebatkan. ES5 Ketat dihapus withsehingga tidak ada lagi hal seperti itu.
Thomas Aylott
27
Patut dicatat di sini bahwa ES5 Strict masih opsional .
Shog9
5
Alih-alih menghapus 'dengan' di ES5 ketat, bukankah lebih baik mengubah standar sehingga jika tidak ada variabel yang ditemukan, tugas apa pun yang dibuat di dalam 'dengan' terikat ke objek argumen?
JussiR
2
@ YussiR: Mungkin. Tetapi masalah dalam melakukan itu adalah, bahwa hal itu mungkin akan merusak hal-hal di browser lama.
Sune Rasmussen

Jawaban:

520

Penggunaan lain terjadi pada saya hari ini, jadi saya mencari di web dengan penuh semangat dan menemukan penyebutan yang ada: Mendefinisikan Variabel di dalam Lingkup Blok .

Latar Belakang

JavaScript, meskipun memiliki kemiripan yang dangkal dengan C dan C ++, tidak membuat variabel pada blok yang didefinisikan dalam:

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

Mendeklarasikan penutupan dalam satu lingkaran adalah tugas umum di mana ini dapat menyebabkan kesalahan:

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

Karena for loop tidak memperkenalkan lingkup baru, hal yang sama num- dengan nilai 2- akan dibagi oleh ketiga fungsi.

Lingkup baru: letdanwith

Dengan diperkenalkannya letpernyataan dalam ES6 , menjadi mudah untuk memperkenalkan ruang lingkup baru bila perlu untuk menghindari masalah ini:

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

Atau bahkan:

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

Hingga ES6 tersedia secara universal, penggunaan ini tetap terbatas pada browser dan pengembang terbaru yang bersedia menggunakan transpiler. Namun, kami dapat dengan mudah mensimulasikan perilaku ini menggunakan 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);
   }
}

Loop sekarang berfungsi sebagaimana mestinya, membuat tiga variabel terpisah dengan nilai dari 0 hingga 2. Perhatikan bahwa variabel yang dideklarasikan di dalam blok tidak dicakup, tidak seperti perilaku blok di C ++ (dalam C, variabel harus dideklarasikan pada awal sebuah blok, jadi agak mirip). Perilaku ini sebenarnya sangat mirip dengan letsintaks blok yang diperkenalkan di versi browser Mozilla sebelumnya, tetapi tidak diadopsi secara luas di tempat lain.

Shog9
sumber
15
Tidak pernah terpikir untuk menggunakan dengan huruf literal, tampaknya sah-sah saja.
Matt Kantor
81
Ini benar-benar mati. Saya tidak pernah berpikir untuk bermain dengan cakupan JavaScript dengan cara ini. Benar-benar memperluas area baru ke pengkodean saya. Seandainya saya bisa menang 10 kali!
kizzx2
27
Bagi mereka yang masih menentang, kita selalu bisa menggunakan penutupan:for (var i = 0; i < 3; ++i) { setTimeout ((function () { var num = i; return function () { alert (num); }; }) (), 10);}
Thomas Eding
4
Sebenarnya, masalah yang ditautkan di atas muncul di sebagian besar browser non-Mozilla (Chrome, Safari, Opera, IE).
Max Shawabkeh
24
biarkan dukungan pernyataan di IE benar-benar akan menghemat daging saya sekarang, aku berjuang dengan hati nurani saya pada apakah atau tidak untuk menggunakan dengan sebaliknya. Masalah sebenarnya adalah bahwa bahkan dengan dengan sebagai membiarkan , perhatian ekstra masih harus diambil karena sifat-sifat yang diwarisi dari suatu objek pada rantai prototipe. Sebagai contoh var toString = function () { return "Hello"; }; with ({"test":1}) { console.log(toString()); };,. Dalam lingkup pernyataan with , toString () adalah properti yang diwarisi dari Object , jadi fungsi yang didefinisikan secara eksplisit tidak dipanggil. Masih jawaban yang bagus :-)
Andy E
161

Saya telah menggunakan pernyataan with sebagai bentuk sederhana impor scoped. Katakanlah Anda memiliki semacam pembuat markup. Daripada menulis:

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

Anda bisa menulis:

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

Untuk kasus penggunaan ini, saya tidak melakukan tugas apa pun, jadi saya tidak memiliki masalah ambiguitas terkait dengan itu.

airportyh
sumber
5
Begitulah cara saya melihatnya digunakan di VB. (Dan satu-satunya kegunaan yang saya sadari.)
Mateen Ulhaq
2
Kelemahan dari hal ini adalah jika Anda mereferensikan variabel di dalam blok with yang berada di luar objek markupbuilder, mesin js akan terlebih dahulu mencarinya di dalam markupbuilder, sehingga mengurangi kinerja.
Adam Thomas
3
Ini sangat membantu mengurangi kode untuk mereka yang bekerja dengan jalur kanvas.
Brian McCutchon
4
Versi "with" dari kode itu benar-benar berjalan lebih dari 240 kali lebih lambat di mesin saya daripada versi "non-dengan" yang sama. Itulah sebabnya orang mengatakan tidak ada gunanya untuk itu. Bukan karena itu tidak dapat membuat kode lebih cantik di beberapa tempat. Lihat patokan: jsfiddle.net/sc46eeyn
Jimbo Jonny
1
@ McBrainy - Itulah jenis tempat Anda seharusnya tidak menggunakan kode yang menjalankan banyak lebih lambat (lihat komentar saya hanya membuat di atas yang satu ini). Jika Anda memerlukan pintasan untuk kode super berulang, Anda dapat mendeklarasikannya. Misalnya jika menggunakan context.bezierCurveToseratus kali berturut-turut, Anda dapat mengatakan var bc2 = context.bezierCurveTo;dan kemudian pergi bc2(x,x,etc);setiap kali Anda ingin menyebutnya. Itu cukup cepat dan bahkan kurang bertele-tele, sementara withsuper lambat.
Jimbo Jonny
83

Seperti yang ditunjukkan oleh komentar saya sebelumnya, saya tidak berpikir Anda dapat menggunakan withdengan aman tidak peduli seberapa menggoda itu dalam situasi tertentu. Karena masalahnya tidak langsung dibahas di sini, saya akan mengulanginya. Pertimbangkan kode berikut

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

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

Tanpa hati-hati menyelidiki panggilan fungsi itu, tidak ada cara untuk mengetahui apa yang akan program Anda setelah kode ini berjalan. Jikauser.name sudah diatur, sekarang akan menjadi Bob. Jika tidak disetel, global nameakan diinisialisasi atau diubah Bobdan userobjek akan tetap tanpa nameproperti.

Bug terjadi. Jika Anda gunakan dengan akhirnya Anda akan melakukan ini dan meningkatkan peluang program Anda akan gagal. Lebih buruk lagi, Anda mungkin menemukan kode kerja yang menetapkan global dalam blok with, baik sengaja atau melalui penulis tidak tahu tentang kekhasan konstruksi ini. Ini seperti menghadapi jatuh melalui saklar, Anda tidak tahu apakah penulis bermaksud ini dan tidak ada cara untuk mengetahui apakah "memperbaiki" kode akan memperkenalkan regresi.

Bahasa pemrograman modern penuh dengan fitur. Beberapa fitur, setelah bertahun-tahun digunakan, diketahui buruk, dan harus dihindari. Javascript withadalah salah satunya.

Alan Storm
sumber
18
Masalah ini hanya muncul ketika Anda menetapkan nilai ke atribut objek. Tetapi bagaimana jika Anda menggunakannya hanya untuk membaca nilai? Saya berpendapat bahwa tidak apa-apa untuk menggunakannya dalam kasus itu.
airportyh
10
Masalah yang sama berlaku untuk membaca nilai-nilai Toby. Dalam cuplikan kode di atas Anda tidak tahu apakah nama diatur pada objek pengguna, jadi Anda tidak akan tahu apakah Anda membaca nama global, atau nama pengguna.
Alan Storm
12
Dengan nilai-nilai bacaan ada aturan presedensi yang jelas: atribut pada objek diperiksa sebelum variabel di luar lingkup. Ini tidak ada bedanya dengan pelingkupan variabel dalam fungsi. Masalah sebenarnya dengan penugasan dan 'dengan', seperti yang saya pahami, terletak pada kenyataan apakah penugasan atribut terjadi atau tidak tergantung pada apakah atribut tersebut ada pada objek saat ini, yang merupakan properti runtime dan tidak dapat disimpulkan dengan mudah dengan melihat kodenya.
airportyh
1
Saya pikir Anda mungkin ada di sana, Toby. Masalah menulis sudah cukup bagi saya untuk menghindar sepenuhnya dari konstruk.
Alan Storm
"Ini seperti menemukan jatuh melalui saklar, Anda tidak tahu ..." - Jadi, mari kita larangan beralih (), juga? ;-p
Sz.
66

Saya sebenarnya menemukan withpernyataan itu sangat berguna baru-baru ini. Teknik ini tidak pernah benar-benar terpikir oleh saya sampai saya memulai proyek saya saat ini - konsol baris perintah yang ditulis dalam JavaScript. Saya mencoba untuk meniru API konsol Firebug / WebKit di mana perintah khusus dapat dimasukkan ke dalam konsol tetapi mereka tidak menimpa variabel dalam lingkup global. Saya memikirkan hal ini ketika mencoba mengatasi masalah yang saya sebutkan di komentar atas jawaban Shog9 yang sangat baik .

Untuk mencapai efek ini, saya menggunakan dua dengan pernyataan untuk "melapisi" lingkup di balik lingkup global:

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

Hal yang hebat tentang teknik ini adalah bahwa, selain dari kerugian kinerja, itu tidak menderita ketakutan biasa with pernyataan itu, karena kita tetap mengevaluasi dalam lingkup global - tidak ada bahaya variabel di luar ruang lingkup pseudo kita dari menjadi diubah.

Saya terinspirasi untuk mengirim jawaban ini ketika, yang mengejutkan saya, saya berhasil menemukan teknik yang sama dengan yang digunakan di tempat lain - kode sumber 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: Baru memeriksa sumber Firebug, mereka menghubungkan 4 dengan pernyataan bersama untuk lebih banyak lapisan. Gila!

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);" +
    "}" +
"}}}}";
Andy E
sumber
1
tetapi khawatir bahwa ecmascript5 menghentikan Anda dari melakukan ini. Apakah ada solusi ecmascript 5?
kybernetikos
@ Adam: Saya tidak yakin tentang itu. ES5 hanya melempar kesalahan untuk ini dalam mode ketat, jadi itu bukan masalah langsung jika Anda tidak memiliki mode ketat dinyatakan secara global. ES Harmony mungkin menimbulkan masalah yang lebih besar, tetapi mungkin bisa diperbaiki dengan beberapa hal baru seperti proxy.
Andy E
@AndyE maaf ini bukan topik, tetapi apakah 'konsol baris perintah Anda ditulis dalam JavaScript' tersedia di mana saja?
kybernetikos
@ Adam: tidak, tidak. Semuanya dimaksudkan untuk menjadi seperangkat alat pengembang untuk Windows Desktop Gadgets, tapi saya tidak pernah menyelesaikannya (meskipun, konsol bekerja dengan sangat baik). Saya mungkin menyelesaikannya di beberapa titik, meskipun WDG tidak memiliki masa depan yang sangat cerah saat ini.
Andy E
3
Beberapa minggu yang lalu kami memindahkan implementasi konsol kami di Chrome dari dengan blok ke beberapa simbol simbol karena dengan blokir memblokir beberapa fitur ES6 :)
Alexey Kozyatinskiy
54

Ya, ya dan ya. Ada penggunaan yang sangat sah. Menonton:

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

Pada dasarnya setiap kait DOM atau CSS lainnya adalah penggunaan yang fantastis. Ini tidak seperti "CloneNode" yang tidak terdefinisi dan kembali ke lingkup global kecuali Anda menyimpang dari jalan dan memutuskan untuk membuatnya mungkin.

Keluhan kecepatan Crockford adalah bahwa konteks baru dibuat dengan. Konteks umumnya mahal. Saya setuju. Tetapi jika Anda baru saja membuat div dan tidak memiliki kerangka kerja untuk mengatur css Anda dan perlu menyiapkan 15 atau lebih properti CSS dengan tangan, maka membuat konteks mungkin akan lebih murah daripada pembuatan variabel dan 15 referensi:

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

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

dll ...


sumber
5
+1 karena saya juga berpikir ada banyak kegunaan yang sah dari with. Namun, dalam kasus khusus ini Anda hanya bisa melakukan:element.style.cssText="background: black ; color: blue ; border: 1px solid green"
GetFree
5
Anda bisa mencapai hal yang sama dalam satu baris dengan menggunakan sederhana extendmetode baik dari jQuery atau Underscore.js: $.extend(element.style, {fontWeight: 'bold', fontSize: '1.5em', color: '#55d', marginLeft: '2px'}).
Trevor Burnham
9
@TrevorBurnham - Jika Anda akan menganggap jQuery tersedia, Anda hanya akan menggunakan .css()metode ini ...
nnnnnn
4
Apa sebenarnya yang mencegah variabel-variabel ini pergi ke lingkup global? Apakah hanya karena semua gaya CSS selalu didefinisikan pada semua elemen, atau apa?
mpen
1
@ Mark ya, mereka selalu didefinisikan, dengan nol atau string kosong sebagai nilai jika tidak ada gaya khusus untuk properti
Esailija
34

Anda dapat mendefinisikan fungsi pembantu kecil untuk memberikan manfaat withtanpa ambiguitas:

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

with_ (object_name_here, function (_)
{
    _.a = "foo";
    _.b = "bar";
});
John Millikin
sumber
8
OMG, KEPALA SAYA DIJELASKAN! tanpa ambiguitas? Harus memilih itu, Bung!
Jarrod Dixon
@Jarrod: apa yang lucu tentang ini ( is.gd/ktoZ )? Kebanyakan orang yang menggunakan situs ini lebih pintar daripada saya, jadi maafkan saya jika saya salah, tapi ini sepertinya informasi yang buruk.
gagak
14
Tapi itu hanya lebih lama dan lebih sulit untuk memahami cara melakukan: var _ = obj_name_here; _.a = "foo"; _.b = "bar;
Rene Saarsoo
3
Rene: Dengan itu, Anda akan mengekspos variabel "_" ke lingkup luar, menghasilkan bug potensial. Ini juga akan mengekspos variabel sementara yang digunakan dalam menghitung parameter objek.
John Millikin
30
Anda akan mendapatkan lebih banyak bug dari with_menjadi versi berlipat ganda berlumpur (function(_){ _.a="foo"; })(object_here);(cara standar untuk mensimulasikan blok gaya c / java). Gunakan itu sebagai gantinya.
mk.
25

Tampaknya tidak sepadan karena Anda dapat melakukan hal berikut:

var o = incrediblyLongObjectNameThatNoOneWouldUse;
o.name = "Bob";
o.age = "50";
Allain Lalonde
sumber
1
Ini tidak masuk akal bagi saya. Meskipun saya bukan ahli JavaScript
JeroenEijkhof
@WmasterJ Untuk kejelasan, lihat posting ini: yuiblog.com/blog/2006/04/11/with-statement-considered-harmful
Dennis
8
Nama variabel panjang bukan satu-satunya kasus penggunaan untuk with.
Chris
18

Saya tidak pernah menggunakannya, tidak melihat alasan, dan tidak merekomendasikannya.

Masalahnya withadalah hal itu mencegah banyak optimasi leksikal yang dapat dilakukan oleh implementasi ECMAScript. Dengan munculnya mesin cepat berbasis JIT, masalah ini mungkin akan menjadi lebih penting dalam waktu dekat.

Itu mungkin terlihat seperti withmemungkinkan untuk konstruksi yang lebih bersih (ketika, katakanlah, memperkenalkan ruang lingkup baru alih-alih pembungkus fungsi anonim yang umum atau mengganti aliasing verbose), tetapi itu benar - benar tidak sepadan . Selain penurunan kinerja, selalu ada bahaya menugaskan ke properti dari objek yang salah (ketika properti tidak ditemukan pada objek dalam ruang lingkup yang disuntikkan) dan mungkin salah memperkenalkan variabel global. IIRC, masalah terakhir adalah yang mendorong Crockford untuk merekomendasikan untuk menghindari with.

Kangax
sumber
6
Bogeyman kinerja sering berlari keluar, hampir sesering hal global ... Selalu menganggap saya aneh, mengingat itu adalah JavaScript yang sedang kita bicarakan. Anda akan berasumsi bahwa hit kinerja benar - benar dramatis untuk menjamin banyak perhatian, tetapi ... Jika Anda memiliki angka yang sulit pada biaya with(){}konstruksi seperti yang diberikan dalam jawaban lain di sini, di browser modern, saya ingin melihat mereka!
Shog9
6
Mengapa itu aneh dalam konteks Javascript? :) Dan ya, itu dramatis. Pikirkan tentang hal ini - suatu implementasi perlu mengevaluasi ekspresi dalam tanda kurung, mengubahnya menjadi objek, memasukkannya ke bagian depan rantai lingkup saat ini, mengevaluasi pernyataan di dalam blok, kemudian mengembalikan rantai lingkup kembali ke normal. Itu banyak pekerjaan. Lebih dari sekadar pencarian properti sederhana yang dapat diubah menjadi kode tingkat rendah yang sangat dioptimalkan. Berikut ini adalah tolok ukur yang sangat sederhana yang baru saja saya buat (beri tahu saya jika Anda menemukan kesalahan) menunjukkan perbedaannya - gist.github.com/c36ea485926806020024
kangax
5
@ Kangax: Saya berasal dari latar belakang C ++, di mana agak tradisional bagi banyak programmer untuk terobsesi dengan efisiensi kecil dalam kode mereka, bahkan ketika mereka tidak benar-benar memiliki efek nyata pada kinerja rutin atau program yang lebih besar. Sepertinya aneh bagi saya dalam konteks JavaScript, di mana sebagian besar kinerja rutin dapat bergantung pada implementasi VM. Saya telah melihat beberapa contoh di mana programmer JS akan menghindari, katakanlah, fungsi anonim karena kekhawatiran atas biaya setup, tetapi ini tampaknya menjadi pengecualian bukan aturan, disediakan untuk area kode yang sangat sensitif.
Shog9
5
Yang mengatakan, Anda benar-benar benar mengenai biaya with(){}: menyiapkan ruang lingkup baru dengan withsangat mahal pada setiap browser yang saya uji. Anda ingin menghindari ini dalam kode apa pun yang disebut sangat sering. Selain itu, Chrome menunjukkan hit dramatis untuk setiap kode yang dieksekusi dalam with()lingkup. Menariknya, IE memiliki karakteristik kinerja terbaik untuk kode dalam with()blok: memperhitungkan biaya pengaturan, with()menyediakan cara tercepat untuk akses anggota di IE6 dan IE8 VMs (meskipun VM ini adalah keseluruhan paling lambat). Bagus, terima kasih ...
Shog9
5
FWIW: inilah serangkaian tes yang sama dengan biaya pengaturan yang diperhitungkan: jsbin.com/imidu/edit Akses variabel untuk with()hampir merupakan urutan besarnya lebih lambat di Chrome, dan dua kali lebih cepat di IE ...!
Shog9
13

Visual Basic.NET memiliki Withpernyataan serupa . Salah satu cara yang lebih umum saya menggunakannya adalah dengan cepat mengatur sejumlah properti. Dari pada:

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

, Saya bisa menulis:

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

Ini bukan hanya masalah kemalasan. Itu juga membuat kode lebih mudah dibaca. Dan tidak seperti JavaScript, itu tidak menderita ambiguitas, karena Anda harus mengawali semua yang dipengaruhi oleh pernyataan dengan .(titik). Jadi, dua yang berikut ini jelas berbeda:

With someObject
    .Foo = ''
End With

vs.

With someObject
    Foo = ''
End With

Yang pertama adalah someObject.Foo; yang terakhir adalah Foodi luar ruang lingkup someObject.

Saya menemukan bahwa kurangnya perbedaan JavaScript membuatnya jauh kurang berguna daripada varian Visual Basic, karena risiko ambiguitas terlalu tinggi. Selain itu, withmasih merupakan ide kuat yang dapat membuat keterbacaan lebih baik.

Sören Kuklau
sumber
2
Cukup benar. Namun tidak menjawab pertanyaannya. Jadi itu di luar topik.
Allain Lalonde
6
Ini mengalir di pikiran saya juga. seseorang harus mengatakannya . Mengapa JavaScript tidak dapat memiliki titik juga?
Carson Myers
Setuju, notasi titik lebih unggul, seandainya JavaScript menggunakannya. +1
8

Anda bisa menggunakan withuntuk memperkenalkan konten objek sebagai variabel lokal ke blok, seperti yang dilakukan dengan mesin template kecil ini .

Jordão
sumber
1
Teknik ini juga digunakan dalam _.template ( underscore.js's ) .
Peter V. Mørch
7

Menggunakan "with" dapat membuat kode Anda lebih kering.

Pertimbangkan kode berikut:

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

Anda dapat mengeringkannya sebagai berikut:

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

Saya kira itu tergantung apakah Anda memiliki preferensi untuk keterbacaan atau ekspresif.

Contoh pertama lebih mudah dibaca dan mungkin direkomendasikan untuk sebagian besar kode. Namun sebagian besar kode cukup jinak. Yang kedua sedikit lebih tidak jelas tetapi menggunakan sifat ekspresif dari bahasa untuk mengurangi ukuran kode dan variabel berlebihan.

Saya membayangkan orang-orang yang suka Java atau C # akan memilih cara pertama (object.member) dan mereka yang lebih suka Ruby atau Python akan memilih yang terakhir.

Jonah
sumber
Ups, aku tidak menyadari seseorang sudah memposting contoh yang sama pada dasarnya tahun lalu. Selain masalah kinerja, "with" membuat kode KERING bagus dengan mengorbankan yang sedikit lebih sulit dibaca. Saya pikir untuk kolaborasi dengan pengembang lain atau sebagian besar kode produksi, praktik yang baik untuk menghindari kata kunci "dengan". Tetapi jika Anda bekerja dengan programmer tingkat ahli dan memahami bagaimana menghindari potensi ketidakefisienan maka dengan segala cara pergi ke kota dengan "dengan."
Jonah
6

Saya pikir penggunaan yang jelas adalah sebagai jalan pintas. Jika Anda mis. Menginisialisasi objek, Anda cukup menyimpan banyak mengetik "ObjectName." Jenis seperti slot "dengan-slot" yang memungkinkan Anda menulis

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

yang sama dengan menulis

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

Lebih jelas mengapa ini merupakan jalan pintas daripada saat bahasa Anda mengizinkan "Objectname.foo" tapi tetap saja.

Sarien
sumber
1
senang melihat kode lisp! Saya pikir "dengan" di javascript jelas terinspirasi oleh skema root sebagai bahasa, tetapi sayangnya Anda semakin downvoted untuk memposting LISP pada pertanyaan javascript.
Fire Crow
1
Upvoting pada prinsip dasar. 'dengan' adalah konstruksi yang sangat kuat. Tetapi kebanyakan orang JS tidak memahami penutupan, dan menulis sistem warisan kelas Java yang sangat rumit di atas JS - jadi bagaimana mereka bisa menyadari potensi pemrograman yang ditawarkan dengan 'dengan'?
jared
Tentu saja with-slotsmengharuskan Anda untuk menentukan slot mana yang Anda gunakan, sedangkan withakan menggunakan slot apa pun yang terikat saat runtime.
Samuel Edwin Ward
6

Memiliki pengalaman dengan Delphi, saya akan mengatakan bahwa menggunakan dengan harus menjadi optimasi ukuran terakhir, mungkin dilakukan oleh beberapa jenis algoritma minimizer javascript dengan akses ke analisis kode statis untuk memverifikasi keamanannya.

Masalah pelingkupan yang bisa Anda lakukan dengan penggunaan bebas pernyataan with bisa menjadi masalah besar di a ** dan saya tidak ingin siapa pun mengalami sesi debug untuk mengetahui apa yang terjadi .. dalam kode Anda , hanya untuk mengetahui bahwa ia menangkap anggota objek atau variabel lokal yang salah, alih-alih variabel lingkup global atau luar yang Anda maksud.

VB dengan statemen lebih baik, karena membutuhkan titik-titik untuk mendisplambasikan pelingkupan, tetapi Delphi dengan statemen adalah pistol bermuatan dengan hairtrigger, dan menurut saya seolah-olah javascript yang cukup mirip untuk menjamin peringatan yang sama.

Lasse V. Karlsen
sumber
5
Javascript dengan pernyataan lebih buruk daripada yang Delphi. Dalam Delphi, dengan berkinerja sama cepat (jika tidak lebih cepat) dari notasi object.member. Dalam javascript, dengan harus berjalan lingkup untuk memeriksa anggota yang cocok, sehingga selalu membuatnya lebih lambat daripada notasi object.member.
Martijn
5

Menggunakan dengan tidak dianjurkan, dan dilarang dalam mode ketat ECMAScript 5. Alternatif yang disarankan adalah menetapkan objek yang propertinya ingin Anda akses ke variabel sementara.

Sumber: Mozilla.org

ianaz
sumber
4

Pernyataan with dapat digunakan untuk mengurangi ukuran kode atau untuk anggota kelas pribadi, contoh:

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

Dengan-pernyataan sangat berguna jika Anda ingin memodifikasi ruang lingkup, apa yang diperlukan untuk memiliki ruang lingkup global Anda sendiri yang dapat Anda manipulasi saat runtime. Anda dapat meletakkan konstanta di atasnya atau fungsi pembantu tertentu yang sering digunakan seperti misalnya "toUpper", "toLower" atau "isNumber", "clipNumber" aso ..

Tentang kinerja buruk yang sering saya baca: Melingkupi fungsi tidak akan berdampak pada kinerja, pada kenyataannya dalam FF saya, fungsi yang dilingkupi berjalan lebih cepat daripada yang tidak dicentang:

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

Jadi dengan cara yang disebutkan di atas digunakan dengan-pernyataan tidak memiliki efek negatif pada kinerja, tetapi bagus karena mengurangi ukuran kode, apa dampak penggunaan memori pada perangkat mobile.

alex
sumber
3

Menggunakan dengan juga membuat kode Anda lebih lambat dalam banyak implementasi, karena semuanya sekarang terbungkus dalam ruang lingkup tambahan untuk pencarian. Tidak ada alasan yang sah untuk digunakan dengan JavaScript.

Svend
sumber
5
Optimalisasi prematur. Jangan mengklaim "lebih lambat" kecuali jika Anda menekan angka-angkanya; setiap overhead kemungkinan sepele pada implikasi js modern dan kuno.
mk.
2
Saya sangat tidak setuju dengan kesimpulan Anda, berdasarkan apa yang mungkin tidak menjadi masalah bagi pengembang selain Anda.
Dave Van den Eynde
4
@mk: ok, angka berderak untuk Anda di sini: 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;memberi rata-rata 2500, sementara 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;memberi rata-rata 750, membuat yang menggunakan lebih dari 3 kali lebih lambat.
Yorick
3
Jalankan saja ini di Chrome 23 di konsol ketika saya melihat ini. Hasil yang saya dapatkan adalah 1138 untuk withkode dan 903 tanpa. Dengan perbedaan kecil ini bahkan dalam satu lingkaran ketat saya akan membuat pilihan berdasarkan kesederhanaan pengkodean dan kemudahan refactoring berdasarkan kasus per kasus sebelum mengkhawatirkan kinerja.
Plynx
3

Saya pikir dengan-pernyataan bisa berguna ketika mengubah bahasa template menjadi JavaScript. Misalnya JST di base2 , tapi saya sudah sering melihatnya.

Saya setuju orang bisa memprogram ini tanpa dengan pernyataan. Tetapi karena tidak memberikan masalah, itu adalah penggunaan yang sah.

doekman
sumber
3

Ini bagus untuk menempatkan kode yang berjalan di lingkungan yang relatif rumit ke dalam wadah: Saya menggunakannya untuk membuat ikatan lokal untuk "jendela" dan untuk menjalankan kode yang dimaksudkan untuk browser web.

aredridel
sumber
3

Saya pikir objek yang digunakan secara literal menarik, seperti pengganti drop-in untuk menggunakan penutupan

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

atau dengan pernyataan setara dengan penutupan

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

Saya pikir risiko sebenarnya adalah secara kebetulan minipulasi variabel yang bukan bagian dari pernyataan with, oleh karena itu saya suka objek literal diteruskan, Anda dapat melihat dengan tepat apa yang akan ada dalam konteks yang ditambahkan dalam kode.

Fire Crow
sumber
3

Saya membuat fungsi "menggabungkan" yang menghilangkan beberapa ambiguitas ini dengan withpernyataan:

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

Saya dapat menggunakannya dengan cara yang sama with, tetapi saya tahu itu tidak akan memengaruhi ruang lingkup apa pun yang tidak saya inginkan.

Pemakaian:

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()});
}
palswim
sumber
3

Untuk beberapa potongan kode pendek, saya ingin menggunakan fungsi trigonometri seperti sin, cosdll dalam mode derajat alih-alih dalam mode radiasi. Untuk tujuan ini, saya menggunakan AngularDegreeobjek:

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

Lalu saya bisa menggunakan fungsi trigonometri dalam mode derajat tanpa gangguan bahasa lebih lanjut di withblok:

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

Ini berarti: Saya menggunakan objek sebagai kumpulan fungsi, yang saya aktifkan di wilayah kode terbatas untuk akses langsung. Saya menemukan ini berguna.

rplantiko
sumber
itu bukan ide yang bagus untuk menggunakan withpernyataan dengan cara ini, itu hanya membuat kode sulit dibaca karena Anda tidak tahu fungsi mana yang global dan fungsi mana yang disebut dalam lingkup dengan objek, jadi jika ada fungsi dengan cara apa pun tidak didefinisikan dalam objek lingkup kemudian akan mencoba mengaksesnya di namespace global
Saket Patel
Menyadari masalah pelingkupan, saya masih menemukan ini berguna. Seorang ahli matematika yang membaca kode ingin melihat langsung bahwa rumus di atas adalah aplikasi dari "hukum 4 bagian yang berurutan" dalam trigonometri bola. Alternatif ketat mengaburkan rumus: 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;akan memberikan hasil yang sama, tetapi ini adalah horor.
rplantiko
@rplantiko Yang perlu diingat adalah bahwa kebanyakan orang tidak merasa nyaman dengannya. Jadi kecuali Anda sedang menulis kode yang tidak akan pernah disentuh orang lain. Juga, saya cukup yakin bisa menunjukkan kepada Anda beberapa penggunaan withyang akan membuat Anda bingung.
Juan Mendes
2

Saya pikir kegunaannya withdapat bergantung pada seberapa baik kode Anda ditulis. Misalnya, jika Anda menulis kode yang muncul seperti ini:

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

maka Anda bisa membantah bahwa withakan meningkatkan keterbacaan kode dengan melakukan ini:

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

Sebaliknya, bisa dikatakan bahwa Anda melanggar Hukum Demeter , tetapi, sekali lagi, mungkin tidak. Saya ngelantur =).

Di atas segalanya, ketahuilah bahwa Douglas Crockford merekomendasikan untuk tidak menggunakan with. Saya mendesak Anda untuk memeriksa posting blognya mengenai withdan alternatifnya di sini .

Tom
sumber
Terima kasih atas tanggapannya, Tom. Saya sudah membaca rekomendasi Crockford, dan meskipun masuk akal, itu hanya berjalan sejauh ini. Saya datang ke ide - disentuh secara tidak langsung oleh doekman - bahwa kekuatan sebenarnya dengan {} adalah cara itu dapat digunakan untuk memanipulasi ruang lingkup ...
Shog9
2

Saya hanya benar-benar tidak melihat bagaimana menggunakan dengan lebih mudah dibaca daripada hanya mengetik objek. Saya tidak berpikir itu kurang terbaca, tetapi saya tidak berpikir itu lebih mudah dibaca juga.

Seperti kata lassevk, saya pasti bisa melihat bagaimana menggunakan dengan akan lebih rentan kesalahan daripada hanya menggunakan sintaks "object.member" yang sangat eksplisit.

17 dari 26
sumber
1

Anda harus melihat validasi formulir dalam javascript di W3schools http://www.w3schools.com/js/js_form_validation.asp mana bentuk objek "dipindai" untuk menemukan input dengan nama 'email'

Tetapi saya telah memodifikasinya untuk mendapatkan dari formulir APA SAJA semua bidang memvalidasi tidak kosong, terlepas dari nama atau jumlah bidang dalam formulir. Yah saya sudah menguji hanya bidang teks.

Tetapi dengan () membuat segalanya lebih sederhana. Berikut kodenya:

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;
}
Elvis Salaris
sumber
1

CoffeeScript ini Coco garpu memiliki withkata kunci, tapi itu hanya set this(juga ditulis sebagai @di CoffeeScript / Coco) ke objek target dalam blok. Ini menghilangkan ambiguitas dan mencapai kepatuhan mode ketat ES5:

with long.object.reference
  @a = 'foo'
  bar = @b
Trevor Burnham
sumber
0

Berikut ini adalah penggunaan yang baik untuk with: menambahkan elemen baru ke Obyek Literal, berdasarkan nilai yang disimpan dalam Objek itu. Berikut ini contoh yang baru saya gunakan hari ini:

Saya memiliki satu set ubin yang mungkin (dengan bukaan menghadap atas, bawah, kiri, atau kanan) yang dapat digunakan, dan saya ingin cara cepat untuk menambahkan daftar ubin yang akan selalu ditempatkan dan dikunci pada awal permainan . Saya tidak ingin terus mengetik types.tbruntuk setiap jenis dalam daftar, jadi saya hanya menggunakannya 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");
Avanderveen
sumber
0

Anda dapat menggunakan dengan untuk menghindari keharusan mengelola arity secara eksplisit saat menggunakan require.js:

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

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

Implementasi requirejs.declare:

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

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

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

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

Seperti yang ditunjukkan oleh Andy E dalam komentar jawaban Shog9, perilaku yang berpotensi tidak terduga ini terjadi saat menggunakan withdengan objek literal:

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

Bukan berarti perilaku tak terduga tidak sudah menjadi ciri khas with.

Jika Anda benar-benar masih ingin menggunakan teknik ini, setidaknya gunakan objek dengan prototipe nol.

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

Tetapi ini hanya akan bekerja di ES5 +. Juga jangan gunakan with.

Jackson
sumber
0

Saya sedang mengerjakan proyek yang akan memungkinkan pengguna untuk mengunggah kode untuk mengubah perilaku bagian dari aplikasi. Dalam skenario ini, saya telah menggunakan withklausa untuk menjaga kode mereka dari memodifikasi apa pun di luar ruang lingkup yang saya ingin mereka mainkan. Bagian (disederhanakan) dari kode yang saya gunakan untuk melakukan ini adalah:

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

Kode ini memastikan (agak) bahwa kode yang ditentukan pengguna tidak memiliki akses ke objek yang dicakup secara global seperti windowmaupun ke variabel lokal saya melalui penutupan.

Seperti kata bijak, saya masih harus melakukan pemeriksaan kode statis pada kode yang dikirimkan pengguna untuk memastikan mereka tidak menggunakan cara licik lainnya untuk mengakses lingkup global. Misalnya, kode yang ditentukan pengguna berikut mengambil akses langsung ke window:

test = function() {
     return this.window
};
return test();
kevin.groat
sumber
0

Saya

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
}

intinya

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

Bisakah Anda mempercayai kode berkualitas rendah? Tidak, kami melihat bahwa itu dibuat benar-benar tidak dapat dibaca. Contoh ini tidak dapat disangkal membuktikan bahwa tidak perlu dengan pernyataan, jika saya mengambil hak keterbacaan;)

Alien kecil
sumber