Có những cách sử dụng hợp pháp nào cho câu lệnh JavaScript với JavaScript không?


369

Nhận xét của Alan Storm để đáp lại câu trả lời của tôi về withtuyên bố khiến tôi suy nghĩ. Tôi hiếm khi tìm thấy lý do để sử dụng tính năng ngôn ngữ cụ thể này và chưa bao giờ suy nghĩ nhiều về việc nó có thể gây rắc rối như thế nào. Bây giờ, tôi tò mò về cách tôi có thể sử dụng hiệu quả with, trong khi tránh những cạm bẫy của nó.

Bạn đã tìm thấy withtuyên bố hữu ích ở đâu?


52
Tôi không bao giờ sử dụng nó. Sống dễ dàng hơn nếu không có nó nếu tôi giả vờ nó không tồn tại.
Nosredna

6
Có thể một lần đã có nhiều sử dụng hợp lệ cho nó. Nhưng đó là tranh luận. ES5 Strict bị loại bỏ withvì vậy không còn bất kỳ điều gì như vậy.
Thomas Aylott

27
Đáng lưu ý ở đây là ES5 Strict vẫn là tùy chọn .
Shog9

5
Thay vì loại bỏ 'với' trong ES5, sẽ tốt hơn nếu thay đổi tiêu chuẩn để nếu không tìm thấy biến nào, bất kỳ phép gán nào được thực hiện bên trong 'với' được liên kết với đối tượng đối số?
JussiR

2
@JussiR: Có lẽ. Nhưng vấn đề trong việc đó là, nó có thể sẽ phá vỡ mọi thứ trong các trình duyệt cũ hơn.
Sune Rasmussen

Câu trả lời:


520

Một sử dụng khác đã xảy ra với tôi ngày hôm nay, vì vậy tôi đã tìm kiếm trên web một cách hào hứng và tìm thấy một đề cập hiện có về nó: Xác định các biến trong Khối phạm vi .

Lý lịch

JavaScript, mặc dù có sự tương đồng bề ngoài với C và C ++, nhưng không phạm vi biến cho khối mà chúng được định nghĩa trong:

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

Khai báo một bao đóng trong một vòng lặp là một nhiệm vụ phổ biến trong đó điều này có thể dẫn đến lỗi:

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

Bởi vì vòng lặp for không giới thiệu một phạm vi mới, tương tự num- với giá trị là 2- sẽ được chia sẻ bởi cả ba hàm.

Một phạm vi mới: letwith

Với việc giới thiệu lettuyên bố trong ES6 , việc giới thiệu một phạm vi mới khi cần thiết để tránh những vấn đề này trở nên dễ dàng:

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

Hoặc thậm chí:

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

Cho đến khi ES6 có sẵn trên toàn cầu, việc sử dụng này vẫn giới hạn ở các trình duyệt và nhà phát triển mới nhất sẵn sàng sử dụng bộ chuyển đổi. Tuy nhiên, chúng ta có thể dễ dàng mô phỏng hành vi này bằng cách sử dụng 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);
   }
}

Vòng lặp bây giờ hoạt động như dự định, tạo ba biến riêng biệt có giá trị từ 0 đến 2. Lưu ý rằng các biến được khai báo trong khối không nằm trong phạm vi của nó, không giống như hành vi của các khối trong C ++ (trong C, các biến phải được khai báo khi bắt đầu một khối, vì vậy theo một cách tương tự). Hành vi này thực sự khá giống với letcú pháp khối được giới thiệu trong các phiên bản trước của trình duyệt Mozilla, nhưng không được áp dụng rộng rãi ở nơi khác.


15
Không bao giờ nghĩ rằng sử dụng với một nghĩa đen, có vẻ hợp pháp.
Matt Kantor

81
Điều này thực sự thực sự chết trên. Tôi chưa bao giờ nghĩ chơi với phạm vi của JavaScript theo cách này. Hoàn toàn mở rộng các lĩnh vực mới để mã hóa của tôi. Tôi ước tôi có thể upvote 10 lần!
kizzx2

27
Đối với những người vẫn phản đối, người ta luôn có thể sử dụng một bao đóng:for (var i = 0; i < 3; ++i) { setTimeout ((function () { var num = i; return function () { alert (num); }; }) (), 10);}
Thomas Eding

4
Trên thực tế, sự cố được liên kết ở trên xuất hiện trên hầu hết các trình duyệt không phải Mozilla (Chrome, Safari, Opera, IE).
Tối đa Shawabkeh

24
chúng ta hãy ủng hộ tuyên bố trong IE thực sự sẽ tiết kiệm bacon của tôi ngay bây giờ, tôi phải vật lộn với lương tâm của tôi về việc có hay không sử dụng với thay thế. Vấn đề thực sự là ngay cả với một với một let , cẩn thận vẫn phải được thực hiện vì tài sản thừa kế của một đối tượng trên chuỗi nguyên mẫu. Ví dụ , var toString = function () { return "Hello"; }; with ({"test":1}) { console.log(toString()); };. Trong phạm vi của câu lệnh with , toString () là thuộc tính được kế thừa của Object , do đó, hàm được xác định rõ ràng không được gọi. Tuy nhiên, vẫn là một câu trả lời tuyệt vời :-)
Andy E

