es6之类和对象

1.1 概念

es6中新增加了类的概念,可以使用class关键字声明一个类

1.2 创建类
  1. 实例 类似于Java
class Star {
    
    
    constructor(name) {
    
    
        this.uname = name;
    }
    song() {
    
    
        console.log(this.uname);
    }
}

var zt = new Star('zt');
console.log(zt.uname);
  1. constructor()构造器(默认方法)

    1. 用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动创建一个constructor()
    2. constructor()构造器不需要声明function
    3. 由于constructor也是一个函数,因此具有arguments内置属性
  2. constructor属性

    1. 对象间拥有一个共享的属性constructor,该属性存储当前对象的构造函数

    2. 实例

      class Star {
              
              
          constructor(name) {
              
              
              this.uname = name;
          }
      }
      
      var zt = new Star('zt');
      console.log(zt.constructor);// 返回class Star {
              
              
      //        constructor(name) {
              
              
      //            this.uname = name;
      //        }
      //    }
          
      console.log(zt.constructor == Star); // 返回true
      
  3. 注意

    1. 类方法之间不需要加逗号分隔
    2. 类方法不需要用function进行声明
1.3 创建对象

es6之前,js并没有引入类的概念,而目前浏览器的js是es5版本的,大多数高版本的浏览器也支持es6,不过只实现了es6部分特性和功能,并且在es6之前,对象不是基于类创建的,而是用构建函数来定义对象。

  1. 通过字面量方式创建

    var obj = {
    	this.uname = 'zt',
    	this.song = function() {
    		
    	}
    }
    
  2. 通过内置对象Object创建

    var obj = new Object();
    obj.uname = 'zt';
    obj.song = function() {
          
          };
    
    1. 通过with程序块新建属性以及方法

      var obj = new Object();	
      with(obj) {
              
              
          uname = 'zt';
          song = function() {
              
              };
          console.log('zt');
      }
      
  3. 通过class创建

    class Star {
          
          
        constructor(uname) {
          
          
            this.uname = uname;
        } 
        song() {
          
          }
    };
    var zt = new Star('zt');
    
  4. 通过构造函数创建对象

    1. 构造函数是一个特殊的函数,主要用来初始化对象,即为对象变量赋初始值,总是与new一起使用。

    2. new在执行时会做的事件

      1. 在内存中创建一个新的空对象
      2. 让this指向这个新的对象
      3. 执行构造函数里面的代码,给这个新对象添加属性和方法
      4. 返回这个新对象,所以构造函数里面不需要return
    3. 语法:

              function Star(uname, age) {
              
              
                  this.uname = uname;
                  this.age = age;
                  this.song = function () {
              
              
                  }
              }
              
              var zt = new Star('zt',18);
      
    4. 成员:构造函数中的属性和方法称为成员,成员可以添加

      1. 实例成员
        1. 构造函数内部通过this添加的成员
        2. 只能通过实例化对象来访问
      2. 静态成员
        1. 在构造函数本身上添加的成员
        2. 只能通过构造函数来访问
    5. 构造函数使用上存在的问题

      1. 存在浪费内存的问题

      2. 实际情况:同一个构造函数创建两个实例,如果构造函数中有方法(复杂数据类型),则创建一个对象都会再开辟一个空间存储方法,因此两个实例对象的方法是存储在不同的空间中的,当实例对象数量上增加时,内存消耗也会同步增加。

      3. 为了所有对象使用同一个函数,且解决空间,解决方式是:使用构造函数原型:prototype

        1. 构造函数通过原型分配的函数是所有对象所共享

        2. js规定,每个构造函数都有一个prototype属性。而该属性指向的是对象,该对象的所有属性和方法,都会被构造函数所拥有。

        3. 对象如何使用prototype属性中的方法?

          1. 通过对象原型_proto_
          2. **对象都有一个属性_proto_**指向构造函数的prototype原型对象
          3. 因此方法的查找规则:先查看当前对象上是否有方法,如果没有则通过属性_proto_来对prototype方法中进行查找
        4. 注意:

          1. 把不变的方法,直接定义在prototype对象上
          2. 把属性直接定义在构造函数内部
          3. _proto_对象原型的意义在于为对象的查找机制提供一个方向,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype
        5. 原型:一个对象,也称为prototype为原型对象

        6. 原型的作用:共享方法

        7. constructor属性

          1. 对象原型_proto_和构造函数原型对象prototype对象里面都有一个属性constructor属性

          2. constructor主要用于记录该对象引用哪个构造函数,可以让原型对象重新指向原来的构造函数

          3. 执行过程:

            1. 当给构造函数原型对象prototype赋值为一个对象时,

              class Star() {
                              
                              
                  
              }
              Star.prototype = {
                              
                              
                  sing:function(){
                              
                              }
              }
              var zt = new Star();
              console.log(zt._proto_.constructor);
              console.log(Star.prototype.constructor);
              // 此时以上的constructor不再指向构造函数本身
              
            2. 解决:手动利用constructor指回原来的构造函数

              class Star() {
                              
                              
                  
              }
              Star.prototype = {
                              
                              
                  constructor:Star,
                  sing:function(){
                              
                              }
              }
              var zt = new Star();
              console.log(zt._proto_.constructor);
              console.log(Star.prototype.constructor);
              // 此时以上的constructor重新指向构造函数本身
              
    6. 构造函数、实例、原型对象三者之间的关系

      1. 构造函数通过属性prototype指向原型对象,原型对象prototype通过其属性constructor指向构造函数

      2. 构造函数通过new方法创建了对象实例,对象实例通过属性_proto_指向原型对象

      3. 关系图:

      proto_01

      1. 原型链

        1. 由于每个对象都有_proto_属性,因此当前原型对象的_proto_会指向Object的prototype对象,而Object的原型对象指向null,而这个_proto_属性的不断指向prototype对象的过程称为原型链

        2. proto_line_01

        3. js成员查找机制

          1. 当访问一个对象的成员时,首先查找这个对象本身有没有该属性
          2. 如果没有就查找它的原型(_proto_指向的prototype原型对象)
          3. 如果还没有就查找原型对象的原型(Object的原型对象)
          4. 依次类推,一直找到Object为止(null)
        4. 例子:Object.prototype里面有toString方法,但是除非手动添加该方法,其他对象中没有该方法,但是其他对象仍然可以使用该方法:原型链存在的原因

