2009-12-26 12 views
209

Tôi đã sử dụng JSLint trên tệp JavaScript của tôi. Nó ném lỗi:Lỗi JSLint 'nội dung của một cho nên được bao bọc trong một câu lệnh if' nghĩa là gì?

for(ind in evtListeners) { 

Problem at line 41 character 9: The body of a for in should be wrapped in an if statement to filter unwanted properties from the prototype.

này có nghĩa là gì?

+4

theo mặc định 'trong' lặp trên tài sản thừa kế là tốt. Thông thường, phần thân được bọc trong 'if (evtListeners.hasOwnProperty (ind))' để hạn chế việc xử lý chỉ với các thuộc tính riêng (không được kế thừa). Tuy nhiên, trong một số trường hợp, bạn thực sự muốn lặp qua tất cả các thuộc tính, bao gồm cả các thuộc tính được kế thừa. Trong trường hợp đó, JSLint buộc bạn quấn thân vòng lặp trong câu lệnh if để quyết định thuộc tính nào bạn thực sự muốn. Điều này sẽ làm việc và làm cho JSlint hạnh phúc: 'if (evtListeners [ind]! == undefined)' – xorcus

+0

Hầu hết các câu trả lời đều lỗi thời. một giải pháp được cập nhật có thể được tìm thấy tại https://stackoverflow.com/a/10167931/3138375 –

Trả lời

377

Trước hết, bao giờ sử dụng một vòng lặp for in liệt kê trên một mảng. Không bao giờ. Sử dụng tốt cũ for(var i = 0; i<arr.length; i++).

Lý do đằng sau điều này là như sau: mỗi đối tượng trong JavaScript có một trường đặc biệt được gọi là prototype. Mọi thứ bạn thêm vào trường đó sẽ có thể truy cập được trên mọi đối tượng thuộc loại đó. Giả sử bạn muốn tất cả các mảng có một hàm mới tuyệt vời được gọi là filter_0 sẽ lọc các số không.

Array.prototype.filter_0 = function() { 
    var res = []; 
    for (var i = 0; i < this.length; i++) { 
     if (this[i] != 0) { 
      res.push(this[i]); 
     } 
    } 
    return res; 
}; 

console.log([0, 5, 0, 3, 0, 1, 0].filter_0()); 
//prints [5,3,1] 

Đây là cách tiêu chuẩn để mở rộng đối tượng và thêm phương pháp mới. Rất nhiều thư viện làm điều này. Tuy nhiên, chúng ta hãy nhìn vào cách for in làm việc bây giờ:

var listeners = ["a", "b", "c"]; 
for (o in listeners) { 
    console.log(o); 
} 
//prints: 
// 0 
// 1 
// 2 
// filter_0 

Bạn có thấy? Nó đột nhiên nghĩ rằng filter_0 là một chỉ số mảng khác. Tất nhiên, nó không thực sự là một chỉ số, nhưng for in liệt kê thông qua các trường đối tượng, không chỉ là các chỉ mục số. Vì vậy, chúng tôi hiện đang liệt kê tất cả các chỉ số số filter_0. Nhưng filter_0 không phải là một trường của bất kỳ đối tượng mảng cụ thể nào, mỗi đối tượng mảng đều có thuộc tính này ngay bây giờ.

May mắn thay, tất cả các đối tượng đều có phương thức hasOwnProperty, kiểm tra xem trường này có thực sự thuộc về đối tượng hay không hoặc chỉ đơn giản là được thừa kế từ chuỗi nguyên mẫu và do đó thuộc về tất cả các đối tượng thuộc loại đó.

for (o in listeners) { 
    if (listeners.hasOwnProperty(o)) { 
     console.log(o); 
    } 
} 
//prints: 
// 0 
// 1 
// 2 

Lưu ý, rằng mặc dù mã này làm việc như mong đợi cho mảng, bạn nên không bao giờ, không bao giờ , sử dụng for infor each in cho mảng. Hãy nhớ rằng for in liệt kê các trường của một đối tượng, không phải chỉ mục hoặc giá trị mảng.

var listeners = ["a", "b", "c"]; 
listeners.happy = "Happy debugging"; 

for (o in listeners) { 
    if (listeners.hasOwnProperty(o)) { 
     console.log(o); 
    } 
} 

//prints: 
// 0 
// 1 
// 2 
// happy 
+35

Bạn không nên sử dụng 'for in' để lặp qua các mảng vì ngôn ngữ không khoanh vùng thứ tự trong đó' cho in' sẽ liệt kê một mảng. Nó có thể không theo thứ tự số. Ngoài ra, nếu bạn sử dụng cấu trúc kiểu 'for (i = 0; i Breton

+0

cảm ơn câu trả lời chi tiết :) – jrharshath

