2010-07-12 41 views
12

Có thể xác định lại chức năng JavaScript từ bên trong cơ thể của chính nó. Ví dụ, tôi có thể làm như sau?Chức năng JavaScript định nghĩa lại

function never_called_again(args) { 
    // Do some stuff 
    never_called_again = function (new_args) { 
    // Do some new stuff 
    } 
} 

Ở trên có hợp lệ và nó có ngữ nghĩa chính xác không? Tôi không muốn tạo một biến toàn cầu mới với tên hàm cũ, bởi vì tôi đang cố gắng thực hiện loại điều này không nằm trong phạm vi toàn cục, nhưng từ các phạm vi đối tượng khác nhau và tôi không muốn các xung đột tên khi tôi xác định lại chức năng trong phạm vi địa phương đó.

+2

Tôi không biết liệu bạn có thể hay không nhưng nó không có vẻ giống như một điều may mắn để làm. Tôi rõ ràng không biết hoàn cảnh của bạn là gì nhưng điều này nghe có vẻ như nó có một tiềm năng lớn cho sự nhầm lẫn, khó để theo dõi lỗi và khó đọc mã. Tôi muốn suy nghĩ kỹ về việc đây có phải là giải pháp đúng hay nếu bạn muốn sao lưu một bước và đặt câu hỏi đưa bạn đến đây như một câu trả lời. Xin lỗi nếu bạn đã xem xét tất cả các tùy chọn mặc dù. Bạn không bao giờ có thể nói những gì ai đó có thoguth về đã hoặc không ... – Chris

+5

có sử dụng hợp pháp cho điều này, và đây là một điều mạnh mẽ. Mọi người sợ những gì họ không biết, nhưng điều này không có nghĩa là họ xấu. – galambalazs

+0

Lưu ý rằng kỹ thuật này không thực sự xác định lại hàm, nó thay đổi hàm nào 'never_called_again' đề cập đến.Hàm ban đầu không nhất thiết phải biến mất, cho rằng (mặc dù bạn không làm như vậy ở đây) có thể tạo một biến khác tham chiếu đến hàm ban đầu: đặt 'original = never_called_again;' trước khi gán lại 'never_called_again' và' original () 'sẽ gọi bản gốc ... – nnnnnn

Trả lời

14

Có, bạn có thể xác định lại chức năng theo cách đó.

Chạy này:

function never_called_again(args) { 
    console.log('Func 1', args); 

    never_called_again = function (new_args, x) { 
     console.log('Func 2', new_args, x); 
    } 
} 

never_called_again('arg A', 'arg B'); 

never_called_again('arg A', 'arg B'); 

sản lượng này:

Func 1 arg A 
Func 2 arg A arg B 
+1

@galambalazs: Tôi đã cho bạn tín dụng và tôi đã mở rộng về các khía cạnh bạn không đề cập đến sự hài lòng của tôi. Ngoài ra, câu trả lời đầu tiên và câu trả lời chính của tôi không liên quan gì đến bạn và đã đủ. –

+1

Đó là ** không phải là một wiki cộng đồng **. Biến toàn cầu không liên quan gì đến chức năng định nghĩa lại, chúng chỉ liên quan đến Func Lazy. Định nghĩa mẫu, là một ví dụ cho chức năng định nghĩa lại. Tất cả mọi thứ được giải thích tốt trong liên kết btw ... Nhưng một lần nữa nó chỉ là một ví dụ. Câu trả lời của bạn là chặt chẽ và không có phiếu bầu nào đó là lý do tại sao bạn thay đổi nó ... – galambalazs

+1

@galambalazs: Không, trước khi tôi thay đổi nó, tôi đã có nhiều phiếu bầu như bạn (1 người) - trước khi tôi upvoted bạn. Tôi đã thay đổi câu trả lời của mình để làm cho nó hoàn chỉnh hơn, kết hợp các bài học từ bài báo bạn trích dẫn mà bạn đã không làm. Hơn nữa tôi đã cho bạn tín dụng. Tôi đã thấy những người khác làm điều này. Đó là nghĩa vụ phải được cải thiện câu trả lời, không phải là ngớ ngẩn snits. Theo logic của bạn, Peter Michaux nên ở sau bạn vì đạo văn. Dù sao, tôi đã cuộn lại câu trả lời của tôi –

2

dụ của bạn là cơ bản giống như:

var never_called_again = function(args) { 
    //do some stuff 
    never_called_again = function (new_args) { 
     //do some new stuff 
    } 
} 

Vì bạn có thể biết rằng công trình này:

var a = 1; 

function myFunction() { 
    //do some stuff 
    a = 2; 
} 

myFunction(); 

alert(a) //==> 2 

rõ ràng là ví dụ đầu tiên làm việc quá.

Cho dù bạn thực sự muốn để làm điều này hay không là một câu hỏi mở hơn. Nó sẽ làm cho mã khó hiểu hơn?

40

Nó thực sự là thể để xác định lại một chức năng từ cơ thể của nó. Kỹ thuật này được sử dụng trong cái gọi là Lazy Function Definition Pattern.

Nó trông như thế này:

// Lazy Function Definition Pattern 
var foo = function() { 
    var t = new Date(); 
    foo = function() { 
     return t; 
    }; 
    return foo(); 
} 

