>  기사  >  웹 프론트엔드  >  범위, 범위 체인 및 클로저에 대한 Javascript의 자세한 설명(그림 및 텍스트)

범위, 범위 체인 및 클로저에 대한 Javascript의 자세한 설명(그림 및 텍스트)

黄舟
黄舟원래의
2017-03-21 14:23:371232검색

이 글에서는 자바스크립트 다이어그램(스코프, 스코프 체인, 클로저 및 기타 지식)을 주로 소개합니다. 매우 좋은 참조 값을 가지고 있습니다. 아래 에디터로 살펴보겠습니다

스코프란 무엇인가요?

범위는 코드 컴파일 단계에서 결정되는 규칙으로, 변수함수의 접근 가능한 범위를 규정합니다. 전역 변수에는 전역 범위가 있고, 지역 변수에는 지역 범위가 있습니다. js는 블록 수준 범위가 없는 언어(if, for 및 기타 문을 포함한 중괄호 코드 블록이나 개별 중괄호 코드 블록은 로컬 범위를 형성할 수 없음)이므로 js의 로컬 역할 도메인이 형성됩니다. 함수의 중괄호 안에 정의된 코드 블록, 즉 함수 범위에 의해서만 가능합니다.

스코프 체인이란 무엇인가요?

스코프 체인은 스코프 규칙을 구현한 것입니다. 스코프 체인을 구현하면 스코프 내에서 변수에 접근할 수 있고 스코프 내에서 함수를 호출할 수 있습니다.

스코프 체인은 한 방향으로만 접근할 수 있는 연결 목록입니다. 이 연결 목록의 각 노드는 실행 컨텍스트의 개체 변수입니다. 코드가 실행됨) 단방향 연결 목록 헤드(액세스할 수 있는 첫 번째 노드)는 항상 현재 호출 및 실행 중인 함수의 변수 개체(활성 개체)이고, 꼬리는 항상 전역 활성 개체입니다.

스코프체인 형성?

코드 실행부터 스코프 체인이 형성되는 과정을 살펴보겠습니다.

function fun01 () {
 console.log('i am fun01...');
 fun02();
}
function fun02 () {
 console.log('i am fun02...');
}
fun01();

범위, 범위 체인 및 클로저에 대한 Javascript의 자세한 설명(그림 및 텍스트)

데이터 접근 과정

위와 같이 프로그램은 변수에 접근할 때 단방향 접근 특성을 따른다. 스코프 체인의 경우 먼저 헤드 노드의 AO에서 검색하고, 그렇지 않으면 다음 노드의 AO에서 테일 노드(글로벌 AO)까지 검색합니다. 이 과정에서 발견되면 발견됩니다. 발견되지 않으면 정의되지 않은 오류가 보고됩니다.

스코프 체인 확장

위의 스코프 체인 구성에서 함수 호출 시 체인의 각 노드가 체인 헤드로 이동하지 않는 것을 볼 수 있습니다. 현재 함수의 AO를 실행하고 노드를 형성하는 또 다른 방법은 "스코프 체인 확장", 즉 스코프 체인의 선두에 원하는 개체 범위를 삽입하는 것입니다. 범위 체인을 확장하는 방법에는 두 가지가 있습니다.

1.with 문

function fun01 () {
 with (document) {
  console.log('I am fun01 and I am in document scope...')
 }
}
fun01();

범위, 범위 체인 및 클로저에 대한 Javascript의 자세한 설명(그림 및 텍스트)

2. try-catch 문의 캐치 블록

function fun01 () {
 try {
  console.log('Some exceptions will happen...')
 } catch (e) {
  console.log(e)
 }
}
fun01();

범위, 범위 체인 및 클로저에 대한 Javascript의 자세한 설명(그림 및 텍스트)

ps: 개인적으로는 수요가 별로 없다고 생각합니다. with 문의 사용 및 try-catch 사용 또한 요구에 따라 달라집니다. 저는 개인적으로 이 두 가지를 잘 사용하지 않지만, 이 부분을 정리하는 과정에서 스코프체인 수준에서 미성숙한 성능 최적화 팁을 생각해냈습니다.

