Tôi có thể đặt tên cho một hàm JavaScript và thực thi nó ngay lập tức không?


90

Tôi có khá nhiều trong số này:

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

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

Việc thêm lại các sự kiện là cần thiết vì DOM đã thay đổi, vì vậy các sự kiện được đính kèm trước đó sẽ biến mất. Vì chúng cũng phải được gắn vào ban đầu (duh), chúng có một chức năng tốt là KHÔ.

Không có gì sai với thiết lập này (hoặc có?), Nhưng tôi có thể làm cho nó một chút được không? Tôi muốn tạo addEventsAndStuff()hàm và gọi nó ngay lập tức, vì vậy nó trông không nghiệp dư cho lắm.

Cả hai câu trả lời sau đều có lỗi cú pháp:

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

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

Bất kỳ người dự thi?



2
Không, @CristianSanchez.
Máxima Alekz

Imho khối mã thứ hai của bạn khó đọc. Trong khối mã đầu tiên, mọi người đọc phải hiểu rõ rằng bạn gọi hàm sau init. Người đọc có xu hướng bỏ qua việc đóng và đọc quá dấu ngoặc ở cuối.
kaiser

Câu trả lời:


123

Không có gì sai với ví dụ bạn đã đăng trong câu hỏi của mình .. Cách làm khác có thể trông kỳ quặc, nhưng:

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

Có hai cách để xác định một hàm trong JavaScript. Một khai báo hàm:

function foo(){ ... }

và một biểu thức hàm, là bất kỳ cách nào để xác định một hàm khác với cách trên:

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

...Vân vân

biểu thức hàm có thể được đặt tên, nhưng tên của chúng không được truyền đến phạm vi chứa. Có nghĩa là mã này hợp lệ:

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

nhưng đây không phải là:

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

Vì vậy, để đặt tên cho hàm của bạn và ngay lập tức gọi nó, bạn cần xác định một biến cục bộ, gán hàm của bạn cho nó dưới dạng một biểu thức, sau đó gọi nó.


1
"biểu thức hàm có thể được đặt tên" Hãy cẩn thận với các biểu thức hàm được đặt tên. Chúng sẽ hoạt động như bạn mô tả, nhưng chúng không hoạt động trên IE trước IE9 - bạn nhận được hai hàm và tên hàm bị rò rỉ. Chi tiết: kangax.github.com/nfe (Điều này cuối cùng đã được khắc phục trong IE9.)
TJ Crowder

@TJ - cảm ơn bạn đã tham khảo. Tôi không nhận ra điều đó vì tôi chưa bao giờ sử dụng IE ngoại trừ để kiểm tra các sản phẩm cuối cùng :) Tôi giả sử rằng IE9 không mô phỏng lỗi này khi đặt nó ở chế độ IE7 / 8? Tôi nghĩ rằng nó vẫn sử dụng động cơ IE9 JS ... Meh
Đánh dấu Kahn

Nếu, giống như tôi, bạn gặp sự cố jshint với phương pháp này, bạn có thể sử dụng call()phương pháp từ đây: stackoverflow.com/a/12359154/1231001
cfx

Cách tiếp cận này có vẻ hữu ích trong một số trường hợp nhất định, nhưng bên cạnh việc khó đọc hơn một chút, nó dường như có lượng văn bản gần như tương đương.
Vladimir

16

Có một cách viết nhanh cho điều này (không cần khai báo bất kỳ biến nào trong việc gán hàm):

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

rHàm nào sẽ trả về? Cái function rhay cái var r? Chỉ tò mò. Giải pháp tốt. Không phải là một loc dù =)
Rudie

Tôi đã đổi tên nó để rõ ràng hơn, nhưng return rsẽ function rlà như vậy vì phạm vi của nó. Đã viết cho Scala cố gắng để có được một cái gì đó trên một dòng.
James Gorrie

