>  기사  >  웹 프론트엔드  >  JS 데이터 유형, 사전 컴파일, 실행 컨텍스트 등 JS 기본 메커니즘에 대한 심층적인 이해

JS 데이터 유형, 사전 컴파일, 실행 컨텍스트 등 JS 기본 메커니즘에 대한 심층적인 이해

WBOY
WBOY앞으로
2021-12-27 18:48:522429검색

JavaScript는 문서 개체 모델 DOM, 브라우저 개체 모델 BOM 및 핵심 ECMAScript의 세 부분으로 구성됩니다. 이 기사는 JavaScript의 기본 원칙에 대한 지식을 제공하고 모든 사람에게 도움이 되기를 바랍니다.

JS 데이터 유형, 사전 컴파일, 실행 컨텍스트 등 JS 기본 메커니즘에 대한 심층적인 이해

JavaScript는 동적이며 약한 유형의 프로토타입 기반 문자 그대로 해석되는 스크립트 언어입니다. JavaScript는 우리가 사용하는 웹 브라우저에 뿌리를 두고 있으며, 그 해석기는 브라우저의 JavaScript 엔진입니다. 클라이언트 측에서 널리 사용되는 이 스크립팅 언어는 이전에 서버 측 언어로 처리되었던 일부 입력 유효성 검사 작업을 처리하는 데 처음 사용되었습니다. 웹 시대의 발전과 함께 JavaScript는 계속 성장하여 완전한 기능을 갖춘 프로그래밍 언어가 되었습니다. . 그 사용은 더 이상 단순한 데이터 검증에만 국한되지 않고 브라우저 창 및 해당 콘텐츠의 거의 모든 측면과 상호 작용할 수 있는 기능을 갖추고 있습니다. JavaScript는 매우 간단한 언어이면서도 매우 복잡한 언어입니다. JavaScript에 능숙해지려면 기본 설계 원칙 중 일부를 심층적으로 이해해야 합니다. 이 기사에서는 "JavaScript 고급 프로그래밍" 및 "당신이 모르는 JS" 시리즈 책을 참조하여 JavaScript에 대한 몇 가지 기본 지식을 설명합니다.

데이터 유형

자바스크립트 데이터 유형은 저장 방식에 따라 원시 데이터 유형(원본 값)과 참조 데이터 유형(참조 값)의 두 가지 유형으로 나눌 수 있습니다.

현재 숫자, 문자열, 부울, Null, 정의되지 않음 및 기호(ES6)를 포함한 6가지 기본 데이터 유형이 있습니다. 이러한 유형은 직접 조작할 수 있는 변수에 저장된 실제 값입니다. 원시 데이터 유형은 스택에 저장되며 데이터 크기가 값으로 직접 저장되므로 값으로 직접 액세스할 수 있습니다.

 참조 데이터 유형은 Object입니다. JavaScript에서는 기본 데이터 유형을 제외한 모든 것이 객체 유형이며 배열, 함수, 정규식 등이 모두 객체입니다. 참조 유형은 힙 메모리에 저장된 객체이고, 변수는 힙 메모리에 있는 객체를 가리키는 스택 메모리에 저장된 참조 주소입니다. 변수가 정의되고 참조 값으로 초기화될 때 다른 변수에 할당되면 두 변수는 동일한 주소를 저장하고 힙 메모리의 동일한 메모리 공간을 가리킵니다. 변수 중 하나를 통해 참조 데이터 유형의 값을 수정하면 다른 변수도 이에 따라 변경됩니다. , 원래 데이터 유형의 경우 NULL이 특별하다는 점을 제외하면

(NULL 은 빈 개체 참조로 간주됩니다) , 다른 사람들은 Typeof를 사용하여 정확한 판단을 내릴 수 있습니다:

표현식 공식

반환 값

typeof 123

'number'

typeof "bc"

'문자열'

typeof true

'boolean'

typeof null

'object'

typeof 정의되지 않음

'정의되지 않음 '

typeofknownVariable(

정의되지 않은 변수)

'undefine'

typeofSymbol()

'기호'

typeof function() {}

'function'

typeof { }

'개체'

typeof []

'object'

typeof(/[0-9,a-z]/)

'객체'

널 유형의 경우 합동 연산자를 사용하여 판단할 수 있습니다. 선언되었지만 초기화되지 않은 변수 값은 기본적으로 정의되지 않은 상태로 할당됩니다 ( 수동으로 정의되지 않은 상태로 할당할 수도 있음) JavaScript에서 동등 연산자 ==는 null과 정의되지 않은 것을 구별할 수 없다고 규정합니다. 동등성 테스트는 true를 반환해야 합니다. 두 값을 정확하게 구별하려면 합동 연산자 ===를 사용해야 합니다.