+0

Cảm ơn! Tôi sẽ lưu điều này làm tài liệu tham khảo. – nyuszika7h

0

Điều này có nghĩa là bạn nên lọc các thuộc tính của evtListeners bằng hasOwnProperty method.

81

Douglas Crockford, tác giả của jslint đã viết và nói về vấn đề này nhiều lần. Có một phần trên this trang của trang web của mình trong đó bao gồm này:

for Statement

A for class of statements should have the following form:

for (initialization; condition; update) { 
    statements 
} 

for (variable in object) { 
    if (filter) { 
     statements 
    } 
} 

The first form should be used with arrays and with loops of a predeterminable number of iterations.

The second form should be used with objects. Be aware that members that are added to the prototype of the object will be included in the enumeration. It is wise to program defensively by using the hasOwnProperty method to distinguish the true members of the object:

for (variable in object) { 
    if (object.hasOwnProperty(variable)) { 
     statements 
    } 
} 

Crockford cũng có một loạt video trên sân khấu YUI nơi ông nói về việc này. Loạt video/cuộc nói chuyện của Crockford về javascript là phải xem liệu bạn có hơi nghiêm túc về javascript hay không.

9

Câu trả lời của Vava là trên nhãn hiệu. Nếu bạn sử dụng jQuery, thì hàm $.each() sẽ giải quyết vấn đề này, do đó sẽ an toàn hơn khi sử dụng.

$.each(evtListeners, function(index, elem) { 
    // your code 
}); 
+5

