สไตล์ JavaScript สำหรับการโทรกลับที่เป็นทางเลือก


94

ฉันมีฟังก์ชั่นบางอย่างซึ่งบางครั้ง (ไม่เสมอไป) จะได้รับการติดต่อกลับและเรียกใช้ กำลังตรวจสอบว่าการเรียกกลับถูกกำหนด / ฟังก์ชั่นสไตล์ที่ดีหรือมีวิธีที่ดีกว่านี้หรือไม่?

ตัวอย่าง:

function save (callback){
   .....do stuff......
   if(typeof callback !== 'undefined'){
     callback();
   };
};

ในเบราว์เซอร์ที่ทันสมัยคุณสามารถใช้ได้typeof callback !== undefinedดังนั้น'
อย่าลืม

1
และถ้าคุณโทรมาsave()? นั่นจะไม่ให้คำเตือนข้อผิดพลาดหรือเป็นขุยเนื่องจากไม่มีอาร์กิวเมนต์หรือไม่? หรือมันโอเคอย่างสมบูรณ์และโทรกลับเป็นเพียงundefined?
João Pimentel Ferreira

คำตอบ:


142

ฉันชอบเป็นการส่วนตัว

typeof callback === 'function' && callback();

อย่างไรก็ตามtypeofคำสั่งนั้นหลบและควรใช้สำหรับ"undefined"และเท่านั้น"function"

ปัญหาที่เกิดขึ้นtypeof !== undefinedคือผู้ใช้อาจส่งผ่านค่าที่กำหนดไม่ใช่ฟังก์ชัน


3
typeofไม่หลบ บางครั้งมันก็คลุมเครือ แต่ฉันจะไม่เรียกมันว่าหลบ แม้ว่าจะตกลงกันว่าถ้าคุณจะโทรกลับเป็นฟังก์ชั่นควรตรวจสอบว่ามันเป็นฟังก์ชั่นจริง ๆ ไม่ใช่ตัวเลข :-)
TJ Crowder

1
@TJCrowder หลบอาจเป็นคำที่ผิดมันถูกกำหนดไว้อย่างดี แต่ไม่มีประโยชน์เนื่องจากการชกมวยและกลับ"object"95% ของเวลา
Raynos

1
typeofไม่ก่อให้เกิดการชกมวย typeof "foo"คือไม่"string" "object"นี่เป็นวิธีเดียวที่แท้จริงที่คุณสามารถบอกได้ว่าคุณกำลังจัดการกับสตริงดั้งเดิมหรือStringอ็อบเจกต์ (บางทีคุณอาจกำลังความคิดของObject.prototype.toStringซึ่งเป็นประโยชน์มากแต่ไม่ก่อให้เกิดมวย.)
TJ Crowder

4
การใช้สำนวนที่ยอดเยี่ยม&&โดยวิธีการ
TJ Crowder

@TJCrowder คุณมีประเด็นฉันไม่รู้ว่าค่าของการเปรียบเทียบสตริงไพรมารีกับอ็อบเจกต์สตริงแบบบรรจุกล่องคืออะไร นอกจากนี้ฉันยอมรับว่า.toStringน่ารักสำหรับการค้นหาไฟล์[[Class]].
Raynos

50

คุณยังสามารถทำ:

var noop = function(){}; // do nothing.

function save (callback){
   callback = callback || noop;
   .....do stuff......
};

เป็นประโยชน์อย่างยิ่งหากคุณใช้callbackสถานที่ไม่กี่แห่ง

นอกจากนี้หากคุณใช้jQueryงานอยู่คุณมีฟังก์ชันเช่นนั้นอยู่แล้วเรียกว่า$ .noop


4
ในมุมมองของฉันนี่เป็นทางออกที่สง่างามที่สุด
Nicolas Le Thierry d'Ennequin

2
เห็นด้วย นอกจากนี้ยังช่วยให้การทดสอบง่ายขึ้นเนื่องจากไม่มีเงื่อนไข if

6
แต่สิ่งนี้ไม่ได้แก้ไขปัญหาประเภทใช่หรือไม่ จะเกิดอะไรขึ้นถ้าฉันส่งอาร์เรย์ หรือสตริง?
Zerho

1
ลดความซับซ้อนเป็นcallback = callback || function(){};
NorCalKnockOut