161

Tôi đã sử dụng câu lệnh with như một hình thức nhập khẩu có phạm vi đơn giản. Giả sử bạn có một trình xây dựng đánh dấu nào đó. Thay vì viết:

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

Thay vào đó bạn có thể viết:

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

Đối với trường hợp sử dụng này, tôi không thực hiện bất kỳ nhiệm vụ nào, vì vậy tôi không có vấn đề mơ hồ liên quan đến điều đó.


5
Đó là cách tôi đã thấy nó được sử dụng trong VB. (Và cách sử dụng duy nhất tôi biết.)
Mateen Ulhaq

2
Một nhược điểm của điều này sẽ là nếu bạn tham chiếu một biến trong khối có bên ngoài đối tượng markupbuilder, công cụ js trước tiên sẽ tìm kiếm nó bên trong markupbuilder, làm giảm hiệu suất.
Adam Thomas

3
Điều này thực sự giúp cắt giảm mã cho những người làm việc với các đường dẫn canvas.
Brian McCutchon

4
Phiên bản "với" của mã đó thực sự chạy chậm hơn 240 lần trên máy của tôi so với phiên bản "không cùng". ĐÓ là lý do tại sao mọi người nói rằng không có sử dụng hợp pháp cho nó. Không phải vì nó không thể làm cho mã đẹp hơn ở một số điểm. Xem điểm chuẩn: jsfiddle.net/sc46eeyn
Jimbo Jonny

1
@McBrainy - Đó chính xác là loại địa điểm bạn không nên sử dụng mã chạy chậm hơn nhiều lần (xem nhận xét tôi vừa thực hiện ở trên địa điểm này). Nếu bạn cần các phím tắt cho mã siêu lặp lại, bạn có thể khai báo chúng. Ví dụ nếu sử dụngcontext.bezierCurveTo thẳng hàng trăm lần, bạn có thể nói var bc2 = context.bezierCurveTo;và sau đó chỉ đi bc2(x,x,etc);mỗi lần bạn muốn gọi nó. Đó là khá nhanh và thậm chí ít dài dòng, trong khi withsiêu chậm.
Jimbo Jonny

83

Như những bình luận trước đây của tôi đã chỉ ra, tôi không nghĩ bạn có thể sử dụng withmột cách an toàn cho dù nó có hấp dẫn đến thế nào trong bất kỳ tình huống nào. Vì vấn đề không được đề cập trực tiếp ở đây, tôi sẽ nhắc lại. Hãy xem xét các mã sau đây

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

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

Nếu không điều tra cẩn thận các lệnh gọi hàm đó, không có cách nào để biết trạng thái chương trình của bạn sẽ như thế nào sau khi mã này chạy. Nếu user.nameđã được thiết lập, bây giờ nó sẽ được Bob. Nếu nó không được đặt, toàn cục namesẽ được khởi tạo hoặc thay đổi thành Bobuserđối tượng sẽ vẫn không cóname tính.

Lỗi xảy ra. Nếu bạn sử dụng với bạn cuối cùng sẽ làm điều này và tăng khả năng chương trình của bạn sẽ thất bại. Tồi tệ hơn, bạn có thể gặp phải mã làm việc đặt toàn cục trong khối với, có chủ ý hoặc thông qua tác giả không biết về sự giải quyết vấn đề này của công trình. Nó rất giống như gặp phải sự cố trên một công tắc, bạn không biết tác giả có ý định này hay không và không có cách nào để biết nếu "sửa" mã sẽ đưa ra hồi quy.

Ngôn ngữ lập trình hiện đại bị bóp nghẹt đầy đủ các tính năng. Một số tính năng, sau nhiều năm sử dụng, được phát hiện là xấu, và nên tránh. Javascript withlà một trong số đó.


18
Vấn đề này chỉ xuất hiện khi bạn đang gán giá trị cho thuộc tính của đối tượng. Nhưng nếu bạn chỉ sử dụng nó để đọc các giá trị thì sao? Tôi cho rằng sử dụng nó trong trường hợp đó là ổn.
sân bay

10
Vấn đề tương tự áp dụng cho việc đọc các giá trị Toby. Trong đoạn mã trên, bạn không biết tên được đặt trên đối tượng người dùng, vì vậy bạn sẽ không biết mình đang đọc tên toàn cầu hay tên người dùng.
Alan Storm

12
Với việc đọc các giá trị, có một quy tắc ưu tiên rõ ràng: các thuộc tính trên đối tượng được kiểm tra trước các biến ngoài phạm vi. Điều này không có gì khác với các biến trong phạm vi chức năng. Vấn đề thực sự với phép gán và 'với', theo tôi hiểu, nằm ở chỗ việc phân bổ thuộc tính có xảy ra hay không phụ thuộc vào việc thuộc tính có tồn tại trên đối tượng hiện tại hay không, có thể suy ra dễ dàng bằng cách nhìn vào mã
sân bay

