Periksa apakah suatu variabel bertipe fungsi

904

Misalkan saya punya variabel apa pun, yang didefinisikan sebagai berikut:

var a = function() {/* Statements */};

Saya ingin fungsi yang memeriksa apakah jenis variabel seperti fungsi. yaitu:

function foo(v) {if (v is function type?) {/* do something */}};
foo(a);

Bagaimana saya bisa mengecek apakah variabelnya abertipe Functiondengan cara yang didefinisikan di atas?

Jesufer Vn
sumber
2
di sini patokan untuk cara paling umum ini untuk memeriksa jsben.ch/#/e5Ogr
EscapeNetscape

Jawaban:

380

Cara underscore memang lebih efisien, tetapi cara terbaik untuk memeriksa, ketika efisiensi bukan masalah, ditulis di halaman underscore yang ditautkan oleh @Paul Rosania.

Terinspirasi oleh garis bawah, fungsi isfungsi terakhir adalah sebagai berikut:

function isFunction(functionToCheck) {
 return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]';
}
Alex Grande
sumber
1
Mungkin ada seseorang yang telah melakukan penelitian yang lebih luas tentang masalah ini, tetapi melihat hasil revisi 4 jsperf dengan "verifikasi hasil" sederhana . isFunctionAmenunjukkan perbedaan implementasi dibandingkan dengan metode lain.
Joel Purra
24
Dengan tes kinerja yang diperbarui , sepertinya ada perbedaan kecepatan yang sangat besar tergantung pada browser Anda. Di Chrome typeof(obj) === 'function'tampaknya menjadi yang tercepat; namun, di Firefox obj instanceof Functionadalah pemenangnya.
Justin Warkentin
7
Mengenai bagian: typeof hanya boleh digunakan untuk memeriksa apakah variabel atau properti tidak terdefinisi. Di javascript.info/tutorial/type-detection di bagian typeof yang baik dan yang berikut penulis nyatakan, bahwa kasusnya persis sebaliknya
Konrad Madej
11
Alex, saya ingin tahu mengapa Anda mengatakan typeof hanya boleh digunakan untuk memeriksa tidak terdefinisi. Namanya menunjukkan bahwa tujuannya adalah untuk memeriksa jenis sesuatu, bukan hanya apakah itu ada. Apakah ada masalah teknis atau masalah yang dapat muncul saat menggunakannya untuk melakukan pengecekan tipe, atau apakah lebih merupakan preferensi? Jika ada peringatan, akan lebih baik untuk mendaftarkan mereka sehingga orang lain dapat menghindari tersandung mereka.
jinglesthula
14
solusi rumit terlalu rumit
Daniel Garmoshka
1714
if (typeof v === "function") {
    // do something
}
selbie
sumber
44
Hanya hati-hati untuk beberapa hal seperti typeof Object, typeof Date, dan typeof String, yang semua kembali'function' juga.
Dave Ward
359
@ Sayang, saya tidak yakin apa masalahnya karena itu adalah fungsi.
Matthew Crumley
6
Apa bedanya dengan if (v instanceOf Function)?
mquandalle
10
@mquandalle Tidak ada perbedaan besar, kecuali instanceoftidak akan berfungsi jika Anda memeriksa fungsi yang berasal dari dokumen lain (iframe, mungkin). Performanya beragam.
rvighne
8
tidak seperti jawaban yang diterima ini bekerja untuk fungsi async juga. Untuk asyncfunctionToCheck, di getType.toString.call(functionToCheck)==='[object AsyncFunction]'manatypeof asyncfunctionToCheck === 'function'
TDreama
132

Underscore.js menggunakan tes yang lebih rumit tetapi berkinerja tinggi:

_.isFunction = function(obj) {
  return !!(obj && obj.constructor && obj.call && obj.apply);
};

Lihat: http://jsperf.com/alternative-isfunction-implementations

EDIT: tes yang diperbarui menunjukkan bahwa typeof mungkin lebih cepat, lihat http://jsperf.com/alternative-isfunction-implementations/4

