ฉันสามารถตั้งชื่อฟังก์ชัน JavaScript และเรียกใช้งานได้ทันทีหรือไม่?


92

ฉันมีไม่กี่สิ่งเหล่านี้:

function addEventsAndStuff() {
  // bla bla
}
addEventsAndStuff();

function sendStuffToServer() {
  // send stuff
  // get HTML in response
  // replace DOM
  // add events:
  addEventsAndStuff();
}

การเพิ่มเหตุการณ์ใหม่เป็นสิ่งที่จำเป็นเนื่องจาก DOM มีการเปลี่ยนแปลงดังนั้นเหตุการณ์ที่แนบมาก่อนหน้านี้จึงหายไป เนื่องจากต้องติดตั้งในตอนแรกเช่นกัน (duh) จึงอยู่ในฟังก์ชันที่ดีที่จะแห้ง

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

ทั้งสองต่อไปนี้ตอบสนองด้วยข้อผิดพลาดทางไวยากรณ์:

function addEventsAndStuff() {
  alert('oele');
}();

(function addEventsAndStuff() {
  alert('oele');
})();

ผู้รับใด ๆ ?


เป็นไปได้ที่จะซ้ำกันของฟังก์ชันการสั่งงานด้วยตนเอง
Cristian Sanchez

2
ไม่นะ @CristianSanchez
Máxima Alekz

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

คำตอบ:


124

ตัวอย่างที่คุณโพสต์ในคำถามไม่มีอะไรผิด .. วิธีอื่นในการทำเช่นนี้อาจดูแปลก แต่:

var addEventsAndStuff;
(addEventsAndStuff = function(){
    // add events, and ... stuff
})();

มีสองวิธีในการกำหนดฟังก์ชันใน JavaScript การประกาศฟังก์ชัน:

function foo(){ ... }

และนิพจน์ฟังก์ชันซึ่งเป็นวิธีใด ๆในการกำหนดฟังก์ชันอื่นนอกเหนือจากข้างต้น:

var foo = function(){};
(function(){})();
var foo = {bar : function(){}};

... ฯลฯ

สามารถตั้งชื่อนิพจน์ฟังก์ชันได้ แต่ชื่อของนิพจน์จะไม่แพร่กระจายไปยังขอบเขตที่มี หมายความว่ารหัสนี้ถูกต้อง:

(function foo(){
   foo(); // recursion for some reason
}());

แต่นี่ไม่ใช่:

(function foo(){
    ...
}());
foo(); // foo does not exist

ดังนั้นในการตั้งชื่อฟังก์ชันของคุณและเรียกใช้ทันทีคุณต้องกำหนดตัวแปรท้องถิ่นกำหนดฟังก์ชันของคุณให้เป็นนิพจน์จากนั้นเรียกมัน


1
"สามารถตั้งชื่อนิพจน์ฟังก์ชันได้"ระวังนิพจน์ฟังก์ชันที่มีชื่อ พวกเขาควรทำงานตามที่คุณอธิบาย แต่พวกเขาไม่ได้ใน IE ก่อนที่จะ IE9 - คุณจะได้รับทั้งสองฟังก์ชั่นและการรั่วไหลชื่อฟังก์ชัน รายละเอียด: kangax.github.com/nfe ( สุดท้ายได้รับการแก้ไขใน IE9)
TJ Crowder

@TJ - ขอบคุณสำหรับข้อมูลอ้างอิง ฉันไม่รู้ว่าเนื่องจากฉันไม่เคยใช้ IE ยกเว้นเพื่อทดสอบผลิตภัณฑ์ขั้นสุดท้าย :) ฉันสมมติว่า IE9 ไม่ได้เลียนแบบข้อผิดพลาดนี้เมื่อตั้งค่าเป็นโหมด IE7 / 8? ฉันคิดว่ามันยังคงใช้เอ็นจิ้น IE9 JS ... Meh
Mark Kahn

หากเช่นฉันคุณพบปัญหา jshint ด้วยวิธีนี้คุณสามารถใช้call()วิธีการจากที่นี่: stackoverflow.com/a/12359154/1231001
cfx

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