1
Tôi nghĩ bạn có thể ở ngay đó Toby. Vấn đề viết là đủ để tôi né tránh việc xây dựng hoàn toàn.
Alan Storm

"Nó rất giống như gặp phải một công tắc, bạn không biết ..." - Vậy thì chúng ta cũng nên cấm công tắc () chứ? ;-p
Sz.

66

Tôi thực sự tìm thấy withtuyên bố là vô cùng hữu ích gần đây. Kỹ thuật này chưa bao giờ thực sự xảy ra với tôi cho đến khi tôi bắt đầu dự án hiện tại của mình - bảng điều khiển dòng lệnh được viết bằng JavaScript. Tôi đã cố gắng mô phỏng API bảng điều khiển Fireorms / WebKit nơi các lệnh đặc biệt có thể được nhập vào bảng điều khiển nhưng chúng không ghi đè bất kỳ biến nào trong phạm vi toàn cầu. Tôi đã nghĩ về điều này khi cố gắng khắc phục một vấn đề mà tôi đã đề cập trong các bình luận cho câu trả lời xuất sắc của Shog9 .

Để đạt được hiệu ứng này, tôi đã sử dụng hai câu lệnh để "lớp" một phạm vi đằng sau phạm vi toàn cầu:

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

Điều tuyệt vời của kỹ thuật này là, ngoài những nhược điểm về hiệu suất, nó không phải chịu những nỗi sợ thông thường của with tuyên bố, bởi vì dù sao chúng tôi cũng đang đánh giá trong phạm vi toàn cầu - không có nguy cơ biến số ngoài phạm vi giả của chúng tôi sửa đổi.

Tôi đã được truyền cảm hứng để đăng câu trả lời này khi, thật ngạc nhiên, tôi đã tìm được kỹ thuật tương tự được sử dụng ở nơi khác - mã nguồn 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: Chỉ cần kiểm tra nguồn Firebird, họ chuỗi 4 với các câu lệnh với nhau để có nhiều lớp hơn. Khùng!

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
nhưng lo lắng rằng ecmascript5 ngăn bạn làm điều này. Có một giải pháp ecmascript 5?
kybernetikos

@Adam: Tôi không chắc về điều đó. ES5 chỉ đưa ra một lỗi cho điều này trong chế độ nghiêm ngặt, vì vậy đó không phải là vấn đề ngay lập tức nếu bạn không có chế độ nghiêm ngặt được tuyên bố trên toàn cầu. ES Harmony có thể đặt ra một vấn đề lớn hơn, nhưng nó có thể khắc phục được với một số nội dung mới hơn như proxy.
Andy E

@AndyE xin lỗi, đây không phải chủ đề, nhưng 'bảng điều khiển dòng lệnh của bạn được viết bằng JavaScript' có ở bất cứ đâu không?
kybernetikos

@Adam: không, không phải vậy. Toàn bộ dự định là một bộ công cụ dành cho nhà phát triển cho Windows Desktop Gadgets, nhưng tôi chưa bao giờ hoàn thành nó (mặc dù, giao diện điều khiển hoạt động rất tốt). Tôi có thể hoàn thành nó vào một lúc nào đó, mặc dù WDGs không có một tương lai rất tươi sáng vào lúc này.
Andy E

3
Vài tuần trước, chúng tôi đã chuyển triển khai bảng điều khiển của mình trong Chrome từ khối sang một số phép thuật biểu tượng vì với khối đã chặn một số tính năng ES6 :)
Alexey Kozyatinskiy

54

Vâng, vâng và vâng. Có một cách sử dụng rất hợp pháp. Đồng hồ đeo tay:

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

Về cơ bản, bất kỳ hook DOM hoặc CSS nào khác đều được sử dụng tuyệt vời với. Nó không giống như "CloneNode" sẽ không được xác định và quay trở lại phạm vi toàn cầu trừ khi bạn đi ra ngoài và quyết định làm cho nó có thể.

Khiếu nại tốc độ của Crockford là một bối cảnh mới được tạo ra bởi. Các bối cảnh thường đắt tiền. Tôi đồng ý. Nhưng nếu bạn chỉ tạo một div và không có sẵn khung để thiết lập css của bạn và cần thiết lập 15 thuộc tính CSS bằng tay, thì việc tạo bối cảnh có thể sẽ rẻ hơn khi tạo biến và 15 lần hủy bỏ:

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

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

Vân vân...


5
+1 như tôi cũng nghĩ có rất nhiều cách sử dụng hợp pháp with. Tuy nhiên, trong trường hợp cụ thể này, bạn chỉ có thể làm:element.style.cssText="background: black ; color: blue ; border: 1px solid green"
GetFree

5
Bạn có thể đạt được điều tương tự trong một dòng bằng extendphương thức đơn giản từ jQuery hoặc Underscore.js : $.extend(element.style, {fontWeight: 'bold', fontSize: '1.5em', color: '#55d', marginLeft: '2px'}).
Trevor Burnham

