《前端》JavaScript知识(黑马)(高级一)

本文探讨了JavaScript中的实例对象与构造函数的关系,强调了通过构造函数创建对象时可能出现的问题及其解决方案——利用原型实现数据共享。文章详细讲解了构造函数、原型对象和实例对象之间的关系,介绍了如何通过原型共享数据、原型的简单写法以及如何在原型对象中添加方法。同时,还讨论了如何为内置对象添加原型方法,并提供了面向对象编程的复习和案例,包括随机小方块和贪吃蛇的实现。
摘要由CSDN通过智能技术生成

1、实例对象和构造函数之间的关系:

  1. 实例对象是通过构造函数来创建的---创建的过程叫实例化
  2. 如何判断对象是不是这个数据类型?
  •     1) 通过构造器的方式 实例对象.构造器==构造函数名字
  •     2) 对象 instanceof 构造函数名字

 尽可能的使用第二种方式来识别,为什么?原型讲完再说。

2、构造函数创建对象引发的问题

 <script>  
 function Person(name,age) {
     this.name=name;
     this.age=age;
     this.eat=function () {
       console.log("今天吃红烧土豆");
     }
   }
   per1.eat();
   per2.eat();
   //不是同一个方法
   console.log(per1.eat==per2.eat);

   for(var i=0;i<100;i++){
     var per=new Person("嘎嘎",20);//创建100个对象
     per.eat();//创建100个方法。非常浪费内存
   }

//    可以采用以下方法查看文件结构,会发现它们分别调用一次eat方法,不是使用的同一个方法。
//    console.dir(per1);
//    console.dir(per2);
  </script>

由此可以看出,使用构造函数创建对象,有多少个对象就调用几次eat方法,使用的不是同一个方法,非常占用内存。

怎么解决?--让它们调用同一个方法。

//先把eat这个方法封装为一个函数
function myEat() {
  console.log("吃大榴莲");
}
//var myEat=10;这里不行了。会造成命名冲突
function Person(name,age) {
  this.name=name;
  this.age=age;
//用在这里,相当于调用
  this.eat=myEat;
}
var per1=new Person("小白",20);
var per2=new Person("小黑",30);

console.dir(per1);
console.dir(per2);
console.log(per1.eat==per2.eat);

这种方法有缺陷,当我再定义一个var myEat=10;的时候,会造成命名冲突。

怎么解决?--通过原型来解决---------数据共享,节省内存空间(作用之一)

  <script>
    function Person(name,age) {
      this.name=name;
      this.age=age;
    }
    //通过【原型】来添加方法,解决数据共享,节省内存空间
    Person.prototype.eat=function () {
      console.log("吃凉菜");
    };

    var p1=new Person("小明",20);
    var p2=new Person("小红",30);
    console.log(p1.eat==p2.eat);//true
// 结果是true 可见是同一个方法了
</script>

查看文件结构,可见实例对象中根本没有eat方法,但是能够使用,这是为什么?

    console.dir(p1);
    console.dir(p2);

    * 原型是什么?

实例对象中有__proto__这个属性,叫原型。也是一个对象,这个属性是给浏览器使用,不是标准的属性。

----->__proto__----->可以叫原型对象

构造函数中有prototype这个属性,叫原型。也是一个对象,这个属性是给程序员使用,是标准的属性。

------>prototype--->可以叫原型对象

* 实例对象的__proto__和构造函数中的prototype相等--->true

* 又因为实例对象是通过构造函数来创建的,构造函数中有原型对象prototype

* 实例对象的__proto__指向了构造函数的原型对象prototype。

什么时候把方法写到原型里?--数据共享

    window是对象

    document是属性,document也是一个对象

    write()是方法

    window.document.write("哈哈");

    对象.style.color=值;

例子:感受面向过程和面向对象的编程思想

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>初级的面对对象思想</title>
    <style>
        div {
            width: 300px;
            height: 200px;
            background-color: red;
        }
    </style>
