รับตัวแปรทั้งหมดในขอบเขต


194

มีวิธีรับตัวแปรทั้งหมดที่อยู่ในขอบเขตใน javascript หรือไม่


ตอบคำถามของคุณกับ Camsoft: นั่นเป็นคำถามที่แตกต่างอย่างสิ้นเชิง ฉันได้อัปเดตคำตอบเพื่อแก้ไขแล้ว
TJ Crowder

3
เป็นเพียงคำถามทั่วไปการมีความเจาะจงมากขึ้นไม่ช่วยอะไรมากนักตั้งแต่ฉันทำงานกับ API ที่คลุมเครือด้วยเอกสารที่ไม่ดี
Ian

คุณหมายถึงตัวแปรทั่วโลก! คุณสามารถแสดงนับfor (v in this) alert(v);ตัวแปรทั่วโลกใช้ แต่ไม่ใช่ทุกคนที่นับได้และฉันรู้ว่าไม่มีวิธีมาตรฐานในการรับรายชื่อคนที่ไม่นับจำนวน
Jason Orendorff

2
@ Jason - ไม่คำถามนี้ชัดเจน ภายในฟังก์ชั่นตัวแปรในขอบเขตจะรวมตัวแปรทั่วโลกthis, argumentsพารามิเตอร์และตัวแปรทั้งหมดที่กำหนดไว้ในการปิดล้อมตรวจวิเคราะห์
Tim Down

2
นี่คือเหตุผลที่ฉันพลาดตารางสัญลักษณ์ของ Perl มีแผนที่จะเพิ่มสิ่งนี้ลงใน Javascript หรือไม่?
Dexygen

คำตอบ:


84

ไม่ตัวแปร "ในขอบเขต" ถูกกำหนดโดย "ขอบเขตลูกโซ่" ซึ่งไม่สามารถเข้าถึงได้ทางโปรแกรม

สำหรับรายละเอียด (ค่อนข้างมาก) ตรวจสอบข้อมูลจำเพาะ ECMAScript (JavaScript) นี่คือการเชื่อมโยงไปยังหน้าอย่างเป็นทางการที่คุณสามารถดาวน์โหลดข้อมูลจำเพาะบัญญัติ (ไฟล์ PDF) และนี่เป็นหนึ่งอย่างเป็นทางการ, HTML รุ่นที่เชื่อมโยงกัน

อัปเดตตามความคิดเห็นของคุณเป็น Camsoft

ตัวแปรในขอบเขตสำหรับฟังก์ชันเหตุการณ์ของคุณถูกกำหนดโดยที่คุณกำหนดฟังก์ชันเหตุการณ์ของคุณไม่ใช่วิธีที่พวกเขาเรียกมัน แต่คุณอาจพบข้อมูลที่เป็นประโยชน์เกี่ยวกับสิ่งที่มีอยู่กับการทำงานของคุณผ่านทางthisและการขัดแย้งโดยการทำสิ่งที่ตามเส้นของสิ่งที่ KennyTM ชี้ให้เห็น (คน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]);
    }
}

(คุณสามารถขยายเพื่อรับข้อมูลที่เป็นประโยชน์มากขึ้น)

แทนที่จะเป็นเช่นนั้นฉันอาจใช้ดีบักเกอร์เช่นเครื่องมือ dev ของ Chrome (แม้ว่าปกติคุณจะไม่ใช้ Chrome เพื่อการพัฒนา) หรือFirebug (แม้ว่าปกติคุณจะไม่ใช้ Firefox สำหรับการพัฒนา) หรือ Dragonfly บน Opera หรือ "เครื่องมือนักพัฒนา F12" บน IE และอ่านไฟล์ JavaScript ที่พวกเขาให้คุณ และเอาชนะพวกเขาเหนือหัวสำหรับเอกสารที่เหมาะสม :-)


ในฐานะที่เป็นบันทึกย่อด้าน Google Chrome มีดีบักเกอร์ที่ยอดเยี่ยมเทียบเท่ากับ Firebug (พร้อมด้วยไอซิ่งด้านบนอีกเล็กน้อย)
หมุน

4
@Swivelgames: โอ้ฉันชอบเครื่องมือ Dev Tools ของ Chrome กับ Firefox + Firebug มาก พวกเขาไม่ได้ย้อนกลับไปมากนักในปี 2010 :-)
TJ Crowder

