มีการใช้งานที่ถูกต้องสำหรับคำสั่ง "กับ" ของ JavaScript หรือไม่


369

ความคิดเห็นของ Alan Stormในการตอบสนองต่อคำตอบของฉันเกี่ยวกับwithคำสั่งทำให้ฉันคิด ฉันไม่ค่อยพบเหตุผลที่จะใช้คุณลักษณะภาษานี้โดยเฉพาะและไม่เคยคิดมากว่าจะสร้างปัญหาได้อย่างไร ตอนนี้ฉันอยากรู้ว่าฉันจะใช้อย่างมีประสิทธิภาพได้withอย่างไรในขณะที่หลีกเลี่ยงหลุมพราง

คุณพบwithคำสั่งที่มีประโยชน์ที่ไหน


52
ฉันไม่เคยใช้มัน มันง่ายที่จะอยู่ได้โดยปราศจากมันถ้าฉันทำเป็นว่ามันไม่มีอยู่จริง
Nosredna

6
อาจมีการใช้งานที่ถูกต้องหลายครั้ง แต่มันเป็นสิ่งที่สงสัย ES5 ลบออกอย่างเข้มงวดwithดังนั้นจึงไม่มีสิ่งนั้นอีกต่อไป
Thomas Aylott

27
มูลค่า noting ที่นี่ที่ ES5 เข้มงวดยังคงเป็นตัวเลือก
Shog9

5
แทนที่จะลบ 'with' ใน ES5 เข้มงวดจะดีกว่าที่จะเปลี่ยนมาตรฐานหรือไม่ถ้าไม่พบตัวแปรการมอบหมายใด ๆ ภายใน 'กับ' จะถูกผูกไว้กับวัตถุอาร์กิวเมนต์หรือไม่
JussiR

2
@JussiR: อาจเป็นไปได้ แต่ปัญหาในการทำเช่นนั้นก็คืออาจเป็นไปได้ว่าสิ่งต่าง ๆ ในเบราว์เซอร์รุ่นเก่า
Sune Rasmussen

คำตอบ:


520

การใช้งานอีกเกิดขึ้นกับผมในวันนี้ดังนั้นฉันค้นหาเว็บพล่านและพบว่ามีการกล่าวถึงที่มีอยู่ของมัน: การกำหนดตัวแปรภายในขอบเขตที่ถูกบล็อก

พื้นหลัง

จาวาสคริปต์ทั้ง ๆ ที่มีความคล้ายคลึงกับ C และ C ++ ก็ไม่ได้มีขอบเขตคล้ายกับตัวแปรที่กำหนดไว้ในบล็อก:

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

การประกาศการปิดในลูปเป็นงานทั่วไปที่สิ่งนี้สามารถนำไปสู่ข้อผิดพลาด:

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

เนื่องจาก for for ไม่แนะนำขอบเขตใหม่เดียวกันnum- ด้วยค่า2- จะถูกแชร์โดยทั้งสามฟังก์ชัน

ขอบเขตใหม่: letและwith

ด้วยการแนะนำของletแถลงการณ์ในES6มันง่ายที่จะแนะนำขอบเขตใหม่เมื่อจำเป็นเพื่อหลีกเลี่ยงปัญหาเหล่านี้:

// variables introduced in this statement 
// are scoped to each iteration of the loop
for (let i=0; i<3; ++i)
{
   setTimeout(function() { alert(i); }, 10);
}

หรือแม้กระทั่ง:

for (var i=0; i<3; ++i)
{
   // variables introduced in this statement 
   // are scoped to the block containing it.
   let num = i;
   setTimeout(function() { alert(num); }, 10);
}

การใช้งานนี้จะ จำกัด เฉพาะเบราว์เซอร์ใหม่ล่าสุดและผู้พัฒนาที่ยินดีใช้ทรานสฟิลเลอร์ อย่างไรก็ตามเราสามารถจำลองพฤติกรรมนี้ได้อย่างง่ายดายโดยใช้with:

for (var i=0; i<3; ++i)
{
   // object members introduced in this statement 
   // are scoped to the block following it.
   with ({num: i})
   {
      setTimeout(function() { alert(num); }, 10);
   }
}

ขณะนี้ลูปทำงานได้ตามต้องการสร้างตัวแปรแยกสามตัวที่มีค่าตั้งแต่ 0 ถึง 2 โปรดทราบว่าตัวแปรที่ประกาศภายในบล็อกไม่ได้ถูกกำหนดขอบเขตซึ่งแตกต่างจากพฤติกรรมของบล็อกใน C ++ (ใน C ตัวแปรจะต้องถูกประกาศเมื่อเริ่มต้น บล็อกดังนั้นในลักษณะที่คล้ายคลึงกัน) พฤติกรรมนี้ค่อนข้างคล้ายกับรูปแบบletบล็อกที่แนะนำในเบราว์เซอร์ Mozilla รุ่นก่อนหน้านี้ แต่ไม่ได้นำมาใช้อย่างแพร่หลายในที่อื่น


15
ไม่เคยคิดว่าจะใช้กับสิ่งที่ดูเหมือนจริง ๆ
Matt Kantor

81
นี่มันช่างตายจริงๆ ฉันไม่เคยคิดที่จะเล่นกับขอบเขตของ JavaScript ด้วยวิธีนี้ ขยายพื้นที่ใหม่ทั้งหมดไปยังการเข้ารหัสของฉัน ฉันหวังว่าฉันจะโหวตได้สูงสุด 10 ครั้ง!
kizzx2

27
สำหรับผู้ที่ยังคงต่อต้านคนหนึ่งสามารถใช้การปิด:for (var i = 0; i < 3; ++i) { setTimeout ((function () { var num = i; return function () { alert (num); }; }) (), 10);}
Thomas Eding

4
ที่จริงแล้วปัญหาที่ลิงก์ด้านบนจะปรากฏในเบราว์เซอร์ที่ไม่ใช่ Mozilla (Chrome, Safari, Opera, IE)
Max Shawabkeh

24
ให้การสนับสนุนงบใน IE จริงๆจะบันทึกเบคอนของฉันตอนนี้ฉันดิ้นรนด้วยจิตสำนึกของฉันหรือไม่ว่าจะใช้กับแทน ปัญหาที่แท้จริงคือแม้ว่าจะต้องมีการอนุญาตด้วยความระมัดระวังเป็นพิเศษเนื่องจากคุณสมบัติที่สืบทอดของวัตถุบนโซ่ต้นแบบ ตัวอย่างเช่นvar toString = function () { return "Hello"; }; with ({"test":1}) { console.log(toString()); };. ในขอบเขตของคำสั่งwith toString ()เป็นคุณสมบัติที่สืบทอดของObjectดังนั้นจึงไม่เรียกใช้ฟังก์ชันที่กำหนดไว้อย่างชัดเจน ยังเป็นคำตอบที่ดี แต่ :-)
Andy E

161