@JamesGorrie, viết tắt Brilliant, tuy nhiên, tôi đã thử hai lần trong bảng điều khiển, có vẻ như func, func (), func () () đều trả về khai báo hàm của f (), nhưng liệu chúng tôi có thể xuất 'Blammo' bằng func () như mong đợi không? :)?
mike652638

@ mike652638 Tôi đã chạy nó trong bảng điều khiển của mình và bảng điều khiển nó ghi nhật ký blammo?
James Gorrie

5

Không có gì sai với thiết lập này (hoặc có?), Nhưng tôi có thể làm cho nó một chút được không?

Thay vào đó, hãy xem việc sử dụng ủy quyền sự kiện. Đó là nơi bạn thực sự theo dõi sự kiện trên một vùng chứa không biến mất, sau đó sử dụng event.target(hoặcevent.srcElement trên IE) để tìm ra nơi thực sự xảy ra sự kiện và xử lý nó một cách chính xác.

Bằng cách đó, bạn chỉ đính kèm (các) trình xử lý một lần và chúng tiếp tục hoạt động ngay cả khi bạn hoán đổi nội dung.

Dưới đây là một ví dụ về ủy quyền sự kiện mà không cần sử dụng bất kỳ libs trợ giúp nào:

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

})();

Ví dụ trực tiếp

Lưu ý cách nếu bạn nhấp vào các thông báo được thêm động vào trang, nhấp chuột của bạn sẽ được đăng ký và xử lý mặc dù không có mã nào để nối các sự kiện trên các đoạn mới được thêm vào. Cũng lưu ý rằng trình xử lý của bạn chỉ là các mục nhập trong bản đồ và bạn có một trình xử lý trên document.bodyđó thực hiện tất cả việc điều động. Bây giờ, bạn có thể bắt nguồn từ điều này vào một cái gì đó được nhắm mục tiêu nhiều hơn document.body, nhưng bạn có được ý tưởng. Ngoài ra, ở phần trên, về cơ bản chúng tôi sẽ điều chỉnh id, nhưng bạn có thể thực hiện kết hợp phức tạp hoặc đơn giản tùy thích.

Các thư viện JavaScript hiện đại như jQuery , Prototype , YUI , Closure hoặc bất kỳ thư viện nào khác sẽ cung cấp các tính năng ủy quyền sự kiện để giải quyết các khác biệt của trình duyệt và xử lý các trường hợp cạnh một cách rõ ràng. jQuery chắc chắn có, với cả nó livevà các delegatechức năng, cho phép bạn chỉ định các trình xử lý bằng cách sử dụng đầy đủ các bộ chọn CSS3 (và sau đó là một số).

Ví dụ: đây là mã tương đương bằng cách sử dụng jQuery (ngoại trừ tôi chắc chắn rằng jQuery xử lý các trường hợp cạnh mà phiên bản thô off-the-cuff ở trên không):

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

Bản sao trực tiếp


Tôi không muốn sử dụng Event.target, vì đó không phải là mục tiêu phù hợp. Đúng rồi. Nếu bạn nhấp vào IMGbên trong a DIVDIVcó sự kiện, Event.targetsẽ là IMGvà không có cách nào để lấy được DIVđối tượng một cách độc đáo. chỉnh sửa Btw Tôi ghét .live'sự kiện' của jQuery
Rudie

@Rudie: Re event.target: Những gì bạn mô tả không sai, chính xác. Nếu bạn nhấp vào imgbên trong a divvà bạn đang xem div, event.targetimg(và thisdiv). Hãy nhìn lại những điều trên. Bạn có thể đính kèm một trình xử lý tại bất kỳ điểm nào trong chuỗi giữa phần tử đã được nhấp ( imgví dụ:) và phần tử bạn đang xem (ở phần trên, ở phần dưới document.body). Re live: Tôi rất thích delegate, phiên bản chứa đựng nhiều hơn live. Tôi đã sử dụng liveở trên vì nó gần nhất với những gì tôi đã làm trong ví dụ thô. Tốt nhất,
TJ Crowder