​​​​​​참조 데이터 유형의 경우 특별한 메소드 설계가 있고 typeof로 정확하게 판단할 수 있는 함수를 제외하고 나머지는 모두 객체 유형을 반환합니다. 참조 유형 값을 판단하기 위해 instanceof를 사용할 수 있습니다. instanceof는 객체 A가 다른 객체 B의 인스턴스인지 여부를 감지합니다. 맨 아래 수준에서는 객체 B가 객체 A의 프로토타입 체인에 존재하는지 확인합니다(인스턴스와 프로토타입 체인은 나중에 설명하겠습니다. 기사) . 있으면 true를 반환하고, 없으면 false를 반환합니다.

모든 참조 유형 값은 Object의 인스턴스이므로 인스턴스 연산자를 사용하여 Object로 판단하면 결과도 true를 반환합니다. ㅋㅋㅋ e'

Expression

반환 값

[1,2,3] 인스턴스 오브 배열

'true'

기능 foo(){ }instanceof Function

'true'

/[0-9,a-z]/instanceof RegExp

'true'

new Date() 인스턴스of Date

'true'

{name:"Alan",age:"22"} 인스턴스of Object

'true'

기능 foo(){ } 인스턴스of Object

'true'

/[0-9,a-z]/ 인스턴스of Object

'true'

new Date() objectofobject

'true'

물론 모든 JavaScript에서 모든 데이터 유형을 정확하게 결정할 수 있는 더 강력한 메서드가 있는데, 바로 Object.prototype.toString.call() 메서드입니다. ES5에서 모든 객체(네이티브 객체 및 호스트 객체)는 내부 속성 [[클래스]]를 가지며, 그 값은 객체 유형을 기록하는 문자열입니다. 현재 "배열", "부울", "날짜", "오류", "함수", "수학", "숫자", "객체", "RegExp", "문자열", "인수", "JSON", "상징". 이 내부 속성은 Object.prototype.toString() 메서드를 통해 볼 수 있으며 다른 방법은 없습니다.

Object.prototype.toString() 메소드가 호출되면 다음 단계가 수행됩니다. 1. 이 객체의 [[Class]] 속성 값을 가져옵니다(이 객체에 대해 이야기하겠습니다. 기사 후반부) . 2. 두 문자열 "[object"와 "]" 사이에 값을 넣고 연결합니다. 3. 이어진 문자열을 반환합니다.

이 값이 null인 경우 Object.prototype.toString() 메서드는 "[object Null]"을 직접 반환합니다. 이 값이 정의되지 않은 경우에는 "[객체 정의되지 않음]"이 직접 반환됩니다.

Expression

반환 값

Object.prototype.toString.call(123)

[객체 번호]

Object.prototype.toString.call("abc")

[객체 문자열]

Object.prototype.toString.call(true)

[객체 부울 ]

Object.prototype.toString.call(null)

[개체 Null]

Object.prototype.toString.call(정의되지 않음)

[객체 정의되지 않음]

Object.prototype.toString.call(Symbol())

[객체 기호]

Object.prototype.toString . call(function foo(){})

[객체 함수]

Object.prototype.toString.call([1,2,3])

[ 객체 배열]

Object.prototype.toString.call ({이름 : "alan"})

[개체 개체]

Object.pro totype. toString.call(new Date())

[객체 날짜]

Object.prototype.toString.call(RegExp())

[객체 RegExp]

Object.prototype.toString.call(window.JSON)

[객체 JSON]

Object.prototype.toString.call(Math)

[객체 수학]


call() 메소드는 Object.prototype.toString() 메소드를 호출할 때 이 지점을 변경하여 우리가 전달한 객체를 가리키도록 할 수 있습니다. 따라서 우리는 객체의 [[Class]] 속성을 얻을 수 있습니다. pass in( Object.prototype.toString.apply()를 사용하여 동일한 효과를 얻을 수 있습니다.

  JavaScript 데이터 유형도 변환할 수 있습니다. 데이터 유형 변환은 명시적 유형 변환과 암시적 유형 변환의 두 가지 방법으로 나뉩니다.

    표시 유형 변환을 위해 호출할 수 있는 메소드에는 Boolean(), String(), Number(),parseInt(),parseFloat()및 toString()(null 및 정의되지 않은 값 포함) ​​이 방법은 없음) 각각의 용도가 한눈에 알 수 있으므로 여기서는 하나씩 소개하지 않겠습니다.

