js面向对象学习

js面向对象学习

1.认识对象

对象:(object)是“键值对”的集合,表示属性和值的映射关系

  • 对象是引用数据类型,所以它的真正数据是存放到堆内存中的

  • 在js中,对象数据类型用花括号的形式来定义,花括号中存储的是属性名:属性值形式的键值对

    • 属性名:也称为(key),可以是字符串类型,也可以是数值类型,数值类型会排在字符串类型的前面
    • 属性值:也称为(value),冒号后面是属性值,属性值可以是任何数据类型
    • 属性名:属性值也称为键值对,也称为k:v 对,k和v之间用冒号分割,键值对与键值对之间用逗号分割
    • 映射关系:映射关系就是指键与值键存在的映射关系
    • 数组在乎项与项之间的顺序关系,而对象在乎的是属性名和属性值之间的映射关系
    • 对象不能通过.length属性查询对象的长度

image-20210927143912263

2.对象的属性

对象中的属性名是否要加引号?

  • 如果属性名不符合js表示符的命名规范,就必须用双引号或单引号来包裹
  • js标识符命名规范:
    • 必须由数字、字母、下划线、$组成,数字不能开头
    • 不能是关键字或保留字
    • 尽量用驼峰命名规则或c风格命名规则

2.1属性的访问

  • 方式一:可以使用“点语法”,来访问对象中指定键的值

    • 用点的方式后面只能跟非数字的属性名,不能跟变量名

    • obj.name;//获取对象中name属性的值	
      obj.age;//获取对象中age属性的值
      
    • 方式二:使用方括号的方式,访问对象中的值

    • 如果属性名不符合js标识符命名规则,则必须使用方括号包裹引号包裹属性名的方式来访问属性,

    • 如果属性名是以变量的形式存储的,那必须使用方括号的形式访问该属性的属性值,如果用点访问则会返回undefined

    • var obj={
      	name:"张三";
      	age : 25;
      }
      //方括号中的属性名必须用引号包起来
      obj["name"];//"张三"
      var key="age";
      obj["key"];//undefined,因为加引号请求的是属性名,obj没有key属性名,所以返回undefined
      obj[key];//25,因为不加引号请求的是变量名,会找key变量的变量值,所以obj[key]==obj["age"]
      obj.key;//undefined,用点的方式后面只能跟属性名,不能跟变量名
      
    • image-20210927145356826

2.2属性的更改

  • 直接使用赋值运算符重新对某属性赋值即可以更改属性

2.3属性的创建

  • 如果对象本身没有某属性值,则用点语法赋值时,这个属性就会创建出来

2.4属性的删除

  • 如果删除某个属性,使用delete操作符,删除后返回一个布尔值,true为删除成功,false为删除失败

    • delete obj.a;//删除obj对象中的a属性
      

3.对象的方法

  • 如果某个属性值是函数,则称为对象的方法

    •     <script>
              //sayHello就是一个方法
              var obj={
                  sayHello:function(){
                      alert(hello);
                  }
              }
          </script>
      
  • 调用方法可以使用“点法式”调用对象的方法

  • 方法也是函数,只不过方法是对象的“函数属性”,它需要用对象打点调用

留下问题,如何实现让obj中的函数输出值随着对象中属性值的修改而修改?

4.对象的遍历

  • 和遍历数组类似,遍历对象使用for…in…循环

  • 使用for…in…循环可以遍历对象的每个键

    • //key表示定义的一个循环变量,会依次对象中每一次循环的属性名
      //obj:要遍历的对象名
      //obj[key]:通过每次循环的属性名,就可以用obj["属性名"]的形式获取变量值,因为key是一个变量,所以不能使用点语法
      		for(var key in obj){
                  console.log("属性名:"+key+"属性值:"+obj[key]);
              }
      

5.对象的克隆