Paul Rosania
sumber
Apakah ada alasan khusus untuk menggunakan pendekatan ini selain typof?
sv_in
1
Versi underscore jauh lebih cepat di sebagian besar browser. Lihat: jsperf.com/alternative-isfunction-implementations
Paul Rosania
13
Saya yakin suka garis bawah, sederhana namun kuat. Yang mengatakan, mungkin perlu dicatat bahwa implementasi ini dapat dipalsukan oleh objek dengan atribut tersebut.
studgeek
3
@PaulRosania Menemukan tes kinerja ini lebih awal hari ini, dan memutakhirkannya dengan verifikasi plus lebih banyak data uji sebagai revisi 4 - silakan jalankan untuk meningkatkan cakupan uji browser. Ini lebih lambat dalam operasi per detik (karena banyak tes), tetapi juga menunjukkan satu perbedaan implementasi (buka konsol javascript Anda dan muat ulang halaman, mungkin ada pesan kesalahan yang dicatat). Catatan: Revisi 3 menambahkan isFunctionD (hanya berdasarkan typeof == "function") - dan tampaknya jauh lebih cepat daripada versi "cepat" Underscore.
Joel Purra
6
Jawaban ini agak menyesatkan sekarang, karena Underscore.js sekarang pada revisi 12 , bukan revisi 4 yang direferensikan di atas, dari pengujian isFunction()implementasi alternatif . Saat ini menggunakan sesuatu yang sangat dekat dengan apa yang disarankan dandean .
Jonathan Eunice
112

Ada beberapa cara jadi saya akan merangkum semuanya

  1. Cara terbaik adalah:
    function foo (v) {if (v instanceof Function) {/ * do something * /}};
    
    
    Sebagian besar performan (tanpa perbandingan string) dan solusi elegan - operator instanceof telah didukung di browser untuk waktu yang sangat lama, jadi jangan khawatir - ini akan bekerja di IE 6.
  2. Cara terbaik berikutnya adalah:
    function foo (v) {if (typeof v === "function") {/ * do something * /}};
    
    
    Kerugian dari typeof adalah bahwa ia rentan terhadap kegagalan diam, buruk, jadi jika Anda memiliki kesalahan ketik (misalnya "fiksi") - dalam hal ini `jika` hanya akan mengembalikan false dan Anda tidak akan tahu Anda memiliki kesalahan sampai nanti di kode Anda
  3. Cara terbaik berikutnya adalah:
    function isFunction (functionToCheck) {
        var getType = {};
        return functionToCheck && getType.toString.call (functionToCheck) === '[Fungsi objek]';
    }
    
    
    Ini tidak memiliki keunggulan dibandingkan solusi # 1 atau # 2 tetapi jauh lebih mudah dibaca. Versi yang disempurnakan dari ini adalah
    function isFunction (x) {
        return Object.prototype.toString.call (x) == '[Fungsi objek]';
    }
    
    
    tetapi semantik masih jauh lebih sedikit daripada solusi # 1
Dalimian
sumber
10
Solusi pertama gagal jika fungsi dilewatkan ke konteks bingkai yang berbeda. Misalnya, dari iframe top.Function !== Function. Yang pasti, gunakan yang kedua (kesalahan ejaan "fungsi" akan diperbaiki selama debug, semoga).
MaxArt
Mengenai poin Anda tentang kesalahan ketik: kode apa pun yang memiliki kesalahan ketik ini cenderung gagal dengan cepat / tidak lebih jelas daripada bug lain berdasarkan kesalahan ketik. typeof === "function" adalah solusi terbersih. Selain itu, solusi "terbaik" Anda di mana Anda menggunakan instanceoftidak memperhitungkan banyak global, seperti pemeriksaan lintas bingkai, dan dapat mengembalikan negatif palsu.
brainkim
84

jQuery (sudah tidak digunakan lagi sejak versi 3.3) Referensi

$.isFunction(functionName);

Referensi AngularJS

angular.isFunction(value);