ฉันใช้คำสั่ง with ในรูปแบบที่เรียบง่ายของการนำเข้าที่กำหนดขอบเขต สมมติว่าคุณมีผู้สร้างมาร์กอัปบางประเภท แทนที่จะเขียน:

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

คุณสามารถเขียนแทน:

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

สำหรับกรณีการใช้งานนี้ฉันไม่ได้ทำการมอบหมายใด ๆ ดังนั้นฉันจึงไม่มีปัญหาความคลุมเครือที่เกี่ยวข้อง


5
นั่นคือวิธีที่ฉันเห็นมันใช้ใน VB (และการใช้งานอย่างเดียวที่ฉันรับรู้)
Mateen Ulhaq

2
ข้อเสียคือถ้าคุณอ้างอิงตัวแปรภายในด้วยบล็อกซึ่งอยู่นอกวัตถุ markupbuilder เครื่องยนต์ js จะค้นหามันใน markupbuilder ก่อนแล้วลดประสิทธิภาพลง
Adam Thomas

3
สิ่งนี้จะช่วยลดรหัสสำหรับผู้ที่ทำงานกับเส้นทางผ้าใบ
Brian McCutchon

4
โค้ด "กับ" เวอร์ชันนั้นรันบนเครื่องของฉันช้ากว่า240 เท่ามากกว่ารุ่นที่ "ไม่ใช้" เหมือนกัน นั่นคือเหตุผลที่ผู้คนบอกว่าไม่มีการใช้งานที่ถูกกฎหมาย ไม่ใช่เพราะมันไม่สามารถสร้างรหัสที่สวยกว่าในบางจุด ดูมาตรฐาน: jsfiddle.net/sc46eeyn
Jimbo Jonny

1
@McBrainy - นั่นคือประเภทของสถานที่ที่คุณไม่ควรใช้รหัสที่ทำงานช้าลงมากมาย (ดูความคิดเห็นที่ฉันทำไว้เหนือสิ่งนี้) หากคุณต้องการช็อตคัตสำหรับรหัสซ้ำสุด ๆ คุณสามารถประกาศได้ ตัวอย่างเช่นหากใช้context.bezierCurveToเป็นร้อยครั้งคุณสามารถพูดได้var bc2 = context.bezierCurveTo;และจากนั้นไปbc2(x,x,etc);ทุกครั้งที่คุณต้องการโทรหา ที่ค่อนข้างเร็วและ verbose น้อยลงในขณะที่withช้าสุด
Jimbo Jonny

83

ดังที่ความคิดเห็นก่อนหน้าของฉันระบุไว้ฉันไม่คิดว่าคุณจะสามารถใช้งานwithได้อย่างปลอดภัยไม่ว่ามันจะดึงดูดในสถานการณ์ใดก็ตามก็ตาม เนื่องจากปัญหาไม่ได้ครอบคลุมที่นี่ฉันจะทำซ้ำ พิจารณารหัสต่อไปนี้

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

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

หากไม่มีการตรวจสอบการเรียกใช้ฟังก์ชันเหล่านั้นอย่างรอบคอบแล้วจะไม่มีวิธีที่จะบอกสถานะของโปรแกรมของคุณที่จะทำงานหลังจากที่โค้ดนี้ทำงาน หากuser.nameตั้งค่าไว้แล้วมันจะเป็นBobเช่นนี้ หากไม่ได้ตั้งค่าโกลบอลnameจะเริ่มต้นหรือเปลี่ยนเป็นBobและuserวัตถุจะยังคงอยู่โดยไม่มีnameคุณสมบัติ

บักเกิดขึ้น หากคุณใช้กับคุณในที่สุดคุณจะทำเช่นนี้และเพิ่มโอกาสที่โปรแกรมของคุณจะล้มเหลว ที่แย่กว่านั้นคือคุณอาจพบรหัสการทำงานที่กำหนดให้โลกในบล็อกมีทั้งโดยเจตนาหรือผ่านผู้เขียนที่ไม่ทราบเกี่ยวกับสิ่งก่อสร้างที่แปลกประหลาดนี้ มันเหมือนกับการเผชิญหน้ากับสวิตช์คุณไม่รู้ว่าผู้เขียนตั้งใจทำสิ่งนี้หรือไม่และไม่มีทางรู้ว่า "การแก้ไข" รหัสจะทำให้เกิดการถดถอยหรือไม่

ภาษาการเขียนโปรแกรมที่ทันสมัยจะถูกทำให้เต็มไปด้วยคุณสมบัติ คุณสมบัติบางอย่างหลังจากใช้งานมาหลายปีถูกค้นพบว่าไม่ดีและควรหลีกเลี่ยง Javascript withนั้นเป็นหนึ่งในนั้น


18
ปัญหานี้จะเกิดขึ้นก็ต่อเมื่อคุณกำหนดค่าให้กับแอตทริบิวต์ของวัตถุ แต่ถ้าคุณใช้เพื่ออ่านค่าเท่านั้น ฉันขอยืนยันว่ามันไม่เป็นไรที่จะใช้มันในกรณีนั้น
airportyh

10
ปัญหาเดียวกันนี้ใช้กับการอ่านค่า Toby ในตัวอย่างโค้ดข้างต้นคุณไม่ทราบว่ามีการตั้งชื่อชื่อไว้ในวัตถุผู้ใช้หรือไม่ดังนั้นคุณจะไม่ทราบว่าคุณกำลังอ่านชื่อโกลบอลหรือชื่อผู้ใช้
Alan Storm

12
ด้วยค่าการอ่านจะมีกฎสำคัญที่ชัดเจน: มีการตรวจสอบแอตทริบิวต์บนวัตถุก่อนตัวแปรที่อยู่นอกขอบเขต สิ่งนี้ไม่แตกต่างจากการกำหนดขอบเขตตัวแปรในฟังก์ชั่น ปัญหาที่แท้จริงของการกำหนดและ 'กับ' ตามที่ฉันเข้าใจนั้นขึ้นอยู่กับความจริงที่ว่าการกำหนดคุณลักษณะนั้นเกิดขึ้นหรือไม่นั้นขึ้นอยู่กับว่าแอตทริบิวต์นั้นมีอยู่บนวัตถุปัจจุบันที่มีปัญหาซึ่งเป็นคุณสมบัติแบบไทม์และไม่สามารถอนุมานได้ง่าย โดยดูที่รหัส
airportyh

1
ฉันคิดว่าคุณอาจจะอยู่ที่นั่นโทบี้ ปัญหาการเขียนเพียงพอสำหรับฉันที่จะหลีกหนีจากสิ่งก่อสร้างทั้งหมด
Alan Storm

"มันเหมือนกับการเผชิญหน้ากับสวิตช์คุณไม่รู้หรอกว่า ... " - งั้นลองแบนสวิตช์ () ด้วยเหรอ? ;-p
Sz.

66

