深入理解JS的原型、原型链、继承

  1. 构造函数、原型、实例三者的关系

    • 构造函数

      • 创建对象的一种常用方式,其他创建对象的方式还包括工厂模式、原型模式、对象字面量等。
      • 每一个构造函数都有一个prototype属性,打印出来之后发现这个属性其实是一个指针,指向一个对象,该对象拥有一个constructor属性(__proto__属性是浏览器引入的一个非官方属性,方便开发者调试使用的)
    • 原型

      • 构造函数的prototype属性所指向的对象成为原型
      • 原型这个对象中,有一个constructor属性,又指回其构造函数本身
      • constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
    • 实例

      • 通过构造函数创建对象的过程被称之为实例化,创建出来的对象被成为实例
    • 原型里的所有属性和方法会连接到其对应的构造函数的实例上

      • 从构造函数角度看:函数名.prototype是它的原型属性
      • 从实例对象角度看:函数名.prototype是它的原型对象
      • 原型式继承:原型对象的属性和方法可以直接被实例对象所访问
        -在这里插入图片描述
    • 构造函数、实例、原型对象的三角关系

      • 构造函数的prototype属性指向了构造函数原型对象
      • 实例对象是由构造函数创建的,实例对象的__proto__属性指向了构造函数的原型对象
      • 构造函数的原型对象的constructor属性指向了构造函数,实例对象原型的constructor属性页指向了构造函数
  2. 原型链

    • 原型对象本质上也是一个对象,它其实是由new Object()实例化出来的.该原型对象也可以访问Object.prototype的所有属性和方法。
    • 原型的顶端:Object.prototype,任何一个默认的内置函数的原型都继承自Object.prototype
    • 原型链:JS的对象结构中出现的指向Object.prototype的一系列原型对象,称之为原型链
    • 属性搜索原则:在访问对象的属性和方法时,会在当前对象中查找,若没有找到,会一直沿着原型链上的原型对象向上查找,直到找到Object.prototype为止
    • 写入原则:若是给对象设置属性和方法,都是在当前对象上设置。
  3. 原型链和成员的查找机制

    • 当访问一个对象的属性/方法时,首先查找这个对象自身有没有这个属性;
    • 若没有就查找它的原型(即__proto__指向的prototype原型对象)
    • 若还是没有找到,就查找原型对象的原型(Object 的原型对象)
    • 依此类推一直找到Object为止(null)
    • __proto__对象原型的意义就是为对象成员查找机制提供一个方法,或者说一条线路。
      在这里插入图片描述
  4. 实现继承的其他方法

    • 原型链继承是JS中实现继承的主要方式,但原型链的继承存在一个问题,就是当对象有一个引用类型的属性时,该引用类型会被其后的所有实例所共享。

    • function TypeOne(){
              this.colors = ['red', 'blue', 'green'];
          }
       
          function TypeTwo(){}
       
          TypeTwo.prototype = new TypeOne();//原型链继承关键点
       
          var t1 = new TypeTwo();
          t1.colors.push('black');
          console.log(t1.colors);  //["red", "blue", "green", "black"]
       
          var t2 = new TypeTwo();
          console.log(t2.colors); //["red", "blue", "green", "black"]
      
    • 借用构造函数

      • 在子类型构造函数的内部调用超类型构造函数,函数不过只是在特定环境中执行代码的对象,因此通过使用apply()和call()方法可以在新创建的对象上执行构造函数。

      • function TypeOne(){
            this.colors = ['red', 'blue', 'green'];
        }
         
        function TypeTwo(){
            TypeOne.call(this);//借助构造函数实现继承的关键点,改变this的指向,谁调用就指向谁,调用对象上的属性和方法都是一个副本
        }
        
        var t1 = new TypeTwo();
        t1.colors.push('black');
        console.log(t1.colors);  //["red", "blue", "green", "black"]
         
        var t2 = new TypeTwo();
        console.log(t2.colors); //["red", "blue", "green"]
        
    • 混入继承(将你的属性和方法克隆为我的,遍历你,判断你有没有这个key,你有,那我的key就等于你的key,这样一来,你的key就是我的key了)

      • 可以通过一个克隆函数extend()将一组对象中的所有属性和方法克隆到一个对象中

      • function extend (des, objs){//遍历对象将你有的方法和属性复制到我身上
            for(var i = 0; i < objs.length; i++){
                for(var key in objs[i]){
                    if(objs[i].hasOwnProperty(key)){
                        des[key] = objs[i][key];
                    }
                }
            }
        }
        
        var o1 = {
            name : 'mery
        }
        var o2 = {
            sayHi: function (){
                console.log('你好,我是' + this.name);
            }
        }
        var o3 = {
            introduce: function (){
                console.log('我来自深圳');
            }
        }
        var obj = {};
        
        //调用该方法实现让obj继承o1,o2,o3
        extend(obj, [o1, o2, o3]);  //调用克隆方法
         
        console.log(obj.name); //mery
        obj.sayHi(); //你好,我是mery
        obj.introduce(); //我来自深圳
        
    • 组合继承

      • 将原型链和借用构造函数组合在一起,使用原型链实现对原型属性和方法的继承,通过借助构造函数来实现对实例属性的继承,不仅实现了函数复用还保证了每个实例都有自己的属性。

      • function TypeOne(name){
            this.name = name;
            this.colors = ['red', 'blue', 'green'];
        }
         
        TypeOne.prototype.sayName = function(){
            console.log(this.name);
        }
         
        function TypeTwo(name, age){
            TypeOne.call(this, name); //通过构造函数实现对实例属性的继承
            this.age = age;
        }
         
        TypeTwo.prototype = new TypeOne(); //通过原型链实现对原型属性和方法的继承
         
         
        TypeTwo.prototype.sayAge = function() {
            console.log(this.age);
        }
         
        var t1 = new TypeTwo('Lee_Tanghui', 23);
        t1.colors.push('black');
        console.log(t1.colors);  //["red", "blue", "green", "black"]
        t1.sayName(); //Lee_Tanghui
        t1.sayAge(); //23
         
        var t2 = new TypeTwo('Joe', 24);
        console.log(t2.colors); //["red", "blue", "green"]
        t2.sayName(); //Joe
        t2.sayAge(); //24
        
    • 原型式继承

      • 借助一个函数,通过传递原型对象作为参数,从而创建新对象的方式实现继承,函数是由道格拉斯提出的

      • function object(o) {
            function F(){};
            F.prototype = o;
            return new F();
        }
        
      • ES5新增了Object.create()方法规范了原型式继承

      • var person = {
            name : 'joe',
            age : 24,
        }
         
        //创建构造函数,用来排列键值对
        function SortedList ( obj ) {
            for ( var key in obj ) {
                this.push({
                    key : key,
                    value : obj[ key ]
                })
            }
        }
         
        //调用Object.create()方法,继承数组的所有属性方法
        SortedList.prototype = Object.create( Array.prototype );
         
        //构造函数的实例化
        var list = new SortedList( person );
         
        console.log( list ); //[{key: "name", value: "joe" }, {key: "age", value: 24}]
        
  5. prototype和__proto__的区别

    • prototype是构造函数的属性,__proto__是每个实例都有的属性,可以访问[[prototype]]属性;实例的__proto__与其构造函数的prototype指向的是同一个对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值