9
@TrevorBurnham - Nếu bạn cho rằng jQuery có sẵn, bạn chỉ cần sử dụng .css()phương thức của nó ...
nnnnnn

4
Chính xác thì điều gì ngăn cản các biến này đi đến phạm vi toàn cầu? Có phải chỉ vì tất cả các kiểu CSS luôn được xác định trên tất cả các thành phần, hoặc là gì?
mở

1
@Mark có, chúng luôn được xác định, với các giá trị null hoặc chuỗi rỗng làm giá trị nếu không có kiểu tùy chỉnh cho một thuộc tính
Esailija

34

Bạn có thể định nghĩa một hàm trợ giúp nhỏ để cung cấp các lợi ích withmà không có sự mơ hồ:

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

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

8
OMG, ĐẦU CỦA TÔI GIẢI THÍCH! không có sự mơ hồ? Hãy bình chọn mà lên, anh bạn!
Jarrod Dixon

@Jarrod: có gì buồn cười về điều này ( is.gd/ktoZ )? Hầu hết mọi người sử dụng trang web này đều thông minh hơn tôi, vì vậy hãy tha thứ cho tôi nếu tôi sai, nhưng đây có vẻ là thông tin xấu.
quạ

14
Nhưng đó chỉ là cách làm lâu hơn và khó hiểu hơn: var _ = obj_name_here; _.a = "foo"; _.b = "thanh;
Rene Saarsoo

3
Rene: Cùng với đó, bạn sẽ đưa biến "_" ra phạm vi bên ngoài, dẫn đến các lỗi tiềm ẩn. Nó cũng sẽ đưa ra bất kỳ biến tạm thời nào được sử dụng trong việc tính toán các tham số đối tượng.
John Millikin

30
Bạn sẽ nhận được nhiều lỗi hơn từ with_việc trở thành một phiên bản nhân đôi của bùn (function(_){ _.a="foo"; })(object_here);(cách tiêu chuẩn để mô phỏng các khối kiểu c / java). Sử dụng thay thế.
mk.

25

Hầu như không có giá trị vì bạn có thể làm như sau:

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

1
Điều này không có ý nghĩa gì với tôi. Mặc dù tôi không phải là chuyên gia về JavaScript
JeroenEijkhof

@WmasterJ Để rõ ràng, hãy xem bài đăng này: yuiblog.com/blog/2006/04/11/with-statement-considered-harmful
Dennis

8
Tên biến dài không phải là trường hợp sử dụng duy nhất cho with.
Chris

18

Tôi không bao giờ sử dụng với, không thấy lý do và không khuyến nghị.

Vấn đề với withlà nó ngăn chặn nhiều tối ưu hóa từ vựng việc triển khai ECMAScript có thể thực hiện. Với sự gia tăng của các công cụ dựa trên JIT nhanh, vấn đề này có thể sẽ trở nên quan trọng hơn nữa trong tương lai gần.

Nó có thể trông giống như withcho phép các cấu trúc sạch hơn (khi, giả sử, giới thiệu một phạm vi mới thay vì trình bao bọc hàm ẩn danh thông thường hoặc thay thế bí danh dài dòng), nhưng nó thực sự không đáng . Bên cạnh hiệu suất giảm, luôn có nguy cơ gán cho một thuộc tính của một đối tượng sai (khi không tìm thấy thuộc tính trên một đối tượng trong phạm vi được tiêm) và có thể giới thiệu sai các biến toàn cục. IIRC, vấn đề thứ hai là vấn đề thúc đẩy Crockford đề nghị tránh with.


6
Bogeyman hiệu suất được phát hiện thường xuyên, gần như thường xuyên như mọi thứ trên toàn cầu ... Luôn luôn gây cho tôi sự kỳ quặc, vì đó là JavaScript mà chúng ta đang nói đến. Bạn sẽ cho rằng hiệu năng thực sự ấn tượng để đảm bảo sự chú ý đó, nhưng ... Nếu bạn có bất kỳ con số khó khăn nào về chi phí with(){}xây dựng như những câu trả lời khác ở đây, trong các trình duyệt hiện đại, tôi rất muốn thấy họ!
Shog9

6
Tại sao nó kỳ lạ trong bối cảnh của Javascript? :) Và vâng, nó rất ấn tượng. Hãy suy nghĩ về nó - việc triển khai cần đánh giá một biểu thức trong ngoặc đơn, chuyển đổi nó thành đối tượng, chèn nó vào phía trước chuỗi phạm vi hiện tại, đánh giá câu lệnh bên trong khối, sau đó khôi phục chuỗi phạm vi trở lại bình thường. Đó là rất nhiều công việc. Nhiều hơn một tra cứu thuộc tính đơn giản có thể được chuyển thành mã cấp thấp được tối ưu hóa cao. Đây là một điểm chuẩn rất đơn giản mà tôi vừa thực hiện (cho tôi biết nếu bạn tìm thấy bất kỳ sai lầm nào) chứng minh sự khác biệt - gist.github.com/c36ea485926806020024
kangax

