Javascript 变量未定义

Javascript Variable is not defined

为什么我移动鼠标总是提示undefined?它应该 return 正确,对吧?我希望有人能帮助我 :) 在你问之前我在另一个 JavaScript 文件中调用了 class!

class Block {

 constructor() {

    this.movable = true;
    this.move = false;

    this.div = document.createElement("div");
    this.div.classList.add("block");

    this.div.addEventListener("mousemove", function() {

        console.log(this.movable); 

    });
    this.div.addEventListener("mousedown", function() {

        this.move = true;
        console.log("test");

    });
    this.div.addEventListener("mouseup", function() {

        this.move = false;
        console.log("test1");

    });

 }
 add() {

     document.body.appendChild(this.div);

 }
 remove() {

     document.body.removeChild(this.div);

 }

}

你必须研究 javascript 上下文。

每个函数都有自己的 this。所以如果你应用外部this,你应该绑定这个。

参考:https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this#bind_method

class Block {
    constructor() {
        this.movable = true;
        this.move = false;

        this.div = document.createElement("div");
        this.div.classList.add("block");

        this.div.addEventListener("mousemove", function() {
            console.log(this.movable); 
        }.bind(this)); // bind this!!
        this.div.addEventListener("mousedown", function() {
            this.move = true;
            console.log("test");
        }.bind(this)); // bind this!!
        this.div.addEventListener("mouseup", function() {
            this.move = false;
            console.log("test1");
        }.bind(this)); // bind this!!

    }
    add() {
        document.body.appendChild(this.div);
    }
    remove() {
        document.body.removeChild(this.div);
    }
}

这是由于 this 范围界定。

事实证明,您的构造函数中的 this 与回调中的不同:

class Block {
    constructor() {
        // here this is your instance of your class block
        this.movable = true;


        this.div.addEventListener("mousemove", function() {
            // here it's not since each function get's it's own from how it's called
            // since it is called by `div.addEventListener` and not you directly
            // JS had no way to get the root this
            console.log(this.movable); 

        });
    }
}

最简单的解决方案是使用 ES6 箭头函数。

class Block {
    constructor() {
        // here this is your instance of your class block
        this.movable = true;


        this.div.addEventListener("mousemove", () => {
            // this is bound to your instance of class block in this scope too !
            console.log(this.movable); 
        });
    }
}

注意 () => { 而不是 function () { !

这是可行的,因为箭头函数没有它们自己的 this,而是保留它们可以访问它们创建位置的那个,非常方便。

你可以使用ES6箭头函数()=>{}来解决你的问题。箭头函数使用当前执行上下文的 this。在这种情况下,它是具有初始化 movable 属性.

class 的实例

普通匿名函数将使用侦听器附加到的对象的 this,在您的情况下是 div。由于 div 没有 movable 属性 你得到 undefined.

class Test{

 constructor() {
      this.movable = true;
      this.move = false;

      this.div = document.createElement("div");
    
      this.div.classList.add("block");

      this.div.addEventListener("mousemove", ()=> {
          console.log(this.movable); 
      });
      this.div.addEventListener("mousedown", ()=> {
          this.move = true;
          console.log("test");
      });
      this.div.addEventListener("mouseup", ()=> {
          this.move = false;
          console.log("test1");
      });
    }

 add(){
     this.div.textContent ="Hover over me.";
     document.body.appendChild(this.div);
 }

 remove(){
     document.body.removeChild(this.div);
 }

}
var test = new Test();
test.add();
<body>
</body>

引用自 MDN:

An arrow function expression has a shorter syntax than a function expression and does not have its own this, arguments, super, or new.target. These function expressions are best suited for non-method functions, and they cannot be used as constructors.

正如其他人提到的,你得到 undefined 的原因是 this 在你的事件处理函数中 而不是 class(如您所料)。

您有三种方法来解决这个问题,其中两种基于 ES5,一种基于 ES6(这应该不是问题 - 您正在使用 ES6已经在你的代码中了)。

1 使用 Function.prototype.bind().

将外部 this 显式绑定到您的处理程序函数

bind() 告诉函数在其函数体内使用什么作为 this

class Test {
  constructor() {
    this.movable = true;
    this.move = false;
    this.div = document.createElement("div");
    this.div.classList.add("block");
    this.div.addEventListener("mousemove", function() {
      console.log(this.movable);
    }.bind(this)); // .bind(this)
    this.div.addEventListener("mousedown", function() {
      this.move = true;
      console.log("test");
    }.bind(this)); // .bind(this)
    this.div.addEventListener("mouseup", function() {
      this.move = false;
      console.log("test1");
    }.bind(this)); // .bind(this)
  }
  add() {
    this.div.textContent = "Test";
    document.body.appendChild(this.div);
  }
  remove() {
    document.body.removeChild(this.div);
  }
}
var test = new Test();
test.add();
<body>
</body>

2 将对 class 实例的引用存储在变量中(此处:self)并在处理函数中使用它。

这曾经是解决您遇到的问题的一种非常常见的模式。如今,随着每个人都跳上 ES6 列车,它的使用越来越少。我也喜欢它,因为它提高了可读性。

class Test {
  constructor() {
    const self = this; // reference to this stored in self
    this.movable = true;
    this.move = false;
    this.div = document.createElement("div");
    this.div.classList.add("block");
    this.div.addEventListener("mousemove", function() {
      console.log(self.movable); // self instead of this used
    });
    this.div.addEventListener("mousedown", function() {
      self.move = true; // self instead of this used
      console.log("test");
    });
    this.div.addEventListener("mouseup", fucntion() {
      self.move = false; // self instead of this used
      console.log("test1");
    });
  }
  add() {
    this.div.textContent = "Test";
    document.body.appendChild(this.div);
  }
  remove() {
    document.body.removeChild(this.div);
  }
}
var test = new Test();
test.add();
<body>
</body>

3 使用 ES6 箭头函数语法。

箭头函数没有它们自己的 this(这在某些情况下可能是一个缺点!),这使得它们可以使用“外部”this

class Test {
  constructor() {
    this.movable = true;
    this.move = false;
    this.div = document.createElement("div");
    this.div.classList.add("block");
    this.div.addEventListener("mousemove", () => {
      console.log(this.movable);
    });
    this.div.addEventListener("mousedown", () => {
      this.move = true;
      console.log("test");
    });
    this.div.addEventListener("mouseup", () => {
      this.move = false;
      console.log("test1");
    });
  }
  add() {
    this.div.textContent = "Test";
    document.body.appendChild(this.div);
  }
  remove() {
    document.body.removeChild(this.div);
  }
}
var test = new Test();
test.add();
<body>
</body>

正如其他人提到的,如果不使用箭头函数来定义事件处理程序,this 在处理程序内部通常会保存对引发事件的元素的引用(这通常非常有用)。尽管使用箭头函数仍要访问它,您可以使用 event.target 属性:

// notice I name a parameter e here
// which will hold the event object
// that is always passed to the event
// handler automatically - it just needs 
// a name to make it accessible inside
(e) => { console.log(e.target); }