ตรวจสอบว่าตัวแปรเป็นประเภทฟังก์ชั่น


904

สมมติว่าฉันมีตัวแปรใด ๆ ซึ่งถูกกำหนดดังนี้:

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

ฉันต้องการฟังก์ชั่นที่ตรวจสอบว่าประเภทของตัวแปรเป็นฟังก์ชั่นเหมือนหรือไม่ เช่น:

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

ฉันจะตรวจสอบว่าตัวแปรaเป็นประเภทFunctionตามวิธีที่กำหนดไว้ข้างต้นได้อย่างไร


2
ที่นี่เป็นมาตรฐานสำหรับวิธีการทั่วไปในการตรวจสอบjsben.ch/#/e5Ogr
EscapeNetscape

คำตอบ:


380

แน่นอนว่าการขีดเส้นใต้นั้นมีประสิทธิภาพมากกว่า แต่วิธีที่ดีที่สุดในการตรวจสอบเมื่อประสิทธิภาพไม่ใช่ปัญหาจะถูกเขียนลงในหน้าของขีดล่างที่เชื่อมโยงโดย @Paul Rosania

แรงบันดาลใจจากขีดล่าง, ฟังก์ชั่นสุดท้ายคือฟังก์ชั่นดังต่อไปนี้:

function isFunction(functionToCheck) {
 return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]';
}

1
อาจจะมีใครบางคนที่ได้ทำวิจัยอย่างกว้างขวางมากขึ้นในเรื่องนี้ แต่ได้ดูที่ผลของการแก้ไขที่ 4 ของ jsperf ด้วยง่าย "การตรวจสอบผลที่" isFunctionAแสดงความแตกต่างของการใช้งานเมื่อเปรียบเทียบกับวิธีอื่น
Joel Purra

24
ด้วยการทดสอบประสิทธิภาพที่อัปเดตดูเหมือนว่าจะมีความแตกต่างความเร็วสูงขึ้นอยู่กับเบราว์เซอร์ของคุณ ใน Chrome typeof(obj) === 'function'นั้นเร็วที่สุด อย่างไรก็ตามใน Firefox obj instanceof Functionเป็นผู้ชนะที่ชัดเจน
Justin Warkentin

7
เกี่ยวกับส่วนที่: typeof ควรใช้สำหรับการตรวจสอบว่าตัวแปรหรือคุณสมบัติจะไม่ได้กำหนด ที่javascript.info/tutorial/type-detectionในส่วนของ typeofและต่อไปนี้ผู้เขียนระบุว่ากรณีตรงข้าม
Konrad Madej

11
อเล็กซ์ฉันอยากรู้ว่าทำไมคุณถึงบอกว่าประเภทควรจะถูกใช้เพื่อตรวจสอบไม่ได้กำหนดเท่านั้น ชื่อแนะนำว่ามันมีวัตถุประสงค์เพื่อตรวจสอบประเภทของบางสิ่งบางอย่าง มีปัญหาทางเทคนิคหรือปัญหาที่อาจเกิดขึ้นเมื่อใช้งานเพื่อทำการตรวจสอบประเภทหรือมีความต้องการมากกว่านี้หรือไม่? หากมีข้อแม้ก็จะเป็นการดีที่จะแสดงรายการพวกเขาเพื่อให้ผู้อื่นสามารถหลีกเลี่ยงการสะดุดพวกเขา
jinglesthula

14
วิธีแก้ปัญหาที่ซับซ้อนเกินสมควรอย่างไม่สมเหตุผล
Daniel Garmoshka

1711
if (typeof v === "function") {
    // do something
}

44
เพียงแค่ดูออกไม่กี่สิ่งที่ต้องการtypeof Object, typeof Dateและtypeof Stringซึ่งผลตอบแทนทั้งหมด'function'เกินไป
Dave Ward

358
@ เดฟฉันไม่แน่ใจว่าปัญหาคืออะไรเพราะเป็นฟังก์ชั่น
Matthew Crumley