1
คุณยังสามารถสร้างอาร์กิวเมนต์เป็นทางเลือกได้โดยให้ค่าเริ่มต้นในการประกาศ:function save (callback=noop) { ...
Keith

41

เพียงแค่ทำ

if (callback) callback();

ฉันชอบที่จะโทรกลับหากให้มาไม่ว่าจะเป็นประเภทใดก็ตาม อย่าปล่อยให้มันล้มเหลวโดยเงียบดังนั้นผู้ปฏิบัติจะรู้ว่าเขาส่งผ่านข้อโต้แย้งที่ไม่ถูกต้องและสามารถแก้ไขได้



3

แทนที่จะทำให้การโทรกลับเป็นทางเลือกเพียงกำหนดค่าเริ่มต้นและเรียกมันว่าไม่ว่าจะเกิดอะไรขึ้น

const identity = x =>
  x

const save (..., callback = identity) {
  // ...
  return callback (...)
}

เมื่อใช้

save (...)              // callback has no effect
save (..., console.log) // console.log is used as callback

สไตล์ดังกล่าวเรียกว่าสไตล์การส่งต่อ นี่คือตัวอย่างจริงcombinationsที่สร้างชุดค่าผสมที่เป็นไปได้ทั้งหมดของอินพุต Array

const identity = x =>
  x

const None =
  Symbol ()

const combinations = ([ x = None, ...rest ], callback = identity) =>
  x === None
    ? callback ([[]])
    : combinations
        ( rest
        , combs =>
            callback (combs .concat (combs .map (c => [ x, ...c ])))
        )

console.log (combinations (['A', 'B', 'C']))
// [ []
// , [ 'C' ]
// , [ 'B' ]
// , [ 'B', 'C' ]
// , [ 'A' ]
// , [ 'A', 'C' ]
// , [ 'A', 'B' ]
// , [ 'A', 'B', 'C' ]
// ]

เนื่องจากcombinationsถูกกำหนดไว้ในรูปแบบการส่งต่อการเรียกใช้ข้างต้นจึงเหมือนกันอย่างมีประสิทธิภาพ

combinations (['A', 'B', 'C'], console.log)
// [ []
// , [ 'C' ]
// , [ 'B' ]
// , [ 'B', 'C' ]
// , [ 'A' ]
// , [ 'A', 'C' ]
// , [ 'A', 'B' ]
// , [ 'A', 'B', 'C' ]
// ]

นอกจากนี้เรายังสามารถส่งต่อแบบกำหนดเองที่ทำอย่างอื่นกับผลลัพธ์

console.log (combinations (['A', 'B', 'C'], combs => combs.length))
// 8
// (8 total combinations)

รูปแบบการส่งต่อสามารถใช้ได้กับผลลัพธ์ที่สวยงามอย่างน่าประหลาดใจ

const first = (x, y) =>
  x

const fibonacci = (n, callback = first) =>
  n === 0
    ? callback (0, 1)
    : fibonacci
        ( n - 1
        , (a, b) => callback (b, a + b)
        )
        
console.log (fibonacci (10)) // 55
// 55 is the 10th fibonacci number
// (0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...)


1

ฉันเบื่อมากที่เห็นตัวอย่างข้อมูลเดิมซ้ำแล้วซ้ำอีกที่ฉันเขียนสิ่งนี้:

  var cb = function(g) {
    if (g) {
      var args = Array.prototype.slice.call(arguments); 
      args.shift(); 
      g.apply(null, args); 
    }
  };

ฉันมีฟังก์ชั่นนับร้อยที่ทำสิ่งต่างๆเช่น

  cb(callback, { error : null }, [0, 3, 5], true);

หรืออะไรก็ได้ ...

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


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

@Raynos - นั่นเป็นกรณีการใช้งานที่เฉพาะเจาะจงมาก JavaScript ถูกพิมพ์อย่างอ่อนไม่เก่งในการแยกแยะประเภทและโดยปกติแล้วการส่งผ่านพารามิเตอร์ที่มีชื่อจะดีกว่าการพยายามแยกแยะประเภทและคาดเดาสิ่งที่ผู้โทรต้องการ:save( { callback : confirmProfileSaved, priority: "low" })
Malvolio

ฉันไม่เห็นด้วยมีความสามารถในการตรวจสอบประเภทเพียงพอ ฉันชอบวิธีการโอเวอร์โหลด แต่นั่นเป็นความชอบส่วนบุคคล นี่เป็นสิ่งที่ jQuery ทำมาก
Raynos

@Raynos - มันแย่กว่าที่จะเพิกเฉยต่อพารามิเตอร์ที่ไม่ถูกต้องจากนั้นจึงเกิดข้อผิดพลาดในการเรียกใช้ฟังก์ชันที่ไม่ใช่ฟังก์ชัน พิจารณากรณีทั่วไป: ฟังก์ชันไลบรารีกำลังดำเนินกิจกรรมแบบอะซิงโครนัสบางอย่างและจำเป็นต้องแจ้งฟังก์ชันการโทร ดังนั้นผู้ใช้ที่ไม่ซับซ้อนการเพิกเฉยและล้มเหลวจึงดูเหมือนกัน: กิจกรรมดูเหมือนจะไม่เสร็จสมบูรณ์ อย่างไรก็ตามสำหรับผู้ใช้ที่มีความซับซ้อน (ตัวอย่างเช่นโปรแกรมเมอร์ดั้งเดิม) ความล้มเหลวจะทำให้เกิดข้อความแสดงข้อผิดพลาดและการติดตามสแต็กที่ชี้ไปที่ปัญหาโดยตรง การละเว้นพารามิเตอร์หมายถึงการวิเคราะห์ที่น่าเบื่อเพื่อพิจารณาว่าอะไรทำให้ "ไม่มีอะไร" เกิดขึ้น
Malvolio

มีการแลกเปลี่ยนที่จะทำแม้ว่า การโยนข้อผิดพลาดจะทำให้รันไทม์ขัดข้อง แต่การเพิกเฉยจะปล่อยให้โค้ดอื่น ๆ ที่อยู่รอบ ๆ ทำงานได้ตามปกติ
Raynos

1

ฟังก์ชันที่ถูกต้องขึ้นอยู่กับต้นแบบของฟังก์ชันใช้:

if (callback instanceof Function)

เพื่อให้แน่ใจว่าการโทรกลับเป็นฟังก์ชัน


0

หากเกณฑ์ในการเรียกใช้การโทรกลับเป็นไปตามที่กำหนดไว้หรือไม่คุณก็สบายดี นอกจากนี้ฉันขอแนะนำให้ตรวจสอบว่ามันเป็นฟังก์ชันจริงๆหรือไม่


0

ฉันได้ย้ายไปที่ coffee-script แล้วและพบว่าอาร์กิวเมนต์เริ่มต้นเป็นวิธีที่ดีในการแก้ปัญหานี้

doSomething = (arg1, arg2, callback = ()->)->
    callback()

0

สามารถทำได้อย่างง่ายดายด้วยArgueJS :

function save (){
  arguments = __({callback: [Function]})
.....do stuff......
  if(arguments.callback){
    callback();
  };
};
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.