จริง ๆ แล้วฉันพบว่าwithคำสั่งนี้มีประโยชน์อย่างไม่น่าเชื่อเมื่อเร็ว ๆ นี้ เทคนิคนี้ไม่เคยเกิดขึ้นกับฉันจนกว่าฉันจะเริ่มโครงการปัจจุบันของฉัน - คอนโซลบรรทัดคำสั่งเขียนใน JavaScript ฉันพยายามเลียนแบบคอนโซล Firebug / WebKit API ที่สามารถป้อนคำสั่งพิเศษลงในคอนโซลได้ แต่ไม่ได้แทนที่ตัวแปรใด ๆ ในขอบเขตส่วนกลาง ผมคิดว่าเรื่องนี้เมื่อพยายามที่จะเอาชนะปัญหาผมกล่าวถึงในการแสดงความคิดเห็นกับคำตอบที่ดีเยี่ยมของ Shog9

เพื่อให้บรรลุผลนี้ฉันใช้สองคำสั่งเพื่อ "เลเยอร์" ขอบเขตด้านหลังขอบเขตทั่วโลก:

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

สิ่งที่ดีเกี่ยวกับเทคนิคนี้คือนอกเหนือจากข้อเสียด้านประสิทธิภาพแล้วมันไม่ได้รับความกลัวตามปกติwithเนื่องจากเราประเมินในขอบเขตของโลกอยู่แล้ว - ไม่มีอันตรายจากตัวแปรนอกขอบเขตหลอกของเรา การแก้ไข

ฉันได้รับแรงบันดาลใจในการโพสต์คำตอบนี้เมื่อฉันประหลาดใจฉันสามารถหาเทคนิคเดียวกับที่ใช้ในที่อื่น - รหัสแหล่ง Chromium !

InjectedScript._evaluateOn = function(evalFunction, object, expression) {
    InjectedScript._ensureCommandLineAPIInstalled();
    // Surround the expression in with statements to inject our command line API so that
    // the window object properties still take more precedent than our API functions.
    expression = "with (window._inspectorCommandLineAPI) { with (window) { " + expression + " } }";
    return evalFunction.call(object, expression);
}

แก้ไข:เพิ่งตรวจสอบแหล่งที่มาของ Firebug พวกเขาเชื่อมโยง 4 ด้วยคำสั่งเข้าด้วยกันเพื่อเพิ่มเลเยอร์ บ้า!

const evalScript = "with (__win__.__scope__.vars) { with (__win__.__scope__.api) { with (__win__.__scope__.userVars) { with (__win__) {" +
    "try {" +
        "__win__.__scope__.callback(eval(__win__.__scope__.expr));" +
    "} catch (exc) {" +
        "__win__.__scope__.callback(exc, true);" +
    "}" +
"}}}}";

1
แต่กังวลว่า ecmascript5 หยุดคุณจากการทำเช่นนี้ มีวิธีแก้ปัญหา ecmascript 5 หรือไม่?
kybernetikos

@ อดัม: ฉันไม่แน่ใจเกี่ยวกับเรื่องนั้น ES5 ส่งข้อผิดพลาดสำหรับสิ่งนี้ในโหมดเข้มงวดเท่านั้นดังนั้นจึงไม่ใช่ปัญหาทันทีหากคุณไม่มีโหมดเข้มงวดที่ประกาศทั่วโลก ES Harmony อาจเป็นปัญหาที่ใหญ่กว่า แต่อาจแก้ไขได้ด้วยสิ่งใหม่ ๆ บางอย่างเช่นพรอกซี
Andy E

@AndyE ขออภัยนี่เป็นหัวข้อนอก แต่ 'คอนโซลบรรทัดคำสั่งของคุณเขียนใน JavaScript' สามารถใช้ได้ทุกที่หรือไม่
kybernetikos

@ อดัม: ไม่มันไม่ใช่ สิ่งทั้งหมดนี้ตั้งใจให้เป็นชุดเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์สำหรับเดสก์ท็อป Windows แต่ฉันไม่เคยทำมันเลย ฉันอาจจะเสร็จในบางจุดแม้ว่า WDGs จะไม่มีอนาคตที่สดใสในขณะนี้
Andy E

3
ไม่กี่สัปดาห์ที่ผ่านมาเราได้ย้ายการใช้งานคอนโซลของเราใน Chrome จากบล็อกไปยังสัญลักษณ์มายากลเนื่องจากบล็อกบล็อกคุณลักษณะบางอย่างของ ES6 :)
Alexey Kozyatinskiy

54

ใช่ใช่และใช่ มีการใช้งานที่ถูกกฎหมายมาก ดู:

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

โดยทั่วไปแล้ว DOM หรือ CSS hooks อื่น ๆ นั้นใช้ประโยชน์ได้อย่างยอดเยี่ยม มันไม่เหมือน "CloneNode" จะไม่ได้กำหนดและกลับไปที่ขอบเขตทั่วโลกเว้นแต่คุณจะออกนอกเส้นทางของคุณและตัดสินใจที่จะทำให้เป็นไปได้

การร้องเรียนเรื่องความเร็วของ Crockford คือบริบทใหม่ถูกสร้างขึ้นด้วย บริบทมักมีราคาแพง ฉันเห็นด้วย. แต่ถ้าคุณเพิ่งสร้าง div และไม่มีเฟรมเวิร์กสำหรับตั้งค่า css ของคุณและจำเป็นต้องตั้งค่าคุณสมบัติ CSS ด้วยตัวเอง 15 ตัวดังนั้นการสร้างบริบทอาจจะถูกกว่าดังนั้นการสร้างตัวแปรและการกำหนดตัวแปร 15 รายการ:

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

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

ฯลฯ ...


5
+1 เนื่องจากฉันคิดว่ามีการใช้งานที่ถูกต้องตามกฎหมายจำนวนwithมาก อย่างไรก็ตามในกรณีนี้คุณสามารถทำได้:element.style.cssText="background: black ; color: blue ; border: 1px solid green"
ฟรี

5
คุณสามารถบรรลุในสิ่งเดียวกันในหนึ่งบรรทัดใช้ง่ายextendวิธีการจากทั้ง jQuery หรือ $.extend(element.style, {fontWeight: 'bold', fontSize: '1.5em', color: '#55d', marginLeft: '2px'})Underscore.js:
เทรเวอร์เบิร์นแฮม

9
@TrevorBurnham - หากคุณกำลังจะถือว่า jQuery พร้อมใช้งานคุณเพียงแค่ใช้.css()วิธีการของมัน...
nnnnnn

4
สิ่งที่ป้องกันไม่ให้ตัวแปรเหล่านี้ไปขอบเขตทั่วโลก เป็นเพราะสไตล์ CSS ทั้งหมดนั้นถูกกำหนดไว้ในองค์ประกอบทั้งหมดเสมอหรืออะไร
mpen

1
@ Mark ใช่พวกเขามีการกำหนดเสมอกับ nulls หรือสตริงว่างเปล่าเป็นค่าถ้าไม่มีรูปแบบที่กำหนดเองสำหรับทรัพย์สิน
Esailija