1.4 类的继承
  1. es6之前组合继承的方式:构造函数+原型对象模拟实现继承

    1. call(thisArg,arg1,arg2,…)

      1. 参数说明

        1. thisArgs:当前调用函数this的指向对象
        2. args…:传递的其他参数
      2. 作用

        1. 调用函数
        2. 修改函数运行时的this指向
      3. 调用函数

        function print_s() {
                  
                  
                console.log('s');
                console.log(this);// 此时指向window对象
            }
            print_s.call();
        
      4. 修改函数运行时的this指向

        function print_s(x,y) {
                  
                  
                console.log('s');
                console.log(this);// 此时指向example_o
                console.log(x + y);
            }
            var example_o = {
                  
                  
                uname: 'zt'
            }
            print_s.call(example_o,1,2);
        
    2. 通过构造函数实现继承父构造函数属性

      function Father(uname,age) {
              
              
              // this 指向父构造函数的对象实例
              this.uname = uname; // this(Son).uname = uname
              this.age = age;
          }
      
          function Son(uname,age,score) {
              
              
              // this 指向Son构造函数的对象实例
              // 通过call 调用当前函数时,修改父类的指向,从而使得父类属性改为了子类属性
              Father.call(this,uname,age);
              this.score = score;
          }
      
          var son = new Son('zt',18,100);
          console.log(son);
      
    3. 通过原型对象实现继承父构造函数方法

      1. 如果只是通过Son.prototype = Father.prototype方式进行赋值,虽然这样使得Son.prototype指向了父构造函数,但是由于对同一个对象的修改会导致同时被修改,因此当通过追加son.prototype中存储的方法的时候,父构造函数prototype也会相应的被修改

        function Father(uname,age) {
                  
                  
                this.uname = uname; 
                this.age = age;
            }
        
            function Son(uname,age,score) {
                  
                  
                Father.call(this,uname,age);
                this.score = score;
            }
        
            Father.prototype.test = function () {
                  
                  };
            Son.prototype = Father.prototype; // 不可取
        
      2. 为了解决上述问题,通过原型链的方式来解决

        function Father(uname,age) {
                  
                  
                this.uname = uname;
                this.age = age;
            }
        
            function Son(uname,age,score) {
                  
                  
                Father.call(this,uname,age);
                this.score = score;
            }
        
            Father.prototype.test = function () {
                  
                  };
            Son.prototype = new Father();
        

        由于实例对象与Father原型对象是互不相关的两个对象,因此当让son.prototype指向Father实例的时候,由于new Father()实例的_proto_指向的是Father中的原型对象,因此根据原型链的思路,如果在当前prototype对象中找不到改方法,会在Father原型对象中进行查找,另一方面,当Son.prototype追加方法的时候,不会影响父构造函数原型对象中的内容。但是还是存在一个问题,son.prototype.constructor指向的是Father构造函数,为了解决,手动添加:

         function Father(uname,age) {
                  
                  
                this.uname = uname;
                this.age = age;
            }
        
            function Son(uname,age,score) {
                  
                  
                Father.call(this,uname,age);
                this.score = score;
            }
        
            Father.prototype.test = function () {
                  
                  };
            Son.prototype = new Father();
            son.prototype.constructor = Son;
        
  2. es6_关键字extends

    1. 语法

       class Father {
              
              
              sum() {
              
              
                  console.log(1);
              }
          }
      
          class Son extends Father {
              
              
          }
      
          var son = new Son();
          son.sum();
      
    2. 子类继承了父类的方法,即可以使用父类的方法

    3. 当父类方法中使用了父类自己的属性的时候,需要在子类中进行调用父类的构造器将属性传递过去,采用super关键字

      1. super关键字用于访问和调用对象父类上的函数。
      2. 可以调用父类的构造函数
      3. 可以调用父类的普通函数
          class Father {
              
              
              constructor(x,y) {
              
              
                  this.x = x;
                  this.y = y;
              }
      
              sum() {
              
              
                  console.log(this.x + this.y);
              }
          }
      
          class Son extends Father {
              
              
              constructor(x,y) {
              
              
                  super(x,y);// 调用父类的构造函数
              }
              sum() {
              
              
                  super().sum();// 调用父类的普通函数
              }
          }
      
          var son = new Son(1,2);
          son.sum();
      
    4. 继承中的属性/方法查找原则:就近原则,先看子类中有没有这个方法,没有再看父类中有没有这个方法

    5. 注意点

      1. 子类在构造函数中使用super,必须放在this前面,也即必须先调用父类的构造方法,在使用子类的构造方法
      2. 在es6中类没有变量提升,所以必须先定义类,才能通过类实例化对象
      3. 类里面的共有属性和方法一定要加this使用
    6. this指向调用者

1.5 类的本质
  1. 类的本质也是function
  2. es6类实际上也是一个语法糖
  3. 类同样含有prototype属性对象以及所创建的对象含有_proto_属性对象,也即含有构造函数的所有用法,只是es6进一步的封装了
  4. 类的所有方法都定义在类的prototype属性上

猜你喜欢

转载自blog.csdn.net/chen__cheng/article/details/114088397