Bắt tất cả các biến trong phạm vi


194

Có cách nào để có được tất cả các biến hiện có trong phạm vi trong javascript không?


Trả lời câu trả lời của bạn cho Camsoft: Đó là một câu hỏi hoàn toàn khác; Tôi đã cập nhật câu trả lời của mình để giải quyết nó.
TJ Crowder

3
Đây chỉ là một câu hỏi chung chung, cụ thể hơn sẽ không giúp ích nhiều vì tôi đang làm việc với một API tối nghĩa với tài liệu kém.
Ian

Bạn có nghĩa là các biến toàn cầu! Bạn có thể hiển thị đếm được biến toàn cầu sử dụng for (v in this) alert(v);. Tuy nhiên, không phải tất cả các thế giới đều là vô số, và tôi biết không có cách tiêu chuẩn nào để có được một danh sách những cái không thể đếm được.
Jason Orendorff

2
@Jason - Không, câu hỏi đã rõ ràng. Bên trong một hàm các biến trong phạm vi sẽ bao gồm các biến toàn cầu, this, arguments, các thông số và tất cả các biến được định nghĩa trong phạm vi kèm theo.
Tim Down

2
Đây là lý do tại sao tôi nhớ các bảng biểu tượng của Perl. Bạn có kế hoạch nào để thêm phần này vào bản phát hành Javascript trong tương lai không?
Dexygen

Câu trả lời:


84

Không. Các biến "trong phạm vi" được xác định bởi "chuỗi phạm vi", không thể truy cập theo chương trình.

Để biết chi tiết (khá nhiều về nó), hãy xem đặc tả ECMAScript (JavaScript). Đây là một liên kết đến trang chính thức nơi bạn có thể tải xuống thông số chính tắc (PDF) và đây là một liên kết đến phiên bản HTML chính thức, có thể liên kết .

Cập nhật dựa trên nhận xét của bạn lên Camsoft

Các biến trong phạm vi cho hàm sự kiện của bạn được xác định theo nơi bạn xác định hàm sự kiện, chứ không phải cách chúng gọi nó. Tuy nhiên , bạn có thể tìm thấy thông tin hữu ích về những gì có sẵn cho chức năng của mình thông qua thisvà lập luận bằng cách thực hiện điều gì đó theo dòng của KennyTM đã chỉ ra ( for (var propName in ____)) vì điều đó sẽ cho bạn biết những gì có sẵn trên các đối tượng khác nhau được cung cấp cho bạn ( thisvà đối số; nếu bạn không chắc chắn những đối số họ đưa ra cho bạn, bạn có thể tìm hiểu thông qua argumentsbiến được định nghĩa ngầm cho mọi hàm).

Vì vậy, ngoài bất kỳ phạm vi nào vì bạn xác định chức năng của mình ở đâu, bạn có thể tìm hiểu những gì khác có sẵn bằng các phương tiện khác bằng cách thực hiện:

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]);
    }
}

(Bạn có thể mở rộng trên đó để có thêm thông tin hữu ích.)

Tuy nhiên, thay vào đó, tôi có thể sử dụng trình gỡ lỗi như công cụ phát triển của Chrome (ngay cả khi bạn không thường sử dụng Chrome để phát triển) hoặc Fireorms (ngay cả khi bạn không thường sử dụng Firefox để phát triển) hoặc Dragonfly trên Opera hoặc "Công cụ dành cho nhà phát triển F12" trên IE. Và đọc qua bất kỳ tệp JavaScript nào họ cung cấp cho bạn. Và đánh bại họ trên đầu cho các tài liệu thích hợp. :-)


Là một lưu ý phụ, Google Chrome có trình gỡ lỗi tuyệt vời có thể so sánh với Fireorms (cũng có thêm một chút đóng băng ở trên cùng).
Xoay

4
@Sweelsgames: Ồ, tôi rất thích Công cụ Dev của Chrome hơn Firefox + Fireorms. Họ chỉ không quay trở lại nhiều vào năm 2010 :-)
TJ Crowder

1
@tjcrowder Thật vậy! Tôi nhận thấy dấu thời gian trên câu trả lời là một lúc trước :)
Xoay

98

Mặc dù tất cả mọi người trả lời " Không " và tôi biết rằng "Không" là câu trả lời đúng nhưng nếu bạn thực sự cần lấy các biến cục bộ của hàm thì có một cách hạn chế.

Hãy xem xét chức năng này:

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

Bạn có thể chuyển đổi chức năng của mình thành một chuỗi:

var s = f + '';

Bạn sẽ nhận được nguồn của hàm như một chuỗi

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

Bây giờ bạn có thể sử dụng một trình phân tích cú pháp như esprima để phân tích mã chức năng và tìm các khai báo biến cục bộ.

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);

và tìm các đối tượng với:

obj.type == "VariableDeclaration"

trong kết quả (tôi đã xóa console.log(x)bên dưới):

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

Tôi đã thử nghiệm điều này trong Chrome, Firefox và Node.