4

Bạn có thể muốn tạo một hàm trợ giúp như sau:

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

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

2
Tại sao đây là một giải pháp tồi? Bởi vì các chức năng được đăng ký trong không gian tên chung? Ai đã bỏ phiếu cho điều này?
Rudie

Ý tưởng tuyệt vời:)) -
Máxima Alekz

4

Mã của bạn có lỗi đánh máy:

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

vì thế

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

làm. Chúc mừng!

[ sửa ] dựa trên nhận xét: và điều này sẽ chạy và trả về hàm trong một lần:

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

Dấu ngoặc ngu ngốc trông giống nhau !! Cảm ơn! Chúc mừng thực sự!
Rudie

Yeah ... Vậy eh ... Thực ra điều đó không hiệu quả. Hàm được thực thi ngay lập tức, có, nhưng nó không được đăng ký ở bất kỳ đâu, vì vậy hãy gọi lại nó ->ReferenceError
Rudie

@Rudie: vừa phát hiện ra rằng KomodoEdit cuối cùng cũng làm được mọi thứ mà tôi luôn muốn Aptana Studio làm (nhưng bị hỏng mọi lúc) và trên hết là nó có thể cấu hình cao, nhanh chóng và được đóng gói với các addon hữu ích. Và nó làm nổi bật các dấu ngoặc phù hợp: bạn thậm chí có thể cung cấp cho các dấu ngoặc vuông đó một màu cảnh báo chắc chắn màu đỏ! Cung cấp cho nó một thử, nó hoàn toàn miễn phí: activestate.com/komodo-edit
KooiInc

Dude ... (Và rõ ràng là tôi đã nhập nó trong trình chỉnh sửa web SO, không phải trong trình chỉnh sửa trên máy tính để bàn.)
Rudie

Và đó là rất nhiều thao tác gõ thêm và ghi nhớ những gì và cách gõ. Tôi thích bản gốc giải pháp (không hoạt động), nhưng cái mới là quá khó =)
Rudie

2

Đơn giản hơn nữa với 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"

Vì vậy, làm thế nào tôi sẽ gọi nó một lần nữa? Tên của nó là gì, để gọi nó bằng? Bạn chỉ giữ kết quả, không phải chức năng.
Rudie

Phương pháp này chỉ được sử dụng trong một số trường hợp nếu bạn cần gọi nó ngay lập tức và bạn không muốn chạy lại. Dù sao tôi sẽ chỉnh sửa câu trả lời để hiển thị cách có thể gọi lại nó, bạn nghĩ sao? @Rudie
Alberto

1
Câu hỏi của tôi là về việc đặt tên và chạy lại nó. concat_twohoạt động hoàn hảo NHƯNG nó luôn định nghĩa nó trong phạm vi toàn cục, vì vậy nó sẽ không hoạt động với "use strict". Điều đó không quan trọng đối với tôi, nhưng đó là một nhược điểm nhỏ.
Rudie

1

Nếu bạn muốn tạo một hàm và thực thi ngay lập tức -

// 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
Hãy lưu ý, khi bạn sử dụng một hàm được đặt tên làm giá trị bên phải (như bạn làm ở trên), bạn đang sử dụng một biểu thức hàm được đặt tên mặc dù nó phải hợp lệ nhưng không may có vấn đề trong các triển khai khác nhau (chủ yếu là - quelle shock - IE ). Chi tiết: kangax.github.com/nfe
TJ Crowder

0

Cố gắng làm như vậy:

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

1
Điều này là gần, nhưng var foo = (function() {...})();gọi hàm trước nhiệm vụ. Dấu ngoặc cần bao gồm bài tập. (Bạn có muốn gán sau đó gọi.)
David

Đó cũng là cách gõ rất nhiều VÀ nhớ những gì cần gõ. Sau đó, tôi chỉ muốn sử dụng các cuộc gọi hiện tại của tôi.
Rudie
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.