programing

범위 내 모든 변수 가져오기

yoursource 2022. 10. 2. 12:20
반응형

범위 내 모든 변수 가져오기

javascript에서 현재 범위 내에 있는 모든 변수를 가져올 수 있는 방법이 있나요?

모두가 "아니오"라고 대답하고 "아니오"가 정답이라는 것을 알지만 함수의 로컬 변수를 정말로 얻어야 한다면 제한된 방법이 있습니다.

다음 기능을 고려합니다.

var f = function() {
    var x = 0;
    console.log(x);
};

함수를 문자열로 변환할 수 있습니다.

var s = f + '';

함수 소스를 문자열로 가져옵니다.

'function () {\nvar x = 0;\nconsole.log(x);\n}'

이제 esprima와 같은 파서를 사용하여 함수 코드를 해석하고 로컬 변수 선언을 찾을 수 있습니다.

var s = 'function () {\nvar x = 0;\nconsole.log(x);\n}';
s = s.slice(12); // to remove "function () "
var esprima = require('esprima');
var result = esprima.parse(s);

다음을 사용하여 개체를 찾습니다.

obj.type == "VariableDeclaration"

(했습니다)console.log(x)이하에 나타냅니다.

{
    "type": "Program",
    "body": [
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "x"
                    },
                    "init": {
                        "type": "Literal",
                        "value": 0,
                        "raw": "0"
                    }
                }
            ],
            "kind": "var"
        }
    ]
}

Chrome, Firefox 및 Node에서 테스트했습니다.

그러나 이 방법의 문제는 함수 자체에 정의된 변수만 있다는 것입니다.예를 들어 다음과 같습니다.

var g = function() {
    var y = 0;
    var f = function() {
        var x = 0;
        console.log(x);
    };
}

y가 아니라 x에만 접근할 수 있습니다.다만, 루프내에서 발신자(arguments.callee.caller.caller.caller)의 체인을 사용하고, 발신자 함수의 로컬 변수를 검색할 수 있습니다.모든 로컬 변수 이름이 있고 범위 변수가 있는 경우.변수 이름을 사용하면 간단한 평가를 사용하여 값에 액세스할 수 있습니다.

아니요. "범위 내" 변수는 프로그래밍 방식으로 액세스할 수 없는 "범위 체인"에 의해 결정됩니다.

상세한 것에 대하여는, ECMAScript(JavaScript)의 사양을 참조하십시오.여기 정규 사양(PDF)을 다운로드할 수 있는 공식 페이지 링크링크 가능한 공식 HTML 버전에 대한 링크가 있습니다.

Camsoft에 대한 코멘트를 기반으로 업데이트

이벤트 함수의 범위 내 변수는 이벤트 함수를 호출하는 방식이 아니라 정의하는 위치에 따라 결정됩니다.그러나 다음 웹 사이트를 통해 귀하의 기능에 사용할 수 있는 유용한 정보를 찾을 수 있습니다.this케니가 했던 것과 비슷한 행동을 함으로써 논쟁을 벌인다.(「 」 「 」 「 。for (var propName in ____)(여러 가지 물건)에 무엇이 알 수this 논거, , 논거, 논거, 논거, 논거, 논거, 논거, 논거, 논거, 논거, 논거, 논거, 논거, 논거, 논거, 논거, 논거, 논거, 논거, 논거, 논거, , 논거, 논거, 논거, 논거, , 논거, 논거, 논거, 논거, 논거, , 논거, 논거, 논거, 논거, 논거, , 논거 arguments모든 함수에 대해 암묵적으로 정의된 변수).

따라서 기능을 정의하는 위치에 따라 범위 내에 있는 것 외에 다음 작업을 수행하여 다른 방법으로 사용할 수 있는 다른 방법을 찾을 수 있습니다.

var n, arg, name;
alert("typeof this = " + typeof this);
for (name in this) {
    alert("this[" + name + "]=" + this[name]);
}
for (n = 0; n < arguments.length; ++n) {
    arg = arguments[n];
    alert("typeof arguments[" + n + "] = " + typeof arg);
    for (name in arg) {
        alert("arguments[" + n + "][" + name + "]=" + arg[name]);
    }
}