1
@tjcrowder แน่นอน! ฉันสังเกตเห็นการประทับเวลาของคำตอบเมื่อสักครู่ที่ผ่านมา :)
หมุน

98

แม้ว่าทุกคนจะตอบว่า " ไม่ " และฉันรู้ว่า "ไม่" เป็นคำตอบที่ถูกต้อง แต่ถ้าคุณต้องการได้รับตัวแปรท้องถิ่นของฟังก์ชั่นมันมีวิธีที่ จำกัด

พิจารณาฟังก์ชั่นนี้:

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

คุณสามารถแปลงฟังก์ชั่นของคุณเป็นสตริง:

var s = f + '';

คุณจะได้รับแหล่งที่มาของฟังก์ชั่นเป็นสตริง

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

ตอนนี้คุณสามารถใช้ parser เช่น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);
    };
}

คุณเพียงแค่มีการเข้าถึงxและไม่Y แต่คุณยังสามารถใช้กลุ่มของผู้เรียก (arguments.callee.caller.caller.caller) ในลูปเพื่อค้นหาตัวแปรโลคัลของฟังก์ชันตัวเรียก ถ้าคุณมีชื่อตัวแปรท้องถิ่นเพื่อให้คุณมีตัวแปรขอบเขต ด้วยชื่อตัวแปรคุณสามารถเข้าถึงค่าด้วย eval อย่างง่าย


7
เทคนิคที่ยอดเยี่ยมที่นี่ในการแก้ไขปัญหาดั้งเดิม สมควรได้รับการโหวต
deepelement

4
ตัวแปรของผู้โทรไม่จำเป็นต้องเป็นตัวแปรขอบเขต นอกจากนี้ตัวแปรขอบเขตไม่จำเป็นต้องอยู่ในห่วงโซ่การโทร ตัวแปรที่ถูกปิดไปอาจถูกอ้างอิงโดยฟังก์ชั่นการปิดเมื่อเรียกมันจากผู้โทรที่อยู่นอกขอบเขตการ จำกัด / ปิด (และตัวแปรที่ไม่สามารถเข้าถึงได้จากการปิดของร่างกาย)
DDS

คุณช่วยอธิบายให้ชัดเจนได้ไหม? ฉันลองด้วยโค้ดด้านล่าง แต่ไม่สามารถรับตัวแปรที่ติดอยู่ในขอบเขตได้ function getCounter(start){ return function(){ start ++; return start; } } var counter = getCounter(1); var startInClosure = eval.call(counter, 'this.start;'); console.log(startInClosure);มันพิมพ์undefinedในขณะที่ฉันคาดหวังว่ามันควรจะเป็น 2
Pylipala

@Pylipala คำถามที่นี่เป็นเรื่องเกี่ยวกับตัวแปรในขอบเขตที่ไม่ได้รับค่าของตัวแปรท้องถิ่นจากนอกขอบเขต ไม่สามารถทำได้เพราะนี่เป็นคำจำกัดความของตัวแปรท้องถิ่นที่ไม่สามารถเข้าถึงได้จากภายนอก เกี่ยวกับรหัสของคุณคุณหมายถึงบริบทไม่ใช่ขอบเขต แต่คุณไม่ได้เริ่มต้นในบริบท (นี่) ดังนั้นคุณจึงไม่สามารถอ่านได้ด้วยสิ่งนี้เริ่ม ดูที่นี่: paste.ubuntu.com/11560449
iman

@iman ขอบคุณสำหรับคำตอบของคุณ ฉันเดาว่ามันเป็นไปไม่ได้ในฝั่ง Javascript ดังนั้นฉันเดาว่ามันเป็นไปได้ในด้าน v8 ฉันพบลิงค์คำถามด้านล่างซึ่งอธิบายปัญหาของฉันอย่างถูกต้อง ในนั้น Lasse Reichstein บอกว่าไม่มี แต่ mako-taco บอกว่าใช่ ฉันลองใช้รหัส C ++ เป็นลิงก์แต่ไม่ทราบวิธีการเขียน
Pylipala

32

ใช่และไม่. "ไม่" ในเกือบทุกสถานการณ์ "ใช่" แต่ในลักษณะที่ จำกัด หากคุณต้องการตรวจสอบขอบเขตทั่วโลก นำตัวอย่างต่อไปนี้:

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

