JS实现贪吃蛇—重点理解原型和自调用函数的沙箱

要点:

1.通过为构造函数的原型对象添加属性或者方法,可以实现数据共享,节省内存空间,不需要每次重新定义。如果构造函数中的属性或者方法跟原型对象中冲突,以构造函数为准,因为原型对象就是建立在构造函数的基础之上的。

注意浏览器中使用的实例对象中的原型__proto__(两个英文状态下的下划线)和构造函数中的原型prototype结构是一样的,但是前一种是通过构造函数的原型创建的,指向后一种,并且后一种是标准写法,我们使用这个。

2.原型方法中,可以调用此原型对象的其他的原型方法。

3.函数的自调用,后面必须要写分号,不然浏览器这个不能和function()这种分开,造成各种错误。函数的自调用,是为了避免命名冲突。这就构成了沙箱,可以不影响其他部分。

4.函数自调用中定义的对象,是只能在自调用函数内部使用的,如果想要在全局范围内进行调用的话,可以将这个对象,赋值给浏览器的顶级对象,window,这样变成全局对象。

5.在这个例子中,有大量地方使用this关键字,原来简单的函数指代是明确,通过自调用或者添加一个定时器,这时this关键字可能不是指向这个实例对象,而是调用定时器的window对象,通过.bind()改变this的指向,以后再详说。

代码:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style type="text/css">
		.map{
			width: 960px;
			height: 600px;
			background-color: #C5ACAC;
			position: relative;
			margin: 0 auto;
		}
	</style>