(더 유용한 정보를 얻기 위해 이 항목을 확장할 수 있습니다.)

다만, 그 대신에, Chrome의 개발 툴(일반적으로 개발에 Chrome을 사용하지 않는 경우라도), Firebug(일반적으로 Firefox를 사용하지 않는 경우라도), Dragon fly on Opera, 또는 IE의 "F12 Developer Tools"와 같은 디버거를 사용할 수 있습니다.그리고 그들이 제공하는 자바스크립트 파일을 읽어보세요.그리고 제대로 된 의사를 얻기 위해 그들의 머리를 두드려라. :-)

ECMAScript 6 에서는, 스테이트먼트내의 코드를 프록시 오브젝트로 랩 하는 것에 의해서, 어느 정도 가능합니다.이 방법은 엄격하지 않은 모드를 필요로 하며 이는 잘못된 관행입니다.

function storeVars(target) {
  return new Proxy(target, {
    has(target, prop) { return true; },
    get(target, prop) { return (prop in target ? target : window)[prop]; }
  });
}
var vars = {}; // Outer variable, not stored.
with(storeVars(vars)) {
  var a = 1;   // Stored in vars
  var b = 2;   // Stored in vars
  (function() {
    var c = 3; // Inner variable, not stored.
  })();
}
console.log(vars);

는 내부에서 되는 모든 합니다.with변수 할당이 타겟에 저장됩니다.룩업의 경우 프록시는 프록시 타깃 또는 글로벌오브젝트(부모 스코프가 아님)에서 값을 가져옵니다. let ★★★★★★★★★★★★★★★★★」const변수는 포함되지 않습니다.

베르기대답에서 영감을 얻었다.

네, 아니오 거의 모든 상황에서 '아니오'예요"Yes" (네) 단, 글로벌 스코프를 체크하는 경우는 제한적으로만 사용할 수 있습니다.다음 예를 들어 보겠습니다.

var a = 1, b = 2, c = 3;

for ( var i in window ) {
    console.log(i, typeof window[i], window[i]);
}

이 결과, 150개 이상의 다른 것 중에서 다음과 같은 출력이 제공됩니다.

getInterface function getInterface()
i string i // <- there it is!
c number 3
b number 2
a number 1 // <- and another
_firebug object Object firebug=1.4.5 element=div#_firebugConsole
"Firebug command line does not support '$0'"
"Firebug command line does not support '$1'"
_FirebugCommandLine object Object
hasDuplicate boolean false

따라서 현재 범위에 일부 변수를 나열할 수는 있지만 신뢰할 없거나 간결하거나 효율적이거나 쉽게 액세스할 없습니다.

좋은 질문은 범위 에 있는 변수를 왜 알고 싶은가 하는 것입니다.

그럴수는 없어요.

함수 선언의 식별자 및 함수 코드의 인수인 변수는 액세스할 수 없는 변수 개체의 속성으로 바인딩됩니다.

다음 항목도 참조하십시오.

시간이 얼마나 있어요?

에 들지 않을 한 변수 이름을할 수 CPU는 brootforce로 설정할 수 .eval!!나 !!!!!!!!!!!!!

다음 스니펫은 첫 번째 1000개의 bluetforce 문자열을 시도합니다.이것은 스코프에서 조작된 변수 이름을 찾기에 충분합니다.

let alpha = 'abcdefghijklmnopqrstuvwxyz';
let everyPossibleString = function*() {
  yield '';
  for (let prefix of everyPossibleString()) for (let char of alpha) yield `${prefix}${char}`;
};
let allVarsInScope = (iterations=1000) => {  
  let results = {};
  let count = 0;
  for (let bruteforceString of everyPossibleString()) {
    if (!bruteforceString) continue; // Skip the first empty string
    try { results[bruteforceString] = eval(bruteforceString); } catch(err) {}
    if (count++ > iterations) break;
  }
  return results;
};

let myScope = (() => {
    
  let dd = 'ddd';
  let ee = 'eee';
  let ff = 'fff';
  
  ((gg, hh) => {
    
    // We can't call a separate function, since that function would be outside our
    // scope and wouldn't be able to see any variables - but we can define the
    // function in place (using `eval(allVarsInScope.toString())`), and then call
    // that defined-in-place function
    console.log(eval(allVarsInScope.toString())());
    
  })('ggg', 'hhh');
  
})();