6
ความแตกต่างif (v instanceOf Function)คืออะไร?
mquandalle

10
@quandalle ไม่มีความแตกต่างใหญ่ยกเว้นว่าinstanceofจะไม่ทำงานหากคุณกำลังตรวจสอบฟังก์ชั่นที่มาจากเอกสารอื่น (อาจเป็น iframe) ประสิทธิภาพแตกต่างกันไป
rvighne

8
ต่างจากคำตอบที่ยอมรับซึ่งใช้งานได้กับฟังก์ชัน async ด้วย สำหรับasyncfunctionToCheck, getType.toString.call(functionToCheck)==='[object AsyncFunction]'ที่เป็นtypeof asyncfunctionToCheck === 'function'
TDreama

132

Underscore.jsใช้การทดสอบที่ซับซ้อน แต่มีประสิทธิภาพสูงกว่า:

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

ดู: http://jsperf.com/alternative-isfunction-implementations

แก้ไข: การทดสอบที่ปรับปรุงแนะนำประเภทของที่อาจจะเร็วกว่าดูhttp://jsperf.com/alternative-isfunction-implementations/4


มีเหตุผลใดที่จะใช้วิธีนี้มากกว่าtypofหรือไม่
sv_in

1
เวอร์ชันของขีดล่างเร็วกว่ามากในเบราว์เซอร์ส่วนใหญ่ ดู: jsperf.com/alternative-isfunction-implementations
Paul Rosania

13
ฉันแน่ใจว่าชอบขีดล่างเรียบง่าย แต่ทรงพลัง ที่กล่าวว่ามันอาจจะคุ้มค่าที่จะสังเกตว่าการใช้งานนี้อาจถูกลวงโดยวัตถุที่มีคุณสมบัติเหล่านั้น
studgeek

3
@PaulRosania สะดุดกับการทดสอบประสิทธิภาพเหล่านี้ก่อนหน้านี้ในวันนี้และอัปเดตพวกเขาด้วยการตรวจสอบและข้อมูลการทดสอบเพิ่มเติมตามที่แก้ไข 4 - โปรดเรียกใช้เพื่อเพิ่มความครอบคลุมการทดสอบเบราว์เซอร์ การดำเนินการช้าลงต่อวินาที (เนื่องจากการทดสอบจำนวนมาก) แต่ก็ยังแสดงให้เห็นถึงความแตกต่างของการใช้งานหนึ่งรายการ (เปิดคอนโซล javascript และหน้าโหลดซ้ำอาจมีข้อความแสดงข้อผิดพลาดที่บันทึกไว้) หมายเหตุ: เพิ่มการแก้ไขรุ่น 3 isFunctionD (ขึ้นอยู่กับเท่านั้นtypeof == "function") - และดูเหมือนว่าจะเร็วกว่ารุ่น "fast" ของ Underscore
Joel Purra

6
คำตอบนี้เป็นความเข้าใจผิดเล็กน้อยในขณะนี้เนื่องจาก Underscore.js อยู่ในรุ่นที่12ไม่ใช่การแก้ไขที่อ้างถึงข้างต้น 4 ของการทดสอบisFunction()การใช้งานทางเลือก ปัจจุบันใช้บางสิ่งบางอย่างที่ใกล้เคียงกับสิ่งที่dandeanแนะนำ
Jonathan Eunice

112

