JavaScript 배열 중복 제거는 프런트엔드 채용을 위한 필기 시험 문제에 자주 나타납니다. 예를 들면 다음과 같습니다.
배열이 있습니다 var arr = ['a', 'b', 'c', '1' , 0, ' c', 1, '', 1, 0], 고유(arr)가 ['a', 'b', 'c', '1', 0, 1, '']
필기시험으로 시험 포인트는 2개입니다.
1. 이 테스트 포인트를 과소평가하지 마세요. JavaScript가 브라우저에서 실행되는 경우가 많다는 점을 고려하면 다양한 브라우저 환경에서 기능의 정확성을 보장하는 것이 쉬운 일이 아닙니다. 제 말을 믿지 못하신다면 이 블로그를 계속 읽어보세요.
2. 공연. 대부분의 경우 JavaScript 언어 자체(좁은 의미에서는 DOM과 같은 확장 제외)는 성능 문제를 일으키지 않지만, 안타깝게도 이는 시험 문제이므로 면접관은 여전히 성능을 테스트 포인트로 간주합니다.
더 읽기 전에 가장 좋다고 생각하는 버전을 구현하는 것이 좋습니다.
직관적인 솔루션
배열 중복 제거의 경우 프로그램을 작성했다면 첫 번째 솔루션을 즉시 얻을 수 있습니다.
function Unique( arr ) {
var ret = []
for (var i = 0; i < arr.length; i++) {
var item = arr[i]
if (ret.indexOf(item) === -1) {
ret.push(item)
}
}
return ret
}
직관은 종종 최신 브라우저에서 매우 정확하고 성능이 좋습니다. . 하지만 프론트엔드의 가장 큰 비극이자 과제는 다양한 운영 환경을 지원해야 한다는 것입니다. IE6-8에서는 배열의 indexOf 메서드가 아직 존재하지 않습니다. 직관적인 솔루션을 약간 수정해야 합니다.
var indexOf = [].indexOf ?
function(arr, item) {
return arr .indexOf(항목)
} :
함수 indexOf(arr, 항목) {
for (var i = 0; i < arr.length; i++) {
~
} function Unique(arr) { var ret = [] for (var i = 0; i < arr.length; i++) { var item = arr[i] if (indexOf(ret, item) === -1 ) { ret.push(item) } } return ret} write 이 시점에서는 정확성은 더 이상 문제가 되지 않지만 성능 측면에서 이중 루프는 면접관을 불행하게 만들 것입니다. 최적화 계획최적화라고 하면 팔선이 바다를 건너 백송이 꽃이 피는 경우가 종종 있다. 그러나 여덟 불멸자는 종종 현실에 속하지 않으며 백화는 빈대를 쉽게 유인할 수 있습니다. 여기서는 어레이 중복 제거를 위한 다양한 최적화 솔루션을 하나씩 설명하지 않습니다. 여기서는 가장 일반적으로 사용되는 매우 좋은 결과에 대해서만 설명합니다. function Unique(arr) { var ret = [] var hash = {} for (var i = 0; i < arr.length; i++) { var item = arr[i] var key = typeof(item) + itemif (hash[key] !== 1) { ret.push(item) hash[key] = 1 } } return ret}핵심은 indexOf를 대체할 해시 객체를 구성하는 것입니다. 객체의 문자열만 가능하므로 값 1과 문자열 '1'을 구별하려면 var key = typeof(item) + item이 필요합니다. 그러나 최적화는 실제로 함정을 야기하기 쉽습니다. 예를 들어 위 구현에서는 다음 입력을 판단하는 것이 불가능합니다. 1unique([ new String (1 ), new Number(1) ])좋은 성능과 정확성을 얻기 위해 계속해서 코드를 수정할 수 있습니다. 하지만 결과가 좋지 않은 경우가 많습니다. 실질적인 요구이 글을 쓰고 나면 이 블로그가 요점을 파악할 수 있습니다. 프로그래머는 모두 마음 속에 좋은 일반 성능과 좋은 성능으로 범용 함수를 작성하는 것과 같은 꿈을 가지고 있습니다. 이런 꿈은 프로그래머가 탁월해지는 데 중요한 원동력이지만 통제하지 않으면 쉽게 길을 잃을 수 있다. 성능 최적화로 돌아갑니다. 요즘에는 핵심 시스템, 데이터베이스, 네트워크, 프런트엔드 등 다양한 최적화가 있습니다. 이러한 모든 최적화는 다음 질문에 답해야 합니다. 1. 현재 사용 가능한 것. 어떤 시나리오에서 최적화를 수행해야 하며, 해당 시나리오에 따른 구체적인 제한 사항은 무엇입니까? 종종 자유를 가져다주는 제한 사항을 정리하는 것이 중요합니다. 2. 정확히 무엇을 원하시나요? 최적화의 목적은 무엇입니까? 안정성을 향상시키거나, 처리량을 높이거나, 사용자 대기 시간을 줄이는 것입니다. 이 질문에 답할 때까지 최적화는 헛된 것입니다. 이 질문에 대한 정확한 대답은 특정 측정 가능한 매개변수를 최적화에 가져올 수 있으므로 최적화에 목표가 있을 수 있습니다. 3. 무엇을 포기할 수 있나요? 케이크를 갖고 있으면서도 먹을 수는 없습니다. 최적화의 본질은 특정 시나리오에서의 선택과 절충입니다. 아무것도 포기할 생각이 없다면 최적화가 어려운 경우가 많습니다.
이 블로그를 쓰는 것은 필기 시험 문제에 답하기 위한 것이 아닙니다. 이 필기 시험 문제는 조금 지루합니다. 이 블로그를 작성하게 된 원동력은 최근 SeaJS의 성능 튜닝을 하고 있다는 것입니다. 요구 사항 중 하나는 다음과 같습니다.
define(function(require,exports) {
var a = require('./a')
var b = require('./b')
...
require('. / a').fn(...)
})
위는 모듈입니다. 함수 문자열을 구문 분석하면 모듈의 종속성 배열을 얻을 수 있습니다: ['./a ', './b', './a'], 이 종속성 정보에는 중복된 필드가 있을 수 있으므로 중복을 제거해야 합니다.
이 특정 시나리오에서는 위의 세 가지 질문에 답해 보겠습니다.
1. 현재 사용 가능한 항목. 몇 가지 입력 제한 사항이 있으므로 문자열만 고려하면 됩니다.
2. 정확히 무엇을 원하시나요? 이 문제는 상대적으로 간단합니다. 고유한 방법이 가능한 한 빠르기를 바랍니다. Chrome 디버깅 도구의 프로필 패널을 사용하여 지정된 테스트 페이지에서 고유한 방법에 소요되는 시간을 확인하는 것입니다. 5ms
3. 무엇을 포기할 수 있나요? 문자열만 처리하면 되며 다른 유형은 지원되지 않습니다. 포기에 대해 이야기하는 것은 종종 흥미롭습니다. 이 문제는 그렇게 간단하지 않으므로 다음에 이야기하겠습니다.
SeaJS의 솔루션
특정 시나리오가 명확하게 분석되면 솔루션은 비교적 간단합니다.
function Unique(arr) {
var obj = {}
forEach(arr, function(item) {
obj[item] = 1
})
return key(obj)
}
위 코드는 forEach와 키에 의존하며 컨텍스트와 분리될 수 없습니다(환경이 매우 중요) , 전체 코드: util-lang.js
위 솔루션은 코드 크기, 정확성 및 다양한 브라우저에서의 전반적인 성능 측면에서 매우 좋습니다.
어느 날 이런 테스트 케이스가 나타났습니다:
define(function(require,exports) {
var a = require('toString' )
var b = require('hasOwnProperty')
...
})
"완벽한" 솔루션
위 테스트 케이스는
unique([ 'toString', 'hasOwnProperty' ]) // [ 'toString', 'hasOwnProperty' ]
를 반환할 것으로 예상됩니다. IE에는 다양한 버그가 있습니다. 덜 유명하지만 실제 버그는 다음과 같습니다.
var obj = { toString: 1, hasOwnProperty: 1 }
for (var p in obj ) {
console.log(p)
}
위의 경우 최신 브라우저에서는 두 값이 올바르게 출력되지만 Old에서는 출력이 없습니다. IE에서. 이것은 IE의 열거형 버그입니다. 더 안전한 Object.keys 호환성 구현 "완벽한" 솔루션은 다음과 같습니다.
varkeys = Object.keys || 🎜 >
var hasOwnProperty = Object.prototype.hasOwnProperty, hasDontEnumBug = !{toString:null}.propertyIsEnumerable("toString"), DontEnums = [ 'toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'is PrototypeOf', 'propertyIsEnumerable', 'constructor' ], DontEnumsLength = DontEnums.length; 반환 함수( o) { if (typeof o != "object" && typeof o != "function" || o === null) throw new TypeError("Object.keys 호출됨) 객체가 아닌"); var result = []; for (var name in o) { if (hasOwnProperty.call (o, 이름)) result.push(이름); 🎜> > ;DontEnums 배열 외에도 , hasOwnProperty 처리에 특별한 주의를 기울일 수도 있습니다. **프런트 엔드의 경우 "정확성"을 보장하기가 쉽지 않습니다. **