博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
当面试官问你了不了解defineProperty的时候。。。
阅读量:7123 次
发布时间:2019-06-28

本文共 6745 字,大约阅读时间需要 22 分钟。

在当前的前端环境下,vue这种框架可以算是一项基础技能,可以说不会vue很难找到工作,而且大多数的面试官都很喜欢问的一个问题就是,关于vue的双向数据绑定原理,这个问题可以说是耳熟能详了,那抛开vue的设计思路,单单就是 Object.defineProperty() 这个api的话,说你写过这个就够了。

这是一个非常简单的贪吃蛇的小游戏(请忽略里面非常多的细节bug。。。),这个小游戏就是通过defineProperty这个api实现的。这个api的一些属性就不多介绍了,相信大家都知道。

首先,先要分析一下这个游戏,主体的组成成分就是三个类,背景,食物,和蛇,剩下的就是那个开始按钮,暂且不管。接下来就开始一个一个来看,先说背景,这个背景可以看成是一个类似于棋盘的东西,既然是棋盘就可以把它当成一个平面直角坐标系构成的网格,

然后这个网格就可以看成是一个二维数组,这样就可以对应坐标了,既然要用defineProperty,所以每一项都要是个对象。好,背景这个类大概的功能就是这样了。

class Qipan {	constructor(w, h, id){		this.w = w		this.h = h		this.box = document.getElementById(id)	}	init(){		var tpl = "
    " var tem = '' for (var i = 0;i < this.w;i++) { tpl += '
  • ' } tpl += '
' for(var j = 0;j < this.h;j++){ tem += tpl } this.box.innerHTML = tem this.box.style.width = 20 * this.w +'px' } getQi () { var arr = [] for (var i = 0;i < this.h; i++) { arr.push(new Array()) for (var j = 0;j < this.w;j++){ arr[i].push({flag : false,newFlag: '', site:[i, j]}) } } return arr }}复制代码

接下来就要开始劫持每个格子对应的对象的key。

function observer(arr) {	arr.forEach(arr1 => {		arr1.forEach( item => {			Object.defineProperty(item,'flag',{				enumerable: true,				configurable: true,				get: ()=>{					return item.newFlag				},			  	set:newVal=> {			  		if (newVal === 'snake') {			            document.getElementsByClassName('point')[item.site[0]].getElementsByTagName('li')[item.site[1]].style.background = '#DB7093'			  			item.newFlag = 'snake'			  		} else if (newVal === 'food') {			  			document.getElementsByClassName('point')[item.site[0]].getElementsByTagName('li')[item.site[1]].style.background = 'red'			  			item.newFlag = 'food'			  		} else {			  			item.newFlag = ''			  			document.getElementsByClassName('point')[item.site[0]].getElementsByTagName('li')[item.site[1]].style.background = '#F5DEB3'			  						  		}		        }			})		})	})}复制代码

介绍一下里面的参数

configurable
当且仅当该属性的configurable为true时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
enumerable
当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。
数据描述符同时具有以下可选键值: value
该属性对应的值。可以是任何有效的JavaScript值(数值,对象,函数等)。默认为 undefined。
writable
当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。 存取描述符同时具有以下可选键值:
get
一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。 默认为 undefined。
set
一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。默认为 undefined。
(摘自MDN)

这里我们监听的属性为flag,flag表示的就是当前这个网格是哪个对象所处的位置,如果是食物处于这个位置,那这个flag就是food,当这个字段改变的时候,根据这个字段来判断,这个位置的状态,并作出相应的改变。

再来分析第二个类,蛇,这个类应该具有的功能,初始化,移动,吃食物,变长,死亡。这里初始化和死亡可以看成是一个方法,这里的移动就要根据上下左右和速度前进,吃食物和变长简单理解就是下一个前进的格子如果食物的话,就直接变成蛇的一部分就好了。

class Snake {	constructor(qipan,h,food){		this.qipan = qipan		this.h = h		this.h2 = JSON.parse(JSON.stringify(h))		this.direct = 'left'		this.que = []		this.food = food	}	init () {		this.qipan.forEach(item=>{			item.forEach(items=>{				items.flag = ''			})		})        this.direct = 'left'				if (this.que.length !== 0) {			this.h = JSON.parse(JSON.stringify(this.h2))			this.que = []			clearInterval(time)			time = null		} 			this.qipan[this.h[0]][this.h[1]].flag = 'snake'			this.qipan[this.h[0]][this.h[1] + 1].flag = 'snake'			this.que.push(this.qipan[this.h[0]][this.h[1]])			this.que.push(this.qipan[this.h[0]][this.h[1] + 1])			}	getUp(){		this.direct = 'up'	}	getLeft() {		this.direct = 'left'	}	getRight(){		this.direct = 'right'	}	getDown(){		this.direct = 'down'	}	getGo (){		var _this = this		switch (this.direct) {			case 'left':                if (this.qipan[this.h[1] - 1]) {                    wooDir(this.qipan[this.h[0]][this.h[1] - 1],'left')                } else {                    this.init()                    this.food.init()                }				break			case 'right':                if (this.qipan[this.h[1] + 1]) {                    wooDir(this.qipan[this.h[0]][this.h[1] + 1], 'right')                } else {                    this.init()                    this.food.init()                }				break			case 'up':				if (this.qipan[this.h[0] - 1]) {					wooDir(this.qipan[this.h[0] - 1][this.h[1]], 'up')				} else {					this.init()                    this.food.init()				}								break			case 'down':				if (this.qipan[this.h[0] + 1]) {					wooDir(this.qipan[this.h[0] + 1][this.h[1]], 'down')				} else {					this.init()                    this.food.init()				}								break		}		function wooDir(oo, dir) {            if (oo) {                if (oo.flag === '') {                    oo.flag = 'snake'                    switch (dir) {						case 'left':                            _this.h[1] -= 1							break                        case 'right':                            _this.h[1] += 1                            break                        case 'up':                            _this.h[0] -= 1                            break                        case 'down':                            _this.h[0] += 1                            break                    }                    _this.que.unshift(oo)                    _this.que[_this.que.length - 1].flag = ''                    _this.que.pop()                } else if (oo.flag === 'snake') {                    _this.init()                    _this.food.init()                } else if (oo.flag === 'food') {                    _this.eat(oo)                }            } else {                _this.init()                _this.food.init()            }        }	}    eat (oo) {        oo.flag = 'snake'        this.que.unshift(oo)        this.h = JSON.parse(JSON.stringify(oo.site))        this.food.init()    }}复制代码

最后是食物,这个就比较简单了,只要能初始化就好了。

class Food {	constructor (qipan) {		this.qipan = qipan	}	init() {		var _this = this		var arr = []		_this.qipan.forEach(item=>{			item.forEach(items=>{				if(items.flag !== 'snake') {					arr.push(items)				}			})		})		arr[Math.floor(Math.random()*arr.length)].flag = 'food'	}}复制代码

好,三个类都准备好了,剩下的就是实例化对象,然后设置一个蛇往前走的定时器,和改变方向的监听事件,就ok了。

var qi = new Qipan(10,10, 'box')		qi.init()		var pan = qi.getQi()		var time = null		observer(pan)		var food = new Food(pan)        food.init()        var sna = new Snake(pan, [0, 3],food)        sna.init()		var btn_s = document.getElementById('start')		btn_s.onclick = function () {			if (time === null) {				time = setInterval(function(){					sna.getGo()				}, 200)			}		}		document.onkeydown = function (ev) {			var e = event || window.event || arguments.callee.caller.arguments[0]			if(e && e.keyCode === 37 ){				sna.getLeft()            }			if(e && e.keyCode === 38 ){				sna.getUp()            }			if(e && e.keyCode === 39 ){				sna.getRight()            }			if(e && e.keyCode === 40 ){				sna.getDown()            }		}复制代码

ok,这样基本的功能就实现了。完整版请看 。不过这个代码是好久之前写的了,结构很模糊,耦合性也很高,之后会优化一下代码,希望大家能够看的明白。。

盒盒盒盒。。。

转载于:https://juejin.im/post/5d0aed9af265da1b672113ce

你可能感兴趣的文章
CSS预处理器之SASS用法指南
查看>>
开源下载工具
查看>>
OllyUni.dll
查看>>
VS2008远程调试
查看>>
Cracking the coding interview--Q2.2
查看>>
brew 中的时间格式转换
查看>>
读者来信与解答 1
查看>>
xdebug 安装及使用规则
查看>>
SharePoint 创建 Lookup 类型的Site Column解决跨站问题
查看>>
VS2010(2012)中使用Unit Testing进行单元测试
查看>>
onclick事件分析
查看>>
MySQL ALTER语法的运用方法 && 操作索引和字段
查看>>
UNIX网络编程读书笔记:poll函数
查看>>
《数据结构教程》(李春葆 主编)课后习题【2.4】
查看>>
英语应用文写作之感谢信
查看>>
[物理学与PDEs]第3章习题参考解答
查看>>
hdu 1728:逃离迷宫(DFS,剪枝)
查看>>
安卓开发_关于WebView加载页面空白问题
查看>>
atitit.自适应设计悬浮图片的大小and 位置
查看>>
最近阅读-201405
查看>>