Nhưng vấn đề với phương thức này là bạn chỉ có các biến được định nghĩa trong chính hàm đó. Ví dụ cho cái này:

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

bạn chỉ có quyền truy cập vào x và không y . Nhưng bạn vẫn có thể sử dụng chuỗi các trình gọi (argument.callee.caller.caller.caller) trong một vòng lặp để tìm các biến cục bộ của các hàm gọi. Nếu bạn có tất cả các tên biến cục bộ để bạn có các biến phạm vi . Với các tên biến bạn có quyền truy cập vào các giá trị với một eval đơn giản.


7
Kỹ thuật tuyệt vời ở đây để giải quyết vấn đề ban đầu. Xứng đáng là một upvote.
đào sâu

4
Các biến của một người gọi không nhất thiết là các biến phạm vi. Ngoài ra, các biến phạm vi không nhất thiết phải có trong chuỗi cuộc gọi. Các biến đã bị đóng có thể được tham chiếu bởi hàm đóng khi gọi nó từ một người gọi nằm ngoài phạm vi định nghĩa / đóng (và các biến không thể truy cập được từ việc đóng thân máy đóng).
DDS

Bạn có thể vui lòng giải thích "một eval đơn giản" rõ ràng? Tôi đã thử với mã dưới đây nhưng không thể có được biến bị mắc kẹt trong phạm vi. function getCounter(start){ return function(){ start ++; return start; } } var counter = getCounter(1); var startInClosure = eval.call(counter, 'this.start;'); console.log(startInClosure);Nó in undefinedtrong khi tôi mong đợi nó sẽ là 2.
Pylipala

@Pylipala Câu hỏi ở đây là về các biến trong phạm vi không nhận được giá trị của một biến cục bộ từ bên ngoài phạm vi. Điều đó là không thể, bởi vì đây là định nghĩa của biến cục bộ không thể truy cập được từ bên ngoài. Về mã của bạn, bạn có nghĩa là bối cảnh không phải phạm vi nhưng bạn đã không bắt đầu trong bối cảnh (cái này), vì vậy bạn không thể đọc nó với this.start . Xem tại đây: paste.ubfox.com/11560449
iman

@iman Cảm ơn câu trả lời của bạn. Tôi cũng đoán là không thể ở bên Javascript, vì vậy tôi đoán nếu có thể ở bên v8. Tôi tìm thấy bên dưới liên kết câu hỏi mô tả chính xác vấn đề của tôi. Trong đó Lasse Reichstein nói không nhưng mako-taco nói có. Tôi đã thử một số mã C ++ dưới dạng liên kết nhưng không biết cách viết nó.
Pylipala

32

Có và không. "Không" trong hầu hết mọi tình huống. "Có", nhưng chỉ trong một cách hạn chế, nếu bạn muốn kiểm tra phạm vi toàn cầu. Lấy ví dụ sau:

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

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

Đầu ra nào, trong số hơn 150 thứ khác , như sau:

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

Vì vậy, có thể liệt kê một số biến trong phạm vi hiện tại, nhưng nó không đáng tin cậy, cô đọng, hiệu quả hoặc dễ dàng truy cập.

Một câu hỏi tốt hơn là tại sao bạn muốn biết những biến nào trong phạm vi?


2
Tôi nghĩ rằng câu hỏi chung chung hơn và không bị giới hạn đối với javascript trong ngữ cảnh của trình duyệt web.
Radu Simionescu

Chỉ cần thay đổi từ "cửa sổ" thành "toàn cầu" nếu bạn đang sử dụng nút ... hoặc bạn có thể sử dụng từ "này" nhưng điều đó bị cấm theo "sử dụng nghiêm ngặt"
Ivan Castellanos

Tôi đã thêm một dòng kiểm tra hasOwnProperty. Ví dụ : for ( var i in window ) { if (window.hasOwnProperty(i)) { console.log(i, window[i]); }}. Điều này ít nhất sẽ cắt giảm các thuộc tính được kế thừa và các biến của bạn sẽ chỉ nằm trong khoảng 50 thuộc tính khác.
timctran

8
Tại sao ít nhất ai đó không muốn kiểm tra các biến trong phạm vi, trong quá trình gỡ lỗi và / hoặc mục đích phát triển.
Dexygen

Một lý do khác để muốn biết những biến nào trong phạm vi cục bộ là để tuần tự hóa một bao đóng.
Michael

29

Trong ECMAScript 6, ít nhiều có thể bằng cách gói mã bên trong một withcâu lệnh với một đối tượng proxy. Lưu ý rằng nó yêu cầu chế độ không nghiêm ngặt và đó là thực hành xấu.

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);

Proxy tuyên bố sở hữu tất cả các mã định danh được tham chiếu bên trong with, vì vậy các phép gán biến được lưu trữ trong mục tiêu. Đối với tra cứu, proxy lấy giá trị từ mục tiêu proxy hoặc đối tượng toàn cầu (không phải phạm vi cha). letconstcác biến không được bao gồm.