Referensi Lodash

_.isFunction(value);

Referensi Garis Bawah

_.isFunction(object); 

Node.js sudah tidak digunakan lagi sejak Referensi v4.0.0

var util = require('util');
util.isFunction(object);
Cesar Loachamin
sumber
56

@grandecomplex: Ada cukup banyak verbositas untuk solusi Anda. Akan jauh lebih jelas jika ditulis seperti ini:

function isFunction(x) {
  return Object.prototype.toString.call(x) == '[object Function]';
}
dandean
sumber
Object.prototype.toString.call berguna untuk jenis javascript lain yang Anda minati. Anda bahkan dapat mengecek terhadap null atau undefined yang membuatnya sangat kuat.
Jason Foglia
49

var foo = function(){};
if (typeof foo === "function") {
  alert("is function")
}

wsc
sumber
1
... dan (() => (typeof obj === 'function') && doSomething())masih opsi tercepat.
darcher
35

Coba instanceofoperator: sepertinya semua fungsi mewarisi dari Functionkelas:

// Test data
var f1 = function () { alert("test"); }
var o1 = { Name: "Object_1" };
F_est = function () { };
var o2 = new F_est();

// Results
alert(f1 instanceof Function); // true
alert(o1 instanceof Function); // false
alert(o2 instanceof Function); // false
Alexander Abakumov
sumber
19

Sesuatu dengan lebih banyak dukungan browser dan juga menyertakan fungsi async dapat berupa:

const isFunction = value => value && (Object.prototype.toString.call(value) === "[object Function]" || "function" === typeof value || value instanceof Function);

dan kemudian mengujinya seperti:

isFunction(isFunction); //true
isFunction(function(){}); //true
isFunction(()=> {}); //true
isFunction(()=> {return 1}); //true
isFunction(async function asyncFunction(){}); //true
isFunction(Array); //true
isFunction(Date); //true
isFunction(Object); //true
isFunction(Number); //true
isFunction(String); //true
isFunction(Symbol); //true
isFunction({}); //false
isFunction([]); //false
isFunction("function"); //false
isFunction(true); //false
isFunction(1); //false
isFunction("Alireza Dezfoolian"); //false
Alireza
sumber
11

Cara sederhana lainnya:

var fn = function () {}
if (fn.constructor === Function) {
  // true
} else {
  // false
}
GuillaumeL
sumber
1
jika fn akan tidak terdefinisi, ia akan melempar Kesalahan
Kamil Orzechowski
fn && fn.constructor === Functionbagaimana dengan ini?
Yaroslav Melnichuk
9

Bagi mereka yang tertarik dengan gaya fungsional, atau mencari pendekatan yang lebih ekspresif untuk digunakan dalam pemrograman meta (seperti pengecekan jenis), mungkin menarik untuk melihat Ramda. perpustakaan untuk menyelesaikan tugas tersebut.

Kode selanjutnya hanya berisi fungsi murni dan pointfree:

const R = require('ramda');

const isPrototypeEquals = R.pipe(Object.getPrototypeOf, R.equals);

const equalsSyncFunction = isPrototypeEquals(() => {});

const isSyncFunction = R.pipe(Object.getPrototypeOf, equalsSyncFunction);

Pada ES2017, asyncfungsi tersedia, sehingga kami juga dapat memeriksanya:

const equalsAsyncFunction = isPrototypeEquals(async () => {});

const isAsyncFunction = R.pipe(Object.getPrototypeOf, equalsAsyncFunction);

Dan kemudian menggabungkannya:

const isFunction = R.either(isSyncFunction, isAsyncFunction);

Tentu saja, fungsi harus dilindungi nulldan undefinedbernilai, sehingga membuatnya "aman":

const safeIsFunction = R.unless(R.isNil, isFunction);

Dan, lengkapi cuplikan untuk menyimpulkan:

const R = require('ramda');

const isPrototypeEquals = R.pipe(Object.getPrototypeOf, R.equals);