JavaScript는 약한 유형의 언어이므로 산술 연산자를 사용할 때 연산자 양쪽의 데이터 유형이 임의적일 수 있습니다. Java 또는 C 언어와 같이 동일한 유형을 지정할 필요가 없습니다. 자동으로 암시적으로 유형 변환을 수행합니다. 암시적 유형 변환은 명시적 유형 변환만큼 직관적이지 않습니다. 세 가지 주요 변환 방법이 있습니다.
1. 값을 기본 값으로 변환: toPrimitive()

2. 값을 숫자로 변환: toNumber()

3 . 값을 숫자로 변환합니다. 값을 문자열로 변환합니다: toString()

일반적으로 숫자와 문자열을 추가하면 진리값 판단(예: ||, &&)을 수행할 때 숫자가 문자열로 변환됩니다. 매개변수는 부울 값으로 변환됩니다. 비교 연산, 산술 연산 또는 자동 증가 및 감소 연산이 수행될 때 매개변수는 객체에 암시적 유형 변환이 필요할 때 객체의 toString() 메서드로 변환됩니다. 또는 valueOf() 메서드의 반환 값입니다.

NaN 정보:

NaN은 숫자가 아닌 값을 나타내는 특수 숫자 값입니다. 첫째, NaN과 관련된 모든 산술 연산은 NaN을 반환합니다. 둘째, NaN은 NaN 자체를 포함한 어떤 값과도 동일하지 않습니다. ECMAScript는 매개변수가 "숫자가 아닌"지 여부를 테스트하는 데 사용할 수 있는 isNaN() 함수를 정의합니다. 먼저 인수를 암시적으로 숫자 값으로 변환하려고 시도하고, 숫자 값으로 변환할 수 없으면 true를 반환합니다.


먼저 typeof를 사용하여 숫자 유형인지 확인한 다음 isNaN을 사용하여 현재 데이터가 NaN인지 확인할 수 있습니다.

문자열 정보:

JavaScript의 문자열은 변경할 수 없습니다. 문자열이 생성되면 해당 값을 변경할 수 없습니다. 변수에 포함된 문자열을 변경하려면 먼저 원래 문자열을 삭제한 다음 새 값이 포함된 다른 문자열로 변수를 채웁니다. 이 프로세스는 백그라운드에서 발생하므로 일부 오래된 브라우저에서는 문자열을 연결할 때 속도가 매우 느려집니다.

실제로 기본 유형 값의 작업을 용이하게 하기 위해 ECMAScript는 Boolean, Number 및 String의 3가지 특수 참조 유형도 제공합니다. 원시 데이터 유형에는 속성과 메소드가 없습니다. 이를 읽기 위해 원시 유형 값에 대한 메소드를 호출하면 액세스 프로세스가 읽기 모드가 되고 해당 원시 래퍼 유형 객체가 백그라운드에서 생성되어 다음을 수행할 수 있습니다. 이 데이터를 조작하려면 몇 가지 메서드를 호출하세요. 이 프로세스는 세 단계로 구분됩니다. 1. 원래 패키징 유형의 인스턴스를 생성합니다. 2. 인스턴스에서 지정된 메서드를 호출합니다. 3. 인스턴스를 삭제합니다.
참조 유형과 기본 래퍼 유형의 주요 차이점은 객체의 수명 주기입니다. 자동으로 생성된 기본 래퍼 유형 객체는 코드 줄이 실행되는 순간에만 존재하고 즉시 소멸되므로 속성을 추가할 수 없습니다. 런타임 및 메소드에서 기본 유형 값으로.

Precompilation

저자는 『당신이 모르는 자바스크립트』라는 책에서 자바스크립트를 '동적 언어'나 '해석된 실행 언어'로 분류하지만 실제로는 컴파일된 언어라고 밝혔습니다. JavaScript 실행은 1. 구문 분석 2. 사전 컴파일 3. 해석 및 실행의 세 단계로 나뉩니다. 구문 분석과 해석 실행은 이해하기 어렵지 않습니다. 하나는 코드에 구문 오류가 있는지 확인하는 것이고, 다른 하나는 프로그램을 한 줄씩 실행하는 것입니다. 그러나 JavaScript의 사전 컴파일 단계는 약간 더 복잡합니다.

        모든 JavaScript 코드는 실행 전에 컴파일되어야 합니다. 대부분의 경우 컴파일 프로세스는 코드가 실행되기 전 몇 마이크로초 내에 발생합니다. 컴파일 단계 동안 JavaScript 엔진은 현재 코드 실행 범위에서 시작하고 코드에 대해 RHS 쿼리를 수행하여 변수 값을 얻습니다. 그런 다음 실행 단계에서 엔진은 LHS 쿼리를 실행하고 변수에 값을 할당합니다.

   컴파일 단계에서 JavaScript 엔진의 작업 중 일부는 모든 선언을 찾아 적절한 범위와 연결하는 것입니다. 사전 컴파일 과정에서 전역 범위에 있는 경우 JavaScript 엔진은 먼저 전역 범위에 전역 객체 (GO 객체, 전역 객체) 을 생성하고 변수 선언 및 함수 선언을 승격합니다. 승격된 변수는 기본적으로 먼저 undefine으로 초기화되며 함수는 함수 본체 전체를 승격시킵니다(함수 표현식 형식으로 함수를 정의하는 경우 변수 승격 규칙이 적용됩니다) 그런 다음 전역 변수에 저장합니다. 함수 선언의 승격은 변수 선언의 승격보다 우선합니다. 변수 선언의 경우 반복되는 var 선언은 엔진에서 무시되며 나중에 나타나는 함수 선언은 이전 함수 선언을 덮어쓸 수 있습니다 (ES6 새 변수 선언 구문 상황이 약간 다르기 때문에 지금은 여기서 논의하지 않겠습니다).

    함수 본문은 독립적인 범위이며 사전 컴파일 단계도 함수 본문 내부에서 수행됩니다. 함수 본문 내부에는 활성 개체 (AO 개체, 활성 개체) 가 먼저 생성되고 형식 매개 변수 및 변수 선언은 물론 함수 본문 내부의 함수 선언도 승격됩니다. 정의되지 않은 상태로 초기화됩니다. 내부 함수는 여전히 내부 함수 본문이며 활성 객체에 저장됩니다.