이 스크립트는 최종적으로 (매우 오랜 시간이 지나면) 모든 범위 변수 이름과abc nifty ★★★★★★★★★★★★★★★★★」swell가 작성한 예, 「 」 「 」 。알파벳 문자로 구성된 변수 이름만 검색됩니다.

let preElem = document.getElementsByClassName('display')[0];
let statusElem = document.getElementsByClassName('status')[0];
let alpha = 'abcdefghijklmnopqrstuvwxyz';
alpha += alpha.toUpperCase();
let everyPossibleString = function*() {
  yield '';
  for (let prefix of everyPossibleString()) for (let char of alpha) yield `${prefix}${char}`;
};

(async () => {
  
  let abc = 'This is the ABC variable :-|';
  let neato = 'This is the NEATO variable :-)';
  let swell = 'This is the SWELL variable :-D';
  
  let results = {};
  let batch = 25000;
  let waitMs = 25;
  let count = 0;
  let startStr = null;

  for (let bruteStr of everyPossibleString()) {
    
    try {

      if (bruteStr === '') continue;
      if (startStr === null) startStr = bruteStr;

      try { results[bruteStr] = eval(bruteStr); } catch(err) {}

      if (count++ >= batch) {
        
        statusElem.innerHTML = `Did batch of ${batch} from ${startStr} -> ${bruteStr}`;
        preElem.innerHTML = JSON.stringify(results, null, 2);
        
        count = 0;
        startStr = null;
        await new Promise(r => setTimeout(r, waitMs));

      }
      
    } catch(err) {
      
      // It turns out some global variables are protected by stackoverflow's snippet
      // system (these include "top", "self", and "this"). If these values are touched
      // they result in a weird iframe error, captured in this `catch` statement. The
      // program can recover by replacing the most recent `result` value (this will be
      // the value which causes the error).
      let lastEntry = Object.entries(results).slice(-1)[0];
      results[lastEntry[0]] = '<a protected value>';
            
    }

  }
  
  console.log('Done...'); // Will literally never happen

})();
html, body { position: fixed; left: 0; top: 0; right: 0; bottom: 0; margin: 0; padding: 0; overflow: hidden }
.display {
  position: fixed;
  box-sizing: border-box;
  left: 0; top: 0;
  bottom: 30px; right: 0;
  overflow-y: scroll;
  white-space: pre;
  font-family: monospace;
  padding: 10px;
  box-shadow: inset 0 0 10px 1px rgba(0, 0, 0, 0.3);
}
.status {
  position: fixed;
  box-sizing: border-box;
  left: 0; bottom: 0px; right: 0; height: 30px; line-height: 30px;
  padding: 0 10px;
  background-color: rgba(0, 0, 0, 1);
  color: rgba(255, 255, 255, 1);
  font-family: monospace;
}
<div class="display"></div>
<div class="status"></div>

이것이 실제로 실현 가능한 상황은 없다는 것을 잘 알고 있습니다.

나는 iman이 개략적으로 설명한 상기의 아이디어를 실행에 옮겼다.의 두 번째 ipsum 위에 마우스를 놓으면 다음과 같이 표시됩니다.return ipsum*ipsum - ...

여기에 이미지 설명 입력

범위 내 변수는 선언된 위치에 강조 표시됩니다(범위마다 다른 색상으로 표시됨).lorem빨간색 테두리가 있는 변수는 음영 변수입니다(범위 내에는 없지만 트리 아래쪽에 다른 로렘이 없을 경우 범위 내에 있습니다).

자바스크립트와 에스트래버, 에스케이프(에스프리마 위에 유틸리티 라이브러리)를 해석하기 위해 esprima 라이브러리를 사용하고 있습니다.무거운 리프팅은 이러한 라이브러리에서 모두 이루어집니다(가장 복잡한 것은 물론 에스프리마 자체입니다).

구조

ast = esprima.parse(sourceString, {range: true, sourceType: 'script'});

