>  기사  >  웹 프론트엔드  >  JavaScript_javascript 기술의 언어 특성 분석에 대한 간략한 토론

JavaScript_javascript 기술의 언어 특성 분석에 대한 간략한 토론

WBOY
WBOY원래의
2016-05-16 17:37:32942검색

서문
자바스크립트에서는 범위, 컨텍스트, 클로저, 함수 등이 최고 중의 최고입니다. 초보자 JSers의 경우 고급 수준의 필수 항목입니다. 프론트 엔드 공성 엔지니어의 경우 이러한 본질을 진정시키고 이해해야만 우아한 코드를 작성할 수 있습니다.

이 글은 잊어버리기 쉽고 기본 개념을 다루지 않는 중요한 지식을 요약하는 것을 목표로 합니다. 기초 지식이 낯설다면 "JavaScript 완벽 가이드"를 읽어보세요~


언어 특징 함수 표현

먼저 코드 스니펫을 살펴보세요.

코드 복사 코드는 다음과 같습니다.

[javascript] view plaincopyprint?
var f = function foo(){
return typeof foo; // foo는 내부 범위 내에서 유효합니다.
// foo는 외부에서 사용될 때 보이지 않습니다.
typeof foo; "
f(); // "함수"
var f = function foo(){
return typeof foo; // foo는 내부 범위에서 유효합니다
};
// foo 외부에서 사용될 때는 보이지 않습니다.
typeof foo; // "undefine"
f() // "function"

여기서 말씀드리고 싶은 것은 함수 표현식의 foo는 함수 내부에서만 참조할 수 있고 외부에서는 참조할 수 없다는 것입니다.


json

많은 JavaScript 개발자가 실수로 JavaScript 개체 리터럴(JSON 개체)을 호출합니다. JSON은 데이터 교환 형식을 설명하도록 설계되었으며 JavaScript의 하위 집합인 자체 구문도 있습니다.

{ “prop”: “val” } 이러한 선언은 사용되는 컨텍스트에 따라 JavaScript 개체 리터럴 또는 JSON 문자열일 수 있습니다. 문자열 컨텍스트에서 사용되는 경우(작은따옴표나 큰따옴표로 묶이거나 텍스트 파일에서 읽음) 객체 리터럴 컨텍스트에서 사용되는 경우 객체 리터럴입니다.

코드 복사 코드는 다음과 같습니다.
[javascript] view plaincopyprint?
// JSON 문자열입니다.
var foo = '{ "prop": "val" }'
// 객체 리터럴입니다.
var bar = { "prop": "val" } ;
// JSON 문자열입니다.
var foo = '{ "prop": "val" }';
// 객체 리터럴입니다.
var bar = { "prop": "발" };

또 알아야 할 점은 JSON.parse는 JSON 문자열을 객체로 역직렬화하는 데 사용되고, JSON.stringify는 객체를 JSON 문자열로 직렬화하는 데 사용된다는 것입니다. 이전 버전의 브라우저는 이 객체를 지원하지 않지만 json2.js를 통해 동일한 기능을 얻을 수 있습니다.


시제품

코드 복사 코드는 다음과 같습니다.
function Animal(){
/ / .. .
}
function cat (){
// ...
}
cat.prototype = new Animal();//이 메소드는 건설자.
cat.prototype = Animal.prototype;//이 메서드는 생성자를 상속하지 않습니다.
//주의해야 할 또 다른 중요한 세부 사항은 자신만의 프로토타입 체인을 유지해야 한다는 것입니다. 초보자는 이것을 항상 잊어버릴 것입니다!
cat.prototype.constructor = 고양이;

새 객체를 할당하여 함수의 프로토타입 속성을 완전히 변경하면 원래 생성자에 대한 참조가 손실됩니다. 왜냐하면 우리가 생성한 객체에 생성자 속성이 포함되어 있지 않기 때문입니다.

코드 복사 코드는 다음과 같습니다.
function A() {}
A.prototype = {
x: 10
};
var a = new A();
alert(a.x) // 10
alert(a.constructor === A ); / / 거짓!

MDN의 생성자에 대한 설명을 살펴보겠습니다. 프로토타입: 인스턴스의 프로토타입을 생성한 개체 함수에 대한 참조를 반환하므로 함수에 대한 프로토타입 참조를 수동으로 복원해야 합니다.

코드 복사 코드는 다음과 같습니다.
function A() {}
A.prototype = {
생성자: A,
x: 10
};
var a = new A();
alert(a.x); // 10
alert( a.constructor === A); // 참

그러나 프로토타입 속성을 제출해도 이미 생성된 객체의 프로토타입에는 영향을 미치지 않습니다(생성자의 프로토타입 속성이 변경되는 경우에만 영향을 받습니다). 즉, 새로 생성된 객체만 새 프로토타입을 갖게 됩니다. 이미 생성된 객체는 여전히 원래의 이전 프로토타입을 참조하여 새 프로토타입을 갖게 됩니다(이 프로토타입은 더 이상 수정할 수 없습니다).

코드 복사 코드는 다음과 같습니다.

function A() {}
A.prototype .x = 10;
var a = new A();
alert(a.x); // 10
A.prototype = {
생성자: A,
x: 20
y: 30
};
//객체 a는 암시적 [[Prototype]] 참조
alert(a.x)를 통해 원유의 원형에서 얻은 값입니다. 🎜>alert (a.y) // 정의되지 않음
var b = new A();
// 하지만 새 객체는 새 프로토타입에서 얻은 값입니다
alert(b.x) // 20
경고(b.y) // 30

따라서 "프로토타입을 동적으로 수정하면 모든 객체에 새로운 프로토타입이 적용됩니다."라는 말은 잘못된 것입니다. 새 프로토타입은 프로토타입이 수정된 후에 새로 생성된 객체에만 적용됩니다. 여기서 주요 규칙은 객체가 생성될 때 객체의 프로토타입이 생성되고 이후에 새 객체로 수정될 수 없다는 것입니다. 여전히 동일한 객체를 참조하는 경우 생성자의 명시적인 프로토타입을 통해 참조할 수 있습니다. 객체가 생성된 후에는 프로토타입의 속성만 추가하거나 수정할 수 있습니다.

변수 객체 함수 실행 컨텍스트에서는 VO(변수 객체)에 직접 접근할 수 없습니다. 이때 활성화 객체(activation object)가 VO 역할을 합니다. 활성 객체는 함수 컨텍스트에 들어갈 때 생성되고 함수의 인수 속성을 통해 초기화됩니다. Arguments 속성의 값은 Arguments 객체입니다:


코드 복사 코드는 다음과 같습니다.
function foo(x, y , z) {
// 선언된 함수 매개변수 수 인수 (x, y, z)
Alert(foo.length); // 3
// 실제로 전달된 매개변수 수(x, y)
Alert(arguments.length); // 2
// 매개변수의 호출 수신자는 함수 자체입니다.
Alert(arguments.callee === foo) // true
}

실행 컨텍스트에 들어갈 때(코드 실행 전) VO에는 이미 다음 속성이 포함되어 있습니다. 1. 함수의 모든 형식 매개변수(함수 실행 컨텍스트에 있는 경우)

•모든 함수 선언(FunctionDeclaration, FD);

•모든 변수 선언(var, VariableDeclaration)

또 다른 전형적인 예:

코드 복사 코드는 다음과 같습니다.
alert(x); 🎜>var x = 10;
alert(x); // 10
x = 20;
function x() {};
alert(x); 🎜>

사양에 따르면 컨텍스트에 들어갈 때 함수 선언이 채워집니다. 컨텍스트에 들어갈 때 변수 선언 "x"도 있는데 위에서 말했듯이 변수 선언은 함수 선언과 형식을 순서대로 따릅니다. 매개변수 선언, 그리고 이 컨텍스트 입력 단계에서 변수 선언은 VO에 이미 존재하는 동일한 이름의 함수 선언이나 형식적 매개변수 선언을 방해하지 않습니다. 단순 속성과 비교하여 변수에는 {DontDelete} 속성이 있습니다. 이 속성의 의미는 변수 속성을 삭제 연산자를 사용하여 직접 삭제할 수 없다는 것입니다.

코드 복사

코드는 다음과 같습니다.a = 10;alert( window.a ); // 10
alert(delete a); // true
alert(window.a); // 정의되지 않음
var b = 20;
alert(window.b) ; // 20
alert(delete b); // false
alert(window.b) // 여전히 20입니다. b는 속성이 아니라 변수입니다!
var a = 10; // 전역 컨텍스트의 변수
(function () {
var b = 20; // 함수 컨텍스트의 지역 변수
} )( );
alert(a); // 10
alert(b); // 전역 변수 "b"가 선언되지 않았습니다.



이것은 함수 컨텍스트에서 호출자가 제공하며 함수가 호출되는 방식에 따라 결정됩니다. 호출 대괄호()의 왼쪽이 참조 유형 값인 경우 이는 참조 유형 값의 기본 개체로 설정됩니다(참조 유형과 다른 모든 속성). 이 값은 null이 됩니다. . 그러나 this 값이 null인 실제 상황은 없습니다. 왜냐하면 this 값이 null이면 해당 값이 암시적으로 전역 개체로 변환되기 때문입니다.

코드 복사

코드는 다음과 같습니다.

(function () {
Alert(this); // null => 전역
})();

이 예에서는 함수 객체가 있지만 참조 유형 객체는 없습니다(식별자도 아니고 속성 접근자도 아님). 따라서 this 값은 최종적으로 전역 객체로 설정됩니다.

코드 복사 코드는 다음과 같습니다.

var foo = {
bar: function () {
Alert(this);
}
};
foo.bar(); // 참고, OK => // 참조, OK => foo
(foo.bar = foo.bar)(); // 전역
(false || foo.bar)() // 전역
(foo.bar , foo .bar)(); // 전역

문제는 다음 세 가지 호출에서 특정 연산을 적용한 후 호출 괄호 왼쪽의 값이 더 이상 참조 유형이 아니라는 것입니다.

•첫 번째 예는 명백합니다. 결과적으로 이것이 기본 개체인 foo입니다.


• 두 번째 예에서는 그룹 연산자가 적용되지 않습니다. 위에서 언급한 GetValue와 같은 참조 유형에서 객체의 실제 값을 가져오는 메서드를 생각해 보세요. 이에 따라 그룹 작업이 반환될 때 여전히 참조 유형을 얻습니다. 이것이 바로 this 값이 기본 개체인 foo로 다시 설정되는 이유입니다.

•세 번째 예에서는 그룹 연산자와 달리 할당 연산자가 GetValue 메서드를 호출합니다. 반환된 결과는 함수 객체입니다(참조 유형은 아님). 이는 null로 설정되고 결과가 전역 객체임을 의미합니다.

• 네 번째와 다섯 번째도 마찬가지입니다. 쉼표 연산자와 논리 연산자(OR)가 GetValue 메서드를 호출하고 이에 따라 참조를 잃고 함수를 가져옵니다. 그리고 다시 전역으로 설정하세요.

아시다시피 지역 변수, 내부 함수, 형식 매개변수는 해당 함수의 활성화 개체에 저장됩니다.

코드 복사 코드는 다음과 같습니다.
function foo() {
function bar() {
Alert(this); // 전역
}
bar() // AO.bar()
}

활성 객체는 항상 다음과 같이 반환되며 값은 null입니다. 즉, 의사 코드의 AO.bar()는 null.bar()와 동일합니다. 여기서는 전역 객체로 설정된 위에서 다시 설명한 예제로 돌아갑니다.


스코프 체인

함수 생성자를 통해 생성된 함수의 범위 속성은 항상 유일한 전역 개체입니다.

한 가지 중요한 예외는 함수 생성자를 통해 생성된 함수와 관련됩니다.

코드 복사 코드는 다음과 같습니다.
var x = 10;
function foo() {
var y = 20;
function barFD() { // 함수 선언
경보(x);
경보(y);
}
var barFn = Function(' 경고(x); 경고(y);');
barFD(); // 10, 20
barFn(); // 10, "y"가 정의되지 않았습니다
}
foo ();

또한:

코드 복사 코드는 다음과 같습니다.
var x = 10, y = 10;
with ({x: 20}) {
var x = 30, y = 30;
//x = 30 여기서는 x = 20을 다룹니다.
// 30
경고(y); // 30
}
경고(x) // 10
경고(y);

컨텍스트에 들어가면 어떻게 되나요? 식별자 "x"와 "y"가 변수 개체에 추가되었습니다. 또한 코드 실행 단계에서 다음과 같이 수정하세요.

•x = 10, y = 10;
•스코프 앞에 {x:20} 객체가 추가됩니다.
•with 내부에서 var 선언이 발견되고 물론 아무것도 없습니다. 컨텍스트를 입력할 때 모든 변수가 구문 분석되고 추가되었기 때문에 생성됩니다.
• 두 번째 단계에서는 변수 "x"만 수정되고 실제로 개체의 "x"가 구문 분석되어 추가됩니다. 스코프 체인 앞의 "x"는 20이고 30이 됩니다.
•파싱된 후 변수 개체 "y"도 수정되었으며 해당 값도 10에서 30으로 변경됩니다.
• 또한 with 문이 완료된 후 해당 특정 개체가 범위 체인에서 제거됩니다(변경된 변수 "x" - 30도 해당 개체에서 제거됨). 즉 범위 체인의 구조가 상태로 복원됩니다. 이전에는 with가 강화되었습니다.
•마지막 두 개의 경고에서 현재 변수 개체의 "x"는 동일하게 유지되고 "y"의 값은 이제 30과 동일하며 with 문 실행 중에 변경되었습니다.
함수

괄호에 관한 질문

다음 질문을 살펴보겠습니다. '함수를 생성한 후 즉시 호출할 때 함수를 괄호로 묶어야 하는 이유는 무엇입니까? '라고 대답하면, 표현문의 제한은 이렇습니다.

표준에 따르면 표현식 문은 코드 블록과 구별하기 어렵기 때문에 중괄호 {로 시작할 수 없습니다. 마찬가지로 함수 선언과 구별하기 어렵기 때문에 함수 키워드로 시작할 수 없습니다. 즉, 생성 직후 즉시 실행되는 함수를 정의하면 다음과 같이 호출합니다.

코드 복사 코드는 다음과 같습니다.

함수() {
.. .
}();
// 이름이 있어도
function foo() {
...
}();

위의 두 가지 정의에 대해 함수 선언을 사용했는데, 해석할 때 해석기가 오류를 보고하지만 이유는 다양할 수 있습니다. 전역 코드(즉, 프로그램 수준)에 정의된 경우 함수 키워드로 시작하므로 인터프리터는 이를 함수 선언으로 처리합니다. 첫 번째 예에서는 함수 선언에 이름이 없기 때문에 SyntaxError가 발생합니다. (앞서 함수 선언에는 이름이 있어야 한다고 언급했습니다). 두 번째 예에는 정상적으로 생성된 foo라는 함수 선언이 있지만 여전히 구문 오류가 발생합니다. 즉, 표현식이 없는 그룹화 연산자 오류입니다. 이는 실제로 함수 호출에 사용되는 괄호가 아니라 함수 선언 뒤의 그룹화 연산자입니다. 따라서 다음 코드를 선언하면:

코드 복사 코드는 다음과 같습니다.

// "foo"는 함수 선언입니다. , Create
alert(foo); // Function
function foo(x) {
Alert(x);
}(1) // 이는 그룹화 연산자일 뿐이며, 함수 호출!
foo(10); // 이는 실제 함수 호출이며 결과는 10

표현식을 만드는 가장 간단한 방법은 그룹화 연산자 대괄호를 사용하는 것입니다. 안에 들어가는 내용은 항상 표현식이므로 통역사가 해석할 때 모호함이 없습니다. 이 함수는 코드 실행 단계에서 생성되어 즉시 실행된 후 자동으로 소멸됩니다(참조가 없는 경우)

코드 복사 코드는 다음과 같습니다.

(function foo(x) {
Alert( x);
})(1); // 그룹화 연산자가 아닌 호출입니다.

위 코드는 식을 괄호로 묶은 다음 (1)을 통해 호출하는 코드입니다. 즉시 실행되는 다음 함수의 경우 함수가 이미 표현식의 위치에 있고 파서는 함수 실행 단계에서 생성되어야 하는 FE를 처리하고 있음을 알고 있기 때문에 주변 괄호가 필요하지 않습니다. , 함수가 생성된 직후에 호출되도록 합니다.

코드 복사 코드는 다음과 같습니다.

var foo = {
bar: 기능(x) {
                                                                >

보시다시피 foo.bar는 함수가 아닌 문자열입니다. 여기서 함수는 조건부 매개변수를 기반으로 이 속성을 초기화하는 데만 사용됩니다. 즉시 생성되고 호출됩니다.


1. 따라서 "괄호에 대하여"라는 질문에 대한 완전한 답변은 다음과 같습니다.
2. 함수가 표현식 위치에 없는 경우 그룹화 연산자 괄호가 필요합니다. 즉, 수동으로 함수는 FE로 변환됩니다.
3. 파서가 FE를 다루고 있다는 것을 알고 있다면 괄호를 사용할 필요가 없습니다.
자유 변수:

코드 복사 코드는 다음과 같습니다.

function testFn() {
var localVar = 10 ;//innerFn 함수의 경우 localVar는 자유 변수입니다.
function innerFn(innerParam) {
Alert(innerParam localVar);
}
return innerFn;
}

정적 폐쇄 범위:

코드 복사 코드는 다음과 같습니다.

var z = 10;
function foo() {
Alert(z);
}
foo(); // 10 – 정적 및 동적 범위를 사용하는 경우
(function () {
var z = 20;
foo(); // 10 - 정적 범위 사용, 20 - 동적 범위 사용
})();
// foo를 매개변수로 사용하는 경우 동일
(function (funArg) {
var z = 30;
funArg(); // 10 – 정적 범위, 30 – 동적 범위
})(foo);

이론: 범위 체인으로 인해 모든 함수는 클로저입니다(함수 유형에 관계없이 익명 함수, FE, NFE 및 FD는 모두 클로저입니다). 실용적인 관점에서 볼 때 다음 함수는 클로저로 간주됩니다. * 생성된 컨텍스트가 파괴된 경우에도 여전히 존재합니다(예: 내부 함수가 상위 함수에서 반환됨)

* 코드에서 자유 변수

를 참조합니다.

마지막으로:
ECMAScript는 프로토타입 기반 위임 상속을 지원하는 객체 지향 언어입니다.

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