컴파일 단계가 완료되면 JavaScript 코드가 실행됩니다. 실행 프로세스에서는 변수나 형식 매개변수에 값을 순차적으로 할당합니다. 엔진은 범위에서 해당 변수 선언이나 형식 매개변수 선언을 찾아보고, 발견되면 값을 할당합니다. 비엄격 모드의 경우 선언 없이 변수가 할당되면 엔진은 자동으로 전역 환경에서 변수에 대한 선언을 생성합니다. 그러나 엄격 모드의 경우 선언되지 않은 변수가 할당되면 오류가 보고됩니다. 값. . JavaScript 실행은 단일 스레드이므로 할당 작업 (LHS 쿼리) 이 실행되기 전에 변수 (RHS 쿼리) 을 얻어 출력하면 정의되지 않은 결과를 얻게 됩니다. 현재 변수에 값이 할당되지 않았기 때문입니다. [ [범위]]. 함수의 경우 [[Scope]] 특성에는 함수가 생성된 범위(범위 체인)에 있는 개체 컬렉션이 포함됩니다. 전역 환경에서 함수를 생성하면 함수의 범위 체인은 전역 범위에 정의된 모든 변수를 포함하는 전역 개체를 삽입합니다.

내부 범위는 외부 범위에 액세스할 수 있지만 외부 범위는 내부 범위에 액세스할 수 없습니다. JavaScript에는 블록 수준 범위가 없으므로 if 문이나 for 루프 문에 정의된 변수는 문 외부에서 액세스할 수 있습니다. ES6 이전에는 JavaScript에는 전역 범위와 함수 범위만 있었습니다. ES6에는 새로운 블록 수준 범위 메커니즘이 추가되었습니다.

함수가 실행되면 실행 함수를 위해 실행 환경 (실행 컨텍스트, 실행 컨텍스트라고도 함) 이라는 내부 개체가 생성됩니다. 각 실행 환경에는 자체 범위 체인이 있습니다. 실행 환경이 생성되면 해당 범위 체인의 최상위가 현재 실행 중인 함수의 [[Scope]] 특성에 있는 개체로 먼저 초기화됩니다. 그 직후, 함수가 실행될 때 활성 개체 (모든 지역 변수, 명명된 매개 변수, 인수 매개 변수 집합 및 이 포함) 도 생성되어 범위 체인의 맨 위로 푸시됩니다.

해당 실행 환경은 함수가 실행될 때마다 고유합니다. 동일한 함수를 여러 번 호출하면 여러 실행 환경이 생성됩니다. 함수 실행이 완료되면 실행 환경이 삭제됩니다. 실행 환경이 파괴되면 활성 객체도 파괴됩니다 (웹 페이지나 브라우저를 닫는 등 애플리케이션이 종료될 때까지 전역 실행 환경은 파괴되지 않습니다) .

​​​​함수 실행 중에 변수가 발견될 때마다 식별자 구문 분석 프로세스를 거쳐 데이터를 얻거나 저장할 위치를 결정합니다. 식별자 확인은 범위 체인을 따라 수준별로 식별자를 검색하는 프로세스입니다. 전역 변수는 항상 범위 체인의 마지막 개체 (즉, 창 개체) 입니다.

