2012/02/09

呼叫自定義的 Javascript 函式

最近碰到一個有趣的事情,不過說不定其實只是很基礎的問題。
(我基礎沒打好,所以說不定這問題根本不是問題 ...)

果然是很基礎的問題:
Javascript 的 eval 行為與 function pointer

在 http://html5boilerplate.com 的環境下,我寫了這段 javascript 在 script.js 檔:
Test 1
(function($){
$(document).ready(function(){

  function sayHi()
  {
    console.log('hello');
  }

  $(function()
  {
    setInterval( "sayHi()", 2000 );
  });

}); // end of $(document).ready
}(jQuery)); // end of all
在執行的時候,Firefox 說:sayHi is not defined

Test 2
(function($){
$(document).ready(function(){

  $(function(){

    function sayHi()
    {
      console.log('hello');
    }

    setInterval( "sayHi()", 2000 );
  });

}); // end of $(document).ready
}(jQuery)); // end of all
一樣是 sayHi is not defined.

因為 sayHi() 是在:
(function($){ 底下的 ...
$(document).ready(function(){ 底下的 ...
$(function(){ 底下的 scope 內,
而使用雙引號 "sayHi()" 呼叫時,Javascript 會尋找全域環境下的 sayHi(),當然找不到。

Test 3
(function($){
$(document).ready(function(){

  $(function(){

    function sayHi()
    {
      console.log('hello');
    }

    setInterval( sayHi(), 2000 );
  });

}); // end of $(document).ready
}(jQuery)); // end of all
可以執行,但是只執行一次。

因為呼叫時加了 () 等於是執行這個 function,而不是 refer 到這個 function 而不執行。
function 在 Javascript 是類似 object(打類比成 Linux 把所有東西看成檔案一樣)。

Test 4
(function($){
$(document).ready(function(){

  $(function(){

    function sayHi()
    {
      console.log('hello');
    }

    setInterval( sayHi, 2000 );
  });

}); // end of $(document).ready
}(jQuery)); // end of all
it works.

sayHi function 的 scope 在:
(function($){ 底下的 ...
$(document).ready(function(){ 底下的 ...
$(function(){ 底下。

setInterval 內的 sayHi refer 到同一層的 sayHi function(object?)

Test 5
(function($){
$(document).ready(function(){

  $(function()
  {
    setInterval( function(){ console.log('hello'); }, 2000 );
  });

}); // end of $(document).ready
}(jQuery)); // end of all
it works.
直接綁定一個匿名函式,沒有問題。

Test 6
(function($){
$(document).ready(function(){

  function sayHi()
  {
    console.log('hello');
  }

  $(function()
  {
    setInterval( sayHi, 2000 );
  });

}); // end of $(document).ready
}(jQuery)); // end of all
it works!?

Test 7
function sayHi()
{
  console.log('hello');
}

$(function()
{
  setInterval( "sayHi()", 2000 );
});
it works.

(function($){ 與 $(document).ready(function(){ 都要拿掉,留下任何一個也不行。
如此一來,宣告的 sayHi function 才是在全域範圍下,"sayHi()" 才 refer 得到。

最後,如果要宣告一個全域的 Javascript function,
或是說“假裝”成 Class 那樣用(Javascript 是 classless 的!),
可以這樣做:
var ClassName = window.ClassName = function()
{
    // your code
};
    
ClassName.prototype.method_of_class = function()
{
    // your code
};

function pointer
function scope

Reference:
http://stackoverflow.com/questions/1191833/how-to-run-a-function-in-jquery
http://jquery-howto.blogspot.com/2008/12/what-heck-is-function-jquery.html
http://jibbering.com/faq/notes/closures/
http://www.javascriptkit.com/javatutors/closures.shtml
http://www.robertsosinski.com/2009/04/28/binding-scope-in-javascript/
http://audi.tw/Blog/JavaScript/javascript.eval.asp
http://24ways.org/2005/dont-be-eval
http://docs.jquery.com/How_jQuery_Works
http://jquery-howto.blogspot.com/2008/12/what-heck-is-function-jquery.html