</head>
<body>
    <div>
        <button id="btn">确定</button>
    </div>
    <div id="div"></div>
    <p>初级的面对对象思想--
        按钮是一个对象,div是一个对象,颜色是一个属性
    </p>
    <script>
        // 创建一个构造方法,传进去三个参数,把它们变成对象
        function ChangeStyle(btnID,divID,color) { 
            // 按钮对象
            this.btnobj=document.getElementById(btnID);
            // div对象
            this.divobj=document.getElementById(divID);
            // 颜色对象
            this.color=color;
        }
        // 数据共享来改变div的背景颜色--把方法添加到原型里
        ChangeStyle.prototype.init=function(){
            // console.log(this);//这条语句就是为了证明this就是实例对象--就是当前对象
            
            // 想实现点击按钮,改变div的背景颜色。

            // that变量用于获取到这个this,为什么看下面
            var that=this;
            this.btnobj.onclick=function(){
            // 这里写  “this.onclick=function()”也可以 ,所以
            //要注意:这里的this和上面 console.log(this);的this不是同一个this
            // 这里的this=this.btnobj
            // 上面的this是cs

            // 如果我想要获取到div,不能在这下面写this.div
            // 如果写了就相当于  this.btnobj.div,这显然不对
            // 那怎么写?--先在这个大括号外把那个this获取一下:var that=this;。
            that.divobj.style.backgroundColor="that.color";
            }
        }
        // 创建的这个init方法怎么用?--实例化对象
        var cs=new ChangeStyle("btn","div","yellew");
        // 调用方法
        cs.init();//=>输出ChangeStyle(实例对象)
        // 所以console.log(this);的this就是实例对象--就是当前对象
    </script>
</body>
</html>

上面console.log(this);得到结果如下图,这是一个实例对象(如果是构造方法,会得到prototype)。

上面初级版本有点弱,在构造函数里传递的是ID。

现在换成对象。--可以改变使用少量代码改变多个属性。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <style>
    div {
      width: 300px;
      height: 200px;
      background-color: red;
    }
  </style>
</head>
<body>
<input type="button" value="显示效果" id="btn"/>
<div id="dv"></div>
<script src="common.js"></script>
<script>

  function ChangeStyle(btnObj, dvObj, json) {
    this.btnObj = btnObj;
    this.dvObj = dvObj;
    this.json = json;
  }
  ChangeStyle.prototype.init = function () {
    //点击按钮,改变div多个样式属性值
    var that = this;
    this.btnObj.onclick = function () {//按钮
      for (var key in that.json) {
        that.dvObj.style[key] = that.json[key];
      }
    };
  };

  //实例化对象
  var json = {"width": "500px", "height": "800px", "backgroundColor": "blue", "opacity": "0.2"};
  var cs = new ChangeStyle(my$("btn"), my$("dv"), json);
  cs.init();//调用方法
</script>
</body>
</html>

注意上面例子里为什么要var that =this;

因为按钮事件的this就是这个按钮,即:按钮事件的this=this.btnobj=按钮。

复习原型

<script>
    // 1、构造函数
    function Person(name,age){
        this.name=name;
        this.age=age;
    }
    // 2、通过原型添加方法
    Person.prototype.sayHi=function(){
        console.log("打招呼,您好!");
    };
    // 3、实例化对象,调用
    var per=new Person("Bella","22");
    per.sayHi();
</script>

3、构造函数,原型对象,实例对象之间的关系

    * 构造函数可以实例化对象

    * 构造函数中有一个属性叫prototype,是构造函数的原型对象。

    * 构造函数的原型对象(prototype)中一个constructor构造器,这个构造器指向的就是自己所在的原型对象所在的构造函数

    * 实例对象的原型对象(__proto__)指向的是该构造函数的原型对象

    * 构造函数的原型对象(prototype)中的方法是可以被实例对象直接访问的

4、利用原型共享数据

什么样的数据是需要写在原型中? --需要共享的数据可写在原型中。

属性需要共享,方法也需要共享。

不需要共享的数据写在构造函数中,需要共享的数据写在原型中。