JavaScript에는 실행 중에 범위 체인을 일시적으로 변경할 수 있는 두 가지 명령문이 있습니다. 첫 번째는 with 문입니다. with 문은 매개변수로 지정된 객체의 모든 속성을 포함하는 변경 가능한 객체를 생성하고 해당 객체를 범위 체인의 첫 번째 위치로 푸시합니다. 이는 함수의 활성 객체가 범위 체인의 두 번째 위치로 압착된다는 의미입니다. 범위 체인. 이렇게 하면 변경 가능한 객체의 속성에 액세스하는 속도가 매우 빨라지지만 지역 변수 등에 액세스하는 속도는 느려집니다. 실행 환경의 범위 체인을 변경할 수 있는 두 번째 문은 try-catch 문의 catch 절입니다. try 코드 블록에서 오류가 발생하면 실행 프로세스가 자동으로 catch 절로 점프한 다음 예외 개체가 변수 개체에 푸시되어 범위의 맨 위에 배치됩니다. catch 블록 내에서 함수의 모든 지역 변수는 두 번째 범위 체인 개체에 배치됩니다. catch 절이 실행되면 범위 체인이 이전 상태로 돌아갑니다.

Constructor

JavaScript의 생성자는 특정 유형의 개체를 만드는 데 사용할 수 있습니다. 다른 함수와 구별하기 위해 생성자는 일반적으로 대문자로 시작합니다. 그러나 JavaScript에는 생성자를 정의하기 위한 특별한 구문이 없기 때문에 JavaScript에서는 이것이 필요하지 않습니다. JavaScript에서 생성자와 다른 함수의 유일한 차이점은 호출 방식입니다. new 연산자를 통해 호출되는 모든 함수는 생성자로 사용할 수 있습니다.

JavaScript 함수에는 네 가지 호출 모드가 있습니다. 1. foo(arg)와 같은 독립적인 함수 호출 모드. 2. obj.foo(arg)와 같은 메소드 호출 모드. 3. new foo(arg)와 같은 생성자 호출 모드. 4. foo.call(this,arg1,arg2) 또는 foo.apply(this,args)(여기서 args는 배열임)와 같은 호출 모드를 호출/적용합니다.

생성자의 인스턴스를 생성하고 생성자의 역할을 수행하려면 new 연산자를 사용해야 합니다. new 연산자를 사용하여 생성자를 인스턴스화하면 생성자 내에서 다음 단계가 수행됩니다.
  1. 암시적으로 이 빈 개체 만들기
  2. 생성자에서 코드 실행(현재 이 개체에 특성 추가)
3 . 현재 this 개체를 암시적으로 반환합니다.

만약 생성자가 명시적으로 개체를 반환하면 인스턴스는 반환된 개체이고, 그렇지 않으면 암시적으로 반환된 이 개체입니다.

인스턴스를 생성하기 위해 생성자를 호출하면 인스턴스는 생성자의 모든 인스턴스 속성과 메서드를 갖게 됩니다. 생성자를 통해 생성된 다양한 인스턴스의 경우 해당 인스턴스 속성과 메서드는 독립적입니다. 동일한 이름을 가진 참조 유형 값이라 하더라도 서로 다른 인스턴스는 서로 영향을 미치지 않습니다.

프로토타입과 프로토타입 체인

프로토타입과 프로토타입 체인은 JavaScript 언어의 본질 중 하나일 뿐만 아니라 이 언어의 어려움 중 하나이기도 합니다. 프로토타입 프로토타입(명시적 프로토타입)은 함수가 생성될 때마다 함수의 고유한 속성이며, 함수는 자동으로 프로토타입 속성을 생성하고 함수의 프로토타입 객체를 가리킵니다. 모든 프로토타입 객체는 자동으로 constructor(생성자, 생성자로 번역될 수도 있음) 속성을 얻습니다. 이 속성에는 함수 (즉, 생성자 자체) 에 대한 포인터가 포함됩니다. 프로토타입 속성이 있습니다. 생성자를 통해 인스턴스를 생성할 때 인스턴스에는 생성자의 프로토타입 객체를 가리키는 [[Prototype]](암시적 프로토타입)의 내부 속성이 포함됩니다. Firefox, Safari 및 Chrome에서 각 객체는 __proto__ 속성을 통해 [[Prototype]] 속성에 액세스할 수 있습니다. 다른 브라우저의 경우 이 속성은 스크립트에 전혀 표시되지 않습니다.

​​​ 생성자의 프로토타입 속성과 인스턴스의 [[Prototype]]은 모두 생성자의 프로토타입 객체를 가리킵니다. [[Prototype]] 속성 사이에는 직접적인 관계가 없습니다. 인스턴스와 생성자. 인스턴스의 [[Prototype]] 속성이 특정 생성자의 프로토타입 객체를 가리키는지 여부를 확인하려면 isPrototypeOf() 또는 Object.getPrototypeOf() 메서드를 사용할 수 있습니다.

