(十一)JavaScript高级

一、对象

   JavaScript 的设计是一个简单的基于对象的范式。在 JavaScript 中,一个对象可以是一个单独的拥有属性和类型的实体。一个对象就是一系列属性的集合,一个属性包含一个名和一个值。一个属性的值可以是函数,这种情况下属性也被称为方法。

   JavaScript 拥有一系列预定义的对象。另外,你可以创建你自己的对象。

1、使用对象初始化器

   可以通过对象初始化器创建对象,即通过字面值创建对象。

	var obj = { property_1:   value_1,   // property_# 可以是一个标识符...
	            2:            value_2,   // 或一个数字...
	           ["property" +3]: value_3,  //  或一个可计算的key名... 
	            // ...,
	            "property n": value_n }; // 或一个字符串
  • obj:新对象的名称;
  • property_i:一个标识符(可以是一个名称、数字或字符串字面量);
  • value_i:一个其值将被赋予 property_i 的表达式。

示例代码:

	var obj = {id:"001", say:function(){alert("Hello!");}};
	obj.say(); // Hello!
2、使用构造函数

   作为另一种方式,你可以通过两步来创建对象:

  1. 通过创建一个构造函数来定义对象的类型(一般首字母大写);
  2. 通过 new 创建对象实例。
	function Obj(arg1,...){
		this.arg1 = arg1;
		...
		this.otherArg = value;
	}
	
	var obj = new Obj(arg1,...);
  • obj:对象的类型名称;
  • arg1,…:创建实例时传入的参数;
  • otherArg,value:未传入的参数,及赋的值。

示例代码:

	var obj = {id:"001", say:function(){alert("Hello!");}};

	function Person(name, age){
		this.name = name;
		this.age = age;
		this.say = function(){alert("I'm " + this.name +", my age is " + age + ".");};
        }

	var per = new Person("Tom", 20);
	per.say();	// I'm Tom, my age is 20.
3、使用 Object.create 方法

   对象也可以用 Object.create() 方法创建。该方法非常有用,因为它允许你为创建的对象选择其原型对象,而不用定义一个构造函数。

	var Obj = { property_i:value_i,  
				...};
	
	var obj= Object.create(Obj);
  • obj:原型对象的名称;
  • property_i:一个标识符(可以是一个名称、数字或字符串字面量);
  • value_i:一个其值将被赋予 property_i 的表达式。

示例代码:

	var Animal = {
		type: "Invertebrates", // Default value of properties
		displayType : function() {  // Method which will display type of Animal
	    console.log(this.type);
  		}
	}
	
	var fish = Object.create(Animal);
	fish.type = "Fishes";
	fish.displayType(); // Output:Fishes

二、原型

   所有的 JavaScript 对象继承于至少一个对象。被继承的对象被称作原型,并且继承的属性可通过构造函数的 prototype 对象找到。

1、构造函数、原型对象、实例对象
  • 在构造函数中,有一个属性——prototype,指向它的原型对象;
  • 在原型对象中,有一个属性——constructor,指向它所在的构造函数;
  • 在实例对象( object )中,有一个私有属性(__proto__),指向它的构造函数的原型对象(prototype )。

在这里插入图片描述

  • 示例代码1:
       利用原型,让不同实例对象共享属性、方法。当然,我们也可以通过原型为系统内置对象添加属性和方法。
    <script>
        function Person(name, age){
            this.name = name;
            this.age = age;
            this.play = function(){console.log("Playing")};
        }

        // 利用原型共享的属性和方法
        Person.prototype.country = "China";
        Person.prototype.say = function(){console.log("hello")};

        var p1 = new Person("Tom", 20);
        var p2 = new Person("Alice", 21);

        console.log(p1.play == p2.play);  // false
        console.log(p1.say == p2.say);  // true
    </script>
  • 示例代码2:
       简单的原型语法,直接赋值给 prototype 对象。注意,采用这种方式的话,会覆盖掉 constructor 属性,因此需要手动指定构造器的引用。
    <script>
        function Person(name, age){
            this.name = name;
            this.age = age;
            this.play = function(){console.log("Playing")};
        }

        // 简单的原型语法
        Person.prototype = {
            // 需要手动指定构造器的引用
            constructor : Person,
            country : "China",
            say : function(){console.log("hello")}
        };

        var p1 = new Person("Tom", 20);

        console.log(p1.country);  // China
        p1.say();   // hello
        console.dir(Person);
    </script>
