2014-09-04 12 views
0

我想弄清楚爲什麼我的Google Chrome控制檯給我錯誤「undefined不是函數。」我有預感,但也許我走錯了路。我的功能boxCollision(...)在我班的底部定義。接近頂端我有一個聲明當你使用JavaScript函數作爲類時,它們是否仍然以自頂向下的方式被解析?

if (this.boxCollision(this.food.getBBox(), this.body[0].getBBox())) 
      this.food.translate(this.linkSize, 0); 

其中第一行導致我提到的錯誤。我想這可能是因爲我還沒有定義boxCollision,所以它基本上不存在。是對的嗎? getBBox()函數被識別,因爲它們來自外部JavaScript文件。

function snakegame(C, C_w, C_h, spd) 
    { 
      /* NOTE TO SELF: C is a Raphel object. Can't find a method to return the height 
       and width of a Raphael object in the documentation: 
       http://raphaeljs.com/reference.html#Raphael. 
       Using C_h and C_w for now, but should probably change it later. 
      */ 


      this.linkSize = 50; /* size of a snake unit, in pixels; must divide C_h and C_w */ 


      this.link = C.rect(C_h/2, C_w/2, this.linkSize, this.linkSize); 
      this.link.attr("fill", "#E9E581"); 
      this.body = [this.link]; 

      this.food = C.rect(randInt(0,C_w/this.linkSize-1) * this.linkSize, randInt(0,C_h/this.linkSize-1) * this.linkSize, this.linkSize, this.linkSize); 
      if (this.boxCollision(this.food.getBBox(), this.body[0].getBBox())) 
       this.food.translate(this.linkSize, 0); 
      this.food.attr("fill","#B43535"); 

      this.maxSnakeSize = C_h * C_w/(this.linkSize * this.linkSize); 

      /* On instantiation, the snake direction is down and has 1 link */ 
      this.dy = 0; 
      this.dx = 0; 


      this.score = 0; 

      /* Event listener for changing the direction of the 
       snake with arroy keys on the keyboard 
      */ 
      this.redirect = function(dirnum) 
      { 
       switch (dirnum) 
       { 
        /* 
         dirnum corresponds to 
         1 ---> right 
         2 ---> down 
         3 ---> left 
         4 ---> up 
        */ 
        case 1: 
         this.dx = this.linkSize; 
         this.dy = 0; 
         break; 

        case 2: 
         this.dx = 0; 
         this.dy = this.linkSize; 
         break; 

        case 3: 
         this.dx = -this.linkSize; 
         this.dy = 0; 
         break; 

        case 4: 
         this.dx = 0; 
         this.dy = -this.linkSize; 
         break; 

        default: /* never happens */ 
         break; 
       } 

      } 
      this.move = function() 
      { 

       if (this.body.length == this.maxSnakeSize) 
       { 
        this.destruct(); 
        return; 
       } 

       var addLink = false; 
       var BBhead = this.body[0].getBBox(); 
       if (this.hitWall(BBhead) || this.hitSnake(BBhead)) 
       { 
        document.getElementById("snakescorediv").innerHTML = "<p>GAME OVER!</p><p>Score: "+ this.score +"</p>"; 
        this.destruct(); 
        return; 
       } 
       var BBfood = this.food.getBBox(); 
       if (this.boxCollision(BBhead, BBfood)) 
       { 
        this.moveFood(); 
        this.score += 10; 
        document.getElementById("snakescorediv").innerHTML = this.score.toString(); 
        addLink = true; 
       } 
       if (addLink) 
        this.body.push(this.body[this.body.length - 1].clone()); 
       for (var i = this.body.length - 1; i > 0; --i) 
       { 
        var prevBB = this.body[i-1].getBBox(); 
        var thisBB = this.body[i].getBBox(); 
        this.body[i].translate(prevBB.x-thisBB.x, prevBB.y-thisBB.y) 
       } 
       this.body[0].translate(this.dx, this.dy); 

      } 

      this.mover = setInterval(this.move.bind(this), spd); 

      this.hitWall = function(bb) 
      { 
       return bb.x < 0 || bb.x2 > C_w || bb.y < 0 || bb.y2 > C_h; 
      } 

      this.hitSnake = function(bb) 
      { 
       var retval = false; 
       for (var i = 1, j = this.body.length; i < j; ++i) 
       { 
        var thisbb = this.body[i].getBBox(); 
        if (this.boxCollision(bb, thisbb)) 
        { 
         retval = true; 
         break; 
        } 
       } 
       return retval; 
      } 

      this.moveFood = function() 
      { 
       var bbf = this.food.getBBox(); // bounding box for food 
       do { 
       /* tx, ty: random translation units */ 
       tx = randInt(0, C_w/this.linkSize - 1) * this.linkSize - bbf.x; 
       ty = randInt(0, C_h/this.linkSize - 1) * this.linkSize - bbf.y; 
       // translate copy of food 
       this.food.translate(tx, ty); 
       bbf = this.food.getBBox(); // update bbf 
       } while (this.hitSnake(bbf)); 

      } 

      this.boxCollision = function(A, B) 
      { 
       return A.x == B.x && A.y == B.y; 
      } 


      this.destruct = function() 
      { 
       clearInterval(this.mover); 
       for (var i = 0, j = this.body.length; i < j; ++i) 
       { 
        this.body[i].removeData(); 
        this.body[i].remove(); 
       } 
       this.food.removeData(); 
       this.food.remove(); 
       this.score = 0; 
      } 

    } 