객체 인스턴스의 속성을 읽을 때마다 지정된 이름의 속성을 대상으로 검색이 수행됩니다. 검색은 먼저 개체 인스턴스 자체에서 시작됩니다. 인스턴스에서 지정된 이름의 속성이 발견되면 해당 속성의 값이 반환됩니다. 검색은 [[Prototype]이 가리키는 프로토타입 개체에 대해 계속됩니다. ] 객체의 속성 프로토타입에서 객체에서 주어진 이름을 가진 속성을 검색하고, 발견되면 해당 속성의 값을 반환합니다.

객체가 직접 인스턴스인 생성자를 확인하려면 인스턴스에서 직접 생성자 속성에 액세스할 수 있습니다. 인스턴스는 [[Prototype]을 통해 프로토타입 객체의 생성자 속성을 읽습니다. ] 생성자 자체를 반환합니다.

프로토타입 객체의 값은 객체 인스턴스를 통해 액세스할 수 있지만 객체 인스턴스를 통해 수정할 수는 없습니다. 인스턴스 프로토타입 객체와 동일한 이름을 가진 속성을 인스턴스에 추가하면 인스턴스에 속성이 생성됩니다. 이 인스턴스 속성은 프로토타입 객체의 해당 속성에 액세스하는 것을 방지하지만 해당 속성을 수정하지는 않습니다. 단순히 인스턴스 속성을 null로 설정하면 프로토타입 객체의 속성에 대한 액세스가 복원되지 않습니다. 프로토타입 객체의 속성에 대한 액세스를 복원하려면 삭제 연산자를 사용하여 객체 인스턴스에서 속성을 완전히 삭제할 수 있습니다.

hasOwnProperty() 메서드를 사용하여 속성이 인스턴스 또는 프로토타입에 존재하는지 감지합니다. 이 메소드는 주어진 속성이 객체 인스턴스에 존재하는 경우에만 true를 반환합니다. 객체 자체의 열거 가능한 모든 인스턴스 속성을 얻으려면 ES5 Object.keys() 메서드를 사용할 수 있습니다. 열거 가능 여부에 관계없이 모든 인스턴스 속성을 가져오려면 Object.getOwnPropertyNames() 메서드를 사용할 수 있습니다.

Prototype은 동적이므로 프로토타입 객체에 대한 모든 수정 사항이 즉시 인스턴스에 반영될 수 있지만 전체 프로토타입 객체를 다시 작성하면 상황이 달라집니다. 생성자를 호출하면 원래 프로토타입 객체에 대한 [[Prototype]] 포인터가 객체 인스턴스에 추가됩니다. 전체 프로토타입 객체를 다시 작성한 후 생성자는 새 프로토타입 객체와 함께 모든 프로토타입 객체 속성과 메서드를 가리킵니다. 객체 인스턴스도 원래 프로토타입 객체를 가리키므로 생성자와 동일한 프로토타입 객체를 가리키는 원래 프로토타입 객체 사이의 연결은 각각 다른 프로토타입 객체를 가리키기 때문에 끊어집니다.

이 연결을 복원하려면 생성자 프로토타입을 다시 작성한 후 객체 인스턴스를 인스턴스화하거나 생성자의 새 프로토타입 객체를 다시 가리키도록 객체 인스턴스의 __proto__ 속성을 수정하면 됩니다.

JavaScript는 상속을 구현하는 주요 방법으로 프로토타입 체인을 사용합니다. 프로토타입을 사용하여 한 참조 유형이 다른 참조 유형의 속성과 메서드를 상속할 수 있습니다. 생성자의 인스턴스에는 프로토타입 객체를 가리키는 [[Prototype]] 속성이 있습니다. 생성자의 프로토타입 객체를 다른 유형의 인스턴스와 동일하게 만들면 프로토타입 객체에도 [[Prototype]] 포인터가 포함됩니다. 다른 프로토타입이 다른 유형의 인스턴스인 경우... 인스턴스와 프로토타입의 체인이 형성됩니다. 이것이 소위 프로토타입 체인의 기본 개념입니다.

프로토타입 체인은 인스턴스 속성을 읽을 때 먼저 인스턴스에서 속성을 검색합니다. 속성을 찾을 수 없으면 [[Prototype]] 인스턴스가 가리키는 프로토타입 개체에 대한 검색이 계속됩니다. 프로토타입 개체도 다른 생성자의 인스턴스가 됩니다. 프로토타입 개체를 찾을 수 없으면 검색이 계속됩니다. 프로토타입 객체 [[Prototype]]은 다른 프로토타입 객체를 가리킵니다. 검색 프로세스는 프로토타입 체인을 따라 위쪽으로 계속 검색됩니다. 지정된 속성이나 메서드를 찾을 수 없으면 검색 프로세스가 하나씩 실행됩니다. 프로토타입 체인이 끝나면 중지됩니다.