Nếu hiệu suất là bất kỳ xem xét ở đây, tôi sẽ không khuyên bạn nên sử dụng '$ .each' (hoặc underscore.js' _.each') nếu bạn có thể lấy đi với vòng lặp 'for' thô. jsperf có [một vài xét nghiệm so sánh mở mắt] (http://jsperf.com/each-vs-each-vs-for-in) có giá trị chạy. – nickb

+3

Điều này (http://jsperf.com/each-vs-each-vs-for-in/3) là thực tế hơn bởi vì nó sử dụng bộ lọc proto cơ bản – dvdrtrgn

2

Chắc chắn đó là một chút cực đoan để nói

...never use a for in loop to enumerate over an array. Never. Use good old for(var i = 0; i<arr.length; i++)

?

Đó là giá trị nổi bật phần trong Douglas Crockford trích

...The second form should be used with objects...

Nếu bạn cần một mảng kết hợp (aka Hashtable/điển), nơi các phím được đặt tên thay vì số lượng được lập chỉ mục, bạn sẽ phải thực hiện điều này như một đối tượng, ví dụ var myAssocArray = {key1: "value1", key2: "value2"...};.

Trong trường hợp này myAssocArray.length sẽ xuất hiện null (vì đối tượng này không có thuộc tính 'chiều dài') và i < myAssocArray.length sẽ không giúp bạn ở xa. Ngoài việc cung cấp sự tiện lợi lớn hơn, tôi mong đợi các mảng kết hợp cung cấp các lợi thế hiệu năng trong nhiều tình huống, vì các khóa mảng có thể là các thuộc tính hữu ích (tức là thuộc tính hoặc tên của một thành viên mảng), có nghĩa là bạn không phải lặp qua một thời gian dài mảng liên tục đánh giá nếu các câu lệnh để tìm mục nhập mảng bạn đang theo dõi.

Dù sao, cũng nhờ giải thích về các thông báo lỗi JSLint, tôi sẽ sử dụng kiểm tra 'isOwnProperty' ngay bây giờ khi liên kết thông qua các mảng liên kết vô số của tôi!

+1

Bạn đang bối rối sâu sắc. Không có thứ như "mảng kết hợp" trong javascript. Đó đúng là một khái niệm php. – Breton

+0

Đúng là các đối tượng này không có thuộc tính 'length', nhưng bạn có thể thực hiện theo cách khác:' var myArr = []; myArr ['key1'] = 'hello'; myArr ['key2'] = 'thế giới'; ' – nyuszika7h

+3

@ Nyuszika7H Đó là cách sai. Nếu bạn không cần chỉ số nguyên Array, bạn không nên sử dụng 'var myArr = []', nó phải là 'var myArr = {}' trong PHP chúng giống nhau, nhưng không phải trong JS. –

7

@all - mọi thứ trong JavaScript là một đối tượng(), do đó, các câu như "chỉ sử dụng điều này trên các đối tượng" là một chút gây hiểu lầm. Ngoài ra JavaScript không được gõ mạnh để 1 == "1" là đúng (mặc dù 1 === "1" là không, Crockford là lớn về điều này). Khi nói đến khái niệm progromatic của mảng trong JS, việc gõ là quan trọng trong định nghĩa.

@Brenton - Không cần phải là nhà độc tài thuật ngữ; "mảng kết hợp", "từ điển", "băm", "đối tượng", tất cả các khái niệm lập trình này đều áp dụng cho một cấu trúc trong JS. Đó là tên (key, index) các cặp giá trị, trong đó giá trị có thể là bất kỳ đối tượng khác (dây là những đối tượng quá)

Vì vậy, new Array() cũng giống như []

new Object() được xấp xỉ tương tự như {}

var myarray = []; 

Tạo cấu trúc là một mảng có hạn chế là tất cả các chỉ mục (còn gọi là khóa) phải là một số nguyên. Nó cũng cho phép tự động ấn định các chỉ số mới qua .push()

var myarray = ["one","two","three"]; 

là thực sự tốt nhất xử lý qua for(initialization;condition;update){

Nhưng những gì về:

var myarray = []; 
myarray[100] = "foo"; 
myarray.push("bar"); 

Hãy thử điều này:

var myarray = [], i; 
myarray[100] = "foo"; 
myarray.push("bar"); 
myarray[150] = "baz"; 
myarray.push("qux"); 
alert(myarray.length); 
for(i in myarray){ 
    if(myarray.hasOwnProperty(i)){ 
     alert(i+" : "+myarray[i]); 
    } 
} 

Có lẽ không phải là cách sử dụng tốt nhất của một mảng, nhưng chỉ là một minh họa rằng mọi thứ không phải lúc nào cũng c learcut.

Nếu bạn biết khóa của mình và chắc chắn nếu chúng không phải là số nguyên, thì mảng duy nhất của bạn giống như tùy chọn cấu trúc là đối tượng..

var i, myarray= { 
    "first":"john", 
    "last":"doe", 
    100:"foo", 
    150:"baz" 
}; 
for(i in myarray){ 
    if(myarray.hasOwnProperty(i)){ 
     alert(i+" : "+myarray[i]); 
    } 
} 
+0

"Chỉ sử dụng nó trên các đối tượng" có nghĩa là không sử dụng nó trên mảng hoặc bất cứ thứ gì khác mở rộng đối tượng, nếu không, như bạn chỉ ra, nó sẽ rất ngớ ngẩn vì mọi thứ mở rộng mảng kết hợp của Object –

+0

', "từ điển", "băm", "đối tượng", tất cả các khái niệm lập trình này đều áp dụng cho một cấu trúc trong JS. ' Không. Chúng là các khái niệm khác nhau từ các ngôn ngữ khác nhau, với * điểm tương đồng * với nhau. Nhưng nếu bạn giả sử rằng chúng giống hệt nhau và được sử dụng theo cách tương tự, cho cùng mục đích, bạn sẽ tự đặt mình để tạo ra một số lỗi thực sự ngu ngốc mà bạn có thể tránh bằng cách ít hiểu biết hơn về ngôn ngữ bạn đang sử dụng công trình. – Breton

0

Chỉ cần thêm vào đến chủ đề của định trong/cho/$ mỗi, tôi đã thêm một trường hợp thử nghiệm jsperf cho việc sử dụng $ .each vs tại: http://jsperf.com/each-vs-for-in/2

trình duyệt khác nhau/phiên bản xử lý nó khác nhau, nhưng có vẻ $ .each và thẳng ra cho là những lựa chọn rẻ nhất hiệu suất-khôn ngoan.

Nếu bạn đang sử dụng để chuyển đổi qua mảng/đối tượng liên kết, biết bạn đang làm gì và bỏ qua mọi thứ khác, hãy sử dụng $ .each nếu bạn sử dụng jQuery, hoặc chỉ cho (và sau đó ngắt); một khi bạn đã đạt được những gì bạn biết nên là phần tử cuối cùng)

Nếu bạn đang lặp qua một mảng để thực hiện điều gì đó với mỗi cặp khóa trong đó, nên sử dụng phương thức hasOwnProperty nếu bạn KHÔNG sử dụng jQuery và sử dụng $ .each nếu bạn sử dụng jQuery.

Luôn luôn sử dụng for(i=0;i<o.length;i++) nếu bạn không cần một mảng kết hợp dù ... lol Chrome thực hiện mà nhanh hơn 97% so với một tại hoặc $.each

13

Xấu: (jsHint sẽ ném một lỗi)

for (var name in item) { 
    console.log(item[name]); 
} 

tốt:

for (var name in item) { 
    if (item.hasOwnProperty(name)) { 
    console.log(item[name]); 
    } 
} 
Các vấn đề liên quan