17

มีชวเลขที่ดีสำหรับสิ่งนี้ (ไม่จำเป็นต้องประกาศตัวแปรใด ๆ ในแถบการกำหนดฟังก์ชัน):

var func = (function f(a) { console.log(a); return f; })('Blammo')

ซึ่งrฟังก์ชั่นจะกลับมา? function rหรือvar r? แค่สงสัย. ทางออกที่ดี. ไม่จำเป็นต้องเป็นหนึ่ง loc แม้ว่า =)
Rudie

ฉันเปลี่ยนชื่อใหม่ให้ชัดเจนมากขึ้น แต่return rจะfunction rเป็นเช่นเดียวกับที่เป็นขอบเขต เคยเขียน Scala อย่างถาวรเพื่อพยายามหาอะไรบางอย่างบนออนไลน์
James Gorrie

@JamesGorrie ชวเลขที่ยอดเยี่ยมอย่างไรก็ตามฉันลองสองครั้งในคอนโซลดูเหมือนว่า func, func (), func () () การประกาศฟังก์ชันการส่งคืนทั้งหมดของ f () แต่เราสามารถส่งออก 'Blammo' โดย func () ตามที่คาดไว้ได้โปรด :)?
mike652638

@ mike652638 ฉันเรียกใช้ในคอนโซลของฉันและคอนโซลบันทึก blammo?
James Gorrie

5

ไม่มีอะไรผิดปกติกับการตั้งค่านี้ (หรือมี) แต่ฉันจะทำให้มันราบรื่นได้ไหม

ดูที่การใช้การมอบหมายเหตุการณ์แทน นั่นคือสิ่งที่คุณเฝ้าดูเหตุการณ์บนคอนเทนเนอร์ที่ไม่หายไปจากนั้นใช้event.target(หรือevent.srcElementบน IE) เพื่อดูว่าเหตุการณ์เกิดขึ้นจริงที่ใดและจัดการได้อย่างถูกต้อง

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

นี่คือตัวอย่างของการมอบหมายงานโดยไม่ใช้ตัวช่วย libs:

(function() {
  var handlers = {};

  if (document.body.addEventListener) {
    document.body.addEventListener('click', handleBodyClick, false);
  }
  else if (document.body.attachEvent) {
    document.body.attachEvent('onclick', handleBodyClick);
  }
  else {
    document.body.onclick = handleBodyClick;
  }

  handlers.button1 = function() {
    display("Button One clicked");
    return false;
  };
  handlers.button2 = function() {
    display("Button Two clicked");
    return false;
  };
  handlers.outerDiv = function() {
    display("Outer div clicked");
    return false;
  };
  handlers.innerDiv1 = function() {
    display("Inner div 1 clicked, not cancelling event");
  };
  handlers.innerDiv2 = function() {
    display("Inner div 2 clicked, cancelling event");
    return false;
  };

  function handleBodyClick(event) {
    var target, handler;

    event = event || window.event;
    target = event.target || event.srcElement;

    while (target && target !== this) {
      if (target.id) {
        handler = handlers[target.id];
        if (handler) {
          if (handler.call(this, event) === false) {
            if (event.preventDefault) {
              event.preventDefault();
            }
            return false;
          }
        }
      }
      else if (target.tagName === "P") {
        display("You clicked the message '" + target.innerHTML + "'");
      }
      target = target.parentNode;
    }
  }

  function display(msg) {
    var p = document.createElement('p');
    p.innerHTML = msg;
    document.body.appendChild(p);
  }

})();

ตัวอย่างสด

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

ไลบรารี JavaScript สมัยใหม่เช่นjQuery , Prototype , YUI , Closureหรืออื่น ๆ อีกมากมายควรนำเสนอคุณสมบัติการมอบหมายเหตุการณ์เพื่อให้ราบรื่นเหนือความแตกต่างของเบราว์เซอร์และจัดการกรณีขอบได้อย่างหมดจด jQuery มีทั้งฟังก์ชันliveและdelegateฟังก์ชันซึ่งช่วยให้คุณสามารถระบุตัวจัดการโดยใช้ตัวเลือก CSS3 แบบเต็มรูปแบบ (และบางตัว)