5
@ Khangax: Tôi đến từ nền tảng C ++, nơi mà nhiều lập trình viên truyền thống bị ám ảnh về hiệu quả nhỏ trong mã của họ, ngay cả khi họ không thực sự có ảnh hưởng đáng kể đến hiệu suất của chương trình hoặc chương trình lớn hơn. Nó có vẻ kỳ lạ đối với tôi trong bối cảnh JavaScript, nơi một phần lớn hiệu suất của một thói quen có thể phụ thuộc vào việc triển khai VM. Tôi đã thấy một vài trường hợp mà các lập trình viên JS sẽ tránh, giả sử, một hàm ẩn danh do lo ngại về chi phí thiết lập, nhưng đây dường như là ngoại lệ không phải là quy tắc, dành riêng cho các lĩnh vực mã rất nhạy cảm.
Shog9

5
Điều đó nói rằng, bạn hoàn toàn chính xác về chi phí with(){}: thiết lập một phạm vi mới withrất tốn kém trên mọi trình duyệt tôi đã thử nghiệm. Bạn muốn tránh điều này trong bất kỳ mã nào được gọi là rất thường xuyên. Ngoài ra, Chrome đã thể hiện một cú hích kịch tính cho bất kỳ mã thực thi nào trong with()phạm vi. Thật thú vị, IE có các đặc tính hiệu suất tốt nhất cho mã trong with()các khối: bao gồm chi phí thiết lập, with()cung cấp phương tiện truy cập thành viên nhanh nhất trong các máy ảo IE6 và IE8 (mặc dù các máy ảo này chậm nhất nói chung). Tốt, cảm ơn ...
Shog9

5
FWIW: đây là tập hợp các bài kiểm tra tương tự với chi phí thiết lập được nêu ra: jsbin.com/imidu/edit Quyền truy cập biến đổi with()gần như là một thứ tự cường độ chậm hơn trong Chrome và nhanh hơn gấp đôi trong IE ...!
Shog9

13

Visual Basic.NET có một Withtuyên bố tương tự . Một trong những cách phổ biến hơn mà tôi sử dụng là nhanh chóng thiết lập một số thuộc tính. Thay vì:

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

, Tôi có thể viết:

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

Đây không chỉ là vấn đề của sự lười biếng. Nó cũng làm cho mã dễ đọc hơn nhiều. Và không giống như JavaScript, nó không bị mơ hồ, vì bạn phải thêm tiền tố vào mọi thứ bị ảnh hưởng bởi câu lệnh bằng .dấu chấm. Vì vậy, hai điều sau đây là rõ ràng rõ ràng:

With someObject
    .Foo = ''
End With

so với

With someObject
    Foo = ''
End With

Các cựu là someObject.Foo; cái sau nằm Footrong phạm vi bên ngoài someObject .

Tôi thấy rằng sự thiếu phân biệt của JavaScript làm cho nó ít hữu ích hơn nhiều so với biến thể của Visual Basic, vì nguy cơ mơ hồ là quá cao. Ngoài ra, withvẫn là một ý tưởng mạnh mẽ có thể làm cho khả năng đọc tốt hơn.


2
Đúng đủ rồi. Không trả lời câu hỏi của anh ấy mặc dù. Vì vậy, nó lạc đề.
Allain Lalonde

6
Điều này đã chạy qua tâm trí của tôi là tốt. ai đó đã phải nói điều đó . Tại sao JavaScript không có dấu chấm là tốt.
Carson Myers

Đồng ý, ký hiệu dấu chấm là ưu việt, mong muốn JavaScript sử dụng nó. +1


7

Sử dụng "với" có thể làm cho mã của bạn khô hơn.

Hãy xem xét các mã sau đây:

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

Bạn có thể làm khô nó như sau:

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

Tôi đoán nó phụ thuộc vào việc bạn có ưu tiên cho tính dễ đọc hay biểu cảm hay không.

Ví dụ đầu tiên dễ đọc hơn và có thể được khuyến nghị cho hầu hết các mã. Nhưng hầu hết các mã là khá thuần hóa. Cái thứ hai khó hiểu hơn một chút nhưng sử dụng bản chất biểu cảm của ngôn ngữ để cắt giảm kích thước mã và các biến không cần thiết.

Tôi tưởng tượng những người thích Java hoặc C # sẽ chọn cách đầu tiên (object.member) và những người thích Ruby hoặc Python sẽ chọn cách thứ hai.


Rất tiếc tôi đã không nhận ra ai đó đã đăng cơ bản ví dụ này một năm trước. Vấn đề về hiệu năng sang một bên, "với" làm cho mã DRY đẹp với chi phí khó đọc hơn một chút. Tôi nghĩ rằng để hợp tác với các nhà phát triển khác hoặc hầu hết mã sản xuất, nên tránh từ khóa "có". Nhưng nếu bạn đang làm việc với các lập trình viên cấp chuyên gia và hiểu làm thế nào để tránh sự thiếu hiệu quả tiềm năng thì bằng mọi cách hãy đến thị trấn với "với".
Giô-na

