当面试官问你了不了解defineProperty的时候。。。
栏目: JavaScript · 发布时间: 5年前
内容简介:在当前的前端环境下,vue这种框架可以算是一项基础技能,可以说不会vue很难找到工作,而且大多数的面试官都很喜欢问的一个问题就是,关于vue的双向数据绑定原理,这个问题可以说是耳熟能详了,那抛开vue的设计思路,单单就是这是一个非常简单的贪吃蛇的小游戏(请忽略里面非常多的细节bug。。。),这个小游戏就是通过defineProperty这个api实现的。这个api的一些属性就不多介绍了,相信大家都知道。首先,先要分析一下这个游戏,主体的组成成分就是三个类,背景,食物,和蛇,剩下的就是那个开始按钮,暂且不管。
在当前的前端环境下,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 = "<ul class='point clearfix'>" var tem = '' for (var i = 0;i < this.w;i++) { tpl += '<li></li>' } tpl += '</ul>' 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,这样基本的功能就实现了。完整版请看 github.com/whyjson/com… 。不过这个代码是好久之前写的了,结构很模糊,耦合性也很高,之后会优化一下代码,希望大家能够看的明白。。
盒盒盒盒。。。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 当面试官问你设计模式,高姿态怼他
- 当面试官问到 ThreadLocal 时,我们应具备怎样的谈资?
- 当面试官问你啥是Kafka的选举时,抓住机会“吊打”他!
- 当面试官问你什么是 wěi 递归,你该怎么回答?
- 当面试官问你什么是 wěi 递归,你该怎么回答?
- 你了解HTTPS,但你可能不了解X.509
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。