當面試官問你了不瞭解defineProperty的時候。。。

在當前的前端環境下,vue這種框架能夠算是一項基礎技能,能夠說不會vue很難找到工做,並且大多數的面試官都很喜歡問的一個問題就是,關於vue的雙向數據綁定原理,這個問題能夠說是耳熟能詳了,那拋開vue的設計思路,單單就是 Object.defineProperty() 這個api的話,說你寫過這個就夠了。前端

這是一個很是簡單的貪吃蛇的小遊戲(請忽略裏面很是多的細節bug。。。),這個小遊戲就是經過defineProperty這個api實現的。這個api的一些屬性就很少介紹了,相信你們都知道。
首先,先要分析一下這個遊戲,主體的組成成分就是三個類,背景,食物,和蛇,剩下的就是那個開始按鈕,暫且無論。接下來就開始一個一個來看,先說背景,這個背景能夠當作是一個相似於棋盤的東西,既然是棋盤就能夠把它當成一個平面直角座標系構成的網格, vue

而後這個網格就能夠當作是一個二維數組,這樣就能夠對應座標了,既然要用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。git

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)
github

這裏咱們監聽的屬性爲flag,flag表示的就是當前這個網格是哪一個對象所處的位置,若是是食物處於這個位置,那這個flag就是food,當這個字段改變的時候,根據這個字段來判斷,這個位置的狀態,並做出相應的改變。面試

再來分析第二個類,蛇,這個類應該具備的功能,初始化,移動,吃食物,變長,死亡。這裏初始化和死亡能夠當作是一個方法,這裏的移動就要根據上下左右和速度前進,吃食物和變長簡單理解就是下一個前進的格子若是食物的話,就直接變成蛇的一部分就行了。json

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()
    }

}
複製代碼

最後是食物,這個就比較簡單了,只要能初始化就行了。api

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… 。不過這個代碼是很久以前寫的了,結構很模糊,耦合性也很高,以後會優化一下代碼,但願你們可以看的明白。。
盒盒盒盒。。。bash

相關文章
相關標籤/搜索