const equalsSyncFunction = isPrototypeEquals(() => {});
const equalsAsyncFunction = isPrototypeEquals(async () => {});

const isSyncFunction = R.pipe(Object.getPrototypeOf, equalsSyncFunction);
const isAsyncFunction = R.pipe(Object.getPrototypeOf, equalsAsyncFunction);

const isFunction = R.either(isSyncFunction, isAsyncFunction);

const safeIsFunction = R.unless(R.isNil, isFunction);

// ---

console.log(safeIsFunction( function () {} ));
console.log(safeIsFunction( () => {} ));
console.log(safeIsFunction( (async () => {}) ));
console.log(safeIsFunction( new class {} ));
console.log(safeIsFunction( {} ));
console.log(safeIsFunction( [] ));
console.log(safeIsFunction( 'a' ));
console.log(safeIsFunction( 1 ));
console.log(safeIsFunction( null ));
console.log(safeIsFunction( undefined ));

Namun, perhatikan bahwa solusi ini dapat menunjukkan kinerja yang lebih rendah daripada opsi lain yang tersedia karena penggunaan fungsi-fungsi tingkat tinggi yang ekstensif.

Organik Rusak
sumber
7

Jika Anda menggunakan Lodash, Anda dapat melakukannya dengan _.isFunction .

_.isFunction(function(){});
// => true

_.isFunction(/abc/);
// => false

_.isFunction(true);
// => false

_.isFunction(null);
// => false

Metode ini mengembalikan truejika nilai adalah fungsi, yang lain false.

slorenzo
sumber
4

Saya menemukan bahwa ketika pengujian fungsi browser asli di IE8, menggunakan toString, instanceofdan typeoftidak bekerja. Berikut adalah metode yang berfungsi dengan baik di IE8 (sejauh yang saya tahu):

function isFn(f){
    return !!(f && f.call && f.apply);
}
//Returns true in IE7/8
isFn(document.getElementById);

Atau, Anda dapat memeriksa fungsi-fungsi asli menggunakan:

"getElementById" in document

Padahal, saya telah membaca di suatu tempat bahwa ini tidak akan selalu berfungsi di IE7 dan di bawah.

Azmisov
sumber
4

Di bawah ini tampaknya bekerja untuk saya juga (diuji dari node.js):

var isFunction = function(o) {
     return Function.prototype.isPrototypeOf(o);
};

console.log(isFunction(function(){})); // true
console.log(isFunction({})); // false
Marcus Junius Brutus
sumber
4

Saya pikir Anda bisa mendefinisikan flag pada Function prototype dan memeriksa apakah instance yang ingin Anda uji mewarisinya

tentukan bendera:

Function.prototype.isFunction = true; 

dan kemudian periksa apakah ada

var foo = function(){};
foo.isFunction; // will return true

Kelemahannya adalah bahwa prototipe lain dapat menentukan flag yang sama dan kemudian itu tidak berharga, tetapi jika Anda memiliki kontrol penuh atas modul yang disertakan itu adalah cara termudah

Danny Mor
sumber
Ini akan gagal dengan Kesalahan jika foo tidak terdefinisi. Tidak menyebutkan bahwa Anda memodifikasi prototipe asli yang sama sekali salah.
Kamil Orzechowski
3

Sejak node v0.11 Anda dapat menggunakan fungsi util standar:

var util = require('util');
util.isFunction('foo');
moklick
sumber
2
util.isFunction sudah tidak digunakan lagi
kehers
2

Anda harus menggunakan typeOfoperator di js.

var a=function(){
    alert("fun a");
}
alert(typeof a);// alerts "function"
Rajesh Paul
sumber
0

Solusi seperti yang ditunjukkan beberapa jawaban sebelumnya adalah menggunakan typeof. berikut ini adalah cuplikan kode Di NodeJs,

    function startx() {
      console.log("startx function called.");
    }

 var fct= {};
 fct["/startx"] = startx;

if (typeof fct[/startx] === 'function') { //check if function then execute it
    fct[/startx]();
  }
Badr Bellaj
sumber