차 한 잔의 노력, 스코프 및 스코프 체인 파악

RC.jpg올바른 요구르트를 마시고 절반의 노력으로 두 배의 결과를 얻으십시오! 모호하고 거의 신화적인 용어에 대해서는 머리를 긁적이지 말고 직접 코드로 이동하여 렌더링할 일반 언어를 추가하고 오늘날 자바스크립트에서 작은 언덕을 처리하는 방법을 살펴보겠습니다.

머리말

가장 먼저 알아야 할 것은 엔진으로, 엔진의 작업은 단순하고 무례하여 자바스크립트 코드를 처음부터 끝까지 실행하는 역할을 합니다. 엔진의 좋은 친구는 주로 코드 분석 및 컴파일을 담당하는 컴파일러이고, 엔진의 또 다른 좋은 친구는 오늘날의 주인공 범위입니다. 그렇다면 범위는 무엇에 사용됩니까? 범위 체인은 범위와 어떤 관련이 있습니까?

첫째, 범위(scope)

범위 정의: 범위는 런타임 시 코드의 특정 부분에 있는 변수, 함수 및 개체의 액세스 가능성입니다.

1. 범위의 분류
  1. 글로벌 범위
var name="global";
function foo(){
    console.log(name);
}
foo();//global

name 변수는 함수 foo() 내에서 선언되지 않았지만 name 값은 여전히 ​​인쇄되어 함수가 전역 범위에 액세스하고 name 변수를 읽을 수 있음을 나타냅니다. 또 다른 예:

hobby='music';
function foo(){
    hobby='book';
    console.log(hobby);
}
foo();//book

여기서 전역 스코프나 foo() 함수 모두 취미라는 변수를 선언하지 않은데 왜 오류가 없을까요? 전역 스코프에 작성 하기 때문에 hobby='music';var, let, const 선언이 없어도 window 객체에 걸려 있기 때문에 foo() 함수는 값을 읽을 수 있을 뿐만 아니라 수정할 수도 있습니다. 즉 , hobby='music';와 동일합니다 window.hobby='music';.

  1. 함수 본문 범위

함수 본문의 범위는 내부를 숨김으로써 달성됩니다. 즉, 우리가 흔히 말하듯이 내부 범위는 외부 범위에 액세스할 수 있지만 외부 범위는 내부 범위에 액세스할 수 없습니다. 그 이유는 범위 체인과 관련하여 쉽게 해결됩니다.

function foo(){
    var age=19;
    console.log(age);
}
console.log(age);//ReferenceError:age is not defined

분명히 전역 범위에는 연령 변수가 없지만 foo() 함수에는 내부에 있지만 외부에서 액세스할 수 없으므로 오류가 자연스럽게 보고되고 다음과 같은 경우 foo() 함수가 실행되지 않습니다. 그것은 호출되지 않습니다.

  1. 블록 범위

우리가 접촉한 let 범위, 코드 블록 {}, for 루프가 let, if, while, switch 등을 사용할 때의 범위와 같은 블록 수준 범위는 놀라운 일이 아닙니다. 그러나 블록 수준 범위에 대한 더 깊은 이해의 전제는 다음 용어를 먼저 인식해야 한다는 것입니다.

--标识符:能在作用域生效的变量。函数的参数,变量,函数名。需要格外注意的是:函数体内部的标识符外部访问不到

--函数声明:function 函数名(){}

--函数表达式: var 函数名=function(){}

--自执行函数: (function 函数名(){})();自执行函数前面的语句必须有分号,通常用于隐藏作用域。

接下来我们就用一个例子,一口气展示完吧

function foo(sex){
    console.log(sex);
}
var f=function(){
    console.log('hello');
}
var height=180;
(
    function fn(){
        console.log(height);
    }
)();
foo('female');
//依次打印:
//180
//female
//hello

分析一下:标识符:foo,sex,height,fn;函数声明:function foo(sex){};函数表达式:var f=function(){};自执行函数:(function fn(){})();需要注意,自执行函数fn()前面的var height=180;语句,分号不能抛弃。否则,你可以试一下。

二、预编译

说好只是作用域和作用域链的,但是考虑到理解作用域链的必要性,这里还是先聊聊预编译吧。先讨论预编译在不同环境发生的情况下,是如何进行预编译的。

  1. 发生在代码执行之前

(1)声明提升

console.log(b);
var b=123;//undefined

这里打印undefined,这不是报错,与Refference:b is not defined不同。这是代码执行之前,预编译的结果,等同于以下代码:

var b;//声明提升
console.log(b);//undefined
b=123;

(2)函数声明整体提升

test();//hello123  调用函数前并没有声明,但是任然打印,是因为函数声明整体提升了
function test(){
    var a=123;
    console.log('hello'+a);
}

2.发生在函数执行之前

理解这个只需要掌握四部曲