Chức năng này lưu trữ các ngày của cuộc gọi đầu tiên, và trả về ngày này sau đó.

Nếu bạn so sánh này với mô hình mô-đun, sự khác biệt là việc khởi tạo chỉ xảy ra khi nó lần đầu tiên được gọi là, không phải khi nó được định nghĩa. Để khởi tạo tốn kém, nó có thể là một lợi ích lớn .

// Conditional Module Pattern 
var foo = (function() { 
    var t; 
    return function() { 
     if (t) { 
      return t; 
     } 
     t = new Date(); 
     return t; 
    } 
})(); 
+0

Đẹp nhất. snip 8 <--- – mplungjan

+0

Cảm ơn bạn đã liên kết nó khá thú vị và tôi đang sử dụng mẫu theo cách tương tự để tránh các đường dẫn thực thi có điều kiện khác nhau. – davidk01

1

Có, có thể và sẽ không tạo ra chức năng toàn cầu. Tôi đã xác minh điều này trong Internet   Trình khám phá   6, Firefox, Chrome và Opera. Xét đoạn mã sau:

<head> 
    <title>Never called again</title> 
    <style type="text/css"> 
    </style> 

    <script type="text/javascript"> 

      function hello() { 
      function never_called_again(args) { 
       alert("Hello world " + never_called_again); 
       //do some stuff 
       never_called_again = function (new_args) { 
        //do some new stuff 
        alert("hello " + never_called_again); 
       } 
      } 
      never_called_again(); 
      never_called_again(); 

      } 

     </script> 

    </head> 
    <body onload=""> 

    <button onclick="hello(); never_called_again();">Call</button> 
    </body> 

này sẽ in "Hello World {function code}" lần đầu tiên của never_called_again và lần thứ hai nó sẽ in "hello {thay đổi mã chức năng}" lần thứ hai.

Nhưng khi nó được gọi trên nút bấm onclick, nó sẽ ném một lỗi nói rằng chức năng không được xác định, rõ ràng chỉ ra rằng các chức năng đã được xác định lại (và không được tạo ra trên toàn cầu).

3

Hoàn toàn có thể xác định lại đối tượng hàm thành bất kỳ thứ gì bạn muốn khi thực hiện cuộc gọi đầu tiên.

Lý do điều này có thể là do các đánh giá bài tập được xử lý từ phải sang trái. Điều này có nghĩa là phạm vi của hàm được bắt khi bạn thực hiện nó (trong trường hợp này, khi bạn gọi hàm này). Điều này thực sự có nghĩa là tại thời điểm thực hiện, đối tượng chức năng đang được thực thi và định nghĩa hàm gốc thực sự là hai thứ khác nhau, cho phép định nghĩa lại chức năng ban đầu như, vâng ... bất cứ điều gì thực sự.

Một trong những cách tốt nhất (và thú vị nhất) để sử dụng điều này là tạo ra một chức năng thực chất là nhà máy riêng của nó. Bằng cách này bạn không cần phải mang theo rất nhiều định nghĩa đối tượng sẽ không được sử dụng.

Ví dụ:

d = function(type){ 
    switch(type.toUpperCase()){ 
     case 'A' : 
      d = (
        function(){ 
         return { 
          name : 'A', 
          getName : function(){return this.name;} 
         }; 
        } 
       )(); 
       break; 

     case 'B' : 
      d = (
       function(){ 
        return { 
         name : 'B', 
         getName : function(){return this.name;} 
        }; 
       } 
      )(); 
      break; 

     default: 
      d = (
       function(){ 
        return { 
         name : 'DEFAULT', 
         getName : function(){return this.name;} 
        }; 
       } 
      )(); 
      break; 
    } 
}; 

console.dir(d); 
d('A'); 
console.dir(d); 
console.log(d.getName()); 
1

Nói đúng ra, không, không thể để xác định lại một hàm JavaScript cả. Mặc dù đó là vấn đề ngữ nghĩa.

Chức năng có thể ghi đè tham chiếu của nó trong một phạm vi nhất định với chức năng khác, hoạt động như một bàn tay trong đó một hàm khác xuất hiện ở vị trí đầu tiên. ví dụ.

greet_once = function() { 
    console.log('Hello.'); 
    greet_once = function() { console.log('Can I help you?') }; 
}; 

Tương tự, một hàm có thể sử dụng trạng thái của khoảng thời gian thứ hai được gọi (không rõ ràng là "định nghĩa lại").

Ví dụ sau minh họa lý do tại sao mẫu trên không định nghĩa lại vì nó phụ thuộc vào phạm vi gốc có thể không được chia sẻ với phạm vi gọi.

greet_always = function() { 
    greet_once = function() { 
    console.log('Hello.') 
    greet_once = function() { console.log('Can I help you?') } 
    }; 
    return greet_once() 
}() 

greet_always() 
greet_always() 

Chức năng được gọi là greet_always (mà luôn luôn in 'Hello.') Là một trong những giống, phân công ban đầu để greet_once. Gần nhất tôi tin rằng bạn có thể xác định lại chức năng là gán lại các phương thức áp dụng và gọi, tạo ra một kịch bản trong đó myFunction.call() khác với myFunction(), điều này thật kỳ lạ nhưng hữu ích.

Các vấn đề liên quan