함수의 프로토타입 객체가 수정되지 않으면 모든 참조 유형에는 기본적으로 Object의 프로토타입 객체를 가리키는 [[Prototype]] 속성이 있습니다. 따라서 모든 함수의 기본 프로토타입은 Object의 인스턴스입니다. 이는 모든 사용자 정의 유형이 toString() 및 valueOf()와 같은 기본 메서드를 상속하는 근본적인 이유입니다. 인스턴스의 프로토타입 체인에 생성자 프로토타입이 존재하는지 여부를 확인하려면 instanceof 연산자 또는 isPrototypeOf() 메서드를 사용할 수 있습니다.

프로토타입 체인은 매우 강력하지만 몇 가지 문제도 있습니다. 첫 번째 문제는 프로토타입 객체의 참조 유형 값이 모든 인스턴스에서 공유된다는 것입니다. 즉, 서로 다른 인스턴스의 참조 유형 속성이나 메서드가 동일한 힙 메모리를 가리킨다는 의미입니다. 한 인스턴스의 프로토타입에 대한 참조 값을 수정하면 영향을 받습니다. 동시에 다른 모든 인스턴스. 프로토타입 객체에 있는 인스턴스의 참조 값. 이것이 바로 프라이빗 속성이나 메서드가 프로토타입이 아닌 생성자에 정의되는 이유입니다. 프로토타입 체인의 두 번째 문제점은 생성자의 프로토타입 프로토타입을 다른 생성자의 인스턴스와 동일시할 때 이때 속성 값을 설정하기 위해 다른 생성자에 매개변수를 전달하면 모든 속성이 원본을 기반으로 한다는 것입니다. constructor will 인스턴스의 이 속성은 프로토타입 체인으로 인해 동일한 값이 할당되며 이는 때때로 우리가 원하는 결과가 아닐 수 있습니다.

Closure

Closure는 JavaScript의 가장 강력한 기능 중 하나입니다. JavaScript에서 클로저란 다른 함수의 범위에 있는 변수에 액세스할 수 있는 권한을 갖는 함수를 의미합니다. 로컬 범위 밖의 데이터. 클로저를 생성하는 일반적인 방법은 다른 함수 내에 함수를 생성하고 이 함수를 반환하는 것입니다.

일반적으로 함수가 실행되면 로컬 활성 객체는 소멸되고 전역 범위만 메모리에 저장됩니다. 그러나 폐쇄의 경우에는 상황이 다릅니다.

클로저 함수의 [[Scope]] 속성은 이를 래핑하는 함수의 범위 체인으로 초기화되므로 클로저에는 실행 환경 범위 체인과 동일한 개체에 대한 참조가 포함됩니다. 일반적으로 함수의 활성 개체는 실행 환경과 함께 소멸됩니다. 그러나 클로저가 도입되면 참조가 클로저의 [[Scope]] 속성에 여전히 존재하기 때문에 원래 함수의 활성 객체를 삭제할 수 없습니다. 이는 클로저 함수가 비클로저 함수보다 더 많은 메모리 오버헤드를 필요로 하여 더 많은 메모리 누수가 발생한다는 것을 의미합니다. 또한 클로저가 원래 래핑 함수의 활성 객체에 액세스할 때 먼저 범위 체인에서 자신의 활성 객체의 식별자를 확인하고 상위 계층을 찾아야 합니다. 따라서 클로저는 원래 래핑 함수의 변수를 사용합니다. 성능에도 큰 영향을 미칩니다.

    타이머, 이벤트 리스너, Ajax 요청, 창 간 통신, 웹 작업자 또는 기타 비동기식 또는 동기식 작업에서 콜백 함수를 사용하는 한 실제로 클로저를 사용하는 것입니다.包 일반적인 클로저 문제는 for 루프에서 타이머 출력 루프 변수를 사용하는 것입니다:

JavaScript 클로저에 익숙하지 않은 친구를 위한 이 코드는 1, 2, 3으로 당연하게 받아들일 수 있습니다. , 실제 상황은 이 코드에서 출력되는 4개의 숫자가 모두 4라는 것입니다.

이는 타이머가 비동기 로딩 메커니즘이므로 for 루프를 통과할 때까지 실행되지 않기 때문입니다. 타이머가 실행될 때마다 타이머는 외부 범위에서 i 변수를 찾습니다. 루프가 종료되었으므로 외부 범위의 i 변수는 4로 업데이트되었으므로 4개의 타이머에서 얻은 i 변수는 이상적인 출력 0, 1, 2, 3 대신 모두 4입니다.