6

Tôi nghĩ rằng việc sử dụng rõ ràng là như một phím tắt. Nếu bạn đang khởi tạo một đối tượng, bạn chỉ cần lưu rất nhiều "ObjectName". Kiểu như "với các khe" của lisp cho phép bạn viết

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

giống như viết

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

Rõ ràng hơn tại sao đây là một phím tắt sau đó khi ngôn ngữ của bạn cho phép "Objectname.foo" nhưng vẫn.


1
thật tuyệt khi thấy mã lisp! Tôi nghĩ "với" trong javascript rõ ràng được lấy cảm hứng từ nguồn gốc lược đồ của nó như một ngôn ngữ, nhưng than ôi việc bạn bị hạ thấp khi đăng LISP trên một câu hỏi javascript.
Lửa quạ

1
Nâng cao trên nguyên tắc cơ bản. 'với' là một cấu trúc mạnh mẽ phi thường. Nhưng hầu hết mọi người JS không hiểu các bao đóng và viết các hệ thống kế thừa lớp Java phức tạp một cách lố bịch lên trên JS - vậy làm sao họ có thể nhận thức được tiềm năng siêu lập trình mà 'với' cung cấp?
Jared

Tất nhiên with-slotsyêu cầu bạn chỉ định các vị trí bạn đang sử dụng, trong khi đó withsẽ sử dụng bất kỳ vị trí nào xảy ra bị ràng buộc trong thời gian chạy.
Phường Samuel Edwin

6

Có kinh nghiệm với Delphi, tôi sẽ nói rằng sử dụng với nên là tối ưu hóa kích thước cuối cùng, có thể được thực hiện bởi một số loại thuật toán tối thiểu hóa javascript có quyền truy cập vào phân tích mã tĩnh để xác minh tính an toàn của nó.

Những vấn đề Phạm vi bạn có thể nhận được vào với việc sử dụng tự do của với tuyên bố có thể là một nỗi đau của hoàng gia trong một ** và tôi không muốn bất cứ ai để trải nghiệm một phiên gỡ rối để tìm ra những gì anh .. đang xảy ra trong mã của bạn , chỉ để tìm ra rằng nó đã bắt một thành viên đối tượng hoặc biến cục bộ sai, thay vì biến phạm vi toàn cầu hoặc phạm vi bên ngoài mà bạn dự định.

VB với tuyên bố là tốt hơn, ở chỗ nó cần các dấu chấm để phân định phạm vi, nhưng Delphi với tuyên bố là một khẩu súng được nạp với một máy xén tóc, và đối với tôi như thể một javascript đủ để đưa ra cảnh báo tương tự.


5
Các javascript với tuyên bố là tồi tệ hơn so với Delphi. Trong Delphi, với hiệu suất chỉ nhanh (nếu không nhanh hơn) so với ký hiệu object.member. Trong javascript, với việc phải đi qua phạm vi để kiểm tra các thành viên phù hợp, do đó luôn làm cho nó chậm hơn ký hiệu object.member.
Martijn

5

Việc sử dụng với không được khuyến nghị và bị cấm trong chế độ nghiêm ngặt ECMAScript 5. Lựa chọn thay thế được đề xuất là gán đối tượng có thuộc tính bạn muốn truy cập vào một biến tạm thời.

Nguồn: Mozilla.org


4

Câu lệnh with có thể được sử dụng để giảm kích thước mã hoặc cho các thành viên lớp riêng, ví dụ:

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

Câu lệnh with rất hữu ích nếu bạn muốn sửa đổi phạm vi, điều cần thiết để có phạm vi toàn cầu của riêng bạn mà bạn có thể thao tác khi chạy. Bạn có thể đặt các hằng số trên nó hoặc một số hàm trợ giúp nhất định thường được sử dụng như "toUpper", "toLower" hoặc "isNumber", "clipNumber" aso ..

Về hiệu suất kém mà tôi thường đọc: Phạm vi một chức năng sẽ không có bất kỳ tác động nào đến hiệu suất, trên thực tế trong FF của tôi, một chức năng trong phạm vi chạy nhanh hơn sau đó không được kiểm soát:

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

Vì vậy, theo cách được đề cập ở trên, câu lệnh with không có ảnh hưởng tiêu cực đến hiệu năng, nhưng một cách tốt vì nó làm giảm kích thước mã, điều gì ảnh hưởng đến việc sử dụng bộ nhớ trên thiết bị di động.


3

Việc sử dụng cũng làm cho mã của bạn chậm hơn trong nhiều lần triển khai, vì mọi thứ bây giờ được gói trong một phạm vi bổ sung để tra cứu. Không có lý do chính đáng để sử dụng với JavaScript.


5
Tối ưu hóa sớm. Đừng yêu cầu "chậm hơn" trừ khi bạn bấm số; bất kỳ chi phí nào cũng có thể tầm thường trên cả js hiện đại và cổ đại.
mk.

