i was writting a greedy snack game ,but i found that the class GreedySnack has too many big functions.so i want put the functions in other files and import them into the class file. but i jst dont know how to do.
part of the codes:
class GreedySnake {
constructor() {
this.canvas = document.querySelector("#snake");
this.ctx = this.canvas.getContext("2d");
this.maxX = 64; // max row
this.maxY = 40; // max col
this.itemWidth = 10; // size of the point
this.direction = "right"; // up down right left direction
this.speed = 150; // ms speed
this.isStop = false; //
this.isOver = false; //
this.isStart = false; //
this.score = 0; //
this.timer = null; // movement timer
this.j = 1;
this.canChange = true;
this.grid = new Array();
for (let i = 0; i < this.maxX; i++) {
for (let j = 0; j < this.maxY; j++) {
this.grid.push([i, j]);
}
}
// this.drawGridLine();
this.getDirection();
}
// create the body
createSnake() {
this.snake = [
[4, 25],
[3, 25],
[2, 25],
[1, 25],
[0, 25],
];
}
// movement
move() {
...
// if the next-step is not food
...
}
// start
start() {
...
}
// pause
stop() {
...
}
}
i've tired the require
createPos = require("./food");
createFood = require("./food");
snackClass.js:29 Uncaught ReferenceError: require is not defined
You can use functions defined elsewhere as functions in your class, although they won't be methods in the ES2015+ sense (specifically: they can't use super). They'll still be "methods" in the general sense that you call them on the instance and they work just like real methods. (They'll be like the methods we used to have before ES2015's class syntax.) You can even set them up using modern property syntax.
Here's a simple example:
// The functions declared elsewhere
function start() {
console.log(`Doing a start operation for ${this.name}`);
}
function stop() {
console.log(`Doing a stop operation for ${this.name}`);
}
// The class
class Example {
constructor(name) {
this.name = name;
}
// Using property syntax to set up the `start` and `stop` "methods"
start = start;
stop = stop;
}
// Using it
const e = new Example("test");
e.start();
e.stop();
Unlike true methods, those:
Will be "own" properties of each instance, not prototype properies
Will be enumerable
You could fix #1 by assigning them to your constructor functions' prototype object instead:
// The functions declared elsewhere
function start() {
console.log(`Doing a start operation for ${this.name}`);
}
function stop() {
console.log(`Doing a stop operation for ${this.name}`);
}
// The class
class Example {
constructor(name) {
this.name = name;
}
}
Object.assign(Example.prototype, { start, stop });
// Using it
const e = new Example("test");
e.start();
e.stop();
You can fix #2 by using Object.defineProperty (or Object.defineProperties):
// The functions declared elsewhere
function start() {
console.log(`Doing a start operation for ${this.name}`);
}
function stop() {
console.log(`Doing a stop operation for ${this.name}`);
}
// The class
class Example {
constructor(name) {
this.name = name;
}
}
for (const [name, method] of Object.entries({start, stop})) {
Object.defineProperty(Example.prototype, name, {
value: method,
writable: true,
configurable: true,
})
}
// Using it
const e = new Example("test");
e.start();
e.stop();
Related
I'm running into the undefinded error when trying to access the this.numberObject from another function init.
I'm pretty sure it's because my this keyword is referencing the window object.
My problem is that I can't figure out an elegant way to structure my code.
Current Code:
class IncrementNumbers {
constructor() {
this.numberObject = document.querySelector(".incrementNum");
if (this.numberObject != null) {
console.log(this.numberObject.innerHTML);
this.num = this.numberObject.innerHTML;
this.events();
}
}
events() {
console.log("events function ran");
this.init();
}
init() {
let target = 345;
let current = 0;
function addOneToElement() {
if (current === target) {
clearInterval(addOne);
}
current++;
this.numberObject.innerHTML = current; <---- ERROR HERE
}
let addOne = setInterval(addOneToElement, 10);
}
}
export default IncrementNumbers;
Error:
IncrementNumbers.js:44 Uncaught TypeError: Cannot set property 'innerHTML' of undefined
I can fix the error by adding in
this.numberObject = document.querySelector(".incrementNum");
to the following function
init() {
let target = 345;
let current = 0;
function addOneToElement() {
this.numberObject = document.querySelector(".incrementNum"); <--- ADDED HERE
if (current === target) {
clearInterval(addOne);
}
current++;
this.numberObject.innerHTML = current;
}
let addOne = setInterval(addOneToElement, 10);
}
However this feels a bit redundant because I'm already referencing the element Object in my constructor. I'd like the function, init to run once the page has loaded.
Is there a better way?
Thank you
Your problem seems to come from a misspropagation of the this object.
In your function init you set a new function named addOneToElement that is called inside of a setInterval, this setInterval instance does not have access to the this element of your class.
To fix the problem you could try to do something like
class IncrementNumbers {
constructor() {
this.numberObject = document.querySelector(".incrementNum");
if (this.numberObject != null) {
console.log(this.numberObject.innerHTML);
this.num = this.numberObject.innerHTML;
this.events();
}
}
events() {
console.log("events function ran");
this.init();
}
init() {
let target = 345;
let current = 0;
function addOneToElement() {
if (current === target) {
clearInterval(addOne);
}
current++;
this.numberObject.innerHTML = current; // <---- ERROR HERE
}
let addOne = setInterval(addOneToElement.bind(this), 10);
}
}
export default IncrementNumbers;
let addOne = setInterval(addOneToElement.bind(this), 10);
Where you bind the this instance of your class to the function addOneToElement.
The problem is the inner function addOneToElement that creates its own this context within init.
A simple fix would be to use arrow functions which don't have a this context:
class IncrementNumbers {
init() {
let target = 345;
let current = 0;
// Use an arrow function here.
const addOneToElement = () => {
if (current === target) {
clearInterval(addOne);
}
current++;
this.numberObject.innerHTML = current; <---- ERROR HERE
}
let addOne = setInterval(addOneToElement, 10);
}
}
Another option would be to bind the this context to addOneToElement:
let addOne = setInterval(addOneToElement.bind(this), 10);
How can we dynamically/programmatically extend a javascript class?
More concretely, given something like
class Polygon {
constructor(area, sides) {
this.area = area;
this.sides = sides;
}
}
const Rectangle = extend(Polygon, (length, width) => {
super(length * width, 4);
this.length = length;
this.width = width;
});
how can we implement something like extend such that it behaves the same as
class Rectangle extends Polygon {
constructor(length, width) {
super(length * width, 4);
this.length = length;
this.width = width;
}
}
?
There are three problems here:
(1) super is only available inside object methods, so there is no way to access super in an arrow function. That needs to be somehow replaced by a regular function call.
(2) Classes can only be constructed, not called (unlike functions acting as constructors). Therefore you cannot just .call the classes constructor onto the "subclass" instance. You have to create an instance of the superclass and copy that into the subclass, eventually loosing getters / setters.
(3) Arrow functions have a lexical this, so you cannot access the instance with this inside an arrow function.
Given these three problems, a viable alternative would be:
function extend(superclass, constructor) {
function Extended(...args) {
const _super = (...args) => Object.assign(this, new superclass(...args));
constructor.call(this, _super, ...args);
}
Object.setPrototypeOf(Extended, superclass);
Object.setPrototypeOf(Extended.prototype, superclass.prototype);
return Extended;
}
const Rectangle = extend(Polygon, function(_super, length, width) {
_super(/*...*/);
/*...*/
});
But honestly ... what's wrong with the native class ... extends ?
After some hacking around, I've found that this horrifyingly works.
function extend(superclass, construct) {
return class extends superclass {
constructor(...args) {
let _super = (...args2) => {
super(...args2)
return this;
};
construct(_super, ...args);
}
};
}
const Rectangle = extend(Polygon, function(_super, length, width) {
let _this = _super(length * width, 4);
_this.length = length;
_this.width = width;
});
class A {
m () {
console.log('A')
}
}
class B extends A {
m () {
console.log('B')
}
}
var a = new A()
var b = new B()
a.m()
b.m()
const MixinClass = superclass =>
class extends superclass {
m () {
console.log('extended')
}
}
const extendsAnyClass = AnyClass =>
class MyMixinClass extends MixinClass(AnyClass) {}
var AA = extendsAnyClass(A)
var BB = extendsAnyClass(B)
var aa = new AA()
var bb = new BB()
aa.m()
bb.m()
This worked for me:
const extend = SuperClass => class E extends SuperClass {
constructor() {
super('E')
}
}
class A {
constructor(arg) {
console.log('Hello from ' + arg)
}
}
class B {
constructor(arg) {
console.log('Hi from ' + arg)
}
}
const C = extend(A)
const D = extend(B)
new C() // Hello from E
new D() // Hi from E
This will dynamically extend E for any given parent class
https://jsfiddle.net/1s0op2ng/
Let's say you're making a game. You want to try and not pollute the global scope and possibly limit the user's ability to easily alter the game (doubtful with client-side). You feel like modules might be unnecessary for your purposes. Is it bad practice to pass references to a class to another class during instantiation to access its methods?
Contrived example:
//game.js
var Game = (function () {
function Game() {
this.currentLevel = null;
this.score = 0;
}
Game.prototype.addScore = function (num) {
this.score += num;
};
Game.prototype.goToLevel = function (diff) {
this.currentLevel = new Level(this, diff);
};
Game.prototype.returnHome = function (level) {
this.currentLevel = null;
};
return Game;
})();
//level.js
var Level = (function () {
function Level(game, difficulty) {
this.game = game; //reference to game
this.difficulty = difficulty;
this.entities = [];
this.load();
}
Level.prototype.load = function () {
this.addEntity({name: 'tim', power: 23, difficulty: this.difficulty});
};
Level.prototype.leave = function () {
this.game.returnHome();
};
Level.prototype.addEntity = function (options) {
this.entities.push(new Entity(this, options));
};
Level.prototype.removeEntity = function (entity) {
for(var x = 0; x < this.entities.length; x++) {
if(this.entities[x] === entity) this.entities.splice(x, 1);
}
};
return Level;
})();
//level.js
var Entity = (function () {
function Entity(level, options) {
this.level = level; //reference to level
this.options = options;
}
Entity.prototype.kill = function () {
this.level.removeEntity(this); // anti-pattern?
this.level.game.addScore(34.53); // too closely coupled?
};
return Entity;
})();
//main.js
var Main;
(function (Main) {
var game = null;
function documentIsReady() {
start(); // Start the game
}
function start() {
game = new Game();
game.goToLevel('hard');
}
return {
documentIsReady: documentIsReady
}
})(Main || (Main = {}));
$(document).ready(function () {
Main.documentIsReady();
});
Forgive the half-baked example. If you end up with many instances of the 'Entity' class, do all the references to 'Level', though the same instance, start taking more memory? Are there other pitfalls? Another method would be to implement some kind of interface that you can access that allow classes to talk to each other.
I'm currently trying to create an HTML5 Canvas game and I want to be able to attach functions to buttons that activate when clicked. I can do this for unique functions but I'm struggling to find a way to do it when looping through many buttons with a predefined function.
I've created an example to show what I've tried so far:
jsFiddle: http://jsfiddle.net/ra1rb74w/1/
// The class that we want to create an array of
myClass = function() {
this.aFunction;
};
myClass.prototype = {
// Add a new function to this class
addFunction: function (newFunction) {
this.aFunction = newFunction;
},
// Use the current function
useFunction: function () {
if (this.aFunction != null) {
this.aFunction;
}
}
};
// The base function we will use in the classes
var baseFunction = function(x) { console.log(x); }
// Create the array of classes
var myClasses = [];
// Add 10 classes to the array and add a function to each of them
for (var x = 0; x < 10; x++) {
myClasses.push(new myClass());
myClasses[x].addFunction(baseFunction(x));
}
// Use the function in the first class
myClasses[0].useFunction();
You can see that all the functions get triggered which I don't want, and the useFunction() function doesn't work. Is there a way to do this?
So you are triggering baseFunction by calling baseFunction(x). You need to either get baseFunction to return a function which can be executed:
// The class that we want to create an array of
myClass = function() {
this.aFunction;
};
myClass.prototype = {
// Add a new function to this class
addFunction: function (newFunction) {
this.aFunction = newFunction;
},
// Use the current function
useFunction: function () {
if (typeof this.aFunction === "function") {
this.aFunction.call(this);
}
}
};
// The base function we will use in the classes
var baseFunction = function(x) {
return function() {
console.log(x);
};
}
// Create the array of classes
var myClasses = [];
// Add 10 classes to the array and add a function to each of them
for (var x = 0; x < 10; x++) {
myClasses.push(new myClass());
myClasses[x].addFunction(baseFunction);
}
// Use the function in the first class
myClasses[3].useFunction();
JsFiddle
Or add another parameter to addFunction which can be called like addFunction(baseFunction, x):
// The class that we want to create an array of
myClass = function() {
this.aFunction;
};
myClass.prototype = {
// Add a new function to this class
addFunction: function (newFunction, value) {
this.aFunction = newFunction;
this.x = value;
},
// Use the current function
useFunction: function () {
if (typeof this.aFunction === "function") {
this.aFunction.call(this, this.x);
}
}
};
// The base function we will use in the classes
var baseFunction = function(x) { console.log(x); }
// Create the array of classes
var myClasses = [];
// Add 10 classes to the array and add a function to each of them
for (var x = 0; x < 10; x++) {
myClasses.push(new myClass());
myClasses[x].addFunction(baseFunction, x);
}
// Use the function in the first class
myClasses[3].useFunction();
JsFiddle
Note I also changed your check for aFunction == null as the function passed in may be null, or a string, or anything else. You want to check if it is executable.
Change to
...
myClass.prototype = {
// Add a new function to this class
addFunction: function (newFunction, x) {
this.aFunction = newFunction;
this.aFunctionX = x;
},
useFunction: function () {
if (this.aFunction != null) {
this.aFunction(this.aFunctionX);
}
}
};
...
...
for (var x = 0; x < 10; x++) {
myClasses.push(new myClass());
myClasses[x].addFunction(baseFunction, x);
}
...
Here is a fiddle http://jsfiddle.net/ra1rb74w/6/
In the following code snippet, 'this.x()' can only be called in case 2 (see main()).
Also Bar unequals this in case 1, but is equal for case 2.
function Class_Bar() {
this.panel = null;
this.init = function () {
// do some stuff
this.panel = 20;
}
this.apply = function () {
alert(Bar == this);
Bar.x();
this.x();
}
this.x = function() {
alert("Some friendly message");
alert(Bar.panel);
}
}
var Bar = new Class_Bar();
function Class_Factory() {
this.factories = new Array();
this.add = function (init, apply) {
this.factories.push({"init":init, "apply":apply});
}
this.init = function () {
for (var i = 0; i < this.factories.length; ++i) {
this.factories[i]["init"]();
}
}
this.apply = function () {
for (var i = 0; i < this.factories.length; ++i) {
this.factories[i]["apply"]();
}
}
}
var Factory = new Class_Factory();
function main() {
// Case 1
Factory.add(Bar.init, Bar.apply);
Factory.init();
Factory.apply();
// Case 2
Bar.init();
Bar.apply();
}
main();
http://pastebin.com/fpjPNphx
Any ideas how to "fix" / workaround this behaviour?
I found a possible solution, but it seems to be a "bad" hack.: Javascript: How to access object member from event callback function
By passing Bar.init, you're really only passing the function but not the information that it belongs to Bar (i.e. what the this value should be). What you can do is binding that information:
Factory.add(Bar.init.bind(Bar), Bar.apply.bind(Bar));