step1:构造函数:

    function Student(name,age,sex) {
      this.name=name;
      this.age=age;
      this.sex=sex;
    }

step2:如果(使用原型对象),所有学生身高188,所有人的体重都是55。所有学生都要每天写500行代码,所有学生每天都要吃一个10斤的西瓜:

    // 原型对象
    Student.prototype.height="188cm";
    Student.prototype.weight="55kg";
    Student.prototype.write=function(){
      console.log("每天写500行代码");
    }
    Student.prototype.eat=function(){
      console.log("每天都要吃一个10斤的西瓜");
    }

step3:实例化对象,并初始化.

    var stu=new Student("bella",22,"男");
    console.dir(Student);
    console.dir(stu);
    console.log(stu.write);

5、原型的简单写法

可以把添加到原型中的属性和方法集中写在原型中,但是这样会丢失构造器(原因是这样看student,是不知道他的构造函数是谁),解决办法是手动修改构造器的指向。

  <script>
    function Student(name, age, sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
    }
    //简单的原型写法
    Student.prototype = {
      //手动修改构造器的指向
      constructor:Student,
      height: "188",
      weight: "55kg",
      study: function () {
        console.log("学习好开心啊");
      },
      eat: function () {
        console.log("我要吃好吃的");
      }
    };

    var stu=new Student("段飞",20,"男");
    stu.eat();
    stu.study();
    console.dir(Student);
    console.dir(stu);
  </script>

6、原型对象中添加方法

原型中的方法是可以相互访问的。

    //原型中的方法,是可以相互访问的
    //原型对象中的方法,可以相互调用
    function Animal(name,age) {
      this.name=name;
      this.age=age;
    }
    //原型中添加方法
    Animal.prototype.eat=function () {
      console.log("动物吃东西");
      this.play();
    };
    Animal.prototype.play=function () {
      console.log("玩球");
      this.sleep();
    };
    Animal.prototype.sleep=function () {
      console.log("睡觉了");
    };

    var dog=new Animal("小苏",20);
    dog.eat();

7、实例对象使用的属性和方法的层次搜索

    function Animal(age,sex){
        this.sex=sex;
        this.age=age;
        this.eat=function(){
            console.log("构造函数中的吃")
        };
    }
    // Animal
    var ani=new Animal(20,"男");
    console.log(ani.sex);//==》男
    function Animal(age,sex){
        this.sex=sex;
        this.age=age;
        this.eat=function(){
            console.log("构造函数中的吃")
        };
    }
    // 在这里添加一个原型,值为女
    Animal.prototype.sex="女";
    var ani=new Animal(20,"男");
    console.log(ani.sex);//结果还是男

原因是它先从实例对象中找,构造函数中的属性方法都变成实例对象中的属性。

如果在构造函数中的sex属性去掉,只在原型中添加sex=女,最后输出sex,结果变为女:

    function Animal(age){
        // this.sex=sex;
        this.age=age;
        this.eat=function(){
            console.log("构造函数中的吃")
        };
    }
    // 在这里添加一个原型,值为女
    Animal.prototype.sex="女";
    var ani=new Animal(20);
    console.log(ani.sex);//==》女

总结:实例对象用到的属性方法先在实例对象中找,没有的到原型中找。

8、为内置对象添加原型方法

  我们能否为系统的对象的原型中添加方法,相当于在改变源码。

    // 我希望字符串中有一个倒序字符串的方法
    String.prototype.myReverse=function () {
      for(var i=this.length-1;i>=0;i--){
        console.log(this[i]);
      }
    };
    var str="abcdefg";
    str.myReverse();


    //为Array内置对象的原型对象中添加方法
    Array.prototype.mySort=function () {
      for(var i=0;i<this.length-1;i++){
          for(var j=0;j<this.length-1-i;j++){
              if(this[j]<this[j+1]){
                  var temp=this[j];
                this[j]=this[j+1];
                this[j+1]=temp;
              }//end if
          }// end for
      }//end for
    };

    var arr=[100,3,56,78,23,10];
    arr.mySort();
    console.log(arr);

    String.prototype.sayHi=function () {
      console.log(this+"哈哈,我又变帅了");
    };

    //字符串就有了打招呼的方法
    var str2="小杨";
    str2.sayHi();
  </script>