ดังนั้นจึงเป็นไปได้ที่จะแสดงรายการตัวแปรบางอย่างในขอบเขตปัจจุบัน แต่ ไม่น่าเชื่อถือรวบรัดมีประสิทธิภาพหรือเข้าถึงได้ง่าย

คำถามที่ดีกว่าคือเหตุใดคุณต้องการทราบว่าตัวแปรใดบ้างที่อยู่ในขอบเขต


2
ฉันคิดว่าคำถามทั่วไปและไม่ จำกัด จาวาสคริปต์ในบริบทของเว็บเบราว์เซอร์
Radu Simionescu

เพียงแค่เปลี่ยนคำว่า "หน้าต่าง" สำหรับ "ทั่วโลก" ถ้าคุณใช้โหนด ... หรือคุณสามารถใช้คำว่า "นี่" แต่นั่นเป็นสิ่งต้องห้ามภายใต้ "ใช้เข้มงวด"
Ivan Castellanos

ฉันเพิ่มการตรวจสอบสายสำหรับ hasOwnProperty ตัวอย่างเช่นfor ( var i in window ) { if (window.hasOwnProperty(i)) { console.log(i, window[i]); }}. อย่างน้อยก็จะลดคุณสมบัติที่สืบทอดมาและตัวแปรของคุณจะอยู่ในกลุ่มอื่นเพียงประมาณ 50 คุณสมบัติเท่านั้น
timctran

8
ทำไมบางคนไม่ควรต้องการตรวจสอบตัวแปรที่อยู่ในขอบเขตระหว่างการแก้ไขข้อบกพร่องและ / หรือวัตถุประสงค์ในการพัฒนา
Dexygen

เหตุผลอื่นที่ต้องการทราบว่าตัวแปรใดที่อยู่ในขอบเขตภายในคือการปิดการจัดลำดับ
Michael

29

ใน ECMAScript 6 เป็นไปได้มากขึ้นหรือน้อยลงโดยการห่อโค้ดไว้ในwithคำสั่งที่มีวัตถุพร็อกซี โปรดทราบว่ามันต้องใช้โหมดที่ไม่เข้มงวดและเป็นการฝึกฝนที่ไม่ดี

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ไม่รวมตัวแปร

แรงบันดาลใจจากคำตอบนี้โดยBergi


1
นี่คือความสำเร็จที่น่าประหลาดใจสำหรับเทคนิคง่ายๆ
Jonathan Eunice

พลังว้าวสุดยอด
pery mimon

16

คุณทำไม่ได้

ตัวแปรตัวบ่งชี้ของการประกาศฟังก์ชันและอาร์กิวเมนต์สำหรับโค้ดฟังก์ชันถูกโยงเป็นคุณสมบัติของVariable Objectซึ่งไม่สามารถเข้าถึงได้

ดูสิ่งนี้ด้วย:


1
จริง แต่ตัวแปรภายในขอบเขตนั้นนอกเหนือไปจากวัตถุตัวแปรปัจจุบันเท่านั้น ตัวแปรในขอบเขตถูกกำหนดโดยขอบเขตลูกโซ่ซึ่งเป็นลูกโซ่ประกอบด้วย (ในกรณีปกติ) ของชุดของวัตถุตัวแปรและยุติด้วยวัตถุทั่วโลก (แม้ว่าwithคำสั่งสามารถใช้เพื่อแทรกวัตถุอื่น ๆ ในห่วงโซ่)
TJ Crowder

+1 สำหรับลิงก์ไปยัง bclary.com ข้อมูลจำเพาะรุ่นล่าสุดของ HTML จะปรากฏในอีกไม่กี่เดือนข้างหน้าบนเว็บไซต์ ECMA ฉันดีใจที่ได้พูด
TJ Crowder

3
เพียงทราบลิงค์ทั้งสองจะเสีย
0xc0de


8

วิธีที่ง่ายที่สุดในการเข้าถึง Vars ในขอบเขตเฉพาะ

  1. เปิดเครื่องมือสำหรับนักพัฒนา> แหล่งข้อมูล (ใน Chrome)
  2. เปิดไฟล์ที่มีฟังก์ชั่นที่สามารถเข้าถึงขอบเขตนั้นได้ (tip cmd / ctrl + p เพื่อค้นหาไฟล์)
  3. ตั้งเบรกพอยต์ภายในฟังก์ชั่นนั้นและเรียกใช้รหัสของคุณ
  4. เมื่อหยุดที่เบรกพอยต์คุณสามารถเข้าถึงขอบเขตขอบเขตผ่านคอนโซล (หรือหน้าต่างขอบเขตขอบเขต)

