Làm thế nào để có được tên hàm từ bên trong hàm đó?


263

Làm thế nào tôi có thể truy cập một tên hàm từ bên trong chức năng đó?

// parasitic inheritance
var ns.parent.child = function() {
  var parent = new ns.parent();
  parent.newFunc = function() {

  }
  return parent;
}

var ns.parent = function() {
  // at this point, i want to know who the child is that called the parent
  // ie
}

var obj = new ns.parent.child();

tốt, trong phần cha mẹ, sau đó tôi có thể truy cập các hàm khác theo quy ước, chẳng hạn như ns [con] [lược đồ] hoặc ns [con] [dbService]. Không có nó, tôi phải mã cứng các tài liệu tham khảo này trong mỗi lớp con.
Scott Klarenbach

Tại sao không chỉ truyền hàm con làm đối số cho cha mẹ? var cha = new ns.parent (cái này);
plodder

bởi vì có hàng tá những cái nhìn như vậy, và hàng tá trẻ em Đó là những gì tôi đang làm, nhưng nó vẫn giống nhau mọi lúc và sẽ hoàn hảo nếu logic trùng lặp đó có thể được đặt bên trong cha mẹ một lần, dựa trên hàm dẫn xuất.
Scott Klarenbach

xem, đó không phải là chức năng con tôi muốn, đó là quy ước đặt tên được sử dụng, bởi vì quy ước đặt tên đó có thể được sử dụng để tải các chức năng khác hiện không được xác định trên đối tượng con, nhưng có liên quan đến đứa trẻ đó trong toàn hệ thống.
Scott Klarenbach

1
@Scott Tôi đồng ý với mọi người khác, việc bạn quản lý sự phức tạp và cấu trúc mã của bạn sai khi cần phải làm điều này. Loại khớp nối cứng này là một quyết định thiết kế tồi và sẽ làm cho một mớ hỗn độn. @SimeVidas bạn là một thầy chiêu hồn tuyệt vời :)
Raynos

Câu trả lời:


163

Trong ES5, điều tốt nhất để làm là:

function functionName(fun) {
  var ret = fun.toString();
  ret = ret.substr('function '.length);
  ret = ret.substr(0, ret.indexOf('('));
  return ret;
}

Sử dụng Function.callerlà không chuẩn. Function.callerarguments.calleeđều bị cấm trong chế độ nghiêm ngặt.

Chỉnh sửa: câu trả lời dựa trên regex của nus dưới đây đạt được điều tương tự, nhưng có hiệu suất tốt hơn!

Trong ES6, bạn chỉ có thể sử dụng myFunction.name.

Lưu ý: Coi chừng một số trình khai thác JS có thể loại bỏ các tên hàm, để nén tốt hơn; bạn có thể cần phải điều chỉnh cài đặt của chúng để tránh điều đó.


Mặc dù điều này không trả lời đủ câu hỏi cho tôi (do các hàm ẩn danh), nhưng nó đã cho tôi ý tưởng để làm điều này: fun.toString().substr(0, 100)vì nhu cầu của tôi sẽ đủ để xác định chức năng trong câu hỏi. Vì vậy, cảm ơn cho cảm hứng!
Campbeln 7/07/2015

3
Yup myFunction.namelà điều tốt nhất để làm trong ES6.
Vlad A Ionescu

15
Điểm trong myFunction.namenếu bạn nhập tên hàm là gì? đánh bại mục đích của các biến số ma thuật.
adi518

2
@ adi518: không nhất thiết .. giả sử bạn có bất kỳ mảng chức năng nào và bạn lặp lại nó, sử dụng cho / foreach .. arr[index].namecũng sẽ hoạt động :)
Debasish Chowdhury

2
@ adi518 Nó không vô dụng. Nếu tôi đổi tên chức năng trong IDE của mình, myFunction.namecũng sẽ được cập nhật. Chắc chắn, intelliJ hỗ trợ đổi tên định danh trong chuỗi, nhưng hoạt động ít ổn định hơn và sẽ âm thầm trở nên lỗi thời nếu ai đó quên đánh dấu vào tùy chọn "tìm kiếm trong chuỗi". myFunction.namelà một lựa chọn tốt hơn một chút so với mã hóa chuỗi.
byxor

141

ES6 (lấy cảm hứng từ câu trả lời của sendy halim bên dưới):

myFunction.name

Giải thích về MDN . Kể từ năm 2015 hoạt động trong nodejs và tất cả các trình duyệt chính trừ IE.