9、局部变量变成全局变量

函数的自调用--一次性函数--声明的同时,直接调用。

    如何把局部变量变成全局变量?--   把局部变量给window就可以了。

  <script>
    //通过自调用函数产生一个随机数对象,在自调用函数外面,调用该随机数对象方法产生随机数
    (function (window) {
      //产生随机数的构造函数
      function Random() {
      }
      //在原型对象中添加方法
      Random.prototype.getRandom = function (min,max) {
        return Math.floor(Math.random()*(max-min)+min);
      };
      //把Random对象暴露给顶级对象window--->使外部可以直接使用这个对象
      window.Random=Random;//把 Random 给了Random这个 属性
    })(window);
    //实例化随机数对象
    var rm=new Random();
    //调用方法产生随机数
    console.log(rm.getRandom(0,5));
    //全局变量
  </script>

 复习:

  • 面向过程和面向对象都是编程的思想,方式不一样
  1. 面向过程:凡事都是亲力亲为,所有的代码都要自己写,每一步都要很清楚,注重的是过程
  2. 面向对象:执行者成为指挥者,只要找对象,然后让对象做相关的事情,注重的是结果
  • 面向对象的特性:封装,继承,多态
  • 封装:就是代码的封装,把一些特征和行为封装在对象中.
  • 面向对象的编程思想:
  1.         根据需求,抽象出相关的对象,总结对象的特征和行为,把特征变成属性,行为变成方法,
  2.         然后定义(js)构造函数,实例化对象,通过对象调用属性和方法,完成相应的需求.---编程的思想。
  • 对象:具体特指的某个事物,有特征(属性)和行为(方法),对象可以看成是一无序属性的集合
  • 如何创建对象?

通过调用new Object(),还有字面量{},自定义构造函数

  • 创建对象的方式
  1. 调用系统Object()----->创建出来的对象都是Object类型的,不能很明确的指出这个对象是属于什么类型
  2. 字面量的方式{}----->只能创建一个对象(一次只能创建一个)
  3. 自定义构造函数的方式---工厂模式创建对象-----自定义构造函数(优化后的工厂模式)
  • 自定义构造函数创建对象:4件事
  1. 在内存中申请一块空闲的空间,存储创建的对象
  2. this就是当前实例化的对象
  3. 设置对象中的属性和方法(为对象添加属性和方法,为属性和方法赋值)
  4. 把创建后的对象返回
  •  都是需要通过new的方式
  • 什么是原型?
  1.        构造函数中有一个属性prototype,是原型,程序员使用的
  2.        实例对象中有一个属性__proto__,是原型,浏览器使用的,不是很标准的,
  3.        实例对象中的__proto__指向的就是该实例对象中的构造函数中的prototype
  4.        构造函数中的prototype里面的属性或者方法,可以直接通过实例对象调用
  • 正常的写法:实例对象.__proto__才能访问到构造函数中的prototype中的属性或者方法

                            per.__proto__.eat();//__proto__不是标准的属性

                            per.eat();

  • 原型就是属性,而这个属性也是一个对象

       Person.prototype--->是属性

       Person.prototype.属性或者Person.ptototype.方法() 

  • 本身在构造函数中定义的属性和方法,当实例化对象的时候,实例对象中的属性和方法都是在自己的空间中存在的。

        如果是多个对象,这些属性和方法都会在单独的空间中存在,浪费内存空间,

        所以,为了数据共享,把想要节省空间的属性或者方法写在原型对象中,达到了数据共享,实现了节点内存空间。

  • 原型的作用之一:数据共享,节省内存空间
  • 原型的写法:
  1. 构造函数.prototype.属性=值
  2. 构造函数.prototype.方法=值---->函数.prototype,函数也是对象,所以,里面也有__proto__
  3. 实例对象.prototype-------->实例对象中没有这个属性,只有__proto__(暂时的)
  • 多个属性方法的简单原型的写法:用大括号一起括起来

        缺陷:--->新的知识点---->原型直接指向{}---->就是一个对象,没有构造器

       构造函数.prototype={

                把共享的属性和方法都放在大括号里;

                切记:如果这这种写法,要把构造器加上

           };

  • 通过原型为内置对象添加原型的属性或者方法----->原因:

         系统的内置对象的属性和方法可能不满足现在需求,所以,可以通过原型的方式加入属性或者方法,为了方便开发。

  • 为内置对象的原型中添加属性和方法,那么这个内置对象的实例对象就可以直接使用了。

          String.prototype.方法=匿名函数;

           var str="哈哈";

           str.方法();---->实例对象可以直接调用原型中的属性或者方法

    function Person(age) {
      this.age=age;
    }
    Person.prototype.sayHi=function () {

    };
    var p=new Person(10);
    p.sayHi();

