너도 안 돼, 집에 가서 농사 지으세요
그런데 삭제 사용법에 대해 알아보겠습니다
몇 주 전에 Stoyan Stefanov의 책 Object-Oriented Javascript를 살펴볼 기회가 있었습니다. 이 책은 Amazon에서 높은 평점(리뷰 12개, 별 5개)을 받았기 때문에 그런 책인지 궁금했습니다. 추천할만한 책이었는데, 함수에 관한 장을 읽기 시작했습니다. 이 책의 설명 방식이 정말 마음에 들었고, 예제도 매우 훌륭하고 점진적으로 구성되어 있어 언뜻 보면 초보자도 익힐 수 있을 것 같았습니다. 그러나 거의 즉시 나는 함수 삭제라는 흥미로운 오해를 발견했습니다. 또한 몇 가지 다른 오류(예: 함수 선언 및 함수 표현식)가 있었지만 지금은 이에 대해 논의하지 않겠습니다.
이 책의 주장:
"함수는 일반 변수처럼 취급됩니다. 다른 변수에 복사할 수도 있고 삭제할 수도 있습니다." 다음 설명은 다음과 같습니다.
누락된 세미콜론을 무시하면 이 코드 줄에서 오류가 어디에 있는지 알 수 있습니까? 분명히 오류는 삭제 표현식이 true를 반환해서는 안 되고 typeof sum이 반환되어서는 안 된다는 것입니다. "정의되지 않음"도 마찬가지입니다. 적어도 이런 선언 방식으로는 변수 삭제가 불가능하기 때문입니다.
그렇다면 이 예에서 정확히 무슨 일이 일어나고 있는 걸까요? 아니면 특별한 사용법일까요? 이 코드는 실제로 Firebug 콘솔의 실제 출력이며 Stoyan은 이를 도구로 사용했을 것입니다. 빠른 테스트는 마치 Firebug가 다른 삭제 규칙을 따르는 것과 같습니다. 그렇다면 여기서 무슨 일이 일어나고 있는 걸까요?
이 질문에 답하기 전에 먼저 JavaScript에서 삭제 연산자가 어떻게 작동하는지 이해해야 합니다. 무엇을 삭제할 수 있고 무엇을 삭제할 수 없습니까? 오늘은 이 질문에 대해 자세히 설명하겠습니다. Firebug의 내용을 살펴보겠습니다. 변수와 함수를 선언하고 속성에 값을 할당하고 삭제하는 과정 뒤에 무엇이 있는지 자세히 살펴보겠습니다. 브라우저의 호환성과 가장 악명 높은 버그에 대해서도 설명하고 ES5의 엄격 모드와 삭제 연산자의 동작을 어떻게 변경하는지에 대해서도 논의하겠습니다.JavaScript와 ECMAScript를 같은 의미로 사용하겠습니다. 둘 다 ECMAScript를 의미합니다(Mozilla의 JavaScript 구현에 대해 명백히 언급하지 않는 한).
놀랍지도 않게 삭제에 대한 설명은 웹에서 꽤 드물습니다. 아마도 MDC 기사가 이해하기에 가장 좋은 자료일 것입니다. 그러나 불행하게도 주제에 대한 몇 가지 흥미로운 세부 사항이 누락되어 있습니다. 예, 잊혀진 것 중 하나가 원인입니다. Firebug의 이상한 동작에 대한 MSDN 참조는 이러한 측면에서 거의 쓸모가 없습니다.
이론
그러면 객체의 속성을 삭제할 수 있는 이유는 무엇입니까?
참고: 속성을 삭제할 수 없는 경우 삭제 연산자는 false만 반환합니다.
이를 이해하려면 먼저 변수 인스턴스와 속성 속성에 대한 개념을 이해해야 합니다. 안타깝게도 JavaScript 책에서는 거의 언급되지 않는 개념입니다. 이러한 개념에 대한 간략한 검토를 통해 이에 대해 설명하겠습니다. .이 개념은 이해하기 어렵습니다! '왜 이런 일이 일어나는지'에 관심이 없다면 이 장을 건너뛰셔도 됩니다.
코드 유형:
ECMAScript에는 전역 코드, 함수 코드, 평가 코드라는 3가지 유형의 실행 코드가 있습니다. 이러한 유형은 이름이 다소 비슷합니다. 간단한 개요는 다음과 같습니다.
소스 코드 조각을 프로그램으로 보면 전역 환경에서 실행되며 전역 코드로 간주됩니다. 브라우저 환경에서는 일반적으로 스크립트 요소의 내용이 프로그램으로 해석되므로 전역적으로 실행됩니다. 코드입니다.
함수에서 직접 실행되는 모든 코드는 당연히 함수 코드로 간주됩니다. 브라우저에서는 이벤트 속성(예:
)의 내용이 일반적으로 함수 코드로 해석됩니다.
마지막으로 내장 함수 eval에 적용된 코드 텍스트가 Eval 코드로 해석됩니다. 곧 이 유형이 왜 특별한지 알게 될 것입니다.
실행 컨텍스트:
ECMAScript 코드가 실행될 때 일반적으로 특정 실행 컨텍스트에서 발생합니다. 실행 컨텍스트는 세 가지 유형 각각에 대해 범위(Scope) 및 변수 인스턴스화(Variable instantiation)가 작동하는 방식을 이해하는 데 도움이 되는 다소 추상적인 엔터티 개념입니다. 실행 가능한 코드에는 그에 상응하는 실행 컨텍스트가 있습니다. 함수가 실행되면 "프로그램 제어가 함수 코드의 실행 컨텍스트에 들어갑니다"라고 말합니다. 글로벌 코드가 실행되면 프로그램 제어가 실행 컨텍스트에 들어갑니다. 글로벌 코드 등
보시다시피 실행 컨텍스트는 논리적으로 스택을 형성할 수 있습니다. 먼저 자체 실행 컨텍스트가 있는 전역 코드 조각이 있을 수 있으며, 그런 다음 해당 코드 조각이 실행 컨텍스트를 사용하여 함수를 호출할 수 있습니다. . 이 함수는 다른 함수 등을 호출할 수 있습니다. 함수가 재귀적으로 호출되더라도 호출될 때마다 새로운 실행 컨텍스트에 들어갑니다.
활성화 개체/변수 개체:
각 실행 컨텍스트에는 소위 변수 개체가 연결되어 있습니다. 실행 컨텍스트와 마찬가지로 변수 개체는 변수 인스턴스를 설명하는 데 사용되는 메커니즘인 추상 엔터티입니다. 소스 코드는 일반적으로 이 변수 개체에 속성으로 추가됩니다.
프로그램 제어가 전역 코드의 실행 컨텍스트에 들어가면 전역 개체가 변수 개체로 사용됩니다. 이것이 전역으로 선언된 함수 변수가 전역 개체 속성이 되는 이유입니다.
var foo = 1;
GLOBAL_OBJECT.foo; // 1
foo === GLOBAL_OBJECT.foo // 참
함수 표시줄(){}
GLOBAL_OBJECT.bar 유형; // "함수"
GLOBAL_OBJECT.bar === bar; // 참
그래서 전역 변수는 전역 개체의 속성이 되지만 지역 변수(함수 코드에 정의된 변수)는 실제로 매우 유사하게 동작합니다. 유일한 차이점은 다음과 같습니다. 함수 코드에서 변수 개체는 전역 개체가 아니라 소위 활성화 개체입니다. 활성화 개체는 함수 코드 생성의 실행 컨텍스트에 들어갈 때마다 호출됩니다.
함수 코드에 선언된 변수와 함수는 활성 객체의 속성이 될 뿐만 아니라 각 함수 매개변수(해당 형식 매개변수의 이름에 해당) 및 특수 Arguments 객체(인수 포함)에서도 수행됩니다. 이름으로) 활성 개체는 내부 설명 메커니즘이므로 프로그램 코드에서 액세스할 수 없습니다.
이제 변수에 어떤 일이 발생하는지(속성이 됨) 명확하게 알았으므로 이해해야 할 유일한 개념은 각 속성이 0개 이상의 속성을 가질 수 있다는 것입니다. 다음 세트: ReadOnly, DontEnum, DontDelete 및 Internal 속성은 속성에 존재할 수도 있고 존재하지 않을 수도 있습니다. 오늘 논의에서는 DontDelete에만 관심이 있습니다.
선언된 변수와 함수가 변수 개체(또는 함수 코드의 활성 개체 또는 전역 코드의 전역 개체)의 속성이 되면 이러한 속성은 DontDelete 속성으로 생성됩니다. (또는 암시적) 속성 할당에는 DontDelete 속성이 없으므로 일부 속성은 삭제할 수 있지만 다른 속성은 삭제할 수 없습니다.
이것이 전부입니다(DontDelete): 이 속성을 삭제할 수 있는지 여부를 제어하는 데 사용되는 속성의 특수 특성입니다. 일부 내장 객체 속성은 DontDelete를 포함하도록 지정되어 있으므로 삭제할 수 없습니다. 예를 들어 특수 인수 변수(또는 현재 알고 있는 활성 객체의 속성)에는 DontDelete 속성이 있습니다.
함수 매개변수에 해당하는 속성도 생성 당시부터 DontDelete 속성을 갖고 있어 삭제할 수 없습니다.
선언되지 않은 할당:
선언되지 않은 할당은 전역 객체 이전 범위 체인의 다른 곳에서 해당 속성이 이미 발견되지 않는 한 전역 객체에 속성을 생성한다는 것을 기억하실 것입니다. 이제 우리는 속성 할당과 변수에 대해 알게 되었습니다. 후자는 DontDelete 속성을 설정하지만 전자는 그렇지 않습니다. 선언되지 않은 할당이 삭제 가능한 속성을 생성하는 이유를 이해해야 합니다.
참고: 속성은 속성이 생성될 때 결정되며 후속 할당은 기존 속성의 속성을 수정하지 않습니다.
방화범 혼란:
Firebug에서 무슨 일이 일어났나요? 콘솔에 선언된 변수가 삭제될 수 있는 이유는 무엇인가요? 이전에 배운 내용에 위배되지 않습니까? 앞서 말했듯이 Eval 코드에서는 변수를 선언할 때 특별한 동작이 발생합니다. Eval에 선언된 변수는 실제로 DontDelete 속성이 없는 속성으로 생성됩니다.
또한 마찬가지로 함수 코드 내에서 호출되는 경우:
eval('var foo = 1;');
푸; // 1
foo 삭제; // 참
typeof foo; // "정의되지 않음"
})();
이것이 Firebug의 비정상적인 동작의 기초입니다. 콘솔의 모든 텍스트는 전역 또는 함수 코드가 아닌 Eval 코드로 구문 분석되고 실행됩니다. 분명히 여기에 선언된 모든 변수는 결국 DontDelete 속성이 없는 속성이 됩니다. 모두 쉽게 삭제할 수 있습니다. 전역 코드와 Firebug 콘솔의 차이점을 이해해야 합니다.
Eval을 통해 변수 삭제:
ECMAScript의 또 다른 측면과 결합된 이 흥미로운 평가 동작을 통해 기술적으로 "삭제할 수 없는" 속성을 제거할 수 있습니다. 함수 선언의 특징은 동일한 실행 컨텍스트에서 동일한 이름을 가진 변수를 재정의할 수 있다는 것입니다.
함수 선언이 어떻게 우선적으로 적용되고 동일한 이름의 변수(또는 변수 개체의 동일한 속성)를 재정의하는지 확인하세요. 이는 함수 선언이 변수 선언 다음에 인스턴스화되고 허용되기 때문입니다. 이를 재정의합니다(변수 선언). 함수 선언은 속성 값을 대체할 뿐만 아니라 해당 속성의 속성도 대체합니다. eval을 통해 함수를 선언하면 해당 함수는 이를 원래 속성으로 대체해야 합니다. (대체) 속성. 또한 eval을 통해 선언된 변수는 DontDelete 속성 없이 속성을 생성하므로 이 새 함수를 인스턴스화하면 실제로 속성에서 기존 DontDelete 속성이 제거되므로 A 속성을 삭제할 수 있습니다. 새로 생성된 함수에 값을 추가합니다.
안타깝게도 이 "치트"는 현재 구현에서는 작동하지 않습니다. 어쩌면 여기에 뭔가가 빠졌거나 동작이 너무 모호해서 구현자가 이를 알아차리지 못했을 수도 있습니다.
브라우저 호환성:
이론적으로 작동하는 방식을 이해하는 것은 유용하지만 실습이 가장 중요합니다. 브라우저는 변수/속성을 생성/삭제할 때 표준을 따르나요? 답은 대부분의 경우 그렇습니다.
전역 코드, 함수 코드 및 Eval 코드에서의 테스트를 포함하여 브라우저의 삭제 연산자 호환성을 테스트하기 위해 간단한 테스트 세트를 작성했습니다. 테스트 세트는 삭제 연산자의 반환 값과 속성 값을 확인했습니다. ) 실제로 삭제됩니다. 삭제의 반환 값은 실제 결과만큼 중요하지 않습니다. 삭제가 false 대신 true를 반환하는지는 중요하지 않습니다. 중요한 것은 DontDelete 속성의 속성이 삭제되지 않는다는 것입니다. 그 반대도 마찬가지입니다.
최신 브라우저는 일반적으로 앞서 언급한 평가 기능을 제외하면 Opera 7.54, Firefox 1.0, Safari 3.1.2, Chrome 4와 같은 브라우저가 모든 테스트 세트를 통과했습니다.
Safari 2.x 및 3.0.4에서는 함수 매개변수를 삭제하는 데 문제가 있습니다. 이러한 속성은 DontDelete 없이 생성된 것으로 나타나므로 삭제할 수 있습니다. Safari 2.x에서는 비참조 유형 변수를 삭제하는 데 더 많은 문제가 있습니다. 1) 예외가 발생합니다. 함수 선언은 삭제 가능한 속성을 생성합니다(그러나 이상하게도 변수 선언은 그렇지 않습니다). eval의 변수 선언은 삭제할 수 없게 됩니다(그러나 함수 선언은 삭제할 수 없습니다).
Safari와 유사하게 Konqueror(4.3이 아닌 3.5)는 비참조 유형(예: delete 1)을 삭제할 때 예외를 발생시키고 실수로 함수 변수를 삭제 가능하게 만듭니다.
번역자 주:
최신 버전의 크롬, 파이어폭스, IE를 테스트했는데 23, 24를 제외하고 기본적으로 합격을 유지했는데, 노키아 E72에 내장된 브라우저 외에 UC와 일부 모바일 브라우저도 테스트했습니다. Fail 15와 16을 제외하고 대부분의 내장 브라우저는 데스크탑 브라우저와 동일한 효과를 가지고 있습니다. 하지만 Blackberry Curve 8310/8900의 내장 브라우저는 테스트 23을 통과할 수 있다는 점에 놀랐습니다.
Gecko 버그 삭제하지 마세요:
Gecko 1.8.x 브라우저(Firefox 2.x, Camino 1.x, Seamonkey 1.x 등) - 매우 흥미로운 버그가 있습니다. 속성에 명시적으로 할당하면 해당 속성이 생성되더라도 DontDelete 속성이 삭제됩니다. 변수 선언이나 함수 선언을 통해
놀랍게도 Internet Explorer 5.5 - 8은 비참조 유형(예: 삭제 1)을 삭제하면 예외가 발생한다는 점을 제외하고 전체 테스트 세트를 통과했습니다(이전 Safari와 마찬가지로). 그러나 IE에는 더 심각한 버그가 있습니다. 그다지 명확하지는 않습니다. 이 버그는 전역 개체와 관련이 있습니다.
IE 버그:
이 장 전체가 Internet Explorer 버그에 관한 내용인가요? 와우!
IE(최소 IE 6-8)에서 다음 표현식은 예외를 발생시킵니다(전역 코드에서 실행되는 경우).
this.x = 1;
delete x; // TypeError: 개체가 이 작업을 지원하지 않습니다
이것도 마찬가지지만 다른 예외가 발생하여 상황이 더욱 흥미로워집니다.
var x = 1;
delete this.x; // 유형 오류: 'this.x'를 삭제할 수 없습니다
IE에서는 전역 코드의 변수 선언이 전역 개체에 속성을 생성하지 않는 것처럼 보입니다. 할당(this.x = 1)을 통해 속성을 생성한 다음 삭제 x를 통해 삭제하면 속성 생성을 선언하면 오류가 발생합니다. (var x = 1) 삭제한 다음 this.x를 삭제하면 또 다른 오류가 발생합니다.
그러나 이것이 전부는 아닙니다. 명시적 할당을 통해 속성을 생성하면 실제로 삭제 시 예외가 발생합니다. 여기에 오류가 있을 뿐만 아니라 생성되는 속성에 DontDelete 속성이 있는 것 같지만 이는 물론 아닙니다. 그랬어야지.
this.x = 1;
delete this.x; // TypeError: 개체가 이 작업을 지원하지 않습니다
typeof x; // "숫자"(아직 존재하지만 삭제되지 않았습니다!)
delete x; // TypeError: 개체가 이 작업을 지원하지 않습니다
typeof x; // "숫자"(다시 삭제되지 않음)
이제 IE에서는 선언되지 않은 할당(전역 개체에 속성을 생성해야 함)이 실제로 삭제 가능한 속성을 생성한다고 가정합니다.
x = 1;
x 삭제; // 참
x 유형; // "정의되지 않음"
그런데 글로벌 코드에서 이 참조를 통해 이 속성을 삭제(this.x 삭제)하면 비슷한 오류가 뜹니다.
x = 1;
delete this.x; // 유형 오류: 'this.x'를 삭제할 수 없습니다
이 동작을 일반화하려면 delete this.x를 사용하여 전역 코드에서 변수를 삭제하는 것이 결코 성공하지 못하는 것 같습니다. 문제의 속성이 명시적 할당(this.x = 1)을 통해 생성되면 속성이 오류를 발생시킵니다. 선언되지 않은 할당(x = 1) 또는 선언(var x = 1)에 의해 생성되고 삭제하면 또 다른 오류가 발생합니다.
Delete x는 속성이 명시적 할당에 의해 생성된 경우에만 오류를 발생시켜야 합니다(this.x = 1). 선언(var x = 1)에 의해 속성이 생성된 경우에는 삭제가 발생하지 않습니다. 선언되지 않은 할당(x = 1)을 통해 속성이 생성된 경우 삭제는 올바르게 false를 반환합니다.
9월에 이 문제에 대해 다시 생각해 보았고 Garrett Smith가 IE에서 제안한 바가 있습니다.
"전역 변수 개체는 JScript 개체로 구현되고, 전역 개체는 호스트에 의해 구현됩니다."
Garrett은 Eric Lippert의 블로그 항목을 참고 자료로 사용했습니다.
몇 가지 테스트를 구현하여 이 이론을 어느 정도 확인할 수 있습니다. this와 window는 동일한 개체를 가리키는 것처럼 보이지만(=== 연산자를 신뢰할 수 있는 경우) 변수 개체(함수 선언 위치해 있음)이 가리키는 것과는 다릅니다.
getBase() === this.getBase() // false
this.getBase() === this.getBase() // true
window.getBase() === this.getBase() // true
window.getBase() === getBase() // false
오해:
"대상 피연산자가 객체 속성이 아닌 경우 삭제는 작동하지 않아야 합니다.".
이제 삭제 작업 동작의 핵심을 이해했으므로 이 답변의 오류가 분명해졌습니다. 삭제는 변수와 속성을 구분하지 않으며(사실 삭제의 경우 둘 다 참조 유형입니다) 실제로는 DontDelete에만 관심이 있습니다. 속성(및 속성 자체가 존재하는지 여부).
서로 반대되는 다양한 오해를 보는 것도 매우 흥미롭습니다. 동일한 스레드에서 한 사람은 처음에 변수를 삭제하라고 제안했지만(평가에서 선언되지 않으면 효과가 없음) 다른 사람은 버그 수정을 제공했습니다. 전역 코드에서 변수를 삭제하는 데 삭제를 사용할 수 있지만 함수 코드에서는 삭제하는 방법을 설명합니다.
인터넷에서 JavaScript를 해석할 때는 각별히 주의하세요. 이상적인 접근 방식은 항상 문제의 본질을 이해하는 것입니다. ;)
삭제 및 호스트 개체:
삭제 알고리즘은 대략 다음과 같습니다.
피연산자가 참조 유형이 아닌 경우 true를 반환합니다
객체에 이 이름을 가진 직접적인 속성이 없으면 true를 반환합니다(아시다시피 객체는 활성 객체이거나 전역 객체일 수 있습니다)
속성이 존재하지만 DontDelete 속성이 있는 경우 false를 반환합니다
다른 경우에는 속성을 삭제하고 true를 반환합니다
그러나 호스트 개체에 대한 삭제 연산자의 동작은 예측할 수 없으며 실제로 이 동작에는 잘못된 것이 없습니다. (표준에 따르면) 호스트 개체는 읽기(내부 [[Get]]), 쓰기와 같은 기능을 수행할 수 있습니다. (내부 [[Put]] 메소드) 및 삭제(내부 [[Delete]] 메소드) 여러 연산자가 임의의 동작을 구현합니다. 사용자 정의 [[Delete]] 동작에 대한 이러한 은혜는 호스트 객체를 혼란의 원인으로 변경하는 것입니다.
특정 객체(분명히 호스트 객체로 구현됨)를 삭제하면 오류가 발생하는 일부 IE 문제를 보았습니다. 일부 Firefox 버전에서는 window.location을 삭제할 때 오류가 발생합니다. 객체가 호스트 객체일 때 삭제의 반환 값. Firefox에서 어떤 일이 일어나는지 살펴보겠습니다.
이 이야기의 교훈은 호스트 개체를 절대 믿지 말라는 것입니다.
ES5 엄격 모드:
엄격 모드 ECMAScript 5는 제한 사항이 거의 없습니다. 삭제 연산자의 표현이 변수에 대한 직접 참조일 때 구문 오류가 발생하며, 추가로 함수 매개변수가 발생합니다. , 속성에 내부 속성 [[Configurable]] == false가 있으면 유형 오류가 발생합니다.
또한 선언되지 않은 변수(또는 해결되지 않은 참조)를 삭제하면 구문 오류가 발생합니다.
"엄격한 사용";
i_dont_exist 삭제 // 구문 오류
선언되지 않은 할당은 엄격 모드에서 선언되지 않은 변수와 유사하게 동작합니다(이번에는 구문 오류 대신 참조 오류가 발생한다는 점만 제외).
"엄격한 사용";
i_dont_exist = 1; // 참조오류
이제 이해하실 수 있듯이, 변수, 함수 선언 및 매개변수를 삭제하면 많은 혼란이 발생하므로 모든 제한 사항은 어느 정도 의미가 있습니다. 삭제를 자동으로 무시하는 대신 엄격 모드는 더욱 공격적이고 설명적인 조치를 취합니다.
요약:
이 블로그 게시물이 꽤 길어져서 delete로 배열 객체를 삭제하는 것과 그 의미에 대해서는 다루지 않겠습니다. 전용 설명은 MDC 기사를 참조하거나 표준을 읽어보세요. 직접 실험해 보세요.
다음은 JavaScript에서 삭제가 작동하는 방식에 대한 간략한 요약입니다.
변수 및 함수 선언은 활성 개체 또는 전역 개체의 속성입니다
속성에는 몇 가지 특성이 있는데, 그 중 DontDelete는 해당 속성을 삭제할 수 있는지 여부를 결정하는 특성입니다.
전역 코드 또는 함수 코드의 변수 및 함수 선언은 항상 DontDelete 특성을 사용하여 속성을 생성합니다.
함수 매개변수는 항상 활성 객체의 속성이며 DontDelete를 갖습니다.
Eval 코드에서 선언된 변수와 함수는 항상 DontDelete 속성 없이 생성됩니다.
새 속성은 생성될 때 속성을 갖지 않습니다(물론 DontDelete도 없습니다).
호스트 개체는 삭제 작업에 어떻게 반응할지 결정할 수 있습니다.
여기에 설명된 내용에 대해 더 자세히 알아보려면 ECMA-262 3판 사양을 참조하세요.
이 기사를 읽고 새로운 것을 배웠기를 바랍니다. 질문, 제안 또는 수정을 환영합니다.