34

คุณสามารถกำหนดฟังก์ชั่นตัวช่วยขนาดเล็กเพื่อให้ประโยชน์ของการwithไม่มีความกำกวม:

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

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

8
OMG หัวของฉันถูกระเบิด! ไม่คลุมเครือหรือไม่ ต้องโหวตว่าผู้ชาย!
Jarrod Dixon

@Jarrod: อะไรตลกเกี่ยวกับเรื่องนี้ ( is.gd/ktoZ )? ทุกคนที่ใช้เว็บไซต์นี้ฉลาดกว่าฉันดังนั้นโปรดยกโทษให้ฉันหากฉันทำผิด แต่ดูเหมือนว่าจะเป็นข้อมูลที่ไม่ดี
กา

14
แต่นั่นเป็นอีกต่อไปและยากที่จะเข้าใจวิธีการทำ: var _ = obj_name_here; _.a = "foo"; _.b = "bar;
Rene Saarsoo

3
Rene: คุณจะเห็นตัวแปร "_" ไปยังขอบเขตด้านนอกซึ่งส่งผลให้เกิดข้อบกพร่องที่อาจเกิดขึ้น นอกจากนี้ยังจะเปิดเผยตัวแปรชั่วคราวใด ๆ ที่ใช้ในการคำนวณพารามิเตอร์วัตถุ
John Millikin

30
คุณจะได้รับข้อบกพร่องมากขึ้นจากwith_การเป็นรุ่นสองเท่าของโคลน(function(_){ _.a="foo"; })(object_here);(วิธีมาตรฐานในการจำลองบล็อก c / สไตล์จาวา) ใช้สิ่งนั้นแทน
mk

25

ดูเหมือนจะไม่คุ้มค่าเพราะคุณสามารถทำสิ่งต่อไปนี้:

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

1
เรื่องนี้ไม่สมเหตุสมผลเลยสำหรับฉัน แม้ว่าฉันไม่ใช่ผู้เชี่ยวชาญที่ JavaScript
JeroenEijkhof

@WmasterJ สำหรับความคมชัดดูโพสต์นี้: yuiblog.com/blog/2006/04/11/with-statement-considered-harmful
Dennis

8
withชื่อตัวแปรยาวไม่ได้เป็นกรณีที่ใช้เฉพาะสำหรับ
Chris

18

ฉันไม่เคยใช้ด้วยไม่เห็นเหตุผลและไม่แนะนำ

ปัญหาที่เกิดขึ้นwithคือมันป้องกันการเพิ่มประสิทธิภาพคำศัพท์จำนวนมากซึ่งการใช้ ECMAScript สามารถทำได้ เมื่อพิจารณาจากการเพิ่มขึ้นของเครื่องยนต์ที่ใช้ JIT อย่างรวดเร็วปัญหานี้อาจมีความสำคัญมากขึ้นในอนาคตอันใกล้

มันอาจจะมีลักษณะเช่นwithช่วยให้การทำความสะอาดโครงสร้าง (เมื่อพูดแนะนำขอบเขตใหม่แทนของเสื้อคลุมทั่วไปฟังก์ชั่นที่ไม่ระบุชื่อหรือเปลี่ยน verbose aliasing) แต่มันเป็น มันไม่คุ้มค่า นอกจากประสิทธิภาพที่ลดลงแล้วยังมีอันตรายจากการกำหนดให้กับคุณสมบัติของวัตถุที่ไม่ถูกต้องเสมอ (เมื่อไม่พบคุณสมบัติบนวัตถุที่อยู่ในขอบเขตของการฉีด) และอาจแนะนำตัวแปรทั่วโลกอย่างผิดพลาด IIRC withปัญหาหลังเป็นหนึ่งที่มีแรงจูงใจครอกที่จะแนะนำให้หลีกเลี่ยง


6
ปิศาจเรื่องการแสดงได้รับการตัดทอนออกมาบ่อยครั้งเกือบจะเหมือนสิ่งที่อยู่รอบตัว ... ทำให้ฉันแปลกประหลาดเสมอเพราะมันเป็นจาวาสคริปต์ที่เรากำลังพูดถึง คุณคิดว่าประสิทธิภาพการทำงานนั้นน่าทึ่งมากที่จะรับประกันความสนใจอย่างมาก แต่ ... หากคุณมีตัวเลขจำนวนมากเกี่ยวกับค่าใช้จ่ายในการwith(){}สร้างเช่นเดียวกับคำตอบอื่น ๆ ที่นี่ในเบราว์เซอร์สมัยใหม่ฉันชอบที่จะเห็น พวกเขา!
Shog9

6
ทำไมมันแปลกในบริบทของ Javascript? :) และใช่มันน่าทึ่งมาก คิดเกี่ยวกับมัน - การใช้งานจำเป็นต้องประเมินนิพจน์ในวงเล็บ, แปลงเป็นวัตถุ, แทรกลงในด้านหน้าของห่วงโซ่ขอบเขตปัจจุบัน, ประเมินคำสั่งภายในบล็อก, จากนั้นเรียกคืนขอบเขตห่วงโซ่กลับสู่ปกติ นั่นเป็นจำนวนมากของการทำงาน มากกว่าการค้นหาคุณสมบัติอย่างง่ายที่สามารถเปลี่ยนเป็นรหัสระดับต่ำที่ปรับให้เหมาะสม นี่คือมาตรฐานที่ง่ายมากที่ฉันเพิ่งทำ (แจ้งให้เราทราบหากคุณพบข้อผิดพลาดใด ๆ ) แสดงให้เห็นถึงความแตกต่าง - gist.github.com/c36ea485926806020024
kangax

5
@ Kangax: ฉันมาจากพื้นหลัง C ++ ซึ่งเป็นแบบดั้งเดิมสำหรับโปรแกรมเมอร์จำนวนมากที่หมกมุ่นเกี่ยวกับประสิทธิภาพเล็กน้อยในรหัสของพวกเขาแม้ว่าพวกเขาจะไม่มีผลกระทบต่อประสิทธิภาพของรูทีนหรือโปรแกรมขนาดใหญ่ ดูเหมือนว่าแปลกสำหรับฉันในบริบทของ JavaScript ซึ่งส่วนใหญ่ของประสิทธิภาพประจำสามารถขึ้นอยู่กับการใช้ VM ฉันเคยเห็นบางครั้งที่โปรแกรมเมอร์ JS จะหลีกเลี่ยงพูดฟังก์ชั่นที่ไม่ระบุชื่อเนื่องจากความกังวลเกี่ยวกับค่าใช้จ่ายในการตั้งค่า แต่ดูเหมือนว่าจะเป็นข้อยกเว้นไม่ใช่กฎที่สงวนไว้สำหรับพื้นที่ที่มีความสำคัญมากของรหัส
Shog9