image-20210927180940932

  • 对象是引用类型,这就意味着:

    • 对象不能通过var obj1=obj2的方式实现值的克隆

    • 使用==或者三个=来做比较,比较的是他们是否为内存中的同一个对象,就是比较的引用地址是否相同,

      而不是比较的值是否相同

5.1对象的浅克隆

  • 之前学习了数组的深克隆和浅克隆
    • 浅克隆:就是只克隆数组的第一层的值,当第一层仍然存在数组或者函数等数据类型,则只会克隆它们的引用地址,而克隆的数组与原数组事实上是藕断丝连的
    • 深克隆:深克隆就是在浅克隆的基础上,进一步的调用浅克隆,采用递归的形式,实现数组中引用类型数据项的深度克隆

对象的浅克隆?

  • 对象的浅克隆,就是通过for…in…循环实现,只克隆对象的表层
    <script>
        // for...in...实现浅克隆
        var obj1={
            age:18,
            name:"小明",
            arr:[12,12,13,5,16]
        }
        // 这样不是实现克隆,而只是引用地址的传递,他们指向同一个内存堆空间数据
        var obj2=obj1;
        // 这样是实现浅克隆
        var obj3={};
        for(var key in obj1){
            obj3[key]=obj2[key];
        }
		console.log(obj1.arr==obj2.arr)//true,他们两个是相等的,因为他们的引用地址相同
        
    </script>

5.2对象的深克隆

对象的深克隆?

  • 对象的深克隆,就是克隆对象的全貌,不论对象的属性值是否又是引用类型值,都能将他们重新克隆一份,使两个对象完全分离开

  • 对象的深克隆也需要采用递归

  • 面试经常考,一定要记住

  • 深克隆思路:

    • 首先判断传入的数据的类型,分为数组类型,对象类型和基本数据类型
    • 如果是数组类型,就通过for循环获取数组中的每一项,并传入一个新的result数组中,而且在数组项的遍历过程中,如果还有数组或对象,还需要调用克隆,所以,直接给每个数据项再调用一次浅克隆
    • 如果是对象类型,同样需要使用for…in…方法遍历每一项,如果存在属性值是对象或者属性的,就仍需要调用克隆方法
    • 如果是基本数据类型,直接返回属性值和属性名
    <script>
        // 递归实现深克隆
        var obj1 = {
            age: 18,
            name: "小明",
            arr: [12, 12, 13, 5, 16],
        }

        function deepClone(o){
            // 如果是数组类型值
            if(Array.isArray(o)){
                var result=[];
                for(i=0;i<o.length;i++){
                    // 再次调用深克隆判断是否还有引用数据类型
                    result.push(deepClone(o[i]));
                }
                // 如果是对象数据类型
            }else if(typeof o=='object'){
                var result={};
                for(var key in o){
                    // 再次调用深克隆判断值是否还是引用类型
                    result[key]=deepClone(o[key]);
                }
                // 否则是基本数据类型,直接返回
            }else{
                var result=o;
            }
            return result;

        }
        var newObj= deepClone(obj1);//深克隆的新对象
        console.log(newObj.age==obj1.age);//true,基本数据类型
        console.log(newObj.name==obj1.name);//true,基本数据类型
        console.log(newObj.arr==obj1.arr);//false,引用数据类型
    </script>

6.认识上下文

语句的上下文,可以通过这来代指前面具体的某一事物或某件事

image-20210929081422650

  • 函数的上下文,就是可以通过this关键字来代指调用函数
  • this代指什么需要调用函数的前言后语来判断

image-20210929082015662

  • 函数的上下文由调用方式来决定的,通过this关键字来代指调用对象

  • 同一个函数,用不同的形式调用它,则函数的上下文不同

    • 情形一:对象打点调用方法,方法中的this代指这个打点的对象:对象.方法()
    • 情形二:圆括号直接调用函数,函数则指代的是window对象
  • 函数只有被调用的时候,它的上下文才能够被确定,不能通过定义确定它的上下文