스코프 체인으로 인한 성능 최적화에 대한 조금 미숙한 제안

1. 변수 노드의 스코프 체인 액세스를 줄입니다

여기에서는 프로그램이 정의되지 않은 변수에 액세스하기 위해 통과하는 범위 체인의 노드 수를 나타내는 "검색 거리"라는 순위를 사용자 정의합니다. 현재 노드에서 변수가 발견되지 않으면 다음 노드로 점프하여 검색을 하고, 검색 중인 변수가 다음 노드에 존재하는지 확인하는 것도 필요하기 때문이다. "검색 거리"가 길어질수록 "점프" 작업과 "판단" 작업이 더 많이 필요하고 리소스 오버헤드가 커져 성능에 영향을 미칩니다. 이러한 성능 차이는 몇 가지 변수 검색 작업에 대해서는 큰 성능 문제를 일으키지 않을 수 있지만, 변수 검색 작업을 여러 번 수행하면 성능 비교가 더욱 명확해집니다.


(function(){
 console.time()
 var find = 1      //这个find变量需要在4个作用域链节点进行查找
 function fun () {
  function funn () {
   var funnv = 1;
   var funnvv = 2;
   function funnn () {
    var i = 0
    while(i <= 100000000){
     if(find){
      i++
     }
    }
   }
   funnn()
  }
  funn()
 }
 fun()
 console.timeEnd()
})()

범위, 범위 체인 및 클로저에 대한 Javascript의 자세한 설명(그림 및 텍스트)


(function(){
 console.time()
 function fun () {
  function funn () {
   var funnv = 1;
   var funnvv = 2;
   function funnn () {
    var i = 0
    var find = 1      //这个find变量只在当前节点进行查找
    while(i <= 100000000){
     if(find){
      i++
     }
    }
   }
   funnn()
  }
  funn()
 }
 fun()
 console.timeEnd()
})()

Mac Pro에서 Chrome 검색 실험은 컴퓨터에서 진행되었으며 검색 작업은 1억 번 수행되었습니다.

실험 결과: 전자는 5회 실행 시 평균 85.599ms가 걸렸고, 후자는 5회 실행 시 평균 63.127ms가 걸렸습니다.

2. 스코프 체인의 노드 AO에 너무 많은 변수 정의를 피하십시오.

너무 많은 변수 정의가 성능 문제를 일으키는 주된 이유 비용이 많이 드는 변수를 찾는 과정에서의 '판단' 작업이다. 성능 비교를 위해 with 를 사용합니다.

(function(){
 console.time()
 function fun () {
  function funn () {
   var funnv = 1;
   var funnvv = 2;
   function funnn () {
    var i = 0
    var find = 10
    with (document) {
     while(i <= 1000000){
      if(find){
       i++
      }
     }
    }
   }
   funnn()
  }
  funn()
 }
 fun()
 console.timeEnd()
})()

범위, 범위 체인 및 클로저에 대한 Javascript의 자세한 설명(그림 및 텍스트)

在mac pro的chrome浏览器下做实验,进行100万次查找运算,借助with使用document进行的延长作用域链,因为document下的变量属性比较多,可以测试在多变量作用域链节点下进行查找的性能差异。

实验结果:5次平均耗时558.802ms,而如果删掉with和document,5次平均耗时0.956ms。

当然,这两个实验是在我们假设的极端环境下进行的,结果仅供参考!

关于闭包

1.什么是闭包?

函数对象可以通过作用域链相互关联起来,函数体内的数据(变量和函数声明)都可以保存在函数作用域内,这种特性在计算机科学文献中被称为“闭包”。既函数体内的数据被隐藏于作用于链内,看起来像是函数将数据“包裹”了起来。从技术角度来说,js的函数都是闭包:函数都是对象,都关联到作用域链,函数内数据都被保存在函数作用域内。