</head>
<body>
	<div class="map"></div>
	<script type="text/javascript">
		(function(){
			var elements = [];
			// 用来存放吃了多少食物

			function Food(x,y,width,height,color){
				this.x = x || 0;
                this.y = y || 0;
                this.width = width || 20;
                this.height = height || 20;
                this.color = color || "blue";
			}
			// 设置食物的基本样式横纵坐标和宽高以及背景颜色,传值使用传值,不传使用默认的参数

			Food.prototype.init = function(map){
                remove();
                // 调用删除函数,外部无法访问的

                var food = document.createElement("DIV");
                map.appendChild(food);
                // 将食物添加到地图上
                food.style.width = this.width + "px";
                food.style.height = this.height + "px";
                food.style.backgroundColor = this.color;
                // 设置基本样式,注意JS中不能设置连字符样式,要用驼峰原则,不然报Invalid left-hand side in assignment

                food.style.position = "absolute";
                this.x = parseInt(Math.random()*(map.offsetWidth/this.width)) * this.width;
                this.y = parseInt(Math.random()*(map.offsetHeight/this.height)) * this.height;
                food.style.left = this.x + "px";
                food.style.top = this.y + "px";

                elements.push(food);
                // 把生成的div添加到elements数组中
			}
            // 为食物的原型对象添加初始化的方法,这里没有传入参数使用默认的值

            function remove(){
            	for(var i=0; i<elements.length; i++){
            		var ele = elements[i];
            		ele.parentNode.removeChild(ele);
            		// 找到数组中的父元素后再删除其子元素,来删除这个元素,这是map中的元素
            		elements.splice(i,1);
            		// 通过数组操作,在数组中的这个元素
            	}
            }

			window.Food = Food;
			// 将自调用函数内部声明的局部的函数,赋给浏览器的顶级对象window,转换为全局函数
		}());
		// 函数自调用的方式创建食物,必须要写分号,不然浏览器这个不能和function()这种分开,造成各种错误

		(function(){
            var elements = [];
            // 存放贪吃蛇身体的部分

			function Snake(width,height,direction){
				this.width = width || 20;
			    this.height = height || 20;
			    // 贪吃蛇每个部分的宽高

			    this.body = [
			      {x:3,y:2,color:"red"},
			      {x:2,y:2,color:"green"},
			      {x:1,y:2,color:"green"}    
			    ];
			    // 贪吃蛇的身体,因为要不断的变大,所以放在数组中

			    this.direction = direction || "right";
			    // 贪吃蛇前进的方向
			}
			// 构建贪吃蛇

		    Snake.prototype.init = function(map){
                remove();
                // 每次创建贪吃蛇,删除前面一条,这里是调用下面创建

		    	for(var i=0; i<this.body.length; i++){
		    		var obj = this.body[i];
		    		// 获取设置的样式原来的对象
		    		var snackBody = document.createElement("DIV");

		    	    map.appendChild(snackBody);
	    	    	snackBody.style.position = "absolute";
	    	    	snackBody.style.width = this.width + "px";
	    	    	snackBody.style.height = this.height + "px";
	    	    	// 基本样式

	    	    	snackBody.style.left = obj.x * this.width + "px";
	    	    	snackBody.style.top = obj.y * this.height + "px";
	    	    	snackBody.style.backgroundColor = obj.color;
	    	    	// 将特定的坐标位置和颜色复制下来

	    	    	elements.push(snackBody);
	    	    	// 将贪吃蛇对象存放到数组中
		    	}

		    	function remove(){
                    var i = elements.length -1;
                    for(i; i>=0; i--){
                    	var ele = elements[i];
                    	ele.parentNode.removeChild(ele);
                    	elements.splice(i,1);
                    }
		    	}
		    	// 从后往前删除贪吃蛇,用户看到的加长部分是原来贪吃蛇的蛇尾部分
		    }
		    // 为小蛇对象的原型对象添加方法,每次贪吃蛇移动的时候,删除之前的创建新的,利用原型节省内存	

		    Snake.prototype.move = function(food,map){
		    	var i = this.body.length - 1;
		    	// 除了头部
		    	for( i; i>0; i--){
		    		this.body[i].x = this.body[i-1].x;
		    		this.body[i].y = this.body[i-1].y;
		    	}
		    	// 头部后面的,当移动一下,后面的小方块占用前面的小方块的位置

		    	switch(this.direction){
		    		case "right":this.body[0].x += 1;break;
		    		case "left":this.body[0].x -= 1;break;
		    		case "up":this.body[0].y -= 1;break;
		    		case "down":this.body[0].y += 1;break;
		    	}
		    	// 头部小方块根据方向决定坐标的改变

		    	var headX = this.body[0].x * this.width;
		    	var headY = this.body[0].y * this.height;
		    	// 获取贪吃蛇的头部的坐标
		    	var foodX = food.x;
		    	var foodY = food.y;
		    	// 获取食物的坐标位置
		    	if(headX == foodX && headY == foodY){
		    		var last = this.body[this.body.length-1];
		    		// 获取贪吃蛇蛇尾元素
		    		this.body.push({
		    			 x:last.x,
		    			 y:last.y,
		    			 color:last.color
		    		});
		    		// 当蛇头跟食物的坐标一致时,在最后添加上一步获取的元素,此时蛇尾两个元素重合
		    		food.init(map);
		    		// 重新初始化,这个函数调用时,会删除之前的元素
		    	}
		    }	

		    window.Snake = Snake;
		    // 将贪吃蛇对象给浏览器顶级对象
		}());
		// 函数自调用的方式创建贪吃蛇

		(function(){
            var that = null;
            //用来替代定时器使用的this,造成的幺蛾子 

			function Game(map){
				this.food = new Food();
				this.snack = new Snake();
				this.map = map;
				// 一个游戏对象,需要食物,贪吃蛇和地图三个对象
				that = this;
				// 用that指代调用的对象
			}
			Game.prototype.init = function(){
				this.food.init(this.map);
				this.snack.init(this.map);
                // 初始化食物和贪吃蛇

                this.runSnake(this.food,this.map);
                this.bindKey();
				
			}

			Game.prototype.runSnake = function(food,map){
				var intervalName = setInterval(function(){
                  this.snack.move(food,map);
                  this.snack.init(map);
                  // this指代的是浏览器顶级对象window

                  var maxX = map.offsetWidth/this.snack.width;
                  var maxY = map.offsetHeight/this.snack.height;
                  // 蛇能移动到的最大的坐标
                  var headX = this.snack.body[0].x;
                  var headY = this.snack.body[0].y;

                  if(headX<0 || headX>=maxX){
                  	clearInterval(intervalName);
                  	alert("Game Over!");
                  }
                  if(headY<0 || headY>=maxY){
                  	clearInterval(intervalName);
                  	alert("Game Over!");
                  }
				}.bind(that),100);
				// 自动移动的方法.这里的this,指代的是window,用.bind()方法进行对象的更换
			}

			Game.prototype.bindKey = function(){
                document.addEventListener("keydown",function(e){
                	switch (e.keyCode){
                		// .keyCode表示键盘码
                		case 37:this.snack.direction = "left";break;
                		case 38:this.snack.direction = "up";break;
                		case 39:this.snack.direction = "right";break;
                		case 40:this.snack.direction = "down";break;
                	}
                }.bind(that),false)
                // 同样的,这里面使用this关键字的话,指代的是触发键盘弹起事件的对象,用that替换
			};
			// 改变贪吃蛇移动的方向
               
			window.Game = Game;
			// 设为全局对象
		}());


		var gm = new Game(document.querySelector(".map"));
		gm.init();
		
	</script>
</body>
</html>

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值