บันทึก:คุณต้องการทำเช่นนี้กับ js ที่ไม่เล็กสุด

วิธีที่ง่ายที่สุดในการแสดง Vars ที่ไม่ใช่ส่วนตัวทั้งหมด

  1. เปิดคอนโซล (ใน Chrome)
  2. ประเภท: this.window
  3. กด Enter

ตอนนี้คุณจะเห็นแผนผังวัตถุที่คุณสามารถขยายได้ด้วยวัตถุที่ประกาศทั้งหมด


7

อย่างที่ทุกคนสังเกตเห็น: คุณทำไม่ได้ แต่คุณสามารถสร้าง obj และกำหนด var ที่คุณประกาศให้กับ obj นั้นได้ ด้วยวิธีนี้คุณสามารถตรวจสอบ vars ของคุณได้อย่างง่ายดาย:

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

1
+1 ขอบคุณสำหรับตัวอย่างที่ยอดเยี่ยมเพียง แต่ทำสำเร็จเท่านั้นที่สามารถเห็นได้ผ่านทางconsole.log(window); jsfiddle.net/4x3Tx
Stano

5

ฉันทำซอปฏิบัติ (โดยพื้นฐาน) เหนือความคิดที่ระบุโดย iman นี่คือลักษณะที่ปรากฏเมื่อคุณเลื่อนเมาส์ไปที่ ipsum อันที่สองreturn ipsum*ipsum - ...

ป้อนคำอธิบายรูปภาพที่นี่

ตัวแปรที่อยู่ในขอบเขตจะถูกเน้นที่ตำแหน่งที่ถูกประกาศ (ด้วยสีที่แตกต่างกันสำหรับขอบเขตที่แตกต่างกัน) loremกับขอบสีแดงเป็นตัวแปรเงา (ไม่อยู่ในขอบเขต แต่ต้องอยู่ในขอบเขตถ้า lorem อื่น ๆ ต่อไปลงที่ต้นไม้จะไม่เป็นมี.)

ฉันกำลังใช้ห้องสมุด esprima เพื่อแยกวิเคราะห์ JavaScript และแยกส่วนแยกส่วนหลบหนี escope (ยูทิลิตี้ไลบรารีที่ด้านบนสุดของ esprima) 'การยกของหนัก' นั้นเสร็จสิ้นโดยห้องสมุดเหล่านั้น (ที่ซับซ้อนที่สุดคือ esprima

มันทำงานอย่างไร

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

ทำให้ต้นไม้ไวยากรณ์ที่เป็นนามธรรม จากนั้น

analysis = escope.analyze(ast);

สร้างโครงสร้างข้อมูลที่ซับซ้อนห่อหุ้มข้อมูลเกี่ยวกับขอบเขตทั้งหมดในโปรแกรม ส่วนที่เหลือคือการรวบรวมข้อมูลที่เข้ารหัสในวัตถุการวิเคราะห์ (และต้นไม้ไวยากรณ์เอง) และสร้างโครงร่างการระบายสีแบบโต้ตอบจากมัน

ดังนั้นคำตอบที่ถูกต้องไม่ใช่ "ไม่" แต่ "ใช่ แต่" "แต่" เป็นชิ้นใหญ่: โดยทั่วไปคุณต้องเขียนส่วนสำคัญของเบราว์เซอร์ chrome (และเป็น devtools) ใน JavaScript JavaScript เป็นภาษาทัวริงที่สมบูรณ์ดังนั้นแน่นอนว่าเป็นไปได้ในหลักการ สิ่งที่เป็นไปไม่ได้กำลังทำสิ่งทั้งหมดโดยไม่ต้องใช้ซอร์สโค้ดทั้งหมดของคุณ (เป็นสตริง) จากนั้นทำสิ่งที่ซับซ้อนสูงด้วยสิ่งนั้น


3

หากคุณต้องการตรวจสอบตัวแปรด้วยตนเองเพื่อช่วยในการดีบักให้เริ่มการทำงานของดีบักเกอร์:

debugger;

ตรงเข้าไปในคอนโซลของเบราว์เซอร์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.