는 추상 구문 트리를 만듭니다.그리고나서,

analysis = escope.analyze(ast);

는 프로그램 내의 모든 스코프에 대한 정보를 캡슐화한 복잡한 데이터 구조를 생성합니다.나머지는 분석 오브젝트(및 추상 구문 트리 자체)에 인코딩된 정보를 모아 인터랙티브한 컬러링 스킴을 만드는 것입니다.

그래서 정답은 '아니오'가 아니라 '네, 하지만'입니다.크롬 브라우저의 중요한 부분(및 devtools)을 자바스크립트로 고쳐 써야 합니다.JavaScript는 튜링 완전 언어이므로 원칙적으로 가능합니다.불가능한 것은 소스 코드 전체를 문자열로 사용하지 않고 모든 작업을 수행하는 것입니다.

특정 스코프에서 Var에 액세스하는 가장 간단한 방법

  1. [ Developer Tools ]> [ Resources ]를 엽니다(Chrome ) 。
  2. 해당 범위에 액세스할 수 있는 함수로 파일 열기(파일을 찾으려면 tip cmd/ctrl+p)
  3. 함수 내부에 중단점을 설정하고 코드를 실행합니다.
  4. 중단점에서 정지하면 콘솔(또는 스코프 변수 창)을 통해 스코프 변수에 액세스할 수 있습니다.

주의: 이 작업은 미확보 js에 대해 수행해야 합니다.

모든 비개인 변수를 표시하는 가장 간단한 방법

  1. 콘솔 열기(Chrome)
  2. 유형: this.윈도
  3. Enter 키를 누르다

이제 선언된 모든 개체와 함께 확장할 수 있는 개체 트리가 표시됩니다.

모두가 알아차렸듯이, 당신은 할 수 없다.단, obj를 생성하여 선언한 모든 var를 obj에 할당할 수 있습니다.그러면 쉽게 대표팀을 확인할 수 있습니다.

var v = {}; //put everything here

var f = function(a, b){//do something
}; v.f = f; //make's easy to debug
var a = [1,2,3];
v.a = a;
var x = 'x';
v.x = x;  //so on...

console.log(v); //it's all there

디버깅에 도움이 되도록 변수를 수동으로 검사하려면 디버거를 실행합니다.

debugger;

브라우저 콘솔에 직접 접속합니다.

는 '범위와 변수'에서 볼 수 .[[Scopes]] " " " 를 사용하여 closure 를 합니다.console.dir().

예 1:

counterWithClosure = (function () {
    let internalVar = 0
    return function () {
        return ++internalVar
    }
})()

counterWithClosure() // 1
counterWithClosure() // 2
counterWithClosure() // 3

console.dir(counterWithClosure)

페이지에 도달 가능한 스크립트가 있는 경우 [[Scopes]> [ Closure ], [Scopes]> [ Global ], [Script]의 변수가 표시됩니다.

출력된 이미지:

중첩된 폐쇄가 있더라도 중첩된 범위를 볼 수 있습니다.

예 2:

adderWithNestedClosures = (function () {
    let varLevel1 = 1
    return function (param1) {
        let varLevel2 = 10
        return function (param2) {
            let varLevel3 = 100
            return function (param3) {
                let varLevel4 = 1000
                return function (param4) {
                    ++varLevel1
                    ++varLevel2
                    ++varLevel3
                    ++varLevel4
                    return {
                        paramsSum: param1 + param2 + param3 + param4,
                        varsSum: varLevel1 + varLevel2 + varLevel3 + varLevel4
                    }
                }
            }
        }
    }
})()

adderWith123 = adderWithNestedClosures(1)(2)(3) // Preparing function with nested scopes
adderWith123(4) // {paramsSum:10,varsSum:1115}
adderWith123(4) // {paramsSum:10,varsSum:1119}
adderWith123(5) // {paramsSum:11,varsSum:1123}
console.dir(adderWith123) 

[Scopes]에 중첩된 모든 스코프가 표시됩니다.출력된 이미지:

크롬 기반 브라우저에서 작동합니다.

언급URL : https://stackoverflow.com/questions/2051678/getting-all-variables-in-scope

반응형