มีหลายวิธีดังนั้นฉันจะสรุปพวกเขาทั้งหมด

  1. วิธีที่ดีที่สุดคือ:
    ฟังก์ชั่น foo (v) {ถ้า (v instanceof ฟังก์ชั่น) {/ * ทำอะไร * /}};
    
    
    นักแสดงส่วนใหญ่ (ไม่มีการเปรียบเทียบสตริง) และโซลูชันที่หรูหรา - ตัวดำเนินการอินสแตนซ์ได้รับการสนับสนุนในเบราว์เซอร์เป็นเวลานานดังนั้นไม่ต้องกังวล - มันจะทำงานใน IE 6
  2. วิธีที่ดีที่สุดถัดไปคือ:
    ฟังก์ชั่น foo (v) {ถ้า (typeof v === "function") {/ * ทำบางสิ่งบางอย่าง * /}};
    
    
    ข้อเสียของtypeofคือมันเป็นความอ่อนแอต่อความล้มเหลวเงียบไม่ดีดังนั้นหากคุณมีการพิมพ์ผิด (เช่น "finction") - ในกรณีนี้ `if` จะเพิ่งกลับเท็จและคุณจะไม่ทราบว่าคุณมีข้อผิดพลาดจนกระทั่งในภายหลัง รหัสของคุณ
  3. วิธีที่ดีที่สุดถัดไปคือ:
    ฟังก์ชั่น isFunction (functionToCheck) {
        var getType = {};
        ส่งคืน functionToCheck && getType.toString.call (functionToCheck) === '[ฟังก์ชั่นวัตถุ]';
    }
    
    
    สิ่งนี้ไม่มีข้อได้เปรียบเหนือโซลูชัน # 1 หรือ # 2 แต่อ่านได้น้อยกว่ามาก รุ่นที่ปรับปรุงแล้วของรุ่นนี้คือ
    ฟังก์ชั่น isFunction (x) {
        return Object.prototype.toString.call (x) == '[ฟังก์ชั่นวัตถุ]';
    }
    
    
    แต่ยังมีความหมายน้อยกว่าโซลูชัน # 1 มาก

10
โซลูชันแรกล้มเหลวในกรณีที่ฟังก์ชันส่งผ่านไปยังบริบทเฟรมอื่น ยกตัวอย่างเช่นจาก top.Function !== Functioniframe เพื่อให้แน่ใจว่าใช้อันที่สอง (การสะกดผิดของ "ฟังก์ชั่น" ใด ๆ จะได้รับการแก้ไขในระหว่างการดีบักหวังว่า)
MaxArt

เกี่ยวกับจุดของคุณเกี่ยวกับการพิมพ์ผิด: รหัสใด ๆ ที่มีการพิมพ์ผิดนี้มีแนวโน้มที่จะล้มเหลวอย่างรวดเร็ว / ไม่ชัดเจนกว่าข้อบกพร่องอื่น ๆ ขึ้นอยู่กับการพิมพ์ผิด typeof === "function" เป็นคำตอบที่สะอาดที่สุด นอกจากนี้โซลูชัน "ที่ดีที่สุด" ของคุณที่คุณใช้instanceofไม่ได้คำนึงถึงหลายกลมเช่นการตรวจสอบข้ามเฟรมและสามารถส่งกลับเชิงลบเท็จ
brainkim

84

jQuery (เลิกใช้แล้วตั้งแต่เวอร์ชั่น 3.3) การอ้างอิง

$.isFunction(functionName);

การอ้างอิงAngularJS

angular.isFunction(value);

การอ้างอิงของLodash

_.isFunction(value);

ขีดเส้นใต้ อ้างอิง

_.isFunction(object); 

Node.jsเลิกใช้แล้วตั้งแต่การอ้างอิง v4.0.0

var util = require('util');
util.isFunction(object);

56

@grandecomplex: มีการใช้คำฟุ่มเฟื่อยในการแก้ปัญหาของคุณ มันจะชัดเจนกว่านี้หากเขียนดังนี้:

function isFunction(x) {
  return Object.prototype.toString.call(x) == '[object Function]';
}

Object.prototype.toString.call มีประโยชน์สำหรับจาวาสคริปต์ประเภทอื่น ๆ ที่คุณสนใจคุณสามารถตรวจสอบกับ null หรือ undefined ซึ่งทำให้มีประสิทธิภาพมาก
Jason Foglia


35

ลองinstanceofโอเปอเรเตอร์: ดูเหมือนว่าฟังก์ชั่นทั้งหมดสืบทอดจากFunctionคลาส:

// 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

19