函数上下文规则?

  • 函数的上下文要看调用不能看定义,function是“运行时上下文”策略

  • 函数如果不调用,则不能确定函数的上下文

  • 规则一:只要函数的调用符合对象.方法()的格式,则this就是指代的调用它的对象,在寻找对象时一定要注意寻找包裹它的对象是谁

  • 规则二:圆括号直接调用函数,那么函数的上下文是window对象,window对象的this.属性会去找全局变量

  • 规则三:数组(类数组对象)枚举类函数进行调用,this代指的是这个数组(类数组对象)

  • arr[3]();//arr[3]存储的是一个函数,所以this指代这个数组
    
  • 规则四:IIFE中的函数,this指代的是window对象

image-20210929084125771

image-20210929084453064

  • outer执行之后,会生成return后的对象,所以就是对象.方法()在调用fn函数,结果是77

image-20210929084835431

image-20210929085254325

image-20210929085646081

    
    <script>
        function fun(){
            return this.a+this.b;

        }

        var a=1;
        var b=2;
        var obj={
            a:3,
            b: fun(),//3,执行到这一步时,会执行fun()函数,符合函数名()的规则,所以this指代的是window对象
            fun:fun
        }
        var res=obj.fun();//6这里符合对象名.方法()的调用形式,所以this指代的是obj对象
        console.log(res);
    </script>

2.思考题二

//注意点:考虑到this.b和this.b()的区别
    //	return this.b表示获取b后面的函数体,函数并不会被调用
	//	return this.b(),表示调用b后面的函数,函数会被执行,返回的是执行结果
<script>
        var c=1;
        var obj={
            a:function(){
                var c=3;
                return this.b;//转为obj.b,调用b后面的匿名函数,this指代window对象,
            },
            b:function(){
                var c=4;
                document.write(this.c)//window.c,所以结果为1
            },
            c:2
        };
        var obj1=obj.a();//符合规则一对象名.函数名()调用,this指代obj
        obj1()
    </script>

3.思考题三

  •     <script>
            var a=1;
            var obj={
                a=2,
                //因为函数是立即执行函数,所以在预编译时期,就会执行这个函数,所以fun的值是return的那个函数
                fun=(function(){
                    var a=this.a;//立即执行函数的this表示window对象,所以找window对象中的全局变量a
                    //返回这个函数,用于上面执行了立即执行函数,形成了闭包,记住了a的值等于1
                    return function(){
                        console.log(a+this.a);//
                    }
                } )()
            }
    
            obj.fun();//3,a是闭包中的a,this.a:由于是对象名.方法名()方式调用的,所以this代指的是obj
        </script>
    

7.call和apply

  • call和apply能够指定函数的上下文

        <script>
            function fun(){
                alert(this.a+this.b+this.c);
    
            }
            var xiaoming={
                a:80,
                b:90,
                c:100,
                // 方法1:直接在对象中存放函数,再通过调用函数实现
                fun:fun
            }
            //方法一
            // console.log(xiaoming.fun());
            //方法二:通过call或者apply给函数指定this上下文对象
            fun.call(xiaoming);//调用fun函数,并且以小明为指定上下文对象
            fun.apply(xiaoming);//调用fun函数,并且以小明为指定上下文对象
            
            
        </script>
    

    call和apply能够指定函数的上下文

            fun.call(xiaoming);//调用fun函数,并且以小明为指定上下文对象
            fun.apply(xiaoming);//调用fun函数,并且以小明为指定上下文对象
    
  • call和apply的区别

    • 当函数有形参时,使用call方法指定上下文,传实参时,用逗号从后面罗列
    • 当函数有形参时,使用apply方法指定上下文,传实参时,使用数组的形式包裹实参
    • 如果传入参数的格式弄错,可能就不能正确传入到形参中了,甚至会报错
        fun.call(xiaoming,50,89);//50和80是传给函数的实参
        fun.apply(xiaoming,[50,80]);//[50,80]是传给函数的实参
  • 什么时候用call,什么时候用apply
    • 当传递的实参是数组或者类数组变量时,如arguments,用apply
    • 传递的参数是逗号罗列时,用call