(1)创建一个AO(Activation Object)

(2)找形参和变量声明,然后将形参和变量声明作为AO的属性名,属性值为undefined

(3)将实参和形参统一

(4)在函数体内找函数声明,将函数名作为AO对象的属性名,属性值予函数体 那么接下来就放大招了:

var global='window';
function foo(name,sex){
    console.log(name);
    function name(){};
    console.log(name);
    var nums=123;
    function nums(){};
    console.log(nums);
    var fn=function(){};
    console.log(fn);
}
foo('html');

这里的结果是什么呢?分析如下:

//从上到下
//1、创建一个AO(Activation Object)
AO:{
    //2、找形参和变量声明,然后将形参和变量声明作为AO的属性名,属性值为undefined
    name:undefined,
    sex:undefined,
    nums=undefined,
    fn:undefined,
    //3、将实参和形参统一
    name:html,
    sex:undefined,
    nums=123,
    fn:function(){},
    //4、在函数体内找函数声明,将函数名作为AO对象的属性名,属性值予函数体
    name:function(){},
    sex:undefined,
    fn:function(){},
    nums:123//这里不仅存在nums变量声明,也存在nums函数声明,但是取前者的值
    
    以上步骤得到的值,会按照后面步骤得到的值覆盖前面步骤得到的值
}
//依次打印
//[Function: name]
//[Function: name]
//123
//[Function: fn]

3.发生在全局(内层作用域可以访问外层作用域)

同发生在函数执行前一样,发生在全局的预编译也有自己的三部曲:

(1) GO(Global Object) 객체 생성 (2) 전역 변수 선언 찾기, 변수 선언을 GO의 속성 이름으로 사용하고 속성 값이 정의되지 않음 (3) 전역적으로 함수 선언 찾기 및 함수 사용 name은 GO 개체의 속성 이름으로, 속성 값은 함수 본문에 할당됩니다. 예를 들면 다음과 같습니다.

var global='window';
function foo(a){
    console.log(a);
    console.log(global);
    var b;
}
var fn=function(){};
console.log(fn);
foo(123);
console.log(b);

이 예제는 비교적 간단합니다. 동일한 단계와 아이디어가 분석에서 반복되지 않을 것입니다. 나는 당신이 이미 알고 있다고 믿습니다. 인쇄 결과는 다음과 같습니다.

[Function: fn]
123
window
ReferenceError: b is not defined

자, 본격적으로 스코프 체인으로 넘어갑시다.

셋째, 범위 체인

범위 체인을 사용하면 내부 레이어가 외부 레이어에 액세스할 수 있지만 외부 레이어가 내부 레이어에 액세스할 수 없는 이유를 찾는 데 도움이 될 수 있습니다. 그러나 다시 범위 체인을 이해하기 전에 더 모호한 추상 용어를 볼 필요가 있습니다.

  1. 执行期上下文: 함수가 실행되면 실행 컨텍스트(AO 객체)라는 객체가 생성되며, 실행 컨텍스트는 함수가 실행되는 환경을 정의합니다. 함수가 실행될 때마다 해당 실행 컨텍스트가 고유하므로 함수를 여러 번 호출하면 여러 실행 컨텍스트가 생성되고 함수가 실행되면 생성된 실행 컨텍스트가 소멸됩니다.
  2. 查找变量: 범위 체인의 맨 위에서 아래로 검색합니다.

3. [[scope]]암시적 속성이라고도 하는 범위 속성은 엔진 자체에서만 지원됩니다. 런타임 컨텍스트의 바인딩이 저장되는 액세스할 수 없는 함수 범위입니다.

먼저 함수의 내장 속성을 살펴보겠습니다.

function test(){//函数被创建的那一刻,就携带name,prototype属性
      console.log(123);
}
console.log(test.name);//test
console.log(test.prototype);//{} 原型
// console.log(test[[scope]]);访问不到,作用域属性,也称为隐式属性

// test() --->AO:{}执行完毕会回收
// test() --->AO:{}执行完毕会回收

범위 체인이 구현되는 방법을 살펴보겠습니다.

var global='window';
function foo(){
    function fn(){
        var fn=222;
    }
    var foo=111;
    console.log(foo);
}
foo();

분석하다:

GO:{
    foo:function(){}
}
fooAO:{
    foo:111,
    fn:function(){}
}
fnAO:{
    fn:222
}
// foo定义时 foo.[[scope]]---->0:GO{}
// foo执行时 foo.[[scope]]---->0:AO{}  1:GO{}  后访问的在前面
//fn定义时 fn.[[scope]]---->0:fnAO{} 1:fooAO{}  2:GO{}
fnAO:fn的AO对象;fooAO:foo的AO对象

이미지.png

요약하자면:作用域链就是[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。

Supongo que te gusta

Origin juejin.im/post/7116516393100853284
Recomendado
Clasificación