5
ที่กล่าวว่าคุณถูกต้องอย่างแท้จริงเกี่ยวกับค่าใช้จ่ายของwith(){}: การตั้งค่าขอบเขตใหม่ด้วยwithราคาแพงอย่างมหาศาลในทุกเบราว์เซอร์ที่ฉันทดสอบ คุณต้องการหลีกเลี่ยงปัญหานี้ในรหัสใด ๆ ที่เรียกว่าบ่อยมาก นอกจากนี้ Chrome ยังได้รับความนิยมอย่างมากสำหรับการใช้งานรหัสใด ๆ ที่อยู่ในwith()ขอบเขต ที่น่าสนใจคือ IE มีคุณสมบัติด้านประสิทธิภาพที่ดีที่สุดสำหรับรหัสภายในwith()บล็อค: การพิจารณาต้นทุนการติดตั้งwith()ให้วิธีการเข้าถึงสมาชิกที่เร็วที่สุดใน IE6 และ IE8 VMs (แม้ว่า VM เหล่านี้จะช้าที่สุด) สิ่งที่ดีขอบคุณ ...
Shog9

5
FWIW: นี่คือชุดการทดสอบเดียวกันกับค่าใช้จ่ายในการติดตั้ง: jsbin.com/imidu/editการเข้าถึงตัวแปรสำหรับwith()เกือบจะเป็นลำดับความสำคัญของ Chrome ที่ช้าลงและเร็วกว่า IE ที่สอง ...
Shog9

13

Visual Basic.NET มีWithคำสั่งที่คล้ายกัน อีกวิธีหนึ่งที่ฉันใช้คือการตั้งค่าคุณสมบัติจำนวนหนึ่งอย่างรวดเร็ว แทน:

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

, ฉันเขียนได้:

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

นี่ไม่ใช่แค่เรื่องของความเกียจคร้าน มันทำให้รหัสที่อ่านได้ง่ายขึ้นอีกด้วย และแตกต่างจาก JavaScript มันไม่ได้เกิดจากความกำกวมเนื่องจากคุณต้องนำหน้าทุกอย่างที่ได้รับผลกระทบจากคำสั่งด้วย.(dot) ดังนั้นทั้งสองต่อไปนี้ชัดเจน:

With someObject
    .Foo = ''
End With

เมื่อเทียบกับ

With someObject
    Foo = ''
End With

อดีตคือsomeObject.Foo; หลังอยู่Fooในขอบเขต someObjectนอก

ฉันพบว่าการขาดความแตกต่างของ JavaScript ทำให้มีประโยชน์น้อยกว่าตัวแปรของ Visual Basic เนื่องจากความเสี่ยงของความคลุมเครือนั้นสูงเกินไป นอกจากนั้นwithยังเป็นความคิดที่ทรงพลังที่สามารถทำให้อ่านง่ายขึ้น


2
จริงเพียงพอ ไม่ตอบคำถามของเขา ดังนั้นมันจึงเป็นหัวข้อ
Allain Lalonde

6
สิ่งนี้ก็วิ่งผ่านใจฉันเช่นกัน ใครบางคนจะบอกว่ามัน เหตุใด JavaScript จึงไม่สามารถมีจุดได้เช่นกัน
Carson Myers

เห็นด้วยสัญกรณ์จุดเป็นที่เหนือกว่าต้องการ JavaScript ใช้มัน +1


7

การใช้ "กับ" สามารถทำให้โค้ดของคุณแห้ง

พิจารณารหัสต่อไปนี้:

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

คุณสามารถทำให้แห้งต่อไปนี้:

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

ฉันคิดว่ามันขึ้นอยู่กับว่าคุณชอบความชัดเจนหรือการแสดงออก

ตัวอย่างแรกนั้นอ่านง่ายขึ้นและอาจแนะนำให้ใช้กับรหัสส่วนใหญ่ แต่รหัสส่วนใหญ่ค่อนข้างจะเชื่องอยู่แล้ว ส่วนที่สองค่อนข้างคลุมเครือกว่า แต่ใช้ลักษณะที่ชัดเจนของภาษาเพื่อลดขนาดรหัสและตัวแปรที่ไม่จำเป็น

ฉันคิดว่าคนที่ชอบ Java หรือ C # จะเลือกวิธีแรก (object.member) และผู้ที่ชอบ Ruby หรือ Python จะเลือกหลัง


อ๊ะฉันไม่ทราบว่ามีคนโพสต์ตัวอย่างเดียวกันนี้โดยทั่วไปเมื่อหนึ่งปีก่อน ปัญหาเรื่องประสิทธิภาพ "ด้วย" ทำให้รหัส DRY ที่ดีมีค่าใช้จ่ายในการอ่านยากขึ้นอีกเล็กน้อย ฉันคิดว่าสำหรับความร่วมมือกับผู้พัฒนารายอื่นหรือรหัสการผลิตส่วนใหญ่เป็นวิธีที่ดีที่จะหลีกเลี่ยงคำหลัก "กับ" แต่ถ้าคุณทำงานร่วมกับโปรแกรมเมอร์ระดับผู้เชี่ยวชาญและเข้าใจวิธีหลีกเลี่ยงความไร้ประสิทธิภาพที่อาจเกิดขึ้นคุณก็ต้องไปที่ "ด้วย"
Jonah

6

ฉันคิดว่าการใช้งานที่ชัดเจนนั้นเป็นทางลัด หากคุณกำลังเริ่มต้นวัตถุเช่นคุณเพียงแค่พิมพ์ "ObjectName" จำนวนมาก ชนิดของเสียงกระเพื่อม "พร้อมช่อง" ที่ให้คุณเขียน

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

ซึ่งเหมือนกับการเขียน

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

เห็นได้ชัดว่าทำไมนี่เป็นทางลัดเมื่อภาษาของคุณอนุญาตให้ "Objectname.foo" แต่ยังคงอยู่


1
ดีที่จะเห็นรหัสเสียงกระเพื่อม! ฉันคิดว่า "กับ" ใน javascript เห็นได้ชัดว่าได้รับแรงบันดาลใจจากแบบแผนรากเป็นภาษา แต่ก็ทำให้คุณรู้สึกแย่เมื่อถูกโพสต์ LISP สำหรับคำถามจาวาสคริปต์
ไฟอีกา

1
upvoting บนหลักการพื้นฐาน 'with' เป็นโครงสร้างที่ทรงพลังอย่างน่าอัศจรรย์ แต่คน JS ส่วนใหญ่ไม่เข้าใจการปิดและเขียนระบบการสืบทอดคลาส Java ที่ซับซ้อนอย่างน่าขันที่ด้านบนของ JS - ดังนั้นพวกเขาจึงตระหนักถึงศักยภาพของ metaprogramming ที่เสนอ 'กับ' ได้อย่างไร
jared

แน่นอนว่าwith-slotsคุณต้องระบุว่าคุณwithใช้สล็อตใดในขณะที่จะใช้สล็อตใด ๆ ก็ตามที่ถูกผูกไว้กับรันไทม์
ซามูเอลเอ็ดวินวอร์ด

