我正在努力学习如何编写好的、干净的和高效的JavaScript。我认为自己充其量是JavaScript的新手。如有任何意见,敬请见谅。
为了成为一个更好的JavaScript程序员,我尝试用HTML5 5‘S新的画布元素编写一个简单的JavaScript动画。从功能上讲,当单击画布时,在画布上添加一个新的圆圈(或球),并开始移动到画布的右下角。当球撞到墙上或与另一个球相撞时,x坐标和y坐标的移动速度是倒转的。
修改this对象是个好主意吗?例如,下面的模式有意义吗?
function Point(I) {
this.x = I.x;
this.y = I.y;
this.z = I.z;
return this;
}为什么我不应该发送一个对象并从输入对象中提取变量呢?我喜欢创建的语法,但是这个模式有什么问题吗?
new Point({
x: 10,
y: 30
});
new Point({
x: 50,
y: 25
z: 3
});<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Ball Collision</title>
<script src="js/modernizr-1.7.js"></script>
<script>
window.onload = function () {
//"use strict";
// Source: http://www.html5rocks.com/tutorials/canvas/notearsgame/
Number.prototype.clamp = function(min, max) {
return Math.min(Math.max(this, min), max);
};
if (Modernizr.canvas) {
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
canvas.width = 500;
canvas.height = 375;
canvas.style.border = "3px solid red";
function Point(I) {
console.log("Point created");
this.x = I.x;
this.y = I.y;
this.z = I.z;
return this;
}
function Ball(I) {
console.log("Ball created");
var SPEED_LIMIT = 10;
this.point = new Point(I.point);
this.radius = (I.radius > 10 ? I.radius : 10);
this.color = I.color;
this.speed = new Point(I.speed);
this.collision = false;
this.mass = I.mass || 1;
this.draw = function() {
this.point.x = this.point.x.clamp(0 + this.radius, canvas.width - this.radius);
this.point.y = this.point.y.clamp(0 + this.radius, canvas.height - this.radius);
ctx.beginPath();
ctx.arc(this.point.x, this.point.y, this.radius, 0, Math.PI*2, false);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
};
this.update = function() {
this.clear();
if ( this.point.x <= this.radius || this.point.x >= (canvas.width - this.radius)) {
this.speed.x *= -1;
}
if ( this.point.y <= this.radius || this.point.y >= (canvas.height - this.radius)) {
this.speed.y *= -1;
}
if ( this.speed.x > SPEED_LIMIT ) {
this.speed.x = SPEED_LIMIT;
}
if ( this.speed.x < SPEED_LIMIT * -1 ) {
this.speed.x = SPEED_LIMIT * -1;
}
if ( this.speed.y > SPEED_LIMIT ) {
this.speed.y = SPEED_LIMIT;
}
if ( this.speed.y < SPEED_LIMIT * -1 ) {
this.speed.y = SPEED_LIMIT * -1;
}
this.point.x += this.speed.x;
this.point.y += this.speed.y;
};
this.clear = function() {
ctx.beginPath();
ctx.arc(this.point.x - 1, this.point.y - 1, this.radius + 2, 0, Math.PI*2, false);
ctx.closePath();
ctx.fillStyle = "#fff";
ctx.fill();
};
return this;
}
var balls = [];
function handleCollisions() {
function collide(ball1, ball2) {
ball1.speed.x *= -1;
ball2.speed.x *= -1;
ball1.speed.y *= -1;
ball2.speed.y *= -1;
}
function checkCollisions() {
var i, j, a, b;
for ( i = 0; i < balls.length; i++ ) {
for ( j = i+1; j < balls.length; j++ ) {
a = balls[i];
b = balls[j];
var dx = Math.abs(a.point.x - b.point.x);
var dy = Math.abs(a.point.y - b.point.y);
var d = (dx*dx)+(dy*dy);
var r = (a.radius + b.radius) * (a.radius + b.radius);
if ( d < r && ( !a.collision || !b.collision ) ) {
a.collision = true;
b.collision = true;
collide(a, b);
} else if ( d > r ) {
a.collision = false;
b.collision = false;
}
}
}
}
checkCollisions();
}
function update() {
balls.forEach(function(ball) {
ball.update();
});
handleCollisions();
}
function draw() {
balls.forEach(function(ball) {
ball.draw();
});
}
var FPS = 30;
var loop = setInterval( function() {
update();
draw();
}, 1000/FPS);
canvas.onclick = function(e) {
var radiusAndMass = Math.floor(Math.random() * 50) + 1;
var speed = Math.floor(Math.random() * 10) + 1;
balls.push(new Ball({
point: {
//x: Math.floor(Math.random() * canvas.width),
//y: Math.floor(Math.random() * canvas.height),
x: e.offsetX,
y: e.offsetY
},
color: "#"+((1<<24)*Math.random()|0).toString(16),
radius: radiusAndMass,
mass: radiusAndMass,
speed: {
x: speed,
y: speed
}
}));
};
document.getElementById('canvas-container').appendChild(canvas);
}
};
</script>
</head>
<body>
<div id="wrap">
<div id="canvas-container">
</div>
</div>
</body>
</html>发布于 2014-06-12 04:05:17
修改这个对象是个好主意吗?例如,下面的模式有意义吗?函数点(I){ this.x = I.x;this.y = I.y;this.z = I.z;返回此;}
是也不是。在构造函数中修改this是正常的,但是您在这里所做的事情是不寻常的。像这样的事情会更常见:
function Point(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}对于这个参数列表,我们传递的内容非常清楚(3D坐标);对于I,除非在构造函数中读取代码,否则就不清楚了。这里没什么大不了的,但是有了Ball,我们就不知道I应该包含什么,除非我们读取了整个构造函数。
没有理由使用return this;您正在使用new调用它,该表达式的结果将是构造的对象。函数不需要返回任何内容。
为什么我不应该发送一个对象并从输入对象中提取变量呢?我喜欢创建的语法,但是这个模式有什么问题吗?
它没有什么问题,但是由于您的Point不做任何事情(即它没有函数属性,只有x、y和z),那么您最好忘记Point,如果您将对象文本传递给Point构造函数,那么只使用对象文本。换句话说,您正在创建一个对象文本,然后将它的所有属性复制到一个恰巧被称为"Point“的东西中,但除此之外,它还具有与传递给构造函数的对象完全相同的功能。为什么要多走一步?
使用Ball,您应该将函数属性附加到Ball.prototype,而不是在构造函数中分配它们。每个Ball对象没有理由拥有自己的一组单独的函数属性,这样做会带来额外的开销。换句话说,使用代码someBall.draw != anotherBall.draw,但是如果所有的球都引用相同的draw函数属性,那么效率就会高得多。
发布于 2014-06-13 21:01:55
我不认为任何一种方法(对象与值)都有很强的理由,但我将列出这两种方法的所有优点/缺点,以便您选择适合您的方法:
function Point(I))+从旧实例或兼容对象( x、y和z)创建新实例非常简单:new Point(oldPoint)或new Point(thingThatQuaksLikeAPoint)*
-从数字中创建新实例变得乏味和烦人(并且会产生垃圾(在某些情况下这可能是一个问题(对于数字处理/在缓慢硬件上的实时应用程序/糟糕的JS实现来说真的很糟糕))。new Point({ x: 1, y: 2, z: 3 }) vs new Point(1, 2, 3),或者,以下面的函数为例:
function add(P1, P2) {
var newX = P1.x + P2.x;
var newY = P1.y + P2.y;
var newZ = P1.z + P2.z;
return new Point({ x: newX, y: newY, z: newZ });
// why do I have to wrap my numbers it in an object?!? :(
// new Point(newX, newY, newZ); is so much more to the point!
}- --构造函数可以访问比它需要的更多的东西--一般来说,在应用程序中设置围栏并保证某些边界不被跨越是很好的。如果您传入一个完整的对象,那么由于错误或诱惑,您可能会修改传递对象的状态(这会导致非常微妙的错误)。
function Point(x, y, z))+从数字中创建新实例是琐碎的、甜蜜的、简短的--就像new Point(1, 2, 3)一样容易
-如果调用其他点,那么每次new Point(otherPoint.x, otherPoint.y, otherPoint.z)对new Point(otherPoint)时提取x、y和z都会变得单调乏味。
function Point(x, y, z) {
if (arguments.length === 1) {
// we got only one argument - it's a Point!
var that = x; // just aliasing
this.x = that.x;
this.y = that.y;
this.z = that.z;
} else {
// let's presume there can be only these 2 cases
this.x = x;
this.y = y;
this.z = z;
}
}这可能会慢一点,但这是值得的,海事组织--它使创造点变得轻而易举;)
我确实看到了一些代码,它们可以在您粘贴的示例中得到改进。如果您希望编写漂亮、干净的JS,那么请考虑查找原型是如何工作的。还要了解为什么requestAnimationFrame比setTimeout好得多(setTimeout比setInterval好)。此外,您可以使用rgb(1, 2, 3)作为有效的颜色,而不是#010203。
*见鸭型
发布于 2011-04-19 14:33:12
我认为这种模式很好。它具有“命名参数”的优点,如果使用不同的参数集调用某个函数/构造函数,它非常可读性强,并且可能非常有用。
我建议的唯一方法是为参数使用另一个名称,因为大写字母通常用于构造函数:
function Point(p) {
this.x = p.x;
this.y = p.y;
this.z = p.z;
return this;
}https://codereview.stackexchange.com/questions/1940
复制相似问题