2、原型链

   对于实例对象中的原型对象,该原型对象也有一个自己的原型对象( __proto__ ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。

   当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

  • 示例代码1:
       当对象与对象的原型存在同名属性时,访问到的是对象中的属性;当访问只存在于原型对象中的属性时,可以直接访问到原型对象中的属性。
    <script>
        function Person(name, age){
            this.name = name;
            this.age = age;
        }

        Person.prototype.age = "18";
        Person.prototype.country = "China";

        var p1 = new Person("Tom", 20);
        console.log(p1.age);    // 20
        console.log(p1.country);    // China
    </script>
3、通过原型实现继承
(1)改变原型引用

   将子类级别的原型引用指向父类级别实例对象,然后隐式地通过父类级别实例对象中的“__proto__”属性共享父类级别的方法和属性。这种方式虽然继承了父级类别的属性和方法,但是子类级别继承属性的值被固定为父类级别实例对象中的值。

  • 示例代码:
    <script>
        // 父类
        function Person(name,age){
            this.name = name;
            this.age = age;
        }

        Person.prototype.say = function(){console.log("hello, my name is " + this.name);};

        // 子类
        function Student(id){
            this.id = id;
        }

        Student.prototype = new Person("Tom", 20);
        Student.prototype.study = function(){console.log(this.id + " are studying.")};

        var stu = new Student("001");
        stu.say();  // hello, my name is Tom
        stu.study();    // 001 are studying.
    </script>
(2)借用构造函数

   在子类级别的构造函数中,通过call()来调用父类级别的构造函数,进行属性的继承和初始化。这种方式虽然解决了属性继承时的值固定问题,但是未继承父级类别中的方法。

  • Function.prototype.call():
       call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
	// thisArg:在 fun 函数运行时指定的 this 值。
	// arg1, arg2, ...:指定的参数列表。
	fun.call(thisArg, arg1, arg2, ...)
  • 示例代码:
    <script>
        // 父类
        function Person(name,age){
            this.name = name;
            this.age = age;
        }

        Person.prototype.say = function(){console.log("hello, my name is " + this.name);};

        // 子类
        function Student(name, age, id){
            this.id = id;
            Person.call(this, name, age);
        }

        Student.prototype.study = function(){console.log(this.name + " are studying.")};

        var stu = new Student("Bob", 18, "002");
        stu.study();    // Bob are studying.
        stu.say();  // 报错
    </script>
(3)组合继承

   通过改变原型引用和借用构造函数这两种组合的方式,来继承父级类别。

   通过这种方式,虽然在子类的构造函数中继承了父类的属性,但显然存在了冗余。由于子类的原型对象指向父类的实例对象,所以子类的原型对象中也会有父类的属性(属性值为 undefined ),只是在子类实例对象访问父类属性时,优先访问了构造函数中继承的属性,所以可以说是一种假继承。

  • 示例代码:
    <script>
        // 父类
        function Person(name,age){
            this.name = name;
            this.age = age;
        }

        Person.prototype.say = function(){console.log("hello, my name is " + this.name);};

        // 子类
        function Student(name, age, id){
            this.id = id;
            Person.call(this, name, age);
        }

        // 在改变原型引用时,不初始化实例对象的属性值
        Student.prototype = new Person();
        Student.prototype.study = function(){console.log(this.name + " are studying.")};

        var stu = new Student("Bob", 18, "002");
        stu.study();    // Bob are studying.
        stu.say();  // hello, my name is Bob
        console.dir(stu);
    </script>
(4)拷贝继承

   把父级对象中需要共享(原型对象中)的属性或者方法,复制到子级对象中。

  • 示例代码:
    <script>
        // 父类
        function Person(){}
        Person.prototype.name = "Alice";
        Person.prototype.age = 20;
        Person.prototype.say = function(){console.log("hello, my name is " + this.name);};

        // 子类
        var obj = {};

        // 循环遍历父类属性并复制到子类对象
        for(var key in Person.prototype){
            obj[key] = Person.prototype[key];
        }

        obj.say();  // hello, my name is Alice
    </script>

三、综合案例——贪吃蛇

  • 网页——1.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>

    <style>
        .map{
            width: 800px;
            height: 600px;
            background-color: lightblue;
            position: relative;
        }
    </style>

</head>
<body>
    <!-- 地图 -->
    <div class="map"></div>

    <script src="food.js"></script>
    <script src="snake.js"></script>
    <script src="Game.js"></script>

    <script>
        var gm = new Game(document.querySelector(".map"));
        gm.init();
    </script>
</body>
</html>
  • 食物对象——food.js
// 食物对象
(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 || "green";
    }

    // 在map上初始化食物
    Food.prototype.init = function(map){
        // 每次初始化之前,先删除以前的食物
        remove();

        // 创建div元素作为食物
        var div = document.createElement("div");
        map.appendChild(div);

        div.style.width = this.width + "px";
        div.style.height = this.height + "px";
        div.style.backgroundColor = this.color;
        div.style.position = "absolute";
        
        // 随机产生x,y坐标
        this.x = parseInt(Math.random() * (map.offsetWidth / this.width));
        this.y = parseInt(Math.random() * (map.offsetHeight / this.height));
        div.style.left = this.x * this.width + "px";
        div.style.top = this.y * this.height + "px";

        // 保存引用
        elements.push(div);
    }

    // 删除食物
    function remove(){
        for(var i = elements.length - 1; i >= 0; i--){
            var e = elements[i];
            e.parentNode.removeChild(e);
            elements.splice(i, 1);
        }
    }

    // 暴露引用接口给window
    window.Food = Food;

})();
  • 小蛇对象——snake.js