6

มีประสบการณ์กับ Delphi ฉันจะบอกว่าใช้ กับควรเป็นการเพิ่มประสิทธิภาพขนาดสุดท้ายซึ่งอาจทำได้โดยอัลกอริทึม javascript minimizer บางชนิดพร้อมการเข้าถึงการวิเคราะห์รหัสแบบคงที่เพื่อตรวจสอบความปลอดภัย

ปัญหาการกำหนดขอบเขตที่คุณสามารถใช้กับการใช้เสรีของ กับการใช้คำสั่งอาจเป็นความเจ็บปวดของราชวงศ์ใน ** และฉันไม่ต้องการให้ใครได้สัมผัสกับการแก้ไขจุดบกพร่องเพื่อหาว่าเขากำลังเกิดอะไรขึ้นในรหัสของคุณ เท่านั้นที่จะพบว่ามันจับสมาชิกวัตถุหรือตัวแปรท้องถิ่นที่ไม่ถูกต้องแทนตัวแปรทั่วโลกหรือนอกขอบเขตที่คุณตั้งใจ

VB ที่มีคำสั่งดีกว่าในการที่มันต้องใช้จุดที่จะแยกแยะการกำหนดขอบเขต แต่ Delphi ที่มีคำสั่งเป็นปืนที่โหลดพร้อม hairtrigger และมันดูเหมือนกับฉันว่าจาวาสคริปต์นั้นคล้ายกันมากพอที่จะรับประกันคำเตือนเดียวกัน


5
จาวาสคริปต์ที่มีคำสั่งเลวร้ายยิ่งกว่า Delphi หนึ่ง ใน Delphi พร้อมดำเนินการอย่างรวดเร็ว (ถ้าไม่ใช่เร็วกว่า) ระบุวัตถุ ในจาวาสคริปต์ด้วยจะต้องเดินขอบเขตเพื่อตรวจสอบสมาชิกที่ตรงกันดังนั้นจึงทำให้มันช้ากว่าวัตถุ
Martijn

5

ไม่แนะนำให้ใช้กับและห้ามใช้ในโหมดเข้มงวด ECMAScript 5 ทางเลือกที่แนะนำคือการกำหนดวัตถุที่มีคุณสมบัติที่คุณต้องการเข้าถึงตัวแปรชั่วคราว

ที่มา: Mozilla.org


4

สามารถใช้คำสั่ง with เพื่อลดขนาดรหัสหรือสำหรับสมาชิกคลาสส่วนตัวเช่น:

// demo class framework
var Class= function(name, o) {
   var c=function(){};
   if( o.hasOwnProperty("constructor") ) {
       c= o.constructor;
   }
   delete o["constructor"];
   delete o["prototype"];
   c.prototype= {};
   for( var k in o ) c.prototype[k]= o[k];
   c.scope= Class.scope;
   c.scope.Class= c;
   c.Name= name;
   return c;
}
Class.newScope= function() {
    Class.scope= {};
    Class.scope.Scope= Class.scope;
    return Class.scope;
}

// create a new class
with( Class.newScope() ) {
   window.Foo= Class("Foo",{
      test: function() {
          alert( Class.Name );
      }
   });
}
(new Foo()).test();

คำสั่ง with-use มีประโยชน์อย่างมากหากคุณต้องการแก้ไขขอบเขตสิ่งที่จำเป็นสำหรับการมีขอบเขตโกลบอลของคุณเองที่คุณสามารถจัดการได้ที่รันไทม์ คุณสามารถใส่ค่าคงที่หรือฟังก์ชันผู้ช่วยบางอย่างที่มักใช้เช่น "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 ไม่มีผลกระทบต่อประสิทธิภาพการทำงาน แต่สิ่งที่ดีเพราะมันลดขนาดรหัสสิ่งที่ส่งผลกระทบต่อการใช้หน่วยความจำบนอุปกรณ์มือถือ


3

การใช้กับทำให้โค้ดของคุณช้าลงในการนำไปใช้งานหลายอย่างเนื่องจากทุกอย่างจะถูกห่อหุ้มด้วยขอบเขตการค้นหาเพิ่มเติม ไม่มีเหตุผลที่ถูกต้องในการใช้กับ JavaScript


5
การเพิ่มประสิทธิภาพก่อนวัยอันควร อย่าอ้างสิทธิ์ "ช้าลง" เว้นแต่คุณจะกระทืบหมายเลข ค่าใช้จ่ายใด ๆ ที่มีแนวโน้มเล็กน้อยใน js ทั้งในปัจจุบันและในอดีต
mk

2
ฉันไม่เห็นด้วยอย่างยิ่งกับข้อสรุปของคุณโดยยึดตามสิ่งที่อาจไม่เป็นปัญหาสำหรับนักพัฒนาอื่นที่ไม่ใช่คุณ
Dave Van den Eynde

4
@mk: โอเคจำนวนที่กระทืบสำหรับคุณที่นี่: var obj={a:0,b:0,c:0};var d=+new Date;with(obj){for(var i=0;i<1000000;++i){a+=1;b+=1;c+=1}}+new Date-d;ให้โดยเฉลี่ย 2,500 ในขณะที่var obj={a:0,b:0,c:0};var d=+new Date;for(var i=0;i<1000000;++i){obj.a+=1;obj.b+=1;obj.c+=1}+new Date-d;ให้โดยเฉลี่ย 750 ทำให้คนที่ใช้ในขณะที่ช้ากว่า 3 ครั้ง
yorick

3
เพิ่งวิ่งใน Chrome 23 ในคอนโซลเมื่อฉันเห็นสิ่งนี้ ผลลัพธ์ที่ฉันได้คือ 1138 สำหรับwithรหัสและ 903 โดยไม่ต้อง ด้วยความแตกต่างเล็ก ๆ น้อย ๆ นี้แม้ในวงแคบ ๆ ฉันจะทำการเลือกตามความเรียบง่ายของการเข้ารหัสและความสะดวกในการปรับสภาพเป็นกรณี ๆ ไปก่อนที่จะกังวลเกี่ยวกับประสิทธิภาพ
Plynx

3

ฉันคิดว่าข้อความที่มีประโยชน์อาจเป็นประโยชน์เมื่อแปลงภาษาเทมเพลตเป็น JavaScript ตัวอย่างเช่นJSTในbase2แต่ฉันเห็นมันบ่อยขึ้น

ฉันเห็นด้วยอย่างใดอย่างหนึ่งสามารถเขียนโปรแกรมนี้โดยไม่ต้องมีคำสั่ง แต่เนื่องจากมันไม่ได้ให้ปัญหาใด ๆ มันจึงเป็นการใช้ที่ถูกต้อง


3

เป็นการดีสำหรับการวางโค้ดที่ทำงานในสภาพแวดล้อมที่ค่อนข้างซับซ้อนลงในคอนเทนเนอร์: ฉันใช้มันเพื่อสร้างการเชื่อมโยงโลคอลสำหรับ "หน้าต่าง" และเพื่อเรียกใช้โค้ดที่มีความหมายสำหรับเว็บเบราว์เซอร์