Lưu ý: Trên các hàm bị ràng buộc, điều này sẽ cho " bound <originalName>". Bạn sẽ phải loại bỏ "ràng buộc" nếu bạn muốn có được tên gốc.


ES5 (lấy cảm hứng từ câu trả lời của Vlad):

Nếu bạn có một tham chiếu đến hàm, bạn có thể làm:

function functionName( func )
{
    // Match:
    // - ^          the beginning of the string
    // - function   the word 'function'
    // - \s+        at least some white space
    // - ([\w\$]+)  capture one or more valid JavaScript identifier characters
    // - \s*        optionally followed by white space (in theory there won't be any here,
    //              so if performance is an issue this can be omitted[1]
    // - \(         followed by an opening brace
    //
    var result = /^function\s+([\w\$]+)\s*\(/.exec( func.toString() )

    return  result  ?  result[ 1 ]  :  '' // for an anonymous function there won't be a match
}
  • Tôi đã không chạy thử nghiệm đơn vị về điều này, hoặc xác minh sự khác biệt thực hiện, nhưng về nguyên tắc nó sẽ hoạt động, nếu không để lại nhận xét.
  • Lưu ý: sẽ không hoạt động trên các chức năng bị ràng buộc
  • Lưu ý: điều đó callercalleeđược coi là không dùng nữa.

[1] Tôi bao gồm nó ở đây vì nó hợp pháp và thường là đủ các công cụ tô sáng cú pháp không tính đến khoảng trắng giữa tên hàm và dấu ngoặc đơn. Mặt khác, tôi không biết về bất kỳ triển khai nào .toString () sẽ bao gồm khoảng trắng ở đây, vì vậy đó là lý do tại sao bạn có thể bỏ qua nó.


Để trả lời cho câu hỏi ban đầu, tôi sẽ bỏ thừa kế ký sinh và đi tìm một số mẫu thiết kế OOP truyền thống hơn. Tôi đã viết một TidBits.OoJs để thoải mái viết mã OOP bằng JavaScript với một bộ tính năng bắt chước C ++ (chưa hoàn thành, nhưng chủ yếu).

Tôi thấy từ các ý kiến ​​mà bạn muốn tránh truyền parentnhu cầu thông tin cho nhà xây dựng của nó. Tôi phải thừa nhận rằng các mẫu thiết kế truyền thống sẽ không cứu bạn khỏi đó, vì nó thường được coi là một điều tốt để làm cho sự phụ thuộc của bạn trở nên rõ ràng và được thi hành.

Tôi cũng sẽ đề nghị để tránh xa các chức năng ẩn danh. Họ chỉ thực hiện gỡ lỗi và định hình Pita vì mọi thứ chỉ hiển thị là "chức năng ẩn danh" và không có lợi ích gì cho họ mà tôi biết.


3
Đẹp RegEx! Đây là một bài kiểm tra hiệu năng cho thấy mã của bạn là nhanh nhất trong nhiều trường hợp. Nhìn chung, có vẻ như RegEx đánh bại trung bình JS với tốc độ gấp khoảng 2 lần ở đây. jsperf.com/get-feft-name/2
Beejor

@Tomasz Xin chào, cảm ơn bạn đã chỉ ra điều đó. Tôi không biết điều đó. Một hàm ràng buộc thực sự là một hàm mới bao bọc hàm ban đầu của bạn. Tiêu chuẩn ecmascript § 19.2.3.2 xác định rằng tên của hàm mới sẽ là "ràng buộc" + gốcName. Phương thức toString sẽ không hoạt động trên các hàm bị ràng buộc ... Bạn sẽ phải loại bỏ từ bị ràng buộc.

Điểm trong myFunction.name là gì nếu bạn nhập tên hàm? đánh bại mục đích của các biến số ma thuật.
Borjovsky

Lưu ý rằng nếu bạn đang sử dụng React Native thì điều này có thể không hoạt động đúng. Tôi có một dự án RN mà tôi đang làm việc với phiên bản expo 36 và thuộc tính của một phương thức thành phần .nameđược hiển thị dưới dạng chuỗi "giá trị", bất kể nó được gọi là gì. Thật kỳ lạ, trong khi thử nghiệm trong ứng dụng hội chợ, nó vẫn ổn, tuy nhiên, một khi được biên dịch thành một ứng dụng thực sự, nó sẽ bị sập một cách im lặng.
Andy Corman

64

những gì bạn đang làm là gán hàm không tên cho một biến. thay vào đó, bạn có thể cần biểu thức hàm được đặt tên ( http: // Khangax.github.com/nfe/ ).

var x = function x() {
    console.log( arguments.callee.name );
}
x();

tuy nhiên tôi không chắc có bao nhiêu trình duyệt chéo; có một vấn đề với IE6 khiến cho chức năng của bạn bị rò rỉ ra phạm vi bên ngoài. Ngoài ra, argument.callee là loại không dùng nữa và sẽ dẫn đến lỗi nếu bạn đang sử dụng strict mode.


1
Điều này không hoạt động trên các phiên bản cũ của trình duyệt Safari.
Anubhav Gupta

17

Bất kỳ constructorphơi bày một tài sản name, đó là tên chức năng. Bạn truy cập constructorthông qua một thể hiện (sử dụng new) hoặc a prototype:

function Person() {
  console.log(this.constructor.name); //Person
}

var p = new Person();
console.log(p.constructor.name); //Person

console.log(Person.prototype.constructor.name);  //Person

6
Nhật console.logký cuộc gọi đầu tiên windowhoặc Objectcho tôi tùy thuộc vào nơi tôi chạy nó, không Person.
Bart S

Bạn có chắc chắn bạn đã khởi tạo bằng cách sử dụng "mới"? Nếu không nó không thể làm việc.
roland

14

Nó trông giống như điều ngu ngốc nhất mà tôi đã viết trong cuộc đời mình, nhưng thật buồn cười: D

function getName(d){
  const error = new Error();
  const firefoxMatch = (error.stack.split('\n')[0 + d].match(/^.*(?=@)/) || [])[0];
  const chromeMatch = ((((error.stack.split('at ') || [])[1 + d] || '').match(/(^|\.| <| )(.*[^(<])( \()/) || [])[2] || '').split('.').pop();
  const safariMatch = error.stack.split('\n')[0 + d];

  // firefoxMatch ? console.log('firefoxMatch', firefoxMatch) : void 0;
  // chromeMatch ? console.log('chromeMatch', chromeMatch) : void 0;
  // safariMatch ? console.log('safariMatch', safariMatch) : void 0;

  return firefoxMatch || chromeMatch || safariMatch;
}

d- độ sâu của ngăn xếp. 0- trả lại tên hàm này, 1- cha mẹ, v.v.;
[0 + d]- chỉ để hiểu - những gì xảy ra;
firefoxMatch- hoạt động cho safari, nhưng tôi thực sự có ít thời gian để thử nghiệm, vì chủ sở hữu của mac đã quay lại sau khi hút thuốc và đuổi tôi đi: '(

Kiểm tra:

function limbo(){
  for(let i = 0; i < 4; i++){
    console.log(getName(i));
  }
}
function lust(){
  limbo();
}
function gluttony(){
  lust();
}

gluttony();

Kết quả:
Chrome:
nhập mô tả hình ảnh ở đây

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

Giải pháp này được tạo ra chỉ để cho vui ! Không sử dụng nó cho các dự án thực tế. Nó không phụ thuộc vào đặc điểm kỹ thuật ES, nó chỉ phụ thuộc vào việc thực hiện trình duyệt. Sau lần cập nhật chrome / firefox / safari tiếp theo, nó có thể bị hỏng.
Hơn thế nữa, không có lỗi (ha) xử lý - nếu dsẽ nhiều hơn chiều dài ngăn xếp - bạn sẽ gặp lỗi;
Đối với mô hình thông báo lỗi của người viết khác - bạn sẽ gặp lỗi;
Nó phải hoạt động cho các lớp ES6 ( .split('.').pop()), nhưng bạn có thể bị lỗi;


13

Điều này có thể làm việc cho bạn:

function foo() { bar(); }

function bar() { console.log(bar.caller.name); }

chạy foo () sẽ xuất ra "foo" hoặc không xác định nếu bạn gọi từ một hàm ẩn danh.

Nó cũng hoạt động với các hàm tạo, trong trường hợp đó, nó sẽ xuất ra tên của hàm tạo gọi (ví dụ "Foo").

Thêm thông tin tại đây: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Caller

Họ cho rằng nó không chuẩn, nhưng cũng được hỗ trợ bởi tất cả các trình duyệt chính: Firefox, Safari, Chrome, Opera và IE.


Không khả dụng ở chế độ nghiêm ngặt ... mmm
Ka Mok

8

Bạn không thể. Các hàm không có tên theo tiêu chuẩn (mặc dù mozilla có thuộc tính như vậy) - chúng chỉ có thể được gán cho các biến có tên.

Ngoài ra bình luận của bạn:

// access fully qualified name (ie "my.namespace.myFunc")

nằm trong hàm my.namespace.myFunc.getFn

Những gì bạn có thể làm là trả về hàm tạo của một đối tượng được tạo bởi new

Vì vậy, bạn có thể nói

var obj = new my.namespace.myFunc();
console.info(obj.constructor); //my.namespace.myFunc

1
Tôi cần nó bên trong func bởi vì tôi chuyển nó lên chuỗi thừa kế và tôi đã bỏ qua những thứ đó cho ngắn gọn.
Scott Klarenbach

1
'cái này' sẽ là ví dụ đã gọi hàm. Vì vậy, nếu bạn 'cái này' bên trong hàm cha sẽ cung cấp cho bạn thể hiện của hàm gọi nó. Đó là tất cả những gì bạn cần - không chắc tại sao bạn cũng cần cái tên đó
plodder

1
tốt, trong phần cha mẹ, sau đó tôi có thể truy cập các hàm khác theo quy ước, chẳng hạn như ns [con] [lược đồ] hoặc ns [con] [dbService]. Không có nó, tôi phải mã cứng các tài liệu tham khảo này trong mỗi lớp con.
Scott Klarenbach

1
trường hợp sử dụng của tôi để tìm tên là để dễ dàng hơn trong việc xây dựng các thông báo console.error cho biết thông điệp đến từ đâu mà không phải hoàn nguyên về kết xuất ngăn xếp. ví dụ: console.error ({'error': {self.name tham khảo} + "lỗi với tham số đầu vào"). Việc cắt / dán các thông báo lỗi lặp lại ở nhiều chức năng trở nên dễ dàng hơn nhiều nếu bạn không phải dừng và thay đổi văn bản trong chúng mỗi lần. Trong trường hợp của tôi, tôi đang trả lại lỗi lời hứa từ nhiều cuộc gọi lời hứa - rất vui để biết lời hứa / chức năng nào đã tạo ra lỗi.
Scott

7

Bạn có thể sử dụng cái này, cho các trình duyệt hỗ trợ Error.stack (gần như không phải tất cả, có lẽ)

function WriteSomeShitOut(){ 
  var a = new Error().stack.match(/at (.*?) /);
  console.log(a[1]);
} 
WriteSomeShitOut();

Tất nhiên đây là chức năng hiện tại, nhưng bạn có ý tưởng.

hạnh phúc chảy nước dãi trong khi bạn code


4

Bạn có thể sử dụng thuộc nametính để lấy tên hàm, trừ khi bạn đang sử dụng hàm ẩn danh

Ví dụ:

var Person = function Person () {
  this.someMethod = function () {};
};

Person.prototype.getSomeMethodName = function () {
  return this.someMethod.name;
};

var p = new Person();
// will return "", because someMethod is assigned with anonymous function
console.log(p.getSomeMethodName());

Bây giờ hãy thử với chức năng được đặt tên

var Person = function Person () {
  this.someMethod = function someMethod() {};
};

bây giờ bạn có thể sử dụng

// will return "someMethod"
p.getSomeMethodName()

4

Bạn có thể sử dụng tên constructor như:

{your_function}.prototype.constructor.name

mã này chỉ đơn giản trả về tên của một phương thức.


3

như một phần như ECMAScript 6bạn có thể sử dụng phương thức Function.name

function doSomething() {}

alert(doSomething.name); // alerts "doSomething"

Trong ReactJS, bạn cần thêm cái này: console.log (this.doSthing.name);
Bitclaw

2
đó là ngoài chức năng
Scott

3

Bạn có thể sử dụngFunction.name :

Trong hầu hết các triển khai JavaScript, một khi bạn có phạm vi tham chiếu của hàm tạo, bạn có thể lấy tên chuỗi của nó từ thuộc tính tên của nó (ví dụ: Function.name hoặc Object.constructor.name

Bạn có thể sử dụngFunction.callee :

arguments.callerPhương thức gốc đã không được chấp nhận, nhưng hầu hết các trình duyệt đều hỗ trợ Function.caller, nó sẽ trả về đối tượng gọi thực tế (phần thân mã): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ Hàm / người gọi? Redirectlocale = en-US & redirectslug = JavaScript% 2FReference% 2FGlobal_Objects% 2FFeft% 2Fcaller

Bạn có thể tạo một bản đồ nguồn :

Nếu cái bạn cần là chữ ký hàm chữ ("tên" của nó) chứ không phải chính đối tượng, bạn có thể phải sử dụng một cái gì đó tùy chỉnh hơn một chút, như tạo một tham chiếu mảng các giá trị chuỗi API bạn sẽ cần truy cập thường xuyên. Bạn có thể ánh xạ chúng lại với nhau bằng cách sử dụng Object.keys()và chuỗi chuỗi của bạn hoặc xem xét thư viện bản đồ nguồn của Mozilla trên GitHub, cho các dự án lớn hơn: https://github.com/mozilla/source-map


2

Tôi biết đây là một câu hỏi cũ nhưng gần đây tôi đã gặp phải một số vấn đề tương tự trong khi cố gắng trang trí một số phương thức của React Element, cho mục đích gỡ lỗi. Như mọi người đã nói, arguments.callerarguments.calleebị cấm trong chế độ nghiêm ngặt có thể được bật theo mặc định trong quá trình chuyển đổi React của bạn. Bạn có thể vô hiệu hóa nó hoặc tôi đã có thể đưa ra một bản hack khác, bởi vì trong React, tất cả các chức năng của lớp đều được đặt tên, bạn thực sự có thể làm điều này:

Component.prototype.componentWillMount = function componentWillMount() {
    console.log('Callee name: ', this.__proto__.constructor.toString().substr(0,30));
...
}

1

Điều này làm việc cho tôi.

function AbstractDomainClass() {
    this.className = function() {
        if (!this.$className) {
            var className = this.constructor.toString();
            className = className.substr('function '.length);
            className = className.substr(0, className.indexOf('('));
            this.$className = className;
        }
        return this.$className;
    }
}

Mã kiểm tra:

var obj = new AbstractDomainClass();
expect(obj.className()).toBe('AbstractDomainClass');

1

Tôi đã có một vấn đề tương tự và tôi đã giải quyết nó như sau:

Function.prototype.myname = function() {
   return this.toString()
       .substr( 0, this.toString().indexOf( "(" ) )
       .replace( "function ", "" ); 
}

Mã này thực hiện, theo một cách thoải mái hơn, một phản hồi tôi đã đọc ở đây ở đầu cuộc thảo luận này. Bây giờ tôi có một hàm thành viên lấy tên của bất kỳ đối tượng hàm nào. Đây là kịch bản đầy đủ ...

<script language="javascript" TYPE="text/javascript">

    Function.prototype.myname = function() { 
        return this.toString()
            .substr( 0, this.toString().indexOf( "(" ) )
            .replace("function ", "" ); 
    }
    function call_this( _fn ) { document.write( _fn.myname() ); }
    function _yeaaahhh() { /* do something */ }
    call_this( _yeaaahhh ); 

</script>


0

Điều này sẽ hoạt động trong ES5, ES6, tất cả các trình duyệt và các chức năng chế độ nghiêm ngặt.

Đây là giao diện của hàm được đặt tên.

(function myName() {
  console.log(new Error().stack.split(/\r\n|\r|\n/g)[1].trim());
})();
at myName (<anonymous>:2:15)

Đây là giao diện của hàm ẩn danh.

(() => {
  console.log(new Error().stack.split(/\r\n|\r|\n/g)[1].trim());
})();
at <anonymous>:2:15

Điều này tạo ra kết quả khác nhau cho mỗi trình duyệt. Chrome: at myName (<anonymous>:2:15)Firefox:@debugger eval code:3:3
jkdev

(hàm myName () {console.log (Lỗi mới (). stack.split (/ \ r \ n | \ r | \ n / g) [1] .trim (). split ("") [1]) ;}) ();
Zibri

-2

xem tại đây: http://www.tek-tips.com/viewthread.cfm?qid=1209619

arguments.callee.toString();

có vẻ phù hợp với nhu cầu của bạn


3
argument.callee.toString () chỉ trả về nguồn của hàm.
Scott Klarenbach

2
Không được phép: các thuộc tính 'người gọi', 'callee' và 'đối số' có thể không được truy cập trên các chức năng chế độ nghiêm ngặt hoặc các đối tượng đối số cho các cuộc gọi đến chúng
Eric Hodonsky

-2

bạn có thể sử dụng Error.stack để theo dõi tên hàm và vị trí chính xác của vị trí bạn đang ở trong đó.

Xem stacktrace.js


-3

Cách dễ dàng để có được tên chức năng từ trong phiên bản bạn đang chạy.

function x(){alert(this.name)};x()


1
cho tôi một
HƯỚNG DẪN

1
nhận thức được nội dung của thisnó về cơ bản có thể là bất cứ điều gì. thử(function(){console.log(this.name);}).bind({name: "put basically anything here even an object maybe a 1TB string maybe a class definition"})()
Valen
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.