10、案例:随机小方块(贪吃蛇里的食物)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <style>
    .map{
      width: 800px;
      height: 600px;
      background-color: #CCC;
      position: relative;
    }
  </style>
</head>
<body>
<div class="map"></div>
<script src="common.js"></script>
<script>
  //产生随机数对象的
  (function (window) {
    function Random() {
    }
    Random.prototype.getRandom=function (min,max) {
      return Math.floor(Math.random()*(max-min)+min);
    };
    //把局部对象暴露给window顶级对象,就成了全局的对象
    window.Random=new Random();
  })(window);//自调用构造函数的方式,分号一定要加上


  //产生小方块对象
  (function (window) {
    //console.log(Random.getRandom(0,5));
    //先获取地图,选择器的方式来获取元素对象
    var map=document.querySelector(".map");

    //食物的构造函数(实例对象由构造函数创建)
    function Food(width,height,color) {
      this.width=width||20;//默认的小方块的宽
      this.height=height||20;//默认的小方块的高
      //横坐标,纵坐标
      this.x=0;//横坐标随机产生的
      this.y=0;//纵坐标随机产生的
      this.color=color;//小方块的背景颜色
      this.element=document.createElement("div");//创建小方块的元素
    }
    //初始化小方块的显示的效果及位置---显示地图上
    Food.prototype.init=function (map) {
      //设置小方块的样式
      var div=this.element;//div对象
      div.style.position="absolute";//脱离文档流
      div.style.width=this.width+"px";
      div.style.height=this.height+"px";
      div.style.backgroundColor=this.color;
      //把小方块加到map地图中
      map.appendChild(div);
      this.render(map);
    };
    //产生随机位置(render是传递的意思)
    Food.prototype.render=function (map) {
      //随机产生横纵坐标
      var x=Random.getRandom(0,map.offsetWidth/this.width)*this.width;//坐标点*宽
      var y=Random.getRandom(0,map.offsetHeight/this.height)*this.height;
      this.x=x;
      this.y=y;
      var div=this.element;
      div.style.left=this.x+"px";
      div.style.top=this.y+"px";
    };

    //实例化对象
    var fd=new Food(20,20,"green");
    fd.init(map);
    console.log(fd.x+"===="+fd.y);
    
  })(window);
</script>
</body>
</html>

11、贪吃蛇案例

<!-- 三个对象:游戏、蛇、食物 
怎么玩?-用户按下上下左右箭头--需要监听键盘按键-来让蛇按照什么方向走。

-->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>贪吃蛇案例</title>
  <style>
    .map {
      width: 800px;
      height: 600px;
      background-color: #CCC;
      position: relative;
      /* 蛇和食物都是要在地图上运动,所以它们是脱离了文档流,地图是父元素,蛇和食物是子元素
      所以父元素position:relative; */
    }
  </style>