// 蛇对象
(function(){
    // 元素数组
    var elements = [];

    function Snake(width,height,direction){
        this.width = width || 20;
        this.height = height || 20;
        this.body = [
            {"x":3, "y":1, "color":"red"},  // 头
            {"x":2, "y":1, "color":"yellow"},
            {"x":1, "y":1, "color":"yellow"},
        ]
        this.direction = direction || "right";
    }

    Snake.prototype.init = function(map){
        // 初始化前,先删除
        remove();

        // 循环,每节身体创建一个div
        for(var i = 0; i < this.body.length; i++){
            var obj = this.body[i];
            var div = document.createElement("div");
            map.appendChild(div);

            div.style.position = "absolute";
            div.style.width = this.width + "px";
            div.style.height =this.height + "px";
            div.style.left = obj.x * this.width + "px";
            div.style.top = obj.y * this.height + "px";
            div.style.backgroundColor = obj.color;
            if(i == 0){
                div.style.zIndex = 1;
            }
            
            elements.push(div);
        }
    }

    // 小蛇移动函数
    Snake.prototype.move = function(food,map,timeId){
        // 前一时刻身体的前一节位置,就是这节这时刻的位置
        for(var i = this.body.length - 1; 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 "up":
                this.body[0].y--;
                break;
            case "down":
                this.body[0].y++;
                break;
            case "left":
                this.body[0].x--;
                break;
            case "right":
                this.body[0].x++;
                break;
        }

        // 如果吃到了食物
        var headX = this.body[0].x;
        var headY = this.body[0].y;
        if(headX == food.x && headY == food.y){
            // 增加一节身体——复制蛇尾
            var last = this.body[this.body.length - 1];
            this.body.push({
                x : last.x,
                y : last.y,
                color : last.color
            });

            // 重新生成食物
            food.init(map);
        }

        // 判断是否撞到了身体
        for(var i = 1; i < this.body.length; i++){
            if(headX == this.body[i].x && headY == this.body[i].y){
                clearInterval(timeId);
                alert("You died, game over!");
            }
        }
    }

    // 删除函数
    function remove(){
        for(var i = elements.length - 1; i >= 0; i--){
            elements[i].parentNode.removeChild(elements[i]);
            elements.splice(i, 1);
        }
    }

    // 暴露接口给外部
    window.Snake = Snake;
})();
  • 游戏对象——Game.js
// 游戏对象
(function (){
    function Game(map){
        this.snake = new Snake();
        this.food = new Food();
        this.map = map;
    }

    // 初始化游戏
    Game.prototype.init = function(){
        this.food.init(this.map);
        this.snake.init(this.map);
        this.runSnake();       
        this.bindKey(); 
    }

    // 小蛇自动前进
    Game.prototype.runSnake = function(){
        var that = this;
        var timeId = setInterval(function(){
            this.snake.move(this.food, this.map, timeId);
            this.snake.init(this.map);

            // 边界判断
            var maxX = this.map.offsetWidth / this.snake.width;
            var maxY = this.map.offsetHeight / this.snake.height;

            var headX = this.snake.body[0].x;
            var headY = this.snake.body[0].y;
            if(headX < 0 || headX >= maxX || headY < 0 || headY >= maxY){
                // 停止定时器
                clearInterval(timeId);
                alert("You died, game over!");
            }
        }.bind(that), 150);
    }

    // 小蛇方向控制(不能反向)
    Game.prototype.bindKey = function(){
        var that = this;
        document.addEventListener("keydown", function(e){
            var direction = this.snake.direction;
            switch(e.keyCode){
                case 37:
                    this.snake.direction = direction == "right"? direction:"left";
                    break;
                case 38:
                    this.snake.direction = direction == "down"? direction:"up";
                    break;
                case 39:
                    this.snake.direction = direction == "left"? direction:"right";
                    break;
                case 40:
                    this.snake.direction = direction == "up"? direction:"down";
                    break;
            }
        }.bind(that), false)
    }

    window.Game = Game;
})();

参考链接:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值