上下文总结

image-20210929175706448

8.new操作符

一个新的函数调用方式:

new 函数名();

  • 我们先不考虑它的“面向对象”的意义,而是先把用new调用函数的执行步骤和它的上下文弄明白

.使用new操作符调用函数的四步走是什么?

  1. 函数体内会自动创建出一个空白对象,(在函数体内所有语句前面new一个空对象)
  2. 函数的上下文(this)会指向这个对象(当函数中调用this时,其实调用的是这个空对象)
  3. 函数体内的语句会执行
  4. 函数会自动返回上下文对象,即使没有return语句
    <script>
        function fun(){
            //第一步隐式new了一个空对象,
            //第二步,将函数的上下文指向该对象
            //第三步,开始执行下面的语句
            this.a=3;//在空对象中传入键值对a:3
            this.b=9;//在new对象中传入键值对b:9
            //第四步,隐式返回new出来的对象,return this;
        }
        var obj=new fun();
        console.log(obj);//{a:3,b:9}
    </script>

9.构造函数

  • 构造函数:能够构造出相同属性的对象的函数
  • 用new调用一个函数,这个函数就被称为“构造函数”,任何函数都可以是构造函数,只需要用new去调用它
  • 顾名思义,构造函数就是构造新对象的函数,它内部的语句将为对象添加若干属性和方法,完成对象的初始化.
  • 构造函数必须使用new关键字来调用,否则不能够正常工作,所以,开发者规定,构造函数的首字母需要大写,以与普通函数区分
  • 一个函数是不是构造函数,需要判断的是有没有使用new关键字来调用它,而首字母大写,只是一个约定的规范而已。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1pgsFigv-1633619629889)(https://i.loli.net/2021/09/30/vxCZA1mQIy2bSRL.png)]

  • 由于是用new调用构造函数,所以他们返回的结果是一个对象

向对象中添加方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xtzyUyYI-1633619629891)(https://i.loli.net/2021/09/30/bJIZTqBVzDdoR2h.png)]

  • 当我们生成对象时,每个对象中都会存在一个初始化的sayhello方法,而且该方法输出的内容会根据其他参数动态变化。

10.类与实例

什么是类?

  • 类就好比是蓝图,类只描述对象拥有哪些属性和方法,但不具体指明属性的值

什么是实例?

  • 实例就是根据类这个蓝图规定,给属性和方法赋值,从而得到的一个具体的实例对象
  • 通过给类的属性和方法赋不同的值,就能够得到多个不同的实例对象
  • 实例一定是一个具体的事物,类表示的是一组具有同样特征的不具体的事物

例子:狗是一类,而金毛、拉布拉多不能说是实例,因为金毛和拉布拉多仍然不能指定一个具体的实物,金毛还可以细分为长毛金毛,短毛金毛等等,所以他们只能称得上是狗的子类

  • 像小白,史努比这些一听到名字就能够想象到具体某一动物,它就是实例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SwW2Dlrn-1633619629892)(https://i.loli.net/2021/09/30/eQh5wKyjNxif4Ik.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zdZQ9jqI-1633619629894)(https://i.loli.net/2021/09/30/goQNKIyqCrtdh7c.png)]

  • 由类转为实力的过程,就称为实例化

什么是面向对象语言,什么是基于对象语言?

  • java、c++是面向对象语言oo
    • 面向对象语言,就是描述刻画很多的类,让这些类的实例进行配合工作,来完成业务需求
  • javaScript是基于对象语言op
    • javaScript中的构造函数,可以类比喻为oo语言中的“类”
    • 二者写法的确相似,但与真正的OO语言还是有本质区别,

11.prototype和原型链查找

什么是prototype?

  • 任何函数都有prototype属性,prototype在英语中是原型的意思

  • prototype属性值是一个对象,默认拥有constructor属性指回函数,就是指向内存中的函数

    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dpu8iLOh-1633619629895)(https://i.loli.net/2021/09/30/Rgs9rzJU75cZf2I.png)]
  • 普通函数的prototype属性没有任何用处,而构造函数的prototype属性非常有用

  • 构造函数的prototype属性是它实例的原型

    • 函数的原型是prototype属性,实例的原型是_proto__属性
    • prototype是构造函数的prototype属性
    • _proto__是构造函数实例 的原型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WgFwzy6a-1633619629896)(https://i.loli.net/2021/09/30/BHf12riJ6GhTnmu.png)]

  • prototype是构造函数的prototype属性
  • _proto__是构造函数实例 的原型

什么是原型链查找?

  • javascript规定:实例可以打点访问它的原型的属性和方法,这被称为“原型链查找
    • 当一个函数在prototype属性中添加了一个nationly属性,这个nationly属性会同时能够被它实例的_proto__属性拿到
    • 当一个函数的实例通过实例名.nationly方式访问这个属性时,实例会先查找自身有没有这个属性,如果没有,就去拿原型中的nationly属性,这就叫原型链的遮蔽效应

hasOwnProperty

hasOwnProperty:自己拥有某属性,检查对象实例是否用有某个属性

  • 对象名.hasOwnProperty("name");//检查对象中是否有name属性
    
  • 原型中存在的属性不属于对象实例

in

  • in运算符只能检查某个属性或方法是否可以被对象访问,不能检查是否是自己的属性或方法
  • 原型中存在的属性或方法也能被对象访问到

12.把方法直接添加到实例上

  • 把方法直接添加到实例上,每个实例和每个实例的方法函数都是内存中不同的函数,造成了内存的浪费
  • 解决方法:将方法写到prototype上

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MCOOH3i7-1633619629897)(https://i.loli.net/2021/10/06/fFPmXsvBoSIeDlG.png)]

  • prototype属性中添加一个sayhello()方法,则只会在内存中生成一次方法,节省了内存空间
  • 把方法写到原型上,并不会引起方法调用时的紊乱,而且会节省内存空间
  • 结论:方法必须写在prototype属性上,属性写在构造函数里

13.原型链的终点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GWXEJalu-1633619629898)(https://i.loli.net/2021/10/06/RbCXc7z5P9uVm2o.png)]

  • Object.prototype是原型链的终点
  • Object.prototype属性内置了hasOwnProperty()和toString()方法,所以我们创建的实例就能够通过原型链调用到这两个方法。

关于数组的原型链

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L4Gcx54g-1633619629899)(https://i.loli.net/2021/10/06/S6Bh3kUjtHWFgVn.png)]

14.继承

  • 两个无关的类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7XwhPv2Q-1633619629901)(https://i.loli.net/2021/10/06/WGMvsp19ZRo5cxB.png)]

  • 两个相关的类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vTB9Z5W0-1633619629902)(https://i.loli.net/2021/10/06/M9fTQO51cav4XSG.png)]

People类和Student类的关系

  • People类拥有的属性和方法,Student类都有,Student类在People类的基础上扩展了一些属性和方法

  • Student“是一种”People,两类之间是“is a kind of”关系

  • 这就是继承关系:Student类继承自People类

什么是继承?

  • 继承描述了两个类之间的“is a kind of”关系,比如学生“是一种人”,所以学生与人之间就构成了继承关系
  • People称为父类(或者超类,基类),Student被称为子类(派生类)
  • 子类丰富了父类,让类描述的更加具体,更加细化

javaScript中如何实现继承?

  • 实现继承的关键在于:子类必须父类全部的属性和方法并且子类拥有自己特有的属性和方法
  • 使用javaScript特有的原型链特性来实现继承,是普遍的做法
  • ES6中有新的实现继承的方法

实现继承的步骤?

  • 首先创建父类,在prototype属性中书写方法,再创建一个子类,子类要拥有父类的全部属性
  • 让子类的prototype属性指向父类new出来的一个对象
  • 子类的实例就可以调用父类的方法,实现类继承
  • 子类可以重写父类的重名方法(override),主要应用到了构造函数的遮蔽效应

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G08BUaWi-1633619629903)(https://i.loli.net/2021/10/06/aBFA2rdnOgtGmuW.png)]

<script>
        // 创建父类
        function People(name, age, sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;

        }
        //在父类中创建方法
        People.prototype.sayHello = function () {
            console.log("你好,我是" + this.name + "我今年" + this.age + "岁了");
        }
        //创建子类
        function Student(name, age, sex, school, num) {
            this.name = name;
            this.age = age;
            this.sex = sex;
            this.school = school;
            this.num = num;
        }
        //最最最重点,实现子类继承父类,一定要写在子类添加方法的前面,这样才不会覆盖子类新写的方法
        Student.prototype=new People();
        //给子类添加方法
        Student.prototype.study= function () {
            console.log(this.name + "正在学习");
        }

        var hanmeimei=new Student("韩梅梅",18);
        hanmeimei.study();
        hanmeimei.sayHello();

    </script>

15.上升到面向对象-红绿灯

  • 面向对象的本质是什么?

    • 定义不同的类,让类的实例去工作。
  • 面向对象的优点是什么?

    • 程序编写更加清晰,代码结构更加严密,使代码更健壮更利于维护。
  • 面向对象经常用到的场合?

    • 需要封装和复用性的场合(组件思维)

面向对象最重要的工作就是去编写类

    <div id="box"></div>
    <script>
        // 创建红绿灯TraficLight类,有颜色、dom属性
        function TraficLight() {
            //初始颜色为红色
            this.color = 1;
            //调用初始化方法,创建一个img节点
            this.init();
            // 添加绑定事件
            this.bindEvent();
        }

        //初始化方法
        TraficLight.prototype.init = function () {
            // 每调用这个init方法,就会创建一个新的img节点元素
            this.dom = document.createElement('img');
            // 设置dom的src属性
            this.dom.src=this.color+".jpg";
            //将节点放到dom树上
            obox.appendChild(this.dom);

        }
        //绑定监听
        TraficLight.prototype.bindEvent=function(){
            //备份上下文,这里指的是js的实例
            var self=this;
            //当自己的dom被点击时
            this.dom.onclick=function(){
                // 当被点击的时候,调用自己的changecolor方法即可
                self.changeColor();
            
            };
        }
        //改变颜色方法
        TraficLight.prototype.changeColor=function(){
            this.color++;
            // 如果颜色等于4
            if(this.color==4){
                this.color=1;
            }
            //改变颜色数字后需要重新传img路径
            this.dom.src=this.color+".jpg";
        }
        // 获取box元素
        obox = document.getElementById('box');

        // 创建实例化对象
        new TraficLight();
        new TraficLight();
        new TraficLight();
        new TraficLight();
      

    </script>

16.上升到面向对象-炫彩小球

ball类的属性有:

  • x、y、r、opacity、color、dom

ball类的方法有:

  • init初始化、update更新方法
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body{
            background-color: black;
        }
        .ball{
            border-radius: 50%;
            position: absolute;
        }
    </style>
</head>
<body>
    
    <script>
        // 小球类
        function Ball(x,y){
            // 传入小球的圆心距离浏览器左上角的坐标值
            this.x=x;
            this.y=y;
            //给定小球的初始半径
            this.r=10;
            //小球的背景颜色
            this.color=colorArr[parseInt(Math.random()*5)];
            //调用初始化方法生成小球元素
            this.init();
            //把自己推入数组,这里的this不是类本身,而是new出来的实例对象
            arr.push(this);
            // 添加随机的x和y的增量,实现小球随机方向的移动,放在构造函数中,是因为在创建实例之前就应该给定一个确定的方向让小球移动
            do{
                this.xadd=parseInt(Math.random()*10-5);
            this.yadd=parseInt(Math.random()*10-5);
            }while(this.xadd==0&&this.add==0);
            //透明度属性
            this.opacity=1;

        }
        //初始化方法
        Ball.prototype.init=function(){
            //创建自己的dom元素
            this.dom=document.createElement('div');
            this.dom.className="ball";
            this.dom.style.width=this.r*2+"px";
            this.dom.style.height=this.r*2+"px";
            this.dom.style.left=this.x-this.r+"px";
            this.dom.style.top=this.y-this.r+"px";
            this.dom.style.backgroundColor=this.color;
            // 将该元素上树到body元素中
            document.body.appendChild(this.dom); 
        }

        // 小球状态更新方法
        Ball.prototype.update=function(){
            // 位置改变
            this.x+=this.xadd;
            this.y+=this.yadd;
            //使小球的透明度减小
            this.opacity-=0.05;
            //半径改变
            this.r+=0.02;
            this.dom.style.width=this.r*2+"px";
            this.dom.style.height=this.r*2+"px";
            this.dom.style.left=this.x-this.r+"px";
            this.dom.style.top=this.y-this.r+"px";
            this.dom.style.backgroundColor=this.color;
            this.dom.style.opacity=this.opacity;
            //如果小球的opacity为0,则删除小球,释放内存
            if(this.opacity<0){
                // 删除数组中的小球
                for(var i=0;i<arr.length;i++){
                    if(arr[i]==this){
                        arr.splice(i,1);
                    }
                }
                //删除dom元素中的小球
                document.body.removeChild(this.dom);
            }

        }
        // 把所有的小球实例都放在同一个数组中
        var arr=[];
        //创建一个颜色数组
        var colorArr=["#666fff","#aaabbb","#ccff66","#ff99cc","#ff6666"];

        //设置定时器,更新所有小球的实例
        setInterval(function(){
            //遍历数组,调用小球的undate方法
            for(var i=0;i<arr.length;i++){
                arr[i].update();
            }
        },50)
        // 鼠标指针的事件监听
        document.onmousemove=function(e){
            var x=e.clientX;
            var y=e.clientY;
            // 只要鼠标移动就会产生一个新的小球随机移动
            new Ball(x,y);
        }

        //测试,创建一个新对象
        new Ball(600,300);

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

17.包装类

什么是包装类?

  • Number()、String()和Boolean()分别是数字、字符串、布尔值的包装类
  • 包装类的目的就是让基本类型值,可以从他们的prototype属性上获得方法
  • 用new包装类得到的基本类型值的类型结果,是object

包装类的作用?:包装类的作用就是能够使基本类型值通过原型链来调用包装类的prototype存在的方法,比如:

    <script>
        var a=123;
        var b=new Number(123);//Number()包装类的一个实例,是object类型
        a.toString();//调用的是Number()包装类中的prototype属性中的方法

    </script>

包装类总结?

  • Number()、String()和boolean()的实例都是object类型,他们的PrimitiveValue属性存储他们本身值
  • new出来的基本类型值可以正常参与运算
  • 包装类的目的就是为了让基本类型值可以从他们的构造函数的prototype上获得方法
  • 包装类指的是对基本类型值的封装,引用类型的封装不能称为包装类
  • undefined和null没有包装类

18.Math对象

Math对象就是数学对象,存放着一些数学计算常用的方法

  • .pow(2,3):幂次方,求2的3次方
  • .sqrt(5):开根号,求根号5
  • .ceil(2.1):向上取整,结果为3
  • .floor(2.9):向下取整,结果为2
  • .round():四舍五入
    • .round(2.5):3
    • .round(-2.5):-2,注意,使用round方法,当判断值为负数时,则以.5为临界点,只要小于等于.5则舍,大于.5则入。
  • .max():得到参数列表的最大值,传入的参数必须是数值类型
  • .min():得到参数列表的最小值,传入的参数必须是数值类型
  • .random():随机数生成0-1之间的随机小数,不会生成1

如何将一个小数四舍五入到指定位数?

  • 以小数点后两位为例,先将小数点向右移2位,就是乘以100
  • 再进行四舍五入,得到后的结果,再除以100
  • 得到最终的结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7vqEnMyb-1633619629904)(https://i.loli.net/2021/10/07/INcoK27rHSnTeO3.png)]

如何求一个数组的最大值?

    <script>
        var a=123;//实质上是Number()包装类的一个实例
        a.toString();//调用的是Number()包装类中的prototype属性中的方法


        var arr=[12,14,15,17,18];
        // 方式一:使用apply方法,apply会将数组参数以零散值的形式传入
        console.log(Math.max.apply(null,arr));
        
        //方式二:使用剩余运算符,剩余运算符可以将数组参数拆分成散列参数
        console.log(Math.max(...arr));
        
        
    </script>

19.Date日期对象

  • 使用new Date()可以获得当前时间的日期对象,它是object类型值
  • 使用new Date(2020,11,1):获取指定时间的日期对象,这种写法不算时区
    • 第二个参数表示月份,从0开始计算,11就表示12月
  • 也可以使用new Date(“2020-12-01”)这样的写法,这种写法算时区,中国属于东八区
    • 要注意月和日要是两位数字,不足两位的向前边补0
    • 这里的月写几就是几,从1开始计算,12就表示12月

日期对象的常见七个方法?

  • getDate():得到日期1-31
  • getDay():得到星期0-6
  • getMonth():得到月份0-11
  • getFullYear():得到年份
  • getHours():得到小时数0-23
  • getMinutes():得到分钟数0-59
  • getSeconds():得到秒数0-59

什么是时间戳?

  • 时间戳表示1970年1月1日距离某时刻的毫秒数
  • 通过.getTime()方法或者Date.parse()方法可以将日期对象变为时间戳
  • 通过new Date(时间戳)的写法,可以将时间戳转为日期对象
    
    <script>

        var d=new Date();

        // 有两种方法能够将日期对象转为时间戳
        console.log(d.getTime());//精确到毫秒
        console.log(Date.parse(d));//精确到秒,但是也是以毫秒显示,最后三位肯定是0
        //将时间戳转为时间对象
        console.log(new Date(d.getTime()));
    </script>

实现倒计时

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <h1 id="tag">2021年高考倒计时</h1>
    <h2 id="tag2">2021年高考倒计时</h2>
    <script>
        // 获取2022年6月7号时间对象
        var gt = new Date("2022-06-21");
        // 设置定时器,每秒更新一次
        setInterval(function () {
            // 获取当前时间
            var nt = new Date();

            // 让后面的时间减前面的时间,获取的是时间戳
            var sy = gt - nt;//得到的是剩余时间戳,毫秒数
            // 获取剩余天数
            syDay = parseInt(sy / (1000 * 60 * 60 * 24));
            //获取减去天数后剩余的小时数
            syHours = parseInt(sy % (1000 * 60 * 60 * 24) / (1000 * 60 * 60));
            //获取减去上面剩余的分钟数
            syMinutes = parseInt(sy % (1000 * 60 * 60) / (1000 * 60));
            // 获取减去上面剩余的秒数
            sySeconds = parseInt(sy % (1000 * 60) / (1000));

            // 获取显示倒计时的dom元素
            var tags = document.getElementById("tag2");
            tags.innerText = "倒计时" + syDay + "天" + syHours + "小时" + syMinutes + "分" + sySeconds + "秒";

        },1000)

    </script>
</body>

</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值