</head>
<body>
<!--画出地图,设置样式-->
<div class=" map"></div>
<script>

  //自调用函数----食物的
  (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--就是页面上的.class=map的这个div)
    Food.prototype.init = function (map) {
      //先删除这个小食物
      //外部无法访问的函数
      remove();

      //创建div
      var div = document.createElement("div");
      //把div加到map中
      map.appendChild(div);
      //设置div的样式
      div.style.width = this.width + "px";
      div.style.height = this.height + "px";
      div.style.backgroundColor = this.color;
      //先脱离文档流(不脱离,不能随机坐标)
      div.style.position = "absolute";
      //随机横纵坐标
      this.x = parseInt(Math.random() * (map.offsetWidth / this.width)) * this.width;
      this.y = parseInt(Math.random() * (map.offsetHeight / this.height)) * this.height;
      div.style.left = this.x + "px";
      div.style.top = this.y + "px";

      //把div加入到数组elements中
      elements.push(div);
    };

    //私有的函数---删除食物的
    function remove() {
      //elements数组中有这个食物
      for (var i = 0; i < elements.length; i++) {
        var ele = elements[i];
        //找到这个子元素的父级元素,然后删除这个子元素
        ele.parentNode.removeChild(ele);
        //再次把elements中的这个子元素也要删除
        elements.splice(i, 1);
      }
    }

    //把Food暴露给Window,外部可以使用
    window.Food = Food;
  }());
  //自调用函数----食物end

  //自调用函数---小蛇
  (function () {
    var elements = [];//存放小蛇的每个身体部分
    //小蛇的构造函数
    function Snake(width, height, direction) {
      //小蛇的每个部分的宽
      this.width = width || 20;
      this.height = height || 20;
      //小蛇的身体
      this.body = [
        {x: 3, y: 2, color: "red"},//头
        {x: 2, y: 2, color: "orange"},//身体
        {x: 1, y: 2, color: "orange"}//身体
      ];
      //方向
      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];
        //创建div
        var div = document.createElement("div");
        //把div加入到map地图中
        map.appendChild(div);
        //设置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;
        //方向暂时不定
        //把div加入到elements数组中----目的是为了删除
        elements.push(div);
      }
    };

    //为原型添加方法---小蛇动起来
    Snake.prototype.move = function (food, map) {
      //改变小蛇的身体的坐标位置
      var i = this.body.length - 1;//2
      for (; 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 "right":
          this.body[0].x += 1;
          break;
        case "left":
          this.body[0].x -= 1;
          break;
        case "top":
          this.body[0].y -= 1;
          break;
        case "bottom":
          this.body[0].y += 1;
          break;
      }

      //判断有没有吃到食物
      //小蛇的头的坐标和食物的坐标一致
      var headX=this.body[0].x*this.width;
      var headY=this.body[0].y*this.height;
      //判断小蛇的头的坐标和食物的坐标是否相同
      if(headX==food.x&&headY==food.y){
        //获取小蛇的最后的尾巴
        var last=this.body[this.body.length-1];
        //把最后的蛇尾复制一个,重新的加入到小蛇的body中
        this.body.push({
          x:last.x,
          y:last.y,
          color:last.color
        });
        //把食物删除,重新初始化食物
        food.init(map);
      }
    }
    ;//删除小蛇的私有的函数=============================================================================
    function remove() {
      //删除map中的小蛇的每个div,同时删除elements数组中的每个元素,从蛇尾向蛇头方向删除div
      var i = elements.length - 1;
      for (; i >= 0; i--) {
        //先从当前的子元素中找到该子元素的父级元素,然后再弄死这个子元素
        var ele = elements[i];
        //从map地图上删除这个子元素div
        ele.parentNode.removeChild(ele);
        elements.splice(i, 1);
      }
    }

    //把Snake暴露给window,外部可以访问
    window.Snake = Snake;
  }());

  //自调用函数---游戏对象================================================
  (function () {

    var that = null;//该变量的目的就是为了保存游戏Game的实例对象-------

    //游戏的构造函数
    function Game(map) {
      this.food = new Food();//食物对象
      this.snake = new Snake();//小蛇对象
      this.map = map;//地图
      that = this;//保存当前的实例对象到that变量中-----------------此时that就是this
    }

    //初始化游戏-----可以设置小蛇和食物显示出来
    Game.prototype.init = function () {
      //初始化游戏
      //食物初始化
      this.food.init(this.map);
      //小蛇初始化
      this.snake.init(this.map);
      //调用自动移动小蛇的方法========================||调用了小蛇自动移动的方法
      this.runSnake(this.food, this.map);
      //调用按键的方法
      this.bindKey();//========================================
    };

    //添加原型方法---设置小蛇可以自动的跑起来
    Game.prototype.runSnake = function (food, map) {

      //自动的去移动
      var timeId = setInterval(function () {
        //此时的this是window
        //移动小蛇
        this.snake.move(food, map);
        //初始化小蛇
        this.snake.init(map);
        //横坐标的最大值
        var maxX = map.offsetWidth / this.snake.width;
        //纵坐标的最大值
        var maxY = map.offsetHeight / this.snake.height;
        //小蛇的头的坐标
        var headX = this.snake.body[0].x;
        var headY = this.snake.body[0].y;
        //横坐标
        if (headX < 0 || headX >= maxX) {
          //撞墙了,停止定时器
          clearInterval(timeId);
          alert("游戏结束");
        }
        //纵坐标
        if (headY < 0 || headY >= maxY) {
          //撞墙了,停止定时器
          clearInterval(timeId);
          alert("游戏结束");
        }
      }.bind(that), 150);
    };

    //添加原型方法---设置用户按键,改变小蛇移动的方向
    Game.prototype.bindKey=function () {

      //获取用户的按键,改变小蛇的方向
      document.addEventListener("keydown",function (e) {
        //这里的this应该是触发keydown的事件的对象---document,
        //所以,这里的this就是document
        //获取按键的值
        switch (e.keyCode){
          case 37:this.snake.direction="left";break;
          case 38:this.snake.direction="top";break;
          case 39:this.snake.direction="right";break;
          case 40:this.snake.direction="bottom";break;
        }
      }.bind(that),false);
    };

    //把Game暴露给window,外部就可以访问Game对象了
    window.Game = Game;
  }());

  //初始化游戏对象
  var gm = new Game(document.querySelector(".map"));

  //初始化游戏---开始游戏
  gm.init();


  //外部测试代码
  //  var fd = new Food();
  //  fd.init(document.querySelector(".map"));
  //  //创建小蛇
  //  var snake = new Snake();
  //  snake.init(document.querySelector(".map"));//先在地图上看到小蛇
  //
  //
  //
  //  setInterval(function () {
  //    snake.move(fd, document.querySelector(".map"));
  //    snake.init(document.querySelector(".map"));
  //  }, 150);


  //  snake.move(fd, document.querySelector(".map"));//走一步
  //  snake.init(document.querySelector(".map"));//初始化---重新画一条小蛇(先删除之前的小蛇,把现在的小蛇显示出来)

  //  snake.move(fd, document.querySelector(".map"));
  //  snake.init(document.querySelector(".map"));
  //  snake.move(fd, document.querySelector(".map"));
  //  snake.init(document.querySelector(".map"));
  //  snake.move(fd, document.querySelector(".map"));
  //  snake.init(document.querySelector(".map"));
  //  snake.move(fd, document.querySelector(".map"));
  //  snake.init(document.querySelector(".map"));
  //  snake.move(fd, document.querySelector(".map"));
  //  snake.init(document.querySelector(".map"));
  //
  //  snake.move(fd, document.querySelector(".map"));
  //  snake.init(document.querySelector(".map"));


  //  fd.init(document.querySelector(".map"));
  //  fd.init(document.querySelector(".map"));
  //  fd.init(document.querySelector(".map"));
  //  fd.init(document.querySelector(".map"));
  //console.log(fd.x+"====>"+fd.y);

  //console.log(fd.width);

</script>
</body>
</html>

12、键盘按下事件

<script>
  //页面的任何的位置.按下键盘,获取按键的值
  document.onkeydown=function (e) {
    console.log(e.keyCode);//事件参数对象
  };
</script>

13、原型链

 

 

14、原型 的指向是否可以改变

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值