回答

1

將方法放在原型上以避免此問題。

這是行不通的:

function Ctor() { 
    this.init() 
    this.init = function() { 
    console.log('init') 
    } 
} 

var inst = new Ctor // Error: undefined is not a function 

但這會:

function Ctor() { 
    this.init() 
} 

Ctor.prototype.init = function() { 
    console.log('init') 
} 

var inst = new Ctor // init 
0

的JavaScript解析分兩步代碼:編譯和評估。

第一步是編譯。在這一步中,所有的定義都被編譯,但沒有語句或表達式被評估。這意味着的是,如定義:

function a() {} 

和:

var x 

被編譯到內存中。

在評估階段,JavaScript解釋器終於開始執行。這使它能夠處理運算符,從而可以執行語句和表達式。正是在這一步驟中的變量得到它們的值:

var x = 10; 
    ^^
    | |______ this part now gets assigned to `x` in the evaluation phase 
    | 
this part was processed in the compilation phase 

這意味着,對於函數表達式:

var x = function() {} 

而這兩個變量和函數體在編譯期進行編譯,匿名函數在評估階段之前未分配給變量。這是因爲=運算符只在評估階段執行(在編譯階段,所有變量都分配了內存並賦值爲undefined)。

編譯階段和評估階段均嚴格按照自上而下的順序進行。

有些人稱之爲「提升」僅僅是編譯階段發生在評估階段之前。

一種解決方法是簡單地使用函數定義而不是函數表達式。支持JavaScript的內部函數,以便在另一個函數定義的函數在全球範圍內不存在:

 function boxCollision (A, B) { 
      return A.x == B.x && A.y == B.y; 
     } 
     this.boxCollision = boxCollision; 

然後你就可以在你的構造函數的頂部使用它:

if (boxCollision(this.food.getBBox(), this.body[0].getBBox())) 
     this.food.translate(this.linkSize, 0); 

注意,你可以不會使用this.boxCollision,因爲它在調用時仍未定義。

另一個明顯的解決方法是在使用前在頂部指定this.boxCollision = function(){}

或者你甚至可以將它分配給構造函數的原型。或者你可以在頂部調用一個init函數(注意:函數,而不是方法 - 再次使用定義而不是函數表達式使用「提升」)。

有很多方法可以解決這個問題。但瞭解爲什麼會發生這種情況以瞭解什麼是有效的,什麼不可行。

有關此行爲的更多示例,請參閱我對此相關問題的回答:JavaScript function declaration and evaluation order

相關問題