>  기사  >  웹 프론트엔드  >  JavaScript에서 루프 코드보다 순회 방법이 선호되는 이유에 대한 자세한 설명

JavaScript에서 루프 코드보다 순회 방법이 선호되는 이유에 대한 자세한 설명

伊谢尔伦
伊谢尔伦원래의
2017-07-26 17:04:471068검색

루프 대신 순회 방법을 사용하는 것을 선호하세요

루프를 사용하면 DRY(Don't Repeat Yourself) 원칙을 위반하기 쉽습니다. 순환문의 단락을 손으로 쓰는 것을 피하기 위해 일반적으로 복사-붙여넣기 방법을 선택하기 때문입니다. 그러나 그렇게 하면 코드에 중복된 코드가 많이 생기고 개발자는 무의미하게 "바퀴를 재발명"하게 됩니다. 더 중요한 것은 복사하여 붙여넣을 때 시작 인덱스 값, 종료 조건 등과 같은 루프의 세부 사항을 간과하기 쉽다는 것입니다.

예를 들어, n이 컬렉션 개체의 길이라고 가정할 때 다음 for 루프에는 이 문제가 있습니다.


for (var i = 0; i <= n; i++) { ... }
// 终止条件错误,应该是i < n
for (var i = 1; i < n; i++) { ... }
// 起始变量错误,应该是i = 0
for (var i = n; i >= 0; i--) { ... }
// 起始变量错误,应该是i = n - 1
for (var i = n - 1; i > 0; i--) { ... }
// 终止条件错误,应该是i >= 0

루프의 일부 세부 사항에서 실수하기 쉽다는 것을 알 수 있습니다. JavaScript에서 제공하는 클로저(항목 11 참조)를 사용하면 재사용을 위해 루프의 세부 정보를 캡슐화할 수 있습니다. 실제로 ES5는 이 문제를 해결할 수 있는 몇 가지 방법을 제공합니다. 그 중 Array.prototype.forEach가 가장 간단한 것입니다. 이를 사용하여 다음과 같은 루프를 작성할 수 있습니다.


// 使用for循环
for (var i = 0, n = players.length; i < n; i++) {
  players[i].score++;
}

// 使用forEach
players.forEach(function(p) {
  p.score++;
});

컬렉션 개체를 순회하는 것 외에도 또 다른 일반적인 패턴은 원본 컬렉션의 각 요소에 대해 일부 작업을 수행한 다음 새 컬렉션을 가져오는 것입니다. 또한 forEach 메서드를 사용하여 다음을 구현합니다.


// 使用for循环
var trimmed = [];
for (var i = 0, n = input.length; i < n; i++) {
  trimmed.push(input[i].trim());
}

// 使用forEach
var trimmed = [];
input.forEach(function(s) {
  trimmed.push(s.trim());
});

그러나 한 컬렉션을 다른 컬렉션으로 변환하는 이 패턴은 매우 일반적이므로 ES5는 코드를 더욱 단순하고 우아하게 만들기 위해 Array.prototype.map 메서드도 제공합니다.


var trimmed = input.map(function(s) {
  return s.trim();
});

또한 특정 조건에 따라 컬렉션을 필터링한 다음 원본 컬렉션의 하위 집합을 얻는 또 다른 공통 모드가 있습니다. 이 모드를 구현하기 위해 ES5에서는 Array.prototype.filter가 제공됩니다. 이 메서드는 true 또는 false를 반환하는 함수인 조건자를 매개 변수로 받아들입니다. true를 반환하면 해당 요소가 새 컬렉션에 유지된다는 의미이고, false를 반환하면 해당 요소가 새 컬렉션에 표시되지 않는다는 의미입니다. 예를 들어, 다음 코드를 사용하여 제품 가격을 필터링하고 가격이 [min, max] 범위에 있는 제품만 유지합니다.


listings.filter(function(listing) {
  return listing.price >= min && listing.price <= max;
});

물론 위 방법은 ES5를 지원하는 환경에서 사용 가능합니다. 다른 환경에는 두 가지 옵션이 있습니다. 1. 개체와 컬렉션을 작동하는 몇 가지 일반적인 방법을 제공하는 밑줄 또는 lodash와 같은 타사 라이브러리를 사용합니다. 2. 필요에 따라 정의하십시오.

예를 들어 특정 조건에 따라 컬렉션의 처음 여러 요소를 가져오는 다음 메서드를 정의합니다.


function takeWhile(a, pred) {
  var result = [];
  for (var i = 0, n = a.length; i < n; i++) {
    if (!pred(a[i], i)) {
      break;
    }
    result[i] = a[i];
  }
  return result;
}

var prefix = takeWhile([1, 2, 4, 8, 16, 32], function(n) {
  return n < 10;
}); // [1, 2, 4, 8]

이 메서드를 더 잘 재사용하려면 Array.prototype 개체에서 정의할 수 있습니다. 특정 영향 항목 42를 참조하십시오.


Array.prototype.takeWhile = function(pred) {
  var result = [];
  for (var i = 0, n = this.length; i < n; i++) {
    if (!pred(this[i], i)) {
      break;
    }
    result[i] = this[i];
  }
  return result; 
};

var prefix = [1, 2, 4, 8, 16, 32].takeWhile(function(n) {
  return n < 10;
}); // [1, 2, 4, 8]

순회 기능을 사용하는 것보다 루프를 사용하는 것이 더 나은 경우는 단 한 번뿐입니다. 중단 및 계속을 사용해야 하는 경우입니다. 예를 들어 forEach를 사용하여 위의 takeWhile 메서드를 구현하는 경우 문제가 발생합니다. 조건자가 충족되지 않으면 어떻게 구현해야 할까요?


function takeWhile(a, pred) {
  var result = [];
  a.forEach(function(x, i) {
    if (!pred(x)) {
      // ?
    }
    result[i] = x;
  });
  return result;
}

내부 예외를 사용하여 판단할 수 있지만 이는 약간 서투르고 비효율적이기도 합니다.


function takeWhile(a, pred) {
  var result = [];
  var earlyExit = {}; // unique value signaling loop break
  try {
    a.forEach(function(x, i) {
      if (!pred(x)) {
        throw earlyExit;
      }
      result[i] = x;
    });
  } catch (e) {
    if (e !== earlyExit) { // only catch earlyExit
      throw e;
    }
  }
  return result;
}

하지만 forEach를 사용한 후에는 사용하기 전보다 코드가 훨씬 더 장황해집니다. 이것은 분명히 문제가 있습니다. 이 문제에 대해 ES5는 루프의 조기 종료를 처리하는 일부 및 모든 방법을 제공합니다. 사용법은 다음과 같습니다:


[1, 10, 100].some(function(x) { return x > 5; }); // true
[1, 10, 100].some(function(x) { return x < 0; }); // false

[1, 2, 3, 4, 5].every(function(x) { return x > 0; }); // true
[1, 2, 3, 4, 5].every(function(x) { return x < 3; }); // false

이 두 가지 방법은 단락(Short-circuiting) 방법입니다. 어떤 요소가 some 메서드의 조건자에서 true를 반환하면 some 메서드가 false를 반환하는 한 모든 메서드도 false를 반환합니다.

따라서 takeWhile은 다음과 같이 구현할 수 있습니다.


function takeWhile(a, pred) {
  var result = [];
  a.every(function(x, i) {
    if (!pred(x)) {
      return false; // break
    }
    result[i] = x;
    return true; // continue
  });
  return result;
}

사실 이게 함수형 프로그래밍의 아이디어입니다. 함수형 프로그래밍에서는 명시적인 for 루프나 while 루프를 거의 볼 수 없습니다. 루프의 세부 사항은 훌륭하게 캡슐화되어 있습니다.

위 내용은 JavaScript에서 루프 코드보다 순회 방법이 선호되는 이유에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.