3

ฉันคิดว่าการใช้วัตถุตามตัวอักษรนั้นน่าสนใจเช่นการแทนที่แบบดรอปอินเพื่อใช้การปิด

for(var i = nodes.length; i--;)
{
       // info is namespaced in a closure the click handler can access!
       (function(info)
       {           
            nodes[i].onclick = function(){ showStuff(info) };
       })(data[i]);
}

หรือด้วยคำสั่งที่เท่าเทียมกันของการปิด

for(var i = nodes.length; i--;)
{
       // info is namespaced in a closure the click handler can access!
       with({info: data[i]})
       {           
            nodes[i].onclick = function(){ showStuff(info) };
       }        
}

ฉันคิดว่าความเสี่ยงที่แท้จริงคือการลดตัวแปรที่ไม่ได้เป็นส่วนหนึ่งของคำสั่ง with โดยไม่ตั้งใจซึ่งเป็นสาเหตุที่ฉันชอบวัตถุที่ถูกส่งเข้ามาด้วยตัวคุณเองคุณจะเห็นได้ว่ามันจะอยู่ในบริบทที่เพิ่มเข้าไปในรหัสอย่างไร


3

ฉันได้สร้างฟังก์ชั่น "ผสาน" ซึ่งช่วยลดความคลุมเครือบางส่วนด้วยwithคำสั่ง:

if (typeof Object.merge !== 'function') {
    Object.merge = function (o1, o2) { // Function to merge all of the properties from one object into another
        for(var i in o2) { o1[i] = o2[i]; }
        return o1;
    };
}

ฉันสามารถใช้มันในทำนองเดียวกันกับ withแต่ฉันสามารถรู้ว่ามันจะไม่ส่งผลกระทบต่อขอบเขตใด ๆ ที่ฉันไม่ต้องการให้มันมีผลกระทบ

การใช้งาน:

var eDiv = document.createElement("div");
var eHeader = Object.merge(eDiv.cloneNode(false), {className: "header", onclick: function(){ alert("Click!"); }});
function NewObj() {
    Object.merge(this, {size: 4096, initDate: new Date()});
}

3

สำหรับชิ้นรหัสสั้นบางฉันต้องการที่จะใช้ฟังก์ชั่นตรีโกณมิติเช่นsin, cosฯลฯ ในโหมดการศึกษาระดับปริญญาแทนในโหมดกระจ่างใส เพื่อจุดประสงค์นี้ฉันใช้AngularDegreeวัตถุ:

AngularDegree = new function() {
this.CONV = Math.PI / 180;
this.sin = function(x) { return Math.sin( x * this.CONV ) };
this.cos = function(x) { return Math.cos( x * this.CONV ) };
this.tan = function(x) { return Math.tan( x * this.CONV ) };
this.asin = function(x) { return Math.asin( x ) / this.CONV };
this.acos = function(x) { return Math.acos( x ) / this.CONV };
this.atan = function(x) { return Math.atan( x ) / this.CONV };
this.atan2 = function(x,y) { return Math.atan2(x,y) / this.CONV };
};

จากนั้นฉันสามารถใช้ฟังก์ชันตรีโกณมิติในโหมดดีกรีโดยไม่มีสัญญาณรบกวนภาษาเพิ่มเติมในwithบล็อก:

function getAzimut(pol,pos) {
  ...
  var d = pos.lon - pol.lon;
  with(AngularDegree) {
    var z = atan2( sin(d), cos(pol.lat)*tan(pos.lat) - sin(pol.lat)*cos(d) );
    return z;
    }
  }

หมายความว่า: ฉันใช้วัตถุเป็นชุดของฟังก์ชั่นซึ่งฉันเปิดใช้งานในขอบเขตรหัสที่ จำกัด สำหรับการเข้าถึงโดยตรง ฉันพบว่ามันมีประโยชน์


มันไม่ใช่ความคิดที่ดีที่จะใช้withคำสั่งด้วยวิธีนี้มันแค่ทำให้โค้ดอ่านยากเพราะคุณไม่ทราบว่าฟังก์ชั่นใดที่เป็นสากลและฟังก์ชั่นที่เรียกว่าในขอบเขตของกับวัตถุดังนั้นถ้าฟังก์ชั่นใด ๆ ขอบเขตวัตถุแล้วมันจะพยายามเข้าถึงมันในเนมสเปซส่วนกลาง
Saket Patel

เมื่อตระหนักถึงปัญหาการกำหนดขอบเขตฉันยังคงพบว่ามีประโยชน์ นักคณิตศาสตร์ที่อ่านโค้ดต้องการเห็นโดยตรงว่าสูตรดังกล่าวเป็นแอปพลิเคชันของ "กฎของส่วนต่อเนื่อง 4 ส่วน" ในตรีโกณมิติทรงกลม ทางเลือกที่เข้มงวดทำให้สับสนสูตร: z = Math.atan2( Math.sin(d * Math.PI / 180), Math.cos( pol.lat * Math.PI / 180) * Math.tan( pos.lat * Math.PI / 180 ) - Math.sin( pol.lat * Math.PI / 180 ) * Math.cos( d * Math.PI / 180) ) * 180 / Math.PI;จะให้ผลเหมือนกัน แต่มันเป็นเรื่องสยองขวัญ
rplantiko

@ rootplant สิ่งที่ต้องจำไว้คือคนส่วนใหญ่รู้สึกไม่สะดวกใจกับมัน ดังนั้นหากคุณกำลังเขียนโค้ดที่ไม่มีใครแตะต้องเลย นอกจากนี้ฉันค่อนข้างแน่ใจว่าฉันสามารถแสดงให้คุณเห็นสองสามประเพณีwithที่จะตอคุณ
Juan Mendes

2

ฉันคิดว่าประโยชน์ของการwithขึ้นอยู่กับว่าเขียนโค้ดของคุณดีแค่ไหน ตัวอย่างเช่นหากคุณกำลังเขียนโค้ดที่มีลักษณะดังนี้:

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

จากนั้นคุณสามารถยืนยันว่าwithจะปรับปรุงความสามารถในการอ่านของรหัสโดยทำสิ่งนี้:

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

ในทางกลับกันอาจเป็นที่ถกเถียงกันอยู่ว่าคุณกำลังละเมิดกฏหมายของ Demeterแต่อาจไม่ใช่อีกครั้ง ฉันเชือนแช =)

เหนือสิ่งอื่นใดรู้ว่าดักลาส Crockford แนะนำไม่ได้withใช้ ฉันขอให้คุณตรวจสอบการโพสต์บล็อกของเขาเกี่ยวกับการwithและทางเลือกของที่นี่


