JavaScript의 함수 ID 또는 이벤트 탐지기를 올바르게 삭제하는 방법
25460 단어 webdevjavascriptbeginners
(나는 또한 그들을 위해 아주 긴 문장을 썼고 이 문제를 묘사하고 어떻게 해결하는지를 묘사했다. 이것은 본문의 기초이다.)
다음은 문제 코드의 첫 번째 부분입니다. (브라우저의 순수한 ES6는 프레임워크가 없습니다. 또한 이것은 원본 코드가 아닙니다. 제가 표현하고자 하는 관점과 무관한 모든 내용을 삭제했습니다.)
Ⓐ
// For completeness, imagine these being something sensible:
let elements = document.querySelectorAll(/* ... */)
function mouseenterHandler(element) {
// ...
}
// This is the interesting part:
elements.forEach(element => {
element.addEventListener('mouseenter', () => {
mouseenterHandler(element)
})
})
일부 DOM 요소를 질의하고 각 요소에 이벤트 탐지기를 첨부합니다.그리고 더 깊은 곳 어딘가에서 찢어지는 방식으로
Ⓑ
elements.forEach(element => {
element.removeEventListener('mouseenter', () => {
mouseenterHandler(element)
})
})
분명히 이것은 호출 removeEventListener
을 통해 이벤트 탐지기를 취소하려는 시도이며, 호출할 때 같은 효과의 익명 함수를 매개 변수로 사용합니다.문제.
() => { this.mouseenterHandler(element) }
인치Ⓐ 익명 함수입니다. 인용을 보류하지 않습니다. (즉, 변수에 저장하지 않고, 어떤 방식으로도 이름을 제공하지 않습니다.)() => { this.mouseenterHandler(element) }
인치Ⓑ 같은 값의 익명 함수다.여기서 주의해야 할 중요한 점은 그것들은 등가이지만 결코 같지 않다는 것이다.JavaScript 함수 비교 방법
JavaScript의 함수는 다른 모든 객체와 마찬가지로 참조를 통해 비교되는 객체입니다.즉, JavaScript에서 두 함수의 등가성을 확인할 수 없습니다.
연재 안 했어요?
이제 자바스크립트는 함수를 서열화할 수 있는데 왜 문자열 표시를 통해 그것들을 비교하지 않겠는가?
let f1 = (x) => { return x + 1 }
let f2 = (x) => { return x + 1 }
console.log(f1.toString()) // '(x) => { return x + 1 }'
console.log(f2.toString()) // '(x) => { return x + 1 }'
// ... so:
console.log(f1.toString() === f2.toString()) // true - yay!?
그러나 이 함수는 약간 다르지만 등효라고 할 수 있는 함수를 생각해 보자.function f3(x) {
return x + 1
}
console.log(f3.toString()) // 'function f3(x) {\n return x + 1\n}'
분명히 f1.toString() === f3.toString()
와 f2.toString() === f3.toString()
는 가짜일 것이다. f1(x) === f3(x)
와 f2(x) === f3(x)
는 [Number.MIN_SAFE_INTEGER
,Number.MAX_SAFE_INTEGER - 1
](그리고 x
의 많은 다른 값) 중 어떤 주어진 x도 사실이지만 이것도 보잘것없다.그래서 이런 방법은 완전히 같은 방식으로 쓴 함수에만 적용된다.
실제로 어떻게 했는지.
JavaScript에서 기본적으로 세 가지의 * 기본 데이터 형식은 변할 수 없다. 이것은 특이한comp-sci 방식으로 그들의 행위가 종이와 펜 수학에서의 행위와 유사하다는 것을 나타낸다.그중 하나는
Number
형이다.수학에는 단지 하나의 숫자만 있다𝟐. 이 일을 이야기하는 것은 아무런 의의가 없다𝟐 여기랑 저거.𝟐 저쪽입니다.우리는 '을 쓸 수 있다𝟐우리가 원하는 모든 것은 동일한 숫자의 참조𝟐. JavaScript에서 동일한 방식으로 작동합니다.let a = 2
let b = 2
console.log(a === b) // true
JS의 또 다른 두 가지 * 기본 데이터 유형은 String
과 Boolean
이다.이것이 바로 우리가 등가성을 통해 f1
,f2
와 f3
의 문자열을 비교할 수 있는 이유이다.JavaScript의 다른 모든 컨텐트는 참조를 통해 비교됩니다.쓰기
[]
할 때마다 새 그룹을 만듭니다. 다음 쓰기 []
할 때와 다른 그룹을 만듭니다. 쓰기 {}
할 때마다 새 대상을 만들고, 쓰기 () => {}
할 때마다 새 함수를 만듭니다.(엄밀히 말하면 우리가 쓸 때마다
[]
,{}
나() => {}
가 아니라 매번 그 중 하나를 평가한다.이것은 사실상 매우 큰 차이이다.function makeObj () { return {} }
라는 함수를 상상해 보세요. makeObj()
호출할 때마다 새로운 대상을 되돌려줍니다.)다시 말하면,
console.log([] === []) // false
console.log({} === {}) // false
console.log((() => {}) === (() => {})) // false, too!
그렇지만let a1 = []
let a2 = a1
console.log(a2 === a1) // true
let o1 = {}
let o2 = o1
console.log(o2 === o1) // true
let f4 = () => {}
let f5 = f4
console.log(f5 === f4) // also true
우리 이벤트 리스트랑 무슨 상관이야?
JavaScript는 DOM의 모든 요소에 대해 하나의 그룹을 만들고 그 안에 모우스enter 탐지기를 저장합니다. 다음과 같습니다.
let myElementMouseenterListeners = []
매번 저희가 이벤트 감청기를 추가합니다.myElement.addEventListener('mouseenter', () => { console.log('yay') })
JavaScript 내부는 배열에만 추가됩니다.let myListenerToAdd = () => { console.log('yay') }
myElementMouseenterListeners.push(myListenerToAdd)
console.log(myElementMouseenterListeners) // [ [Function] ]
'mouseenter'
이벤트가 발생하면 JS에서 배열의 각 함수를 호출합니다.let myMouseenterEvent = new MouseEvent('mouseenter')
myElementMouseenterListeners.forEach(mouseenterListener => {
mouseenterListener(myMouseenterEvent)
})
이벤트 감청기를 삭제하려고 할 때, 자바스크립트는 이벤트 감청기 그룹에서 교체되며, 그 중의 모든 함수를 우리가 삭제하려고 하는 함수와 비교하고, 만약 꼭 같으면 그룹에서 삭제합니다.우리가 이렇게 했다고 상상해 보세요.
myElement.removeEventListener('mouseenter', () => { console.log('yay') })
JavaScript는 다음을 수행할 수 있습니다.let myListenerToRemove = () => { console.log('yay') }
for (let i = 0; i < myElementMouseenterListeners.length; i++) {
if (myElementMouseenterListeners[i] === myListenerToRemove) {
myElementMouseenterListeners.splice(i, 1)
break
}
}
console.log(myElementMouseenterListeners) // still [ [Function] ]
이것은 우리가 시작할 때 추가한 탐지기에 순환이 도착하면 우리가 제공한 탐지기removeEventListener
와 비교하기 때문에 발생하는 기본적인 상황은 다음과 같다는 것을 의미한다.() => { console.log('yay') } === () => { console.log('yay') }
우리가 전에 검사한 바와 같이, 그것의 평가 결과는 틀렸다.이것은 코드가 이렇다는 것을 의미합니까
element.removeEventListener('eventname', () => { console.log('event handled') })
그때 새로 만든 익명 함수를 두 번째 매개 변수로 호출 removeEventListener
하면 영원히 아무런 효과가 없습니다.반대로 묵묵히 실패할 것이다.우리가 무엇을 해야 하는가(가능한 해결 방안)
removeEventListener
를 효력을 발생시키기 위해서 우리는 반드시 하나의 함수에 대한 인용을 제공해야 한다. 이 함수는 우리가 이전에 addEventListener
를 통해 실제 등록한 것이다.일반적으로 말하면
let element = document.querySelector(/* ... */)
function mouseenterHandler() {
// ...
}
element.addEventListener('mouseenter', mouseenterHandler)
element.removeEventListener('mouseenter', mouseenterHandler)
우리는 어느 곳에서든 같은 함수의 인용을 사용하기 때문에 removeEventListener
을 호출할 때 비교this.mouseenterHandler === this.mouseenterHandler
를 통해 삭제할 함수를 찾아내는 것이 옳다는 것을 알 수 있다.현재의 '문제' 는 우리의 실제 값
mouseenterHandler
이 일반화되어 있다는 것이다. 원소를 매개 변수로 삼는 것이다.이것은 우리가 사용하고자 하는 모든 요소를 위해 새로운 mouseenterHandler
함수를 작성하는 것보다 틀림없이 더 좋을 것이다.그러나 현재 우리는 어떤 방식으로 파라미터를 그 안에 넣어야 하며, 익명 함수에 mouseenterHandler
호출을 포장하는 것은 여기에서 작용하지 않는다. 내가 위에서 상세하게 설명한 바와 같다.솔루션 1: 각 요소에 대해 특수 버전의 이벤트 프로세서 만들기
우리는
mouseenterHandler
의 전문 버전을 만들 수 있으며 elements
을 채운 후에 이미 추가 파라미터가 생겼다.예를 들면 다음과 같습니다.let elements = document.querySelectorAll(/* ... */)
let enhancedElements = []
elements.forEach(element => {
enhancedElements.push({
element,
mouseenterHandler() { mouseenterHandler(element) },
})
}
그리고 코드를 변경하고 처리 프로그램을Ⓐ
enhancedElements.forEach(ee => {
ee.element.addEventListener('mouseenter', ee.mouseenterHandler)
})
각각 이입Ⓑ
enhancedElements.forEach(ee => {
ee.element.removeEventListener('mouseenter', ee.mouseenterHandler)
})
이것은 작용하지만, 또한 모든 원소에 추가 대상과 함수를 만들 것이다. 만약 그렇게 많은 원소가 없다면, 이것은 문제가 되지 않을 수도 있지만, 여전히 더욱 우아한 방식이 있다.솔루션 2: 이미 얻은 매개 변수를 처리하기 위해 이벤트 처리 프로그램을 변경합니다
브라우저는 이벤트를 첫 번째 매개 변수로 우리의 이벤트 처리 프로그램을 호출할 것입니다.이벤트는 여러 속성을 가진 대상일 뿐이고 그 중 하나는
event.target
이며 이벤트가 발생하는 요소에 대한 인용이다.그러면, 왜 우리의 이벤트 처리 프로그램을 바꾸지 않고 그것을 사용합니까? 그러면 우리는 수동으로 원소를 거기에 고정시킬 필요가 없습니다.예를 들어 MouseenterHandler가 다음과 같이 보일 경우
mouseenterHandler(element) {
element.classList.add(/* ... */)
}
Dell은 이를 사용으로 변경할 수 있습니다event.target
.mouseenterHandler(event) {
event.target.classList.add(/* ... */)
}
또는 매개 변수 목록에 destructuring을 사용하면 우리는 event.
부분을 중복할 필요가 없다.
mouseenterHandler({ target }) {
target.classList.add(/* ... */)
}
이 해결 방안이 있으면 우리는 let elements = document.querySelectorAll(/* ... */)
행을 원상태로 유지할 수 있다.추가 대상이나 기능이 필요하지 않습니다. 저희는 변경만 하면 됩니다.Ⓐ 입장:elements.forEach(element => {
element.addEventListener('mouseenter', mouseenterHandler)
})
및Ⓑ, 따라서elements.forEach(element => {
element.removeEventListener('mouseenter', mouseenterHandler)
})
우리의 이벤트 처리 프로그램은 현재 '유니버설' 으로 모든 요소와 함께 사용할 수 있습니다.*거짓말했어요.
undefined
도 한 유형이다.읽어주셔서 감사합니다!이것은 제가 dev.to에 올린 첫 번째 댓글입니다. 그리고 저는 영어를 모국어로 하는 사람이 아니기 때문에 스타일, 도움 등에 대한 일반적인 피드백에 감사하겠습니다.
Reference
이 문제에 관하여(JavaScript의 함수 ID 또는 이벤트 탐지기를 올바르게 삭제하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/smotchkkiss/function-identity-in-javascript-or-how-to-remove-event-listeners-properly-1ll3텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)