2
Tôi hoàn toàn không đồng ý với kết luận của bạn, dựa trên những gì có thể không phải là vấn đề đối với các nhà phát triển khác ngoài bạn.
Dave Van den Eynde

4
@mk: ok, số crunching cho bạn ở đây: 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;cho trung bình 2500, trong khi 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;cho trung bình 750, làm cho một người sử dụng trong khi chậm hơn 3 lần.
Yorick

3
Chỉ cần chạy những thứ này trong Chrome 23 trong bảng điều khiển khi tôi thấy điều này. Kết quả tôi nhận được là 1138 cho withmã và 903 không có. Với sự khác biệt nhỏ này ngay cả trong một vòng lặp chặt chẽ, tôi sẽ đưa ra lựa chọn dựa trên sự đơn giản mã hóa và dễ dàng tái cấu trúc trên cơ sở từng trường hợp trước khi lo lắng về hiệu suất.
Plynx

3

Tôi nghĩ rằng câu lệnh có thể có ích khi chuyển đổi ngôn ngữ mẫu sang JavaScript. Ví dụ JST trong cơ sở 2 , nhưng tôi đã thấy nó thường xuyên hơn.

Tôi đồng ý người ta có thể lập trình điều này mà không cần tuyên bố. Nhưng bởi vì nó không cung cấp cho bất kỳ vấn đề nào, nó là một sử dụng hợp pháp.


3

Thật tốt khi đặt mã chạy trong một môi trường tương đối phức tạp vào một thùng chứa: Tôi sử dụng nó để tạo một ràng buộc cục bộ cho "cửa sổ" và như vậy để chạy mã có nghĩa là cho trình duyệt web.


3

Tôi nghĩ rằng việc sử dụng đối tượng theo nghĩa đen là thú vị, giống như một sự thay thế thả vào để sử dụng một bao đóng

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

hoặc với tuyên bố tương đương của một đóng cửa

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

Tôi nghĩ rằng rủi ro thực sự là vô tình làm giảm các biến không phải là một phần của câu lệnh with, đó là lý do tại sao tôi thích đối tượng được truyền vào, bạn có thể thấy chính xác nó sẽ nằm trong ngữ cảnh được thêm vào trong mã.


3

Tôi đã tạo một hàm "hợp nhất" để loại bỏ một số sự mơ hồ này với withcâu lệnh:

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

Tôi có thể sử dụng nó tương tự with, nhưng tôi có thể biết nó sẽ không ảnh hưởng đến bất kỳ phạm vi nào mà tôi không có ý định ảnh hưởng đến nó.

Sử dụng:

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

Đối với một số đoạn mã ngắn, tôi muốn sử dụng các hàm lượng giác như sin, cosv.v. ở chế độ độ thay vì ở chế độ bức xạ. Với mục đích này, tôi sử dụng một AngularDegreeđối tượng:

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

Sau đó, tôi có thể sử dụng các hàm lượng giác trong chế độ độ mà không bị nhiễu ngôn ngữ trong một withkhối:

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

Điều này có nghĩa là: Tôi sử dụng một đối tượng như một tập hợp các hàm, mà tôi kích hoạt trong một vùng mã giới hạn để truy cập trực tiếp. Tôi thấy điều này hữu ích.


Cách này không phải là một ý tưởng hay khi sử dụng withcâu lệnh theo cách này, nó chỉ làm cho mã khó đọc vì bạn không biết hàm nào là toàn cục và hàm nào được gọi trong phạm vi với đối tượng, vì vậy nếu có bất kỳ hàm nào không được định nghĩa trong phạm vi đối tượng sau đó nó sẽ cố gắng truy cập nó trong không gian tên toàn cầu
Saket Patel

Nhận thức được vấn đề phạm vi, tôi vẫn thấy điều này hữu ích. Một nhà toán học đọc mã muốn thấy trực tiếp rằng công thức trên là một ứng dụng của "định luật 4 phần liên tiếp" trong lượng giác hình cầu. Sự thay thế nghiêm ngặt làm xáo trộn công thức: 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;sẽ cho kết quả tương tự, nhưng đó là nỗi kinh hoàng.
rplantiko

@rplantiko Điều cần lưu ý là hầu hết mọi người không cảm thấy thoải mái với nó. Vì vậy, trừ khi bạn đang viết mã mà không ai khác sẽ chạm vào. Ngoài ra, tôi khá chắc chắn rằng tôi có thể cho bạn thấy một vài cách sử dụng withnó sẽ làm bạn bối rối.
Juan Mendes

2

Tôi nghĩ rằng sự hữu ích của withcó thể phụ thuộc vào mức độ mã của bạn được viết. Ví dụ: nếu bạn đang viết mã xuất hiện như thế này:

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

sau đó bạn có thể lập luận rằng withsẽ cải thiện khả năng đọc mã bằng cách thực hiện điều này:

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

