标签:time bind cas doctype 意义 初始 update sel timer
JS本身不存在类的概念,它对于继承的判定有时非常混乱,当然这并不影响我们以面向对象的思维去编程。
例如我们有两个类A类B类,我们想让B类继承A类,在python中可以轻而易举的实现,在JS中需要一种折中法
function A() { } A.prototype.hello = function() { console.log(1) } function B() { } B.prototype = new A(); (new B()).hello()
B的原型对象指向了A的实例
// 在子类需要继承父类属性的情况下需要用这种方法 function A(a) { this.a = a; } A.prototype.hello = function() { console.log(this.a) } function B(a, b) { this.constructor.apply(this, arguments) this.b = b; } B.prototype = new A(); (new B(1, 2)).hello()
1 var obj = { 2 a : 1, 3 b : 2, 4 c : false 5 } 6 7 console.log("a" in obj); //true 8 console.log("b" in obj); //true 9 console.log("c" in obj); //true console.log("d" in obj); //false
in不仅仅检测是对象自己有没有这个属性,如果原型链上有这个属性,那么也会返回true。整个原型链如果没有这个属性,就返回false。也就是说,in操作符会进行原型链查找。
for in 这个循环,会把原型链上所有的可枚举的属性列出来
而hasOwnProperty方法则是只检测自己是否有这个属性
值得说的是instanceof这个运算符的机理,它会查找原型链所有原型,只有要构造函数指向了原型链上的任意原型它就会返回true
function A() { } function B() { } B.prototype = new A(); console.log(new B() instanceof B) // 此时B的原型已经是A的实例,在原型链层面上没有任何逻辑可以证明B的实例的构造函数是B但是结果是true // 再举个例子 function C() {} C.prototype = Array.prototype; var arr = []; console.log(arr instanceof C) // 返回的也是true,如果说上一个例子B的实例和构造函数的关系是真实的,那么构造函数C和arr并不存在任何关系 // 只是C的原型指向了Array的原型,但instanceof的意义是证明arr 是 C 的实例,实际并不是
面向对象对于代码的组织结构更加清晰例如,贪吃蛇小游戏
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
table,
tr,
td {
border: 1px solid #000;
border-collapse: collapse;
}
td {
width: 18px;
height: 18px;
}
.red {
background-color: red;
}
.blue {
background-color: blue;
}
</style>
<body>
<table id="snake">
</table>
<script>
// 面向对象的思维做游戏,首先确认需要的类
// 地图类
// 蛇类
// 食物类
// 创建地图类
function Map() {
this.data = [];
this.timer = null;
this.dom = document.getElementById(‘snake‘);
this.init();
}
// 地图类有一个初始化的方法
Map.prototype.init = function() {
var rowArr;
var tr;
var td;
for (var row = 0; row < 40; row++) {
rowArr = [];
tr = document.createElement(‘tr‘);
this.dom.appendChild(tr);
for (var col = 0; col < 40; col++) {
td = document.createElement(‘td‘);
tr.appendChild(td);
rowArr.push(td);
}
this.data.push(rowArr)
}
}
// Map有一个清空画布的方法
Map.prototype.clear = function() {
for (var i = 0; i < 40; i++) {
for (var j = 0; j < 40; j++) {
this.data[i][j].className = "";
}
}
};
// 创建蛇类
function Snake() {
this.location = [{
‘x‘: 5,
‘y‘: 10
}, {
‘x‘: 5,
‘y‘: 9
}, {
‘x‘: 5,
‘y‘: 8
}, {
‘x‘: 5,
‘y‘: 7
}, {
‘x‘: 5,
‘y‘: 6
}, {
‘x‘: 5,
‘y‘: 5
}, ];
this.direction = ‘right‘;
this.isEnd = false;
this.render();
this.bindEvenet();
}
// 蛇有一个渲染方法
Snake.prototype.render = function() {
var tempDom;
for (var i = 0; i < this.location.length; i++) {
if (this.isEnd) {
break;
}
tempDom = map.data[this.location[i].x][this.location[i].y];
tempDom.className = ‘blue‘
}
};
// 蛇有一个更新自己的函数
Snake.prototype.update = function() {
// 蛇头向用户输入方向加1蛇尾减1
this.location.pop();
// 随时都要监听用户输入了什么方向
switch (this.direction) {
case ‘left‘:
this.location.unshift({
‘x‘: this.location[0].x,
‘y‘: this.location[0].y - 1,
});
break;
case ‘top‘:
this.location.unshift({
‘x‘: this.location[0].x - 1,
‘y‘: this.location[0].y,
});
break;
case ‘right‘:
this.location.unshift({
‘x‘: this.location[0].x,
‘y‘: this.location[0].y + 1,
});
break;
case ‘bottom‘:
this.location.unshift({
‘x‘: this.location[0].x + 1,
‘y‘: this.location[0].y,
});
break;
}
this.check();
};
// 给蛇类绑定一个方向事件
Snake.prototype.bindEvenet = function() {
var self = this;
document.onkeydown = function(event) {
event = event || window.event;
switch (event.keyCode) {
case 37:
// 蛇头只有一个方向,假设当前方向为左
// 那么我们蛇的X值是-1的,如果这时候用户输入右
// 那么X值变为 + 1,此时就会出现bug也就是短暂的重合
if (self.direction == ‘right‘) {
return;
}
self.direction = ‘left‘
break;
case 38:
if (self.direction == ‘bottom‘) {
return;
}
self.direction = ‘top‘
break;
case 39:
if (self.direction == ‘left‘) {
return;
}
self.direction = ‘right‘
break;
case 40:
if (self.direction == ‘top‘) {
return;
}
self.direction = ‘bottom‘
break;
}
};
};
// 蛇有一个吃食物的方法
Snake.prototype.eatFood = function() {
// 蛇头碰到食物
if (this.location[0].x == food.x && this.location[0].y == food.y) {
// 当我们碰到食物时重新计算位置
food.change();
// 蛇头加1
switch (this.direction) {
case ‘left‘:
this.location.unshift({
‘x‘: this.location[0].x,
‘y‘: this.location[0].y - 1,
});
break;
case ‘top‘:
this.location.unshift({
‘x‘: this.location[0].x - 1,
‘y‘: this.location[0].y,
});
break;
case ‘right‘:
this.location.unshift({
‘x‘: this.location[0].x,
‘y‘: this.location[0].y + 1,
});
break;
case ‘bottom‘:
this.location.unshift({
‘x‘: this.location[0].x + 1,
‘y‘: this.location[0].y,
});
break;
}
}
};
// 蛇有要检测是否撞到自己或边缘
Snake.prototype.check = function() {
if (snake.location[0].x < 0 || snake.location[0].x > 39 || snake.location[0].y < 0 || snake.location[0].y > 39) {
this.isEnd = true;
}
for (var i = 0; i < this.location.length - 1; i++) {
if (this.location[0].x == this.location[i + 1].x && this.location[0].y == this.location[i + 1].y) {
this.isEnd = true
}
}
};
// 食物类
function Food() {
this.x = NaN;
this.y = NaN;
this.change();
};
// 食物类有个方法,计算食物的位置
Food.prototype.change = function() {
// 食物不能和蛇重合
this.x = parseInt(Math.random() * 40)
this.y = parseInt(Math.random() * 40)
for (var i = 0; i < snake.location.length; i++) {
if (this.x == snake.location[i].x && this.y == snake.location[i].y) {
food.create();
}
}
};
// 食物类有一个渲染方法
Food.prototype.render = function() {
map.data[this.x][this.y].className = ‘red‘;
};
var map = new Map();
var snake = new Snake();
var food = new Food();
var timer = setInterval(function() {
// 清空画布
map.clear();
// 我们将其想象成canvas现在我们自己建的画布每秒都在重新绘制
// 清空画布后我们需要渲染蛇与食物
snake.update();
snake.eatFood();
snake.render();
food.render();
if (snake.isEnd) {
clearInterval(timer)
alert(‘GAME OVER‘)
}
}, 300);
// 当然这里只是最简单的实现,如果你想同时产生多个不同大小的食物,根据蛇身体大小加速等等,都可以自由实现
// 比如要产生多个食物,可以给食物类添加食物地图...
// 面向对象的思维就是可以根据人类的思维无限延伸,理论上是这样,创造性和协调性更强
</script>
</body>
</html>
标签:time bind cas doctype 意义 初始 update sel timer
原文地址:https://www.cnblogs.com/tengx/p/12360891.html