บางอย่างที่รองรับเบราว์เซอร์มากขึ้นและยังรวมถึงฟังก์ชั่น async อาจเป็น:

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

จากนั้นทดสอบเช่น:

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

11

วิธีอื่น ๆ :

var fn = function () {}
if (fn.constructor === Function) {
  // true
} else {
  // false
}

1
หาก fn จะไม่ได้กำหนดไว้มันจะโยนข้อผิดพลาด
Kamil Orzechowski

fn && fn.constructor === Functionแล้วเรื่องนี้ล่ะ
Yaroslav Melnichuk

9

สำหรับผู้ที่สนใจในรูปแบบการทำงานหรือมองหาวิธีการแสดงออกที่ชัดเจนมากขึ้นเพื่อใช้ในการเขียนโปรแกรมเมตา (เช่นการตรวจสอบประเภท) มันอาจเป็นเรื่องที่น่าสนใจที่จะเห็นห้องสมุดRamdaเพื่อทำงานให้สำเร็จ

โค้ดถัดไปมีฟังก์ชั่น pure และ pointfree เท่านั้น:

const R = require('ramda');

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

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

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

ในฐานะของ ES2017 asyncมีฟังก์ชั่นให้ใช้ดังนั้นเราสามารถตรวจสอบกับมันได้เช่นกัน:

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

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

แล้วรวมเข้าด้วยกัน:

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

แน่นอนว่าฟังก์ชั่นควรได้รับการปกป้องnullและundefinedค่านิยมดังนั้นเพื่อให้ "ปลอดภัย":

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

และวางตัวอย่างเพื่อสรุป:

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

อย่างไรก็ตามโปรดทราบว่าโซลูชันนี้อาจแสดงประสิทธิภาพน้อยกว่าตัวเลือกอื่น ๆ เนื่องจากการใช้งานฟังก์ชั่นการสั่งซื้อที่สูง


7

ถ้าคุณใช้ Lodash คุณสามารถทำมันได้ด้วย_.isFunction

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

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

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

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

วิธีการนี้จะส่งกลับถ้าค่าเป็นฟังก์ชั่นอื่น ๆtruefalse


4

ฉันพบว่าเมื่อการทดสอบฟังก์ชั่นเบราว์เซอร์ใน IE8, ใช้toString, instanceofและtypeofไม่ได้ทำงาน นี่เป็นวิธีที่ใช้งานได้ดีใน IE8 (เท่าที่ฉันรู้):

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

หรือคุณสามารถตรวจสอบฟังก์ชันดั้งเดิมโดยใช้:

"getElementById" in document

แม้ว่าฉันได้อ่านบางที่ว่านี้จะไม่ทำงานใน IE7 และด้านล่าง


4

ด้านล่างดูเหมือนจะทำงานสำหรับฉันเช่นกัน (ทดสอบจากnode.js):

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

console.log(isFunction(function(){})); // true
console.log(isFunction({})); // false

4

ฉันคิดว่าคุณสามารถกำหนดค่าสถานะบนต้นแบบฟังก์ชันและตรวจสอบว่าอินสแตนซ์ที่คุณต้องการทดสอบรับค่านั้นหรือไม่

กำหนดธง:

Function.prototype.isFunction = true; 

จากนั้นตรวจสอบว่ามีอยู่หรือไม่

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

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


มันจะล้มเหลวด้วยข้อผิดพลาดหาก foo ไม่ได้กำหนด ไม่พูดถึงว่าคุณปรับเปลี่ยนต้นแบบดั้งเดิมซึ่งผิดทั้งหมด
Kamil Orzechowski

3

ตั้งแต่โหนด v0.11 คุณสามารถใช้ฟังก์ชั่น util มาตรฐาน:

var util = require('util');
util.isFunction('foo');

2
util.isFunction เลิกใช้แล้ว
kehers


0

วิธีการแก้ปัญหาตามที่บางคำตอบก่อนหน้านี้ได้แสดงคือการใช้ typeof ต่อไปนี้เป็นข้อมูลโค้ดใน 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]();
  }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.