Ngược lại, có thể lập luận rằng bạn đang vi phạm Luật Demeter , nhưng, một lần nữa, có thể không. Tôi lạc đề =).

Trên hết, hãy biết rằng Douglas Crockford khuyên bạn không nên sử dụng with. Tôi đề nghị bạn kiểm tra bài viết trên blog của anh ấy withvà các lựa chọn thay thế ở đây .


Cảm ơn đã trả lời, Tom. Tôi đã đọc khuyến nghị của Crockford, và trong khi nó có ý nghĩa thì nó chỉ đi xa đến vậy. Tôi đang nảy ra ý tưởng - được gián tiếp chạm vào doekman - rằng sức mạnh thực sự của {} là theo cách nó có thể được sử dụng để thao túng phạm vi ...
Shog9

2

Tôi thực sự không thấy cách sử dụng với bất kỳ dễ đọc hơn là chỉ gõ object.member. Tôi không nghĩ nó ít đọc hơn, nhưng tôi cũng không nghĩ nó dễ đọc hơn.

Giống như lassevk đã nói, tôi chắc chắn có thể thấy việc sử dụng với sẽ dễ bị lỗi hơn là chỉ sử dụng cú pháp "object.member" rất rõ ràng.


1

Bạn phải xem xác thực của một biểu mẫu trong javascript tại W3schools http://www.w3schools.com/js/js_form_validation.asp trong đó biểu mẫu đối tượng được "quét" để tìm đầu vào có tên 'email'

Nhưng tôi đã sửa đổi nó để lấy từ BẤT K form tất cả các trường xác thực là không trống, bất kể tên hoặc số lượng trường trong một biểu mẫu. Vâng, tôi đã chỉ thử nghiệm các trường văn bản.

Nhưng với () làm cho mọi thứ đơn giản hơn. Đây là mã:

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

Cái ngã ba Coco của CoffeeScript có một withtừ khóa, nhưng nó chỉ đơn giản là đặt this(cũng có thể ghi như @trong CoffeeScript / Coco) cho đối tượng đích trong khối. Điều này loại bỏ sự mơ hồ và đạt được sự tuân thủ chế độ nghiêm ngặt ES5:

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

0

Đây là một cách sử dụng tốt để with: thêm các yếu tố mới vào Đối tượng, dựa trên các giá trị được lưu trữ trong Đối tượng đó. Đây là một ví dụ mà tôi vừa sử dụng ngày hôm nay:

Tôi đã có một bộ gạch có thể (có các lỗ mở hướng lên trên, dưới, trái hoặc phải) có thể được sử dụng và tôi muốn có một cách nhanh chóng để thêm danh sách các ô sẽ luôn được đặt và khóa khi bắt đầu trò chơi . Tôi không muốn tiếp tục gõ types.tbrcho từng loại trong danh sách, vì vậy tôi chỉ sử dụng 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

Bạn có thể sử dụng với để tránh phải quản lý rõ ràng arity khi sử dụng allow.js:

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

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

Triển khai các yêu cầu:

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

Như Andy E đã chỉ ra trong các bình luận về câu trả lời của Shog9, hành vi có khả năng bất ngờ này xảy ra khi sử dụng withvới một đối tượng theo nghĩa đen:

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

Không phải là vấn đề nghi ngờ không phải là đã một dấu hiệu củawith .

Nếu bạn thực sự vẫn muốn sử dụng kỹ thuật này, ít nhất hãy sử dụng một đối tượng có nguyên mẫu null.

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

Nhưng điều này sẽ chỉ hoạt động trong ES5 +. Cũng không sử dụng with.


0

Tôi đang làm việc trên một dự án sẽ cho phép người dùng tải lên mã để sửa đổi hành vi của các bộ phận của ứng dụng. Trong kịch bản này, tôi đã sử dụng một withmệnh đề để giữ cho mã của họ không sửa đổi bất cứ điều gì ngoài phạm vi mà tôi muốn chúng gây rối. Phần (đơn giản hóa) của mã tôi sử dụng để làm điều này là:

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

Mã này đảm bảo (phần nào) rằng mã do người dùng xác định không có quyền truy cập vào bất kỳ đối tượng nào trong phạm vi toàn cầu, chẳng hạn như window cũng như bất kỳ biến cục bộ nào của tôi thông qua việc đóng.

Giống như một lời nói khôn ngoan, tôi vẫn phải thực hiện kiểm tra mã tĩnh trên mã do người dùng gửi để đảm bảo họ không sử dụng các cách thức lén lút khác để truy cập phạm vi toàn cầu. Chẳng hạn, đoạn mã do người dùng định nghĩa sau đây truy cập trực tiếp vào window:

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


0

Của tôi

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
}

sôi xuống

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

Bạn có thể tin tưởng mã chất lượng thấp như vậy? Không, chúng tôi thấy rằng nó đã được thực hiện hoàn toàn không thể đọc được. Ví dụ này không thể phủ nhận chứng minh rằng không cần phải có câu lệnh, nếu tôi đang đọc đúng;)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.