In the current front-end environment, vUE framework can be regarded as a basic skill, it can be said that VUE is not difficult to find a job, and most interviewers like to ask a question about the two-way data binding principle of VUE, this question can be said to be familiar, regardless of vUE design ideas, For the Object.defineProperty() API alone, it’s enough to say you wrote this.

This is a very simple snake game (please ignore the details of the bug…) This little game is implemented via defineProperty API. Some of the properties of this API are not described, I believe you know. First of all, we need to analyze the game. The main composition of the game is three classes, background, food, and snake, and the rest is the start button. So let’s start with one by one, the background, the background can be thought of as something like a checkerboard, and since it’s a checkerboard you can think of it as a grid of planar rectangular coordinates,

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
	}
}
Copy the code

The next step is to start hijacking the key of the object corresponding to each grid.

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'}}})})})}Copy the code

The property descriptor can be changed and deleted from the corresponding video system only if and if the property of the 64x works without any additional control system, which is true. The default is false. An enumerable property can appear in an object’s enumerated property if and only if its Enumerable is true. The default is false. The data descriptor also has the following optional key values: value Specifies the value of the property. It can be any valid JavaScript value (numeric value, object, function, etc.). The default is undefined. Writable Value can be changed by the assignment operator if and only if the writable of the property is true. The default is false. The access descriptor also has the following optional key values: get A method that provides a getter for the property, or undefined if there is no getter. When the property is accessed, the method is executed with no arguments, but with this object (which is not necessarily the object defining the property because of inheritance). The default is undefined. Set a method that provides a setter for a property, or undefined if there is none. This method is triggered when the property value is modified. This method takes a unique parameter, the new parameter value for the property. The default is undefined. From (MDN)

Here, the monitored attribute is Flag, which represents the position of the object in the current grid. If food is in this position, the flag is food. When this field changes, the state of this position can be judged according to this field and corresponding changes can be made.

Let’s look at the second class, the snake, and what it should do: initialize, move, eat, grow, die. So initialization and death can be viewed as one way, where the movement is going up, down, left, right, and speed, and eating food and getting longer is simply the next grid that moves forward and if it’s food, it just becomes part of the snake.

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

}
Copy the code

And then finally food, which is a little bit easier, as long as you can initialize it.

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'}}Copy the code

Ok, all three classes are ready, all that’s left is to instantiate the object, and then set a timer for the snake to go forward, and a listening event for the change of direction, and that’s it.

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()
            }
		}
Copy the code

Ok, so the basic function is done. See the full version at github.com/whyjson/com… . But this code was written a long time ago, the structure is very fuzzy, the coupling is also very high, after will optimize the code, I hope you can see clearly. Box, box, box…