zoukankan      html  css  js  c++  java
  • js入门 |高级

    原型基础

    引入

    原型对象的作用

    • 数据共享,节省空间(需要共享的卸载原型对象中,不需要的写在构造函数中)
    • 为了实现继承

    简单了解原型对象

    问题:用构造函数创建实例化对象时,如果实例化对象中有相同的属性或方法,实例化多个对象会占多个内存,浪费空间

    解决:通过原型的方式,把共享的方法单独提出来写

    //构造函数
    function Person(name,age){
        this.name=name;
        this.age=age;
    }
    
    //通过原型对象来添加方法和属性,共享方法和属性
    Person.prototype.eat=function(){}
    Person.prototype.height="160"
    
    //实例化对象
    var per1=new Person("小明",18)
    var per2=new Person("小红",16)
    per1.eat()//可调用
    per2.eat()//可调用
    

    构造函数,原型与实例对象

    构造函数可以实例化对象

    构造函数中有个属性prototype,它是构造函数的原型对象;而构造函数的原型对象(prototype)中有一个构造器,这个构造器指向的就是该原型对象所在的构造函数

    原型对象有个构造器,就是构造函数本身

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

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

    原型链:实例对象和原型对象之间的关系,关系是通过原型(__proto__)来联系的。

    原型对象也有__proto__,它指向的是Objec原型对象,而Objec原型对象的__proto__最后指向的是null。这在原型指向中有图解

    微信截图_20200816154055.png

    三者的特点

    • 构造函数和原型对象的方法都可以相互访问

      //以原型对象举例
      function Person(name,age){
          this.name=name;
          this.age=age;
      }
      Person.prototype.eat=function(){
          this.play()
      }
      Person.prototype.play=function(){}
      
    • 实例对象使用的方法和属性,先在实例对象中找,没有再到原型对象里找

      function Person(name,age){
          this.name=name;
          this.age=age;
      }
      Person.prototype.age=20
      
      
      //情况1:实例对象有该属性
      var person=new Person("小明",18)
      console.log(person.age)//--->18
      //情况2:实例对象没有该属性
      var person=new Person("小明")
      console.log(person.age)//--->20
      
    • 可以给内置对象的原型对象添加方法,会直接改变源码

    • 如果构造函数没有某一属性,实例对象却去访问了,结果是undefined而不是报错

      function Person(age){
          this.age=age
      }
      var per=new Person(){10}
      cosnole.log(per.name)//undefined
      //解释:因为js是一门动态类型的语言。如果对象没有,但只要obj.——点了,那么这个对象就相当于有了这个东西,只是没有属性值。
      

    原型对象的简单写法

    Person.prototype={
        //注意:需要手动修改构造器的指向
        constructor:Person
        height:"160",
        age:18,
        eat:function(){}
    }
    

    原型指向

    functin Person(age){
        this.age=age
        console.log(this)//------------------构造函数的this
    }
    Person.prototype.eat=function(){
        console.log(this)//------------------原型对象的方法中的this
    }
    
    var per=new Person(10)
    console.log(per)//-----------------------实例对象
    
    //打印得出结论:构造函数中this是实例对象;原型对象中方法中的this是实例对象;
    

    改变原型指向

    //人的构造函数
    function Person(){}
    //人的原型对象方法
    Person.prototype.eat=function(){}
    
    //学生的构造函数
    function Student(){}
    //学生的原型对象方法
    Student.prototype.say=function(){}
    
    //改变原型指向
    //方式1:
    Student.prototype=new Person()
    //实例化student对象
    var stu=new Student()
    stu.say()//报错
    stu.eat()//成功
    

    最开始
    在这里插入图片描述
    在这里插入图片描述

    //人的构造函数
    function Person(){}
    //添加原型对象方法
    Person.prototype.eat=function(){}
    
    //改变原型指向
    //方式2:
    Person.prototype={
       say:function(){} 
    }
    //实例化Person对象
    var per=new Person()
    per.eat()//报错
    per.say()//成功
    

    如何在原型指向后添加方法?

    //在上面方式一的基础上,只需要在指向改变后面添加方法即可。此时的say在Person实例对象中
    Student.prototype=new Person()
    Student.prototype.say=function(){}
    stu.say()//成功
    

    原型继承

    继承就是类与类之间的关系。js没有类,但可以通过构造函数模拟类,然后通过原型来实现继承。继承也是为了实现数据共享

    1. 改变原型指向

    问题:同样是人,但是身份不一样,人们具有的属性和方法就会有差异。因为这些差异我就要重新创建一个构造函数来创建实例对象,太烦太占空间了!
    解决:改变原型指向

    //我需要继承的人共有的方法和属性
    function Person(name,age){
        this.name=name;
        this.age=age
    }
    Person.prototype.eat=function(){console.log("人可以吃")}
    Person.prototype.sleep=function(){console.log("人可以睡")}
    
    //这是我作为学生特有的属性,我该怎么继承人的呢?
    function Student(score){
        this.score=score
    }
    
    //解决:改变学生的原型指向
    Student.prototype=new Person("小明",18)
    
    //给Student原型对象添加方法
    Student.prototype.study=function(){console.log("学生要学习")}
    
    //ok完成
    //实例化对象
    var stu=new Student(100)
    //学生所继承的
    console.log(stu.name)//小明
    console.log(stu.age)//18
    stu.eat()//人可以吃
    stu.sleep()//人可以睡
    //学生所特有的
    console.log(stu.score)//100
    stu.study()//学生要学习
    

    2. 借用构造函数

    问题:但是吧,如果我改变了指向,做到了继承。那如果我创建第二个学生?第三个学生呢?他们的名字和年龄是一样的!只有分数不一样。太可怕了,怎么办?

    解决:借用构造函数:构造函数名字.call(当前对象,属性,属性,...)——解决属性值重复问题

    //被继承的
    function Person(name,age){
        this.name=name;
        this.age=age
    }
    Person.prototype.eat=function(){console.log("人可以吃")}
    
    
    //我需要继承
    function Student(name,age,score){
        Person.call(this,name,age,score)
        //这里相当于直接把Person的构造函数拿过来使用
        //这里的this指向实例对象,上面讲过。表示实例对象在调用Person的构造函数
        this.score=score
    }
    
    //ok完成
    //实例化对象
    var stu=new Student("小明",18,100)
    console.log(stu.name)//小明
    console.log(stu.age)//18
    console.log(stu.score)//100
    
    stu.eat()//报错,继承不了方法
    

    3. 组合继承

    问题:借用构造函数的方法不能继承方法,怎么办!

    解决:组合继承:改变原型指向+借用构造函数

    //被继承的
    function Person(name,age){
        this.name=name;
        this.age=age
    }
    Person.prototype.eat=function(){console.log("人可以吃")}
    
    //借用构造函数
    function Student(name,age,score){
        Person.call(this,name,age,score)
        this.score=score
    }
    //改变原型指向,不传值
    Student.prototype=new Person()
    
    //给Student原型对象添加方法
    Student.prototype.study=function(){console.log("学生要学习")}
    
    //ok完成
    //实例化对象
    var stu=new Student("小明",18,100)
    console.log(stu.name)//小明
    console.log(stu.age)//18
    console.log(stu.score)//100
    stu.eat()//人可以吃
    stu.study()//学生要学习
    

    4. 拷贝继承

    把一个对象中的属性或者方法直接复制到另一个对象中

    var person1={
        name:"小明",
        age:20,
        eat:function(){
            console.log("吃东西")
        }
    }
    
    var person2=person1
    //仅仅改变了地址的指向
    
    //真正的拷贝继承
    function Person(){}
    Person.prototype.name="小明"
    Person.prototype.age=18
    Person.prototype.eat=function(){}
    
    var person={}
    for(var key in Person.prototype){
        person[key]=Person.prototype[key]
    }
    cosnole.log(person.name)//小明
    person.eat()//成功
    

    函数进阶

    回顾

    • 函数的声明

      function f1(){}
      
    • 函数表达式

      var f1=function(){}
      

    函数的不同调用方式

    • 普通函数

      function f1(){}
      f1()
      
    • 构造函数

      //通过new调用,创建对象
      function F1(){}
      var f1=new F1()
      
    • 对象的方法

      function F1(){
          this.eat=function(){}
      }
      var f1=new F1()
      f1.eat()
      

    函数中的this

    • 普通函数的this是谁?——window
    • 对象.方法中的this是谁?——实例对象
    • 定时器方法中的this是谁?——window
    • 构造函数中的this是谁?——实例对象
    • 原型对象方法中的this是谁?——实例对象

    严格模式

    //普通
    function f1(){console.log(this)}
    f1()//window
    
    //严格模式
    "use strict"
    function f1(){console.log(this)}
    f1()//undefined  //你没有指明是谁调用的,我也不知
    window.f1()//window  //你指明了,那就window把
    

    改变指向

    • apply和call

      apply和call的第一个参数如果没有传入或者传入了null,对象的this就是window。
      它们都可以调用函数,调用的时候改变指向,效果 一样。只是传入参数的方式不一样,apply是数组,call是一个一个的参数

      • apply(对象,[参数1,参数2,...])

      • call(对象,参数1,参数2...])

        f1.apply(null,[1,2])
        f1.call(null,1,2)
        
    • bind

      复制,即不用调用函数就能改变指向:bind(obj,参数1,参数2)

      //人的构造函数
      function Person(age){
          this.age=age
      }
      Person.prototype.go=function(){
          console.log(this+"--"+this.age)
      }
      
      //学生的构造函数
      function Student(age){
          this.age=age
      }
      
      //实例化人与学生
      var per=new Person(10)
      var stu=new Student(100)
      
      //复制一份人的实例对象,传入学生对象===>指向改变
      var f=per.go.bind(stu)//[object object]--100
      

      应用

      //一个构造函数
      function ShowRandom(){
          this.number=parseInt(Math.random()*10+1)
      }
      //给原型对象添加两个方法
      ShowRandom.prototype.show1=function{ 
          //定时器里边的this.show2里的this是window
          //此时我想改变指向,改成实例对象而不是wimdow,这里就要用到.bind(this),这个时候,show2指向的就是实例对象。即bind里面写的是哪个对象,bind前面的方法就指向谁
          window.setInterval(this.show2.bind(this),1000)
      }
      ShowRandom.prototype.show2=function{
          console.log(this.number)
      }
      
      //实例对象
      var sr=new ShowRandom()
      //调用
      sr.show2()
      

    函数也是对象

    函数也是对象,但对象不一定是函数

    对象中有__proto__原型,是对象
    构造函数中有prototype原型

    //构造函数
    function F1(){}
    console.log(F1)//既有prototype,也有__proto__,即构造函数既是对象又是函数
    
    //已知Math是内置对象
    console.log(Math)//有__proto__但没有prototype,即对象不是函数
    

    所有的函数实际上是Function的构造函数创建出来的实例对象

    var f1=new Function()
    

    数组中的函数调用

    数组可以储存任何类型的数据

    var arr=[
        function(){console.log("我是f1")},
        function(){console.log("我是f2")},
        function(){console.log("我是f3")}
    ]
    
    //回调函数:函数作为参数使用
    arr.foreach(function(item){
        item()
    })
    //我是f1 我是f2 我是f3
    

    函数的成员

    • name——函数名称,只读

    • arguments——实参的个数

    • length——形参的个数

    • caller——函数调用者

      function f1(x,y){
          console.log(f1.name)//f1
          console.log(f1.arguments)//[10,20,...]
          console.log(f1.arguments.length)//3
          console.log(f1.length)//2
          console.log(f1.caller)//f2
      }
      function f2(){
      	f1(10,20,30)    
      }
      f2()
      

    函数作为参数

    //这是一个以函数为参数的函数
    function f1(fn){
        fn()
    }
    
    //给函数从传入匿名函数
    f1(function(){})
    //给函数传入命名函数,只需传入名字
    function f2(){我是一个要被传进去的命名函数}
    f1(f2)
    

    应用——定时器传入函数

    function f1(fn){
        setInterval(function(){
            console.log("定时器开始")
            fn()
            console.log("定时器结束") 
        },1000)
    }
    //传入匿名函数
    f1(function(){console.log("我在中间")})
    
    //打印输出
    定时器开始
    我在中间
    定时器结束
    

    函数作为返回值

    function f1(){
        console.log("我是f1")
        return function(){console.log("我来了")}
    }
    f1()
    //此时只能打印出:我是f1
    
    //已知f1返回了一个匿名函数,那么我把这个返回函数赋值给一个变量,这个变量就是这个返回的函数
    var f=f1()
    //调用这个返回的函数
    f()//打印出:我来了
    

    闭包

    闭包的作用:缓存数据,延长作用域链

    函数闭包:在函数中有一个函数

    function f1(){
        num=10
        function f2(){
            console.log(num)
        }
        f2()
    }
    f1()
    

    对象闭包:函数中有对象

    function f1(){
        var num=10;
        var obj={
            age:num
        }
        console.log(obj.age)
    }
    1()
    

    应用

    function f1(){
        var num=10
        return function(){
            return num
        }  
    }
    //把返回的函数赋值给变量
    var f=f1()
    //把返回函数返回的值赋值给变量
    var result=f()
    console.log(result)//10
    
    function f1(){
        var num=10;
        return function(){
            num++;
            return num
        }
    }
    var f=f1()
    console.log(f())//11
    console.log(f())//12
    console.log(f())//13
    //解析:在这个例子中,函数f1只调用了一次,返回函数的num是在第一次调用f1的基础上处理的,即在num=10的基础上。之后处理的是返回函数自身,不受外部干扰
    

    递归

    函数中调用函数自己

    function getSum(x){
        if(x==1){
            return 1;
        }
        return x+getSum(x-1)
    }
    
    getSum(5)//15
    //解析:当你传5的时候-->5+getSum(4)
    //此时这个getSum(4)就是-->4+getSum(3)
    //这个getSum(3)就是-->3+getSum(2)
    //这个getSum(2)就是-->2+getSum(1)
    //这个getSum(1)就是1
    //总的就是:5+4+3+2+1
    

    浅拷贝

    相当于把一个对象中的所有内容,复制一份给另一个对象。即把一个对象的地址给了另一个对象,两者指向相同。

    var obj1={
        name:"小明",
        dog:["小黑","小白"]
    }
    var obj2={}
    
    //写一个函数,把a对象中的所有属性复制到b对象
    function extend(a,b){
        for(var key in a){
            b[key]=a[key]
        }
    }
    
    //调用复制函数
    extend(obj1,obj2)
    consolo.log(obj1)//一样
    console.log(obj2)//一样
    

    深拷贝

    把一个对象中的所有属性和方法,一个一个的找到并在另一个对象中开辟相应的地址,一个一个的储存到另一个对象中

    var obj1={
        name:"小明",
        dog:["小黑","小白"]
        pen:{
            color:black;
            price:5
        }
    }
    var obj2={}
    
    //写一个函数,把a对象中的所有属性复制到b对象
    function extend(a,b){
        for(var key in a){
            //先把对象的属性的值放在item存储
            var item=a[key];
            //判断属性类型
            //如果是数组
            if(item instanceof Array){
               //开辟一个新数组(数组)
               b[key]=[]
                //再把存的数组拷贝到新的空间去
                exentd(item,b[key])
            //下面同理
            }else if(item instanceof Object){
                b[key]={}
                exentd(item,b[key])     
            }else{
             //如果值不是数组也不是对象,即普通的数据,就直接把值直接拷贝给另一个对象
              b[key]=item
            }
        }
    }
    
    //调用复制函数
    extend(obj1,obj2)
    consolo.log(obj1)//一样
    console.log(obj2)//一样
    //与浅拷贝不同的地方就是开辟了一个新地址
    

    正则表达式

    按照一定的规则组成一个表达式,这个表达式主要匹配字符串

    元字符

    • .——表示除了\n以外的任意一个字符

    • []——表示范围

      • [0-9]——表示0到9之间的任意一个数字。

        //注意:如果想表示100-200不能直接写成[100,200]而是:
        [1][0-9][0-9]
        
      • [a-z]——表示所有小写字母中的任意一个

      • [A-Z]——表示所有大写字母中的任意一个

      • [a-zA-Z]——表示所有字母中的任意一个

      • [0-9a-zA-Z]——表示所有数字或字母中的任意一个

        []还可以表示把元字符中的意义取消,如[.]就只是一个.

    • |——或者

    • ()——分组 提升优先级

      //要么是数字、要么是小写字母、要么是大写字母。其中小写字母优先
      [0-9]|([a-z])|[A-Z]
      //分组 从左边开始计算
      ([0-9])([a-z])([A-Z])
      
    • *——表示前面的表达式出现了0次到多次

      [a-z][0-9]*
      "sgfja" //可以匹配,因为*包含了出现0次的情况
      
    • +——表示前面的表达式出现了1次到多次

      [a-z][9]+//表示小写字母后面最少有一个9
      
    • ?——表示前面的表达式出现了0次或1次

      [4][a-z]?//表示有4且4后面有1个小写字母或者没有小写字母
      "12e324jjk"//可以匹配
      
    • 限定符——限定前面表达式出现的次数

      • {0,}——表示前面的表达式出现了0次到多次,和*一样

      • {1,}——表示前面的表达式出现了1次到多次,和+一样

      • {0,1}——表示前面的表达式出现了1次到多次,和?一样

        可以更明确前面表达式出现的次数
        {5,10}//出现5到10次
        {4}//只能4次
        
    • ^——表示以什么开始,或者是取非

      ^[0-9]//以数字开头
      ^[a-z]//以小写字母开头
      [^0-9]//非数字
      [^0-9a-zA-Z]//特殊符号
      
    • $——表示以什么结束

      [0-9]$//以数字结束
      ^[a-z][0-9]$//严格模式:是能是一个字母一个数字比如"a1"
      
    • \d——数字中的任意一个,即[0-9]

    • \D——非数字中的一个

    • \s——空白符的一个

    • \S——非空白符的一个

    • \w——非特殊符号

    • \W——特殊符号

    创建正则表达式对象

    正则表达式要写在一对//中

    1. 通过构建函数创建对象

      var reg=new RegExp(/\d{5}/)
      
    2. 通过字面量的方式创建

      var reg=/\d{5}/
      

    使用正则表达式

    //创建对象,正则表达式要写在一对//里面
    var reg=new RegExp(/\d{5}/)
    //要检测的字符串
    var str="我的电话是10086"
    //调用正则表达式方法验证
    reg.test(str)//返回的是布尔值
    

    字符串中使用正则表达式

    match()——匹配符合正则表达式的并返回该字符串

    var str="中国移动10086;中国联通10010"
    var array=str.match(/\d{5}/)//这样只能匹配一个
    var array=str.match(/\d{5}/g)//全局匹配 ["10086","10010"]
    

    replace()——替换符合正则表达式的字符

    var str="小明睡懒觉了"
    str=str.replace(/睡懒觉/,"起床")
    console.log(str)//小明起床了
    

    正则表达式的方法

    • test()——用来查看正则表达式与指定的字符串是否匹配。返回 truefalse

    • exec()——在一个指定字符串中执行一个搜索匹配。返回一个结果数组或null

      //一个字符串
      var str="中国移动10086;中国联通10010"
      //一个正则对象
      var reg=/\d{5}/
      //把exec返回的结果放在array变量中
      var array=reg.exec(str)
      

    其他

    • g——表示全局模式匹配,在表达式后加

    • i——表示忽略大小写

    • RegExp.$n——获取匹配后的第n组值,一个()表示一组

  • 相关阅读:
    Power BI官方视频(2) Power BI嵌入到应用中的3种方法
    一起学微软Power BI系列-使用技巧(1)连接Oracle与Mysql数据库
    本站博客园首页头条推荐文章
    微软Power BI技术文章与资源目录
    【目录】本博客其他.NET开源项目文章目录
    【目录】数据挖掘与机器学习相关算法文章总目录
    博客园博客美化相关文章目录
    【目录】Newlife XCode组件相关文章目录
    【目录】Matlab和C#混合编程文章目录
    C#下取得Exif中照片拍摄日期
  • 原文地址:https://www.cnblogs.com/sanhuamao/p/13595580.html
Copyright © 2011-2022 走看看