Lấy cảm hứng từ câu trả lời này của Bergi .


1
Đây là thành công đáng ngạc nhiên cho một kỹ thuật đơn giản như vậy.
Jonathan Eunice

WOW siêu năng lực
pery mimon

16

Bạn không thể.

Các biến, định danh của khai báo hàm và đối số cho mã hàm, bị ràng buộc là các thuộc tính của Đối tượng biến , không thể truy cập được.

Xem thêm:


1
Đúng, nhưng các biến trong phạm vi không chỉ vượt qua đối tượng biến hiện tại. Các biến trong phạm vi được xác định bởi chuỗi phạm vi , là một chuỗi bao gồm (trong trường hợp bình thường) của một loạt các đối tượng biến và kết thúc với đối tượng toàn cầu (mặc dù withcâu lệnh có thể được sử dụng để chèn các đối tượng khác trong chuỗi).
TJ Crowder

+1 cho các liên kết đến bclary.com. Một phiên bản HTML của thông số cập nhật sẽ xuất hiện trong vài tháng tới trên trang web ECMA, tôi rất vui mừng nói.
TJ Crowder

3
Chỉ cần một lưu ý, cả hai liên kết đều bị hỏng.
0xc0de

bạn CÓ THỂ - với phân tích tĩnh jsfiddle.net/mathheadinclouds/bvx1hpfn/11
mathheadinclouds

8

Cách đơn giản nhất để có quyền truy cập vào Vars trong một phạm vi cụ thể

  1. Mở Công cụ dành cho nhà phát triển> Tài nguyên (trong Chrome)
  2. Mở tệp có chức năng có quyền truy cập vào phạm vi đó (mẹo cmd / ctrl + p để tìm tệp)
  3. Đặt điểm dừng bên trong chức năng đó và chạy mã của bạn
  4. Khi nó dừng ở điểm dừng của bạn, bạn có thể truy cập var var thông qua giao diện điều khiển (hoặc cửa sổ var var)

Lưu ý: Bạn muốn làm điều này chống lại js chưa được khai thác.

Cách đơn giản nhất để hiển thị tất cả các Vars không riêng tư

  1. Bảng điều khiển mở (trong Chrome)
  2. Gõ: this.window
  3. Nhấn Enter

Bây giờ bạn sẽ thấy một cây đối tượng bạn có thể mở rộng với tất cả các đối tượng được khai báo.


7

Như mọi người nhận thấy: bạn không thể. Nhưng bạn có thể tạo một obj và gán mọi var bạn khai báo cho obj đó. Bằng cách đó bạn có thể dễ dàng kiểm tra các lọ của bạn:

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 cảm ơn vì ví dụ tuyệt vời, chỉ để hoàn thành điều tương tự cũng có thể được nhìn thấy qua console.log(window); jsfiddle.net/4x3Tx
Stano

5

Tôi đã thực hiện một fiddle thực hiện (về cơ bản) trên các ý tưởng được phác thảo bởi iman. Đây là giao diện của nó khi bạn di chuột qua ipsum thứ hai trongreturn ipsum*ipsum - ...

nhập mô tả hình ảnh ở đây

Các biến trong phạm vi được tô sáng nơi chúng được khai báo (với các màu khác nhau cho các phạm vi khác nhau). Cáclorem viền màu đỏ là một biến bóng (không thuộc phạm vi, nhưng nằm trong phạm vi nếu lorem khác ở dưới cây sẽ không ở đó.)

Tôi đang sử dụng thư viện esprima để phân tích JavaScript và estraverse, escodegen, escope (thư viện tiện ích trên đỉnh esprima.)

Làm thế nào nó hoạt động

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

làm cho cây cú pháp trừu tượng. Sau đó,

analysis = escope.analyze(ast);

tạo ra một cấu trúc dữ liệu phức tạp đóng gói thông tin về tất cả các phạm vi trong chương trình. Phần còn lại đang tập hợp các thông tin được mã hóa trong đối tượng phân tích đó (và chính cây cú pháp trừu tượng) và tạo ra một sơ đồ màu tương tác từ nó.

Vì vậy, câu trả lời đúng thực sự không phải là "không", mà là "có, nhưng". "Nhưng" là một cái lớn: về cơ bản, bạn phải viết lại các phần quan trọng của trình duyệt chrome (và đó là devtools) trong JavaScript. JavaScript là một ngôn ngữ hoàn chỉnh Turing, vì vậy về nguyên tắc là có thể. Điều không thể là làm toàn bộ mà không sử dụng toàn bộ mã nguồn của bạn (dưới dạng chuỗi) và sau đó thực hiện các công cụ rất phức tạp với điều đó.


3

Nếu bạn chỉ muốn kiểm tra các biến theo cách thủ công để giúp gỡ lỗi, chỉ cần kích hoạt trình gỡ lỗi:

debugger;

Đi thẳng vào bảng điều khiển trình duyệt.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.