ขอบคุณสำหรับคำตอบทอม ฉันอ่านคำแนะนำของ Crockford และในขณะที่มันสมเหตุสมผลแล้วมันไปไกลแค่ไหน ฉันมาถึงแนวคิด - สัมผัสโดยอ้อมโดย doekman - พลังที่แท้จริงของด้วย {} เป็นวิธีที่มันสามารถใช้เพื่อจัดการขอบเขต ...
Shog9

2

ฉันแค่ไม่เห็นว่าการใช้งานกับนั้นสามารถอ่านได้มากกว่าการพิมพ์ object.member ฉันไม่คิดว่ามันจะอ่านได้น้อยลง แต่ฉันไม่คิดว่ามันจะสามารถอ่านได้อีก

เช่น lassevk กล่าวว่าฉันสามารถเห็นได้อย่างชัดเจนว่าการใช้งานด้วยจะเป็นข้อผิดพลาดได้ง่ายกว่าการใช้ไวยากรณ์ "object.member" ที่ชัดเจนมาก


1

คุณต้องดูการตรวจสอบความถูกต้องของฟอร์มในจาวาสคริปต์ที่ W3schools http://www.w3schools.com/js/js_form_validation.aspโดยที่รูปแบบวัตถุนั้น "สแกน" ผ่านเพื่อค้นหาอินพุตที่มีชื่อ 'อีเมล'

แต่ฉันได้ทำการแก้ไขเพื่อให้ได้ฟอร์มใด ๆ จากทุกฟิลด์ที่ตรวจสอบว่าไม่ว่างเปล่าโดยไม่คำนึงถึงชื่อหรือปริมาณของฟิลด์ในฟอร์ม ฉันได้ทดสอบเฉพาะฟิลด์ข้อความ

แต่ด้วย () ทำให้สิ่งต่าง ๆ ง่ายขึ้น นี่คือรหัส:

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

Coco fork ของ CoffeeScript มีwithคีย์เวิร์ด แต่มันก็ตั้งค่าthis(เช่นเขียนได้@ใน CoffeeScript / Coco) ไปยังวัตถุเป้าหมายภายในบล็อก สิ่งนี้จะลบความกำกวมและบรรลุการปฏิบัติตามโหมดเข้มงวด ES5:

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

0

นี่คือการใช้ที่ดีสำหรับ with : การเพิ่มองค์ประกอบใหม่ให้กับวัตถุตามตัวอักษรตามค่าที่เก็บไว้ในวัตถุนั้น นี่คือตัวอย่างที่ฉันเพิ่งใช้ในวันนี้:

ฉันมีชุดไพ่ที่เป็นไปได้ (โดยมีช่องเปิดหันด้านบน, ด้านล่าง, ซ้ายหรือขวา) ที่สามารถใช้ได้และฉันต้องการวิธีที่รวดเร็วในการเพิ่มรายการของกระเบื้องที่จะวางและล็อคไว้เสมอเมื่อเริ่มเกม . ฉันไม่ได้ต้องการที่จะให้การพิมพ์สำหรับแต่ละประเภทในรายการดังนั้นฉันเพิ่งใช้types.tbrwith

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

คุณสามารถใช้เพื่อหลีกเลี่ยงการจัดการ arity อย่างชัดเจนเมื่อใช้ require.js:

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

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

การใช้งาน 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;
        }
    }   
}

0

ดังที่ Andy E ชี้ให้เห็นในความคิดเห็นของคำตอบของ Shog9 พฤติกรรมที่ไม่คาดคิดนี้เกิดขึ้นเมื่อใช้withกับวัตถุตามตัวอักษร:

for (var i = 0; i < 3; i++) {
  function toString() {
    return 'a';
  }
  with ({num: i}) {
    setTimeout(function() { console.log(num); }, 10);
    console.log(toString()); // prints "[object Object]"
  }
}

ไม่ว่าพฤติกรรมที่ไม่คาดคิดก็ไม่ได้อยู่แล้วwithจุดเด่นของ

หากคุณยังต้องการใช้เทคนิคนี้อย่างน้อยก็ให้ใช้วัตถุที่มีต้นแบบต้นเป็นโมฆะ

function scope(o) {
  var ret = Object.create(null);
  if (typeof o !== 'object') return ret;
  Object.keys(o).forEach(function (key) {
    ret[key] = o[key];
  });
  return ret;
}

for (var i = 0; i < 3; i++) {
  function toString() {
    return 'a';
  }
  with (scope({num: i})) {
    setTimeout(function() { console.log(num); }, 10);
    console.log(toString()); // prints "a"
  }
}

แต่จะใช้งานได้กับ ES5 + เท่านั้น withนอกจากนี้ยังไม่ได้ใช้


0

ฉันกำลังทำงานในโครงการที่จะอนุญาตให้ผู้ใช้อัปโหลดรหัสเพื่อแก้ไขพฤติกรรมของส่วนต่างๆของแอปพลิเคชัน ในสถานการณ์นี้ฉันได้ใช้withประโยคเพื่อป้องกันไม่ให้โค้ดของพวกเขาแก้ไขสิ่งใดนอกขอบเขตที่ฉันต้องการให้พวกเขายุ่งกับ ส่วนของโค้ดที่เรียบง่ายที่ฉันใช้ทำคือ:

// this code is only executed once
var localScope = {
    build: undefined,

    // this is where all of the values I want to hide go; the list is rather long
    window: undefined,
    console: undefined,
    ...
};
with(localScope) {
    build = function(userCode) {
        eval('var builtFunction = function(options) {' + userCode + '}');
        return builtFunction;
    }
}
var build = localScope.build;
delete localScope.build;

// this is how I use the build method
var userCode = 'return "Hello, World!";';
var userFunction = build(userCode);

รหัสนี้รับรอง (ค่อนข้าง) ว่ารหัสที่ผู้ใช้กำหนดไม่สามารถเข้าถึงวัตถุที่มีการกำหนดขอบเขตทั่วโลกเช่นwindowหรือตัวแปรท้องถิ่นใด ๆ ของฉันผ่านการปิด

ฉันยังต้องทำการตรวจสอบรหัสคงที่ในรหัสที่ผู้ใช้ส่งเพื่อให้แน่ใจว่าพวกเขาไม่ได้ใช้มารยาทที่หลอกลวงอื่น ๆ เพื่อเข้าถึงขอบเขตทั่วโลก ตัวอย่างเช่นรหัสที่ผู้ใช้กำหนดต่อไปนี้จะเข้าถึงโดยตรงไปที่window:

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

ที่เกี่ยวข้อง: stackoverflow.com/questions/543533/…
Shog9

0

ของฉัน

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

เดือดลงไป

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

คุณสามารถเชื่อถือรหัสที่มีคุณภาพต่ำได้หรือไม่ ไม่เราเห็นว่ามันทำให้อ่านไม่ได้อย่างแน่นอน ตัวอย่างนี้พิสูจน์ได้อย่างปฏิเสธไม่ได้ว่าไม่จำเป็นต้องมีคำสั่งหากฉันอ่านง่าย;)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.