Có phải các tác dụng phụ trong Mảng của mỗi Array, hay một số Lỗi xấu?


9

Tôi luôn được dạy rằng có tác dụng phụ trong iftình trạng là xấu. Ý của tôi là;

if (conditionThenHandle()) {
    // do effectively nothing
}

... như trái ngược với;

if (condition()) {
    handle();
}

... Và tôi hiểu điều đó, và đồng nghiệp của tôi rất vui vì tôi không làm điều đó, và tất cả chúng tôi về nhà vào lúc 17:00 ngày thứ Sáu và mọi người đều có một ngày cuối tuần vui vẻ.

Bây giờ, ECMAScript5 đã giới thiệu các phương thức như every()some()đến Array, và tôi thấy chúng rất hữu ích. Chúng sạch hơn so với for (;;;), cung cấp cho bạn một phạm vi khác làm cho phần tử có thể truy cập được bằng một biến.

Tuy nhiên, khi xác thực đầu vào, tôi thường không thấy mình sử dụng every/ sometrong điều kiện để xác thực đầu vào, sau đó sử dụng every/ some một lần nữa trong phần thân để chuyển đổi đầu vào thành một mô hình có thể sử dụng được;

if (input.every(function (that) {
    return typeof that === "number";
})) {
    input.every(function (that) {
        // Model.findById(that); etc
    }
} else {
    return;
}

... khi điều tôi muốn làm là;

if (!input.every(function (that) {
    var res = typeof that === "number";

    if (res) {
        // Model.findById(that); etc.
    }

    return res;
})) {
    return;
}

... Điều này mang lại cho tôi những tác dụng phụ trong một iftình trạng tồi tệ.

Trong so sánh, đây là mã sẽ nhìn với một cái cũ for (;;;);

for (var i=0;i<input.length;i++) {
    var curr = input[i];

    if (typeof curr === "number") {
        return;
    }

    // Model.findById(curr); etc.
}

Câu hỏi của tôi là:

  1. Đây chắc chắn là một thực hành xấu?
  2. Am I (mis | ab) sử dụng someevery( nên tôi được sử dụng một for(;;;)cho việc này?)
  3. Có một cách tiếp cận tốt hơn?

3
Mọi và một số cũng như bộ lọc, bản đồ và giảm là các truy vấn, chúng không có tác dụng phụ, nếu chúng làm bạn lạm dụng chúng.
Benjamin Gruenbaum

@BenjaminGruenbaum: Vì vậy, điều đó không làm cho họ mất răng thường xuyên hơn không? 9/10, nếu tôi sử dụng some, tôi muốn làm gì đó với phần tử, nếu tôi sử dụng every, tôi muốn làm gì đó cho tất cả các phần tử đó ... someeveryđừng để tôi truy cập thông tin đó, vì vậy tôi không thể sử dụng chúng, hoặc tôi phải thêm tác dụng phụ.
Isaac

Không. Khi tôi đề cập đến tác dụng phụ, ý tôi là bên trong đầu nếu không phải là cơ thể. Bên trong cơ thể bạn có thể sửa đổi nó theo bất kỳ cách nào bạn muốn. Chỉ không biến đổi đối tượng bên trong cuộc gọi lại mà bạn chuyển sang một số / khi.
Benjamin Gruenbaum

@BenjaminGruenbaum: Nhưng đó chính xác là quan điểm của tôi. Nếu tôi sử dụng sometrong ifđiều kiện của mình để xác định xem một phần tử nào đó trong mảng có thể hiện một thuộc tính nhất định hay không, 9/10 tôi cần phải hoạt động trên phần tử đó trong ifcơ thể; bây giờ, khi somekhông cho tôi biết các yếu tố trưng bày tài sản (chỉ là "một đã"), tôi hoặc là có thể sử dụng some lại trong cơ thể (O (2n)), hoặc tôi có thể chỉ cần thực hiện các hoạt động bên trong nếu điều kiện ( đó là xấu, bởi vì nó là một tác dụng phụ trong đầu).
Isaac

... tất nhiên cũng áp dụng everynhư vậy.
Isaac

Câu trả lời:


8

Nếu tôi hiểu chính xác quan điểm của bạn, bạn dường như đang sử dụng sai hoặc lạm dụng everysomeđiều đó hơi khó tránh khỏi nếu bạn muốn thay đổi trực tiếp các yếu tố của mảng. Sửa lỗi cho tôi nếu tôi sai, nhưng những gì bạn đang cố gắng làm là tìm hiểu xem một số hoặc mọi yếu tố trong chuỗi của bạn thể hiện một điều kiện nhất định sau đó sửa đổi các yếu tố đó. Ngoài ra, mã của bạn dường như đang áp dụng một cái gì đó cho tất cả các mục cho đến khi bạn tìm thấy một mục không vượt qua vị ngữ và tôi không nghĩ đó là những gì bạn muốn làm. Dù sao đi nữa.

Hãy lấy ví dụ đầu tiên của bạn (sửa đổi một chút)

if (input.every(function (that) {
    return typeof that === "number";
})) {
    input.every(function (that) {
        that.foo();
    }
} else {
    return;
}

Những gì bạn đang làm ở đây thực sự đi ngược lại với tinh thần của một số khái niệm / every / map / less / filter / etc. Everykhông có nghĩa là được sử dụng để ảnh hưởng đến mọi mặt hàng phù hợp với một cái gì đó, thay vào đó nó chỉ nên được sử dụng để cho bạn biết nếu mọi mặt hàng trong bộ sưu tập đều có. Nếu bạn muốn áp dụng một hàm cho tất cả các mục mà một vị từ đánh giá là đúng, thì cách "tốt" để làm điều đó là

var filtered = array.filter(function(item) {
    return typeof item === "number";
});

var mapped = filtered.map(function(item) {
    return item.foo(); //provided foo() has no side effects and returns a new object of item's type instead.  See note about foreach below.
});

Ngoài ra, bạn có thể sử dụng foreachthay vì bản đồ để sửa đổi các mục tại chỗ.

Logic tương tự áp dụng cho some, về cơ bản:

  • Bạn sử dụng everyđể kiểm tra nếu tất cả các phần tử trong một mảng vượt qua một số thử nghiệm.
  • Bạn sử dụng someđể kiểm tra nếu ít nhất một phần tử trong một mảng vượt qua một số thử nghiệm.
  • Bạn sử dụng mapđể trả về một mảng mới chứa 1 phần tử (là kết quả của hàm bạn chọn) cho mọi phần tử trong một mảng đầu vào.
  • Bạn sử dụng filterđể trả lại một mảng có độ dài 0 < length< initial array lengthyếu tố, tất cả chứa trong mảng gốc và tất cả đi qua các bài kiểm tra vị cung cấp.
  • Bạn sử dụng foreachnếu bạn muốn bản đồ nhưng tại chỗ
  • Bạn sử dụng reducenếu bạn muốn kết hợp các kết quả của một mảng trong một kết quả đối tượng duy nhất (có thể là một mảng nhưng không phải).

Bạn càng sử dụng chúng (và bạn càng viết mã LISP), bạn càng nhận ra chúng có liên quan như thế nào và thậm chí có thể mô phỏng / triển khai mã này với những cái khác như thế nào. Điều gì mạnh mẽ với những truy vấn này và điều thực sự thú vị là ngữ nghĩa của chúng và cách chúng thực sự thúc đẩy bạn hướng tới việc loại bỏ các tác dụng phụ có hại trong mã của bạn.

EDIT (theo ý kiến): Vì vậy, giả sử bạn muốn xác thực rằng mọi phần tử là một đối tượng và chuyển đổi chúng thành Mô hình ứng dụng nếu tất cả đều hợp lệ. Một cách để làm điều này trong một lần duy nhất là:

var dirty = false;
var app_domain_objects = input.map(function(item) {
    if(validate(item)) {
        return new Model(item);
    } else {
        dirty = true; //dirty is captured by the function passed to map, but you know that :)
    }
});
if(dirty) {
    //your validation test failed, do w/e you need to
} else {
    //You can use app_domain_objects
}

Theo cách này, khi một đối tượng không vượt qua xác thực, bạn vẫn tiếp tục lặp qua toàn bộ mảng, sẽ chậm hơn so với việc xác thực every. Tuy nhiên, hầu hết thời gian mảng của bạn sẽ hợp lệ (hoặc tôi cũng hy vọng như vậy), vì vậy, trong hầu hết các trường hợp, bạn sẽ thực hiện một lần vượt qua mảng của mình và kết thúc với một mảng các đối tượng Mô hình ứng dụng có thể sử dụng được. Ngữ nghĩa sẽ được tôn trọng, tránh tác dụng phụ và mọi người sẽ được hạnh phúc!

Lưu ý rằng bạn cũng có thể viết truy vấn của riêng mình, tương tự như foreach, sẽ áp dụng một hàm cho tất cả các thành viên của một mảng và trả về true / false nếu tất cả chúng đều vượt qua kiểm tra vị ngữ. Cái gì đó như:

function apply_to_every(arr, predicate, func) {
    var passed = true;
    for(var i = 0; i < array.length; ++i) {
        if(predicate(arr[i])) {
            func(arr[i]);
        } else {
            passed = false;
            break;
        }
    }
    return passed;
}

Mặc dù điều đó sẽ sửa đổi các mảng tại chỗ.

Tôi hy vọng điều này sẽ giúp, nó rất thú vị để viết. Chúc mừng!


Cảm ơn câu trả lời của bạn. Tôi không nhất thiết phải cố gắng sửa đổi các yếu tố tại chỗ ; trong mã thực tế của tôi, tôi nhận được một mảng định dạng JSON của các đối tượng, vì vậy tôi đầu tiên xác nhận đầu vào if (input.every()), để kiểm tra xem mỗi phần tử một đối tượng ( typeof el === "object && el !== null) vv, sau đó nếu đó xác nhận, tôi muốn chuyển đổi từng phần tử vào Model tương ứng ứng dụng (trong đó, bây giờ bạn có đề cập đến map()tôi có thể sử dụng input.map(function (el) { return new Model(el); });, nhưng không nhất thiết phải ở vị trí .
Isaac

.. nhưng thấy rằng ngay cả khi map()tôi phải lặp đi lặp lại trên mảng hai lần; một lần để xác nhận, và một lần khác để chuyển đổi. Tuy nhiên, sử dụng một tiêu chuẩn for(;;;)vòng lặp, tôi có thể làm điều này bằng một lần lặp, nhưng tôi không thể tìm thấy một cách để áp dụng every, some, maphoặc filtertrong trường hợp này, và thực hiện chỉ một vượt qua, mà không cần phải không mong muốn-tác dụng phụ hoặc giới thiệu bad- thực hành.
Isaac

@Isaac Được rồi, xin lỗi vì sự chậm trễ, tôi hiểu rõ hơn tình hình của bạn bây giờ. Tôi sẽ chỉnh sửa câu trả lời của tôi để thêm một số thứ.
pwny

Cảm ơn câu trả lời tuyệt vời; nó thực sự hữu ích :).
Isaac

-1

Các tác dụng phụ không có trong điều kiện if, chúng nằm trong cơ thể if. Bạn chỉ xác định có thực thi cơ thể đó trong điều kiện thực tế hay không. Không có gì sai với cách tiếp cận của bạn ở đây.


Chào, cảm ơn cho câu trả lời của bạn. Xin lỗi, nhưng tôi đã hiểu nhầm câu trả lời của bạn hoặc bạn đã hiểu sai mã ... mọi thứ trong đoạn mã của tôi đều nằm trong ifđiều kiện, chỉ có sự returntồn tại bên trong ifcơ thể; rõ ràng là tôi đang nói về mẫu mã được đặt trước bởi " những gì muốn làm là; ...
Isaac

1
Xin lỗi, tác dụng phụ của @ Issac thực sự đang trong iftình trạng.
Ross Patterson
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.