function main()
{
Hello();
}
function Hello()
{
// How do you find out the caller function is 'main'?
}
Có cách nào để tìm ra ngăn xếp cuộc gọi?
function main()
{
Hello();
}
function Hello()
{
// How do you find out the caller function is 'main'?
}
Có cách nào để tìm ra ngăn xếp cuộc gọi?
Câu trả lời:
function Hello()
{
alert("caller is " + Hello.caller);
}
Lưu ý rằng tính năng này không chuẩn , từ Function.caller
:
Phi tiêu chuẩn
Tính năng này không chuẩn và không theo dõi tiêu chuẩn. Không sử dụng nó trên các trang web sản xuất phải đối mặt với Web: nó sẽ không hoạt động cho mọi người dùng. Cũng có thể có sự không tương thích lớn giữa việc thực hiện và hành vi có thể thay đổi trong tương lai.
Sau đây là câu trả lời cũ từ năm 2008, không còn được hỗ trợ trong Javascript hiện đại:
function Hello()
{
alert("caller is " + arguments.callee.caller.toString());
}
arguments.callee.caller.name
sẽ nhận được tên của chức năng.
'use strict';
có thể giúp đỡ.
arguments
CÓ THỂ được truy cập từ bên trong một chức năng trong chế độ nghiêm ngặt, sẽ thật ngu ngốc nếu không tán thành điều đó. chỉ không từ function.argument từ bên ngoài. Ngoài ra, nếu bạn có một đối số được đặt tên, dạng đối số [i] của nó sẽ không theo dõi các thay đổi bạn thực hiện đối với phiên bản được đặt tên bên trong hàm.
Bạn có thể tìm thấy toàn bộ dấu vết ngăn xếp bằng mã cụ thể của trình duyệt. Điều tốt là ai đó đã thực hiện nó ; đây là mã dự án trên GitHub .
Nhưng không phải tất cả các tin tức đều tốt:
Thực sự rất chậm để có được dấu vết ngăn xếp vì vậy hãy cẩn thận (đọc phần này để biết thêm).
Bạn sẽ cần xác định tên hàm cho dấu vết ngăn xếp dễ đọc. Bởi vì nếu bạn có mã như thế này:
var Klass = function kls() {
this.Hello = function() { alert(printStackTrace().join('\n\n')); };
}
new Klass().Hello();
Google Chrome sẽ cảnh báo ... kls.Hello ( ...
nhưng hầu hết các trình duyệt sẽ mong đợi một tên hàm ngay sau từ khóa function
và sẽ coi nó là một hàm ẩn danh. Thậm chí Chrome sẽ không thể sử dụng Klass
tên nếu bạn không đặt tên kls
cho chức năng.
Và nhân tiện, bạn có thể chuyển đến hàm printStackTrace tùy chọn {guess: true}
nhưng tôi không tìm thấy bất kỳ cải tiến thực sự nào bằng cách làm điều đó.
Không phải tất cả các trình duyệt cung cấp cho bạn cùng một thông tin. Đó là, tham số, cột mã, v.v.
Nhân tiện, nếu bạn chỉ muốn tên của chức năng người gọi (trong hầu hết các trình duyệt, nhưng không phải IE), bạn có thể sử dụng:
arguments.callee.caller.name
Nhưng lưu ý rằng tên này sẽ là tên sau function
từ khóa. Tôi không tìm thấy cách nào (ngay cả trên Google Chrome) để nhận được nhiều hơn thế mà không nhận được mã của toàn bộ chức năng.
Và tóm tắt phần còn lại của các câu trả lời hay nhất (của Pablo Cabrera, nourdine và Greg Hewgill). Điều duy nhất trên trình duyệt chéo và thực sự an toàn bạn có thể sử dụng là:
arguments.callee.caller.toString();
Mà sẽ hiển thị mã của chức năng người gọi. Đáng buồn thay, điều đó là không đủ đối với tôi và đó là lý do tại sao tôi cung cấp cho bạn các mẹo cho StackTrace và tên người gọi chức năng (mặc dù chúng không phải là trình duyệt chéo).
Function.caller
mỗi @ câu trả lời của Greg
Function.caller
Tuy nhiên, sẽ không làm việc trong chế độ nghiêm ngặt.
Tôi biết bạn đã đề cập "trong Javascript", nhưng nếu mục đích là gỡ lỗi, tôi nghĩ việc sử dụng các công cụ phát triển trình duyệt của bạn sẽ dễ dàng hơn. Đây là giao diện của Chrome: Chỉ cần thả trình gỡ lỗi nơi bạn muốn điều tra ngăn xếp.
Để tóm tắt (và làm cho nó rõ ràng hơn) ...
mã này:
function Hello() {
alert("caller is " + arguments.callee.caller.toString());
}
tương đương với điều này:
function Hello() {
alert("caller is " + Hello.caller.toString());
}
Rõ ràng bit đầu tiên dễ mang theo hơn, vì bạn có thể thay đổi tên của hàm, nói từ "Xin chào" thành "Ciao", và vẫn có thể làm cho toàn bộ hoạt động.
Trong trường hợp sau, trong trường hợp bạn quyết định cấu trúc lại tên của hàm được gọi (Xin chào), bạn sẽ phải thay đổi tất cả các lần xuất hiện của nó :(
Bạn có thể nhận được stacktrace đầy đủ:
arguments.callee.caller
arguments.callee.caller.caller
arguments.callee.caller.caller.caller
Cho đến khi người gọi là null
.
Lưu ý: nó gây ra một vòng lặp vô hạn trên các hàm đệ quy.
Tôi thường sử dụng (new Error()).stack
trong Chrome. Điều tuyệt vời là điều này cũng cung cấp cho bạn các số dòng nơi người gọi gọi hàm. Nhược điểm là nó giới hạn độ dài của ngăn xếp xuống còn 10, đó là lý do tại sao tôi đến trang này ngay từ đầu.
.
'use strict';
có mặt. Đã cho tôi thông tin tôi cần - cảm ơn!
Nếu bạn không chạy nó trong IE <11 thì console.trace () sẽ phù hợp.
function main() {
Hello();
}
function Hello() {
console.trace()
}
main()
// Hello @ VM261:9
// main @ VM261:4
Bạn có thể sử dụng Function.Caller để nhận chức năng gọi điện. Phương thức cũ sử dụng argument.caller được coi là lỗi thời.
Đoạn mã sau minh họa việc sử dụng nó:
function Hello() { return Hello.caller;}
Hello2 = function NamedFunc() { return NamedFunc.caller; };
function main()
{
Hello(); //both return main()
Hello2();
}
Ghi chú về đối số lỗi thời.caller: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Fiances/argument/caller
Lưu ý rằng Function.caller không chuẩn: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller
Cannot access caller property of a strict mode function
Tôi sẽ làm điều này:
function Hello() {
console.trace();
}
function Hello() {
alert(Hello.caller);
}
arguments.callee.caller.toString()
Đó là an toàn hơn để sử dụng *arguments.callee.caller
kể từ khi arguments.caller
được tán thành ...
arguments.callee
cũng không được chấp nhận trong ES5 và bị xóa trong chế độ nghiêm ngặt.
arguments.callee
là một giải pháp tồi cho một vấn đề hiện đã được nhà phát triển
Có vẻ như đây là một câu hỏi đã được giải quyết nhưng gần đây tôi phát hiện ra rằng callee không được phép ở 'chế độ nghiêm ngặt', vì vậy với mục đích sử dụng của riêng tôi, tôi đã viết một lớp sẽ lấy đường dẫn từ nơi nó được gọi. Đó là một phần của lib trợ giúp nhỏ và nếu bạn muốn sử dụng mã độc lập, hãy thay đổi phần bù được sử dụng để trả về dấu vết ngăn xếp của người gọi (sử dụng 1 thay vì 2)
function ScriptPath() {
var scriptPath = '';
try {
//Throw an error to generate a stack trace
throw new Error();
}
catch(e) {
//Split the stack trace into each line
var stackLines = e.stack.split('\n');
var callerIndex = 0;
//Now walk though each line until we find a path reference
for(var i in stackLines){
if(!stackLines[i].match(/http[s]?:\/\//)) continue;
//We skipped all the lines with out an http so we now have a script reference
//This one is the class constructor, the next is the getScriptPath() call
//The one after that is the user code requesting the path info (so offset by 2)
callerIndex = Number(i) + 2;
break;
}
//Now parse the string for each section we want to return
pathParts = stackLines[callerIndex].match(/((http[s]?:\/\/.+\/)([^\/]+\.js)):/);
}
this.fullPath = function() {
return pathParts[1];
};
this.path = function() {
return pathParts[2];
};
this.file = function() {
return pathParts[3];
};
this.fileNoExt = function() {
var parts = this.file().split('.');
parts.length = parts.length != 1 ? parts.length - 1 : 1;
return parts.join('.');
};
}
function a(){ function b(){ function c(){ return ScriptPath(); } return c(); } return b(); } a()
trong bảng điều khiển (chưa thử trong tệp), nhưng dường như có một ý tưởng hợp lý. Dù sao cũng nên nâng cấp cho tầm nhìn.
Hãy thử truy cập này:
arguments.callee.caller.name
Chỉ cần bàn điều khiển đăng nhập ngăn xếp lỗi của bạn. Sau đó bạn có thể biết bạn được gọi như thế nào
const hello = () => {
console.log(new Error('I was called').stack)
}
const sello = () => {
hello()
}
sello()
Trong cả chế độ ES6 và Strict, hãy sử dụng cách sau để nhận chức năng Người gọi
console.log((new Error()).stack.split("\n")[2].trim().split(" ")[1])
Xin lưu ý rằng, dòng trên sẽ ném một ngoại lệ, nếu không có người gọi hoặc không có ngăn xếp trước đó. Sử dụng cho phù hợp.
console.log((new Error()).stack.split("\n")[1].trim().split(" ")[1])
caller
bị cấm trong chế độ nghiêm ngặt . Đây là một thay thế bằng cách sử dụng Error
ngăn xếp (không chuẩn) .
Chức năng sau đây dường như thực hiện công việc trong Firefox 52 và Chrome 61-71 mặc dù việc triển khai của nó đưa ra rất nhiều giả định về định dạng ghi nhật ký của hai trình duyệt và nên được sử dụng một cách thận trọng, vì nó ném ngoại lệ và có thể thực thi hai regex trận đấu trước khi được thực hiện.
'use strict';
const fnNameMatcher = /([^(]+)@|at ([^(]+) \(/;
function fnName(str) {
const regexResult = fnNameMatcher.exec(str);
return regexResult[1] || regexResult[2];
}
function log(...messages) {
const logLines = (new Error().stack).split('\n');
const callerName = fnName(logLines[1]);
if (callerName !== null) {
if (callerName !== 'log') {
console.log(callerName, 'called log with:', ...messages);
} else {
console.log(fnName(logLines[2]), 'called log with:', ...messages);
}
} else {
console.log(...messages);
}
}
function foo() {
log('hi', 'there');
}
(function main() {
foo();
}());
Tôi muốn thêm fiddle của tôi ở đây cho điều này:
http://jsfiddle.net/bladnman/EhUm3/
Tôi đã thử nghiệm đây là chrome, safari và IE (10 và 8). Hoạt động tốt. Chỉ có 1 chức năng quan trọng, vì vậy nếu bạn cảm thấy sợ hãi bởi câu đố lớn, hãy đọc phần bên dưới.
Lưu ý: Có một số lượng khá lớn "nồi hơi" của riêng tôi trong câu đố này. Bạn có thể loại bỏ tất cả những thứ đó và sử dụng split nếu bạn muốn. Đây chỉ là một bộ chức năng cực kỳ an toàn mà tôi tin tưởng.
Ngoài ra còn có một mẫu "JSFiddle" trong đó tôi sử dụng cho nhiều câu đố để đơn giản là nhanh chóng.
String.prototype.trim = trim;
Nếu bạn chỉ muốn tên hàm chứ không phải mã và muốn một giải pháp độc lập với trình duyệt, hãy sử dụng như sau:
var callerFunction = arguments.callee.caller.toString().match(/function ([^\(]+)/)[1];
Lưu ý rằng ở trên sẽ trả về một lỗi nếu không có chức năng người gọi vì không có phần tử [1] trong mảng. Để làm việc xung quanh, sử dụng như sau:
var callerFunction = (arguments.callee.caller.toString().match(/function ([^\(]+)/) === null) ? 'Document Object Model': arguments.callee.caller.toString().match(/function ([^\(]+)/)[1], arguments.callee.toString().match(/function ([^\(]+)/)[1]);
Chỉ muốn cho bạn biết rằng trên PhoneGap / Android các name
doesnt dường như làm việc. Nhưng arguments.callee.caller.toString()
sẽ làm được mẹo.
Ở đây, mọi thứ trừ cái functionname
bị tước khỏi caller.toString()
, với RegExp.
<!DOCTYPE html>
<meta charset="UTF-8">
<title>Show the callers name</title><!-- This validates as html5! -->
<script>
main();
function main() { Hello(); }
function Hello(){
var name = Hello.caller.toString().replace(/\s\([^#]+$|^[^\s]+\s/g,'');
name = name.replace(/\s/g,'');
if ( typeof window[name] !== 'function' )
alert ("sorry, the type of "+name+" is "+ typeof window[name]);
else
alert ("The name of the "+typeof window[name]+" that called is "+name);
}
</script>
Đây là một chức năng để có được stacktrace đầy đủ :
function stacktrace() {
var f = stacktrace;
var stack = 'Stack trace:';
while (f) {
stack += '\n' + f.name;
f = f.caller;
}
return stack;
}
Câu trả lời của heystewart và câu trả lời của JiarongWu đều đề cập rằng Error
đối tượng có quyền truy cập vào stack
.
Đây là một ví dụ:
function main() {
Hello();
}
function Hello() {
var stack;
try {
throw new Error();
} catch (e) {
stack = e.stack;
}
// N.B. stack === "Error\n at Hello ...\n at main ... \n...."
var m = stack.match(/.*?Hello.*?\n(.*?)\n/);
if (m) {
var caller_name = m[1];
console.log("Caller is:", caller_name)
}
}
main();
Các trình duyệt khác nhau hiển thị ngăn xếp trong các định dạng chuỗi khác nhau:
Safari : Caller is: main@https://stacksnippets.net/js:14:8
Firefox : Caller is: main@https://stacksnippets.net/js:14:3
Chrome : Caller is: at main (https://stacksnippets.net/js:14:3)
IE Edge : Caller is: at main (https://stacksnippets.net/js:14:3)
IE : Caller is: at main (https://stacksnippets.net/js:14:3)
Hầu hết các trình duyệt sẽ thiết lập ngăn xếp với var stack = (new Error()).stack
. Trong Internet Explorer, ngăn xếp sẽ không được xác định - bạn phải đưa ra một ngoại lệ thực sự để truy xuất ngăn xếp.
Kết luận: Có thể xác định "chính" là người gọi "Xin chào" bằng cách sử dụng stack
trong Error
đối tượng. Trong thực tế, nó sẽ hoạt động trong trường hợp callee
/ caller
phương pháp không hoạt động. Nó cũng sẽ hiển thị cho bạn ngữ cảnh, tức là tệp nguồn và số dòng. Tuy nhiên, nỗ lực là cần thiết để làm cho giải pháp đa nền tảng.
Lưu ý rằng bạn không thể sử dụng Function.caller trong Node.js, thay vào đó hãy sử dụng gói id người gọi . Ví dụ:
var callerId = require('caller-id');
function foo() {
bar();
}
function bar() {
var caller = callerId.getData();
/*
caller = {
typeName: 'Object',
functionName: 'foo',
filePath: '/path/of/this/file.js',
lineNumber: 5,
topLevelFlag: true,
nativeFlag: false,
evalFlag: false
}
*/
}
Hãy thử đoạn mã sau:
function getStackTrace(){
var f = arguments.callee;
var ret = [];
var item = {};
var iter = 0;
while ( f = f.caller ){
// Initialize
item = {
name: f.name || null,
args: [], // Empty array = no arguments passed
callback: f
};
// Function arguments
if ( f.arguments ){
for ( iter = 0; iter<f.arguments.length; iter++ ){
item.args[iter] = f.arguments[iter];
}
} else {
item.args = null; // null = argument listing not supported
}
ret.push( item );
}
return ret;
}
Làm việc cho tôi trong Firefox-21 và Chromium-25.
arguments.callee
đã bị phản đối trong nhiều năm .
Một cách khác để giải quyết vấn đề này là chỉ cần truyền tên của hàm gọi làm tham số.
Ví dụ:
function reformatString(string, callerName) {
if (callerName === "uid") {
string = string.toUpperCase();
}
return string;
}
Bây giờ, bạn có thể gọi hàm như thế này:
function uid(){
var myString = "apples";
reformatString(myString, function.name);
}
Ví dụ của tôi sử dụng kiểm tra mã hóa cứng của tên hàm, nhưng bạn có thể dễ dàng sử dụng câu lệnh chuyển đổi hoặc một số logic khác để làm những gì bạn muốn ở đó.
Theo như tôi biết, chúng tôi có 2 cách cho việc này từ các nguồn nhất định như thế này-
function whoCalled()
{
if (arguments.caller == null)
console.log('I was called from the global scope.');
else
console.log(arguments.caller + ' called me!');
}
function myFunc()
{
if (myFunc.caller == null) {
return 'The function was called from the top!';
}
else
{
return 'This function\'s caller was ' + myFunc.caller;
}
}
Hãy nghĩ rằng bạn có câu trả lời của bạn :).
Tại sao tất cả các giải pháp trên trông giống như một khoa học tên lửa. Trong khi đó, nó không nên phức tạp hơn đoạn trích này. Tất cả các khoản tín dụng cho anh chàng này
Làm thế nào để bạn tìm ra chức năng người gọi trong JavaScript?
var stackTrace = function() {
var calls = [];
var caller = arguments.callee.caller;
for (var k = 0; k < 10; k++) {
if (caller) {
calls.push(caller);
caller = caller.caller;
}
}
return calls;
};
// when I call this inside specific method I see list of references to source method, obviously, I can add toString() to each call to see only function's content
// [function(), function(data), function(res), function(l), function(a, c), x(a, b, c, d), function(c, e)]
Tôi đang cố gắng giải quyết cả câu hỏi và tiền thưởng hiện tại với câu hỏi này.
Tiền thưởng yêu cầu người gọi phải có được ở chế độ nghiêm ngặt và cách duy nhất tôi có thể thấy điều này được thực hiện là bằng cách tham khảo một chức năng được khai báo bên ngoài chế độ nghiêm ngặt.
Ví dụ: những điều sau đây là không chuẩn nhưng đã được thử nghiệm với các phiên bản trước đó (29/03/2016) và hiện tại (ngày 1 tháng 8 năm 2018) của Chrome, Edge và Firefox.
function caller()
{
return caller.caller.caller;
}
'use strict';
function main()
{
// Original question:
Hello();
// Bounty question:
(function() { console.log('Anonymous function called by ' + caller().name); })();
}
function Hello()
{
// How do you find out the caller function is 'main'?
console.log('Hello called by ' + caller().name);
}
main();
Nếu bạn thực sự cần chức năng vì một số lý do và muốn nó tương thích với nhiều trình duyệt và không phải lo lắng về những thứ nghiêm ngặt và tương thích về phía trước thì hãy chuyển qua tham chiếu này:
function main()
{
Hello(this);
}
function Hello(caller)
{
// caller will be the object that called Hello. boom like that...
// you can add an undefined check code if the function Hello
// will be called without parameters from somewhere else
}
Tôi nghĩ rằng đoạn mã sau đây có thể hữu ích:
window.fnPureLog = function(sStatement, anyVariable) {
if (arguments.length < 1) {
throw new Error('Arguments sStatement and anyVariable are expected');
}
if (typeof sStatement !== 'string') {
throw new Error('The type of sStatement is not match, please use string');
}
var oCallStackTrack = new Error();
console.log(oCallStackTrack.stack.replace('Error', 'Call Stack:'), '\n' + sStatement + ':', anyVariable);
}
Thực thi mã:
window.fnPureLog = function(sStatement, anyVariable) {
if (arguments.length < 1) {
throw new Error('Arguments sStatement and anyVariable are expected');
}
if (typeof sStatement !== 'string') {
throw new Error('The type of sStatement is not match, please use string');
}
var oCallStackTrack = new Error();
console.log(oCallStackTrack.stack.replace('Error', 'Call Stack:'), '\n' + sStatement + ':', anyVariable);
}
function fnBsnCallStack1() {
fnPureLog('Stock Count', 100)
}
function fnBsnCallStack2() {
fnBsnCallStack1()
}
fnBsnCallStack2();
Nhật ký trông như thế này:
Call Stack:
at window.fnPureLog (<anonymous>:8:27)
at fnBsnCallStack1 (<anonymous>:13:5)
at fnBsnCallStack2 (<anonymous>:17:5)
at <anonymous>:20:1
Stock Count: 100