이 문제를 해결하기 위해 즉시 실행 기능을 래핑하는 새로운 범위를 생성하고, 각 루프의 외부 범위의 i 변수를 새로 생성된 범위에 저장하고, 매번 새 범위에서 타이머가 시작되도록 할 수 있습니다. 즉시 실행 함수를 사용하여 이 새로운 범위를 만듭니다.

이런 식으로 루프 실행 결과는 0, 1, 2, 3을 순서대로 출력합니다. 또한 이 즉시 실행 함수를 다시 사용하여 단순화할 수도 있습니다. 조금, 실제 매개변수 i를 즉시 실행 함수에 직접 전달하므로 그 안에 있는 j에 값을 할당할 필요가 없습니다.

물론 즉시 실행 함수를 사용할 필요는 없습니다. 또한 익명이 아닌 함수를 생성하여 실행합니다. 루프를 돌 때마다 실행되지만 함수 선언을 저장하려면 더 많은 메모리를 차지합니다.

ES6 이전에는 블록 수준 범위 설정이 없었기 때문에 수동으로 새 범위를 생성해야만 이 문제를 해결할 수 있습니다. ES6에서는 블록 수준 범위를 설정하기 시작했습니다. let을 사용하여 블록 수준 범위를 정의할 수 있습니다.

let 연산자는 블록 수준 범위를 생성하고 let으로 선언된 변수는 현재 블록 수준에 저장됩니다. , 즉시 실행되는 각 함수는 매번 현재 블록 범위에서 변수를 조회합니다.个LET에는 변수가 주기 동안 한 번만 선언되는 것이 아니라 각 주기마다 다시 선언되며, 새 문의 값은 주기의 끝 값으로 초기화됩니다. for 루프의 헤드:

this을 가리킵니다. 이 키워드는 JavaScript에서 가장 복잡한 메커니즘 중 하나이며 모든 함수의 범위에서 자동으로 정의됩니다. 사람들은 이를 함수 자체를 가리키는 것으로 이해하기 쉽습니다. 그러나 ES5에서는 함수가 선언될 때 바인딩되지 않고 함수가 호출되는 방식에 따라 바인딩됩니다. 함수가 선언된 위치와는 아무런 관련이 없습니다.

(ES6의 새로운 화살표 함수에서는 이것이 다르며 그 포인팅은 함수 선언의 위치에 따라 다릅니다.) 앞서 언급한 네 가지 함수 호출 모드를 기억하세요. 1.

독립 함수 호출 패턴

, foo(arg)와 같은 것입니다. 2.obj.foo(arg)와 같은 모드를 호출하는 개체 메서드입니다. 3.new foo(arg)와 같은 생성자 호출 모드. 4.call/apply통화 모드, foo.call(this) 또는 foo.apply(this). 독립 함수 호출 모드의 경우 비엄격 모드에서는 기본적으로 전역 개체를 가리킵니다. 엄격 모드에서는 기본적으로 전역 객체에 바인딩하는 것이 허용되지 않으므로 undefound에 바인딩됩니다.

객체 메서드 호출 모드의 경우 함수의 this는 이를 호출하는 객체 자체를 가리킵니다.

생성자 호출 모드의 경우 생성자 내부 실행 단계가 이전에 소개되었습니다.

1 . 암시적으로 빈 this 객체 생성

2. 생성자에서 코드 실행(현재 this 객체에 속성 추가)
3. 암시적으로 현재 this 객체 반환

따라서 새 메서드를 사용하여 함수를 호출할 때 this는 이 개체에 대한 포인터는 생성자 내부에서 암시적으로 독립적으로 생성됩니다. 이를 통해 추가된 모든 속성이나 메서드는 결국 이 빈 개체에 추가되고 생성자의 인스턴스로 반환됩니다.

호출/적용 호출 모드의 경우 그림에 표시된 대로 함수의 이 매개변수가 전달한 첫 번째 매개변수에 바인딩됩니다.

foo.apply() 및 foo.call() 이것이 가리키는 지점을 변경하는 기능은 동일하며 두 번째 매개변수를 배열 형식으로 전달하는지, 분산 매개변수 형식으로 전달하는지의 차이만 있습니다.

JavaScript의 기본 원리에 대해 오늘은 여기에 임시로 작성하겠습니다. 앞으로도 계속해서 JavaScript에 대한 내용을 업데이트하겠습니다.

【관련 권장 사항:

javascript 학습 튜토리얼

위 내용은 JS 데이터 유형, 사전 컴파일, 실행 컨텍스트 등 JS 기본 메커니즘에 대한 심층적인 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 csdn.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제