ตัวอย่างเช่นนี่คือรหัสที่เทียบเท่าโดยใช้ jQuery (ยกเว้นฉันแน่ใจว่า jQuery จัดการกับ edge case แต่เวอร์ชันดิบนอกข้อมือข้างต้นไม่ได้):

(function($) {

  $("#button1").live('click', function() {
    display("Button One clicked");
    return false;
  });
  $("#button2").live('click', function() {
    display("Button Two clicked");
    return false;
  });
  $("#outerDiv").live('click', function() {
    display("Outer div clicked");
    return false;
  });
  $("#innerDiv1").live('click', function() {
    display("Inner div 1 clicked, not cancelling event");
  });
  $("#innerDiv2").live('click', function() {
    display("Inner div 2 clicked, cancelling event");
    return false;
  });
  $("p").live('click', function() {
    display("You clicked the message '" + this.innerHTML + "'");
  });

  function display(msg) {
    $("<p>").html(msg).appendTo(document.body);
  }

})(jQuery);

สำเนาสด


ฉันไม่ต้องการใช้Event.targetเพราะนั่นไม่ใช่เป้าหมายที่ถูกต้อง ถูกตัอง. หากคุณคลิกที่IMGภายในDIVและDIVมีเหตุการณ์Event.targetจะเป็นIMGและไม่มีทางที่จะได้รับDIVวัตถุอย่างสวยงาม แก้ไข Btw ฉันเกลียด.live'เหตุการณ์' ของ jQuery
Rudie

@ รูดี้: Re event.target: สิ่งที่คุณอธิบายไม่ผิดมันถูกต้อง หากคุณคลิกimgภายในdivและคุณกำลังดูdiv, event.targetเป็นimg(และthisเป็นdiv) ลองดูอีกครั้งที่ด้านบน คุณสามารถแนบตัวจัดการที่จุดใดก็ได้ในห่วงโซ่ระหว่างองค์ประกอบที่ถูกคลิก ( imgตัวอย่างเช่น) และองค์ประกอบที่คุณกำลังดู (ในด้านบนลงไปจนสุดdocument.body) Re live: ฉันชอบdelegateมากเวอร์ชันที่มีอยู่มากกว่าของlive. ฉันใช้liveข้างต้นเพราะใกล้เคียงที่สุดกับสิ่งที่ฉันทำในตัวอย่างดิบ ดีที่สุด
TJ Crowder

4

คุณอาจต้องการสร้างฟังก์ชันตัวช่วยดังนี้:

function defineAndRun(name, func) {
    window[name] = func;
    func();
}

defineAndRun('addEventsAndStuff', function() {
    alert('oele');
});

2
เหตุใดจึงเป็นทางออกที่ไม่ดี เนื่องจากฟังก์ชันได้รับการลงทะเบียนในเนมสเปซส่วนกลาง? ใครโหวตให้เรื่องนี้?
Rudie

ความคิดที่ดี
:)

4

รหัสของคุณมีการพิมพ์ผิด:

(function addEventsAndStuff() {
  alert('oele');
)/*typo here, should be }*/)();

ดังนั้น

(function addEventsAndStuff() {
  alert('oele');
 })();

ได้ผล ไชโย!

[ แก้ไข ] ตามความคิดเห็น: และสิ่งนี้ควรเรียกใช้และส่งคืนฟังก์ชันในครั้งเดียว:

var addEventsAndStuff = (
 function(){
  var addeventsandstuff =  function(){
    alert('oele');
  };
  addeventsandstuff();
  return addeventsandstuff;
 }()
);

วงเล็บโง่ ๆ เหมือนกันหมด !! ขอบคุณ! ไชโย!
Rudie

ใช่ ... เอ๊ะ ... ที่จริงไม่ได้ผล ฟังก์ชั่นจะทำงานทันทีใช่ แต่ไม่ได้ลงทะเบียนที่ใดจึงเรียกอีกครั้ง ->ReferenceError
Rudie