2.闭包的几种实现方式

实现方式就是函数A在函数B的内部进行定义了,并且当函数A在执行时,访问了函数B内部的变量对象,那么B就是一个闭包。如下:

범위, 범위 체인 및 클로저에 대한 Javascript의 자세한 설명(그림 및 텍스트)

범위, 범위 체인 및 클로저에 대한 Javascript의 자세한 설명(그림 및 텍스트)

如上两图所示,是在chrome浏览器下查看闭包的方法。两种方式的共同点是都有一个外部函数outerFun(),都在外部函数内定义了内部函数innerFun(),内部函数都访问了外部函数的数据。不同的是,第一种方式的innerFun()是在outerFun()内被调用的,既声明和被调用均在同一个执行上下文内。而第二种方式的innerFun()则是在outerFun()外被调用的,既声明和被调用不在同一个执行上下文。第二种方式恰好是js使用闭包常用的特性所在:通过闭包的这种特性,可以在其他执行上下文内访问函数内部数据。

我们更常用的一种方式则是这样的:

//闭包实例
function outerFun () {
 var outerV1 = 10
 function outerF1 () {
  console.log(&#39;I am outerF1...&#39;)
 }
 function innerFun () {
  var innerV1 = outerV1
  outerF1()
 }
 return innerFun //return回innerFun()内部函数
}
var fn = outerFun()  //接到return回的innerFun()函数
fn()     //执行接到的内部函数innerFun()

此时它的作用域链是这样的:

범위, 범위 체인 및 클로저에 대한 Javascript의 자세한 설명(그림 및 텍스트)

3.闭包的好处及使用场景

js的垃圾回收机制可以粗略的概括为:如果当前执行上下文执行完毕,且上下文内的数据没有其他引用,则执行上下文pop出call stack,其内数据等待被垃圾回收。而当我们在其他执行上下文通过闭包对执行完的上下文内数据仍然进行引用时,那么被引用的数据则不会被垃圾回收。就像上面代码中的outerV1,放我们在全局上下文通过调用innerFun()仍然访问引用outerV1时,那么outerFun执行完毕后,outerV1也不会被垃圾回收,而是保存在内存中。另外,outerV1看起来像不像一个outerFun的私有内部变量呢?除了innerFun()外,我们无法随意访问outerV1。所以,综上所述,这样闭包的使用情景可以总结为:

(1)进行变量持久化。

(2)使函数对象内有更好的封装性,内部数据私有化。

进行变量持久化方面举个栗子:

我们假设一个需求时写一个函数进行类似id自增或者计算函数被调用的功能,普通青年这样写:

 var count = 0
 function countFun () {
  return count++
 }

这样写固然实现了功能,但是count被暴露在外,可能被其他代码篡改。这个时候闭包青年就会这样写:

function countFun () {
 var count = 0
 return function(){
  return count++
 }
}
var a = countFun()
a()

这样count就不会被不小心篡改了,函数调用一次就count加一次1。而如果结合“函数每次被调用都会创建一个新的执行上下文”,这种count的安全性还有如下体现:

function countFun () {
 var count = 0
 return {
  count: function () {
   count++
  },
  reset: function () {
   count = 0
  },
  printCount: function () {
   console.log(count)
  }
 }
}
var a = countFun()
var b = countFun()
a.count()
a.count()
b.count()
b.reset()
a.printCount()  //打印:2 因为a.count()被调用了两次
b.printCount()  //打印出:0 因为调用了b.reset()

以上便是闭包提供的变量持久化和封装性的体现。

4.闭包的注意事项

클로저의 변수는 다른 일반 변수처럼 가비지 수집되지 않고 항상 메모리에 존재하므로 클로저를 과도하게 사용하면 성능 문제가 발생할 수 있습니다.

위 내용은 범위, 범위 체인 및 클로저에 대한 Javascript의 자세한 설명(그림 및 텍스트)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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