@Rudie: เพิ่งค้นพบว่าในที่สุด KomodoEdit ก็ทำทุกอย่างที่ฉันอยากให้ Aptana Studio ทำ (แต่พังตลอดเวลา) และยิ่งไปกว่านั้นมันสามารถกำหนดค่าได้สูงรวดเร็วและเต็มไปด้วยส่วนเสริมที่มีประโยชน์ และไฮไลต์วงเล็บที่ตรงกัน: คุณยังสามารถกำหนดสีเตือนสีแดงให้กับวงเล็บที่ตรงกันได้อีกด้วย! ทดลองใช้ฟรี: activestate.com/komodo-edit
KooiInc

เพื่อน ... (และเห็นได้ชัดว่าฉันพิมพ์ลงในโปรแกรมแก้ไขเว็บ SO ไม่ใช่ในโปรแกรมแก้ไขเดสก์ท็อป)
Rudie

และนั่นเป็นการพิมพ์พิเศษจำนวนมากและจดจำสิ่งที่และวิธีการพิมพ์ ฉันชอบโซลูชันดั้งเดิม (ใช้งานไม่ได้) แต่วิธีใหม่นั้นยากเกินไป =)
Rudie

2

ง่ายกว่าด้วย ES6:

var result = ((a, b) => `${a} ${b}`)('Hello','World')
// result = "Hello World"
var result2 = (a => a*2)(5)
// result2 = 10
var result3 = (concat_two = (a, b) => `${a} ${b}`)('Hello','World')
// result3 = "Hello World"
concat_two("My name", "is Foo")
// "My name is Foo"

แล้วฉันจะเรียกมันอีกครั้งได้อย่างไร? ชื่ออะไรเรียกตาม? คุณเอาแต่เก็บผลลัพธ์ไม่ใช่ฟังก์ชั่น
Rudie

วิธีนี้ใช้ในบางกรณีเฉพาะในกรณีที่คุณต้องการเรียกใช้ทันทีและคุณไม่ต้องการเรียกใช้ซ้ำ ยังไงก็ตามฉันจะแก้ไขคำตอบเพื่อแสดงว่าจะสามารถเรียกมันได้อีกครั้งคุณคิดว่าอย่างไร? @Rudie
Alberto

1
คำถามของฉันเกี่ยวกับการตั้งชื่อและเรียกใช้งานใหม่ concat_twoทำงานอย่างสมบูรณ์แบบ "use strict"แต่ก็มักจะกำหนดไว้ในขอบเขตทั่วโลกดังนั้นมันจะไม่ทำงานกับ นั่นไม่สำคัญสำหรับฉัน แต่มันเป็นข้อเสียเล็กน้อย
Rudie

1

หากคุณต้องการสร้างฟังก์ชันและดำเนินการทันที -

// this will create as well as execute the function a()
(a=function a() {alert("test");})();

// this will execute the function a() i.e. alert("test")
a();

1
ระวังเมื่อคุณใช้ฟังก์ชันที่มีชื่อเป็นค่าทางขวามือ (ตามที่คุณทำด้านบน) คุณกำลังใช้นิพจน์ฟังก์ชันที่มีชื่อซึ่งในขณะที่ควรจะใช้ได้ แต่น่าเสียดายที่มีปัญหาในการใช้งานต่างๆ (ส่วนใหญ่ - quelle shock - IE ). รายละเอียด: kangax.github.com/nfe
TJ Crowder

0

ลองทำเช่นนั้น:

var addEventsAndStuff = (function(){
    var func = function(){
        alert('ole!');
    };
    func();
    return func;
})();

1
สิ่งนี้อยู่ใกล้ แต่var foo = (function() {...})();เรียกใช้ฟังก์ชันก่อนการกำหนด วงเล็บต้องรวมงานที่มอบหมาย (คุณต้องการมอบหมายแล้วโทร)
เดวิด

นอกจากนี้ยังมีการพิมพ์จำนวนมากและการจดจำสิ่งที่ต้องพิมพ์ จากนั้นฉันก็แค่ใช้การโทรปัจจุบันของฉัน
Rudie
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.