ES6系列教程第五篇--Class基本知识

一、ES5的类实现

由于js并不是真正面向对象的语言,所以在ES6规范之前,一直没有"类"的概念,但是可以用变通的方式实现。最常用的就是利用构造函数。

function Person(name,age)
  {
        this.name = name;
        this.age = age;
        this.sayName = function(){
            console.log("name:"+this.name);
        }
  }
  var person1 = new Person("tcy",20);//new出新的对象

分析这段代码:

1、创建一个构造函数Person,我们可以认为是"类",其中有两个类属性(name,age)和一个类方法(sayName)。

2、采用new实例化Person,返回一个对象person1,我们认为是"类对象"。

这段代码已经很接近java的"类"了,但是Person还是个function,那么这个构造函数和普通的函数有什么区别呢。

有以下两点区别:

1、在函数内部对新对象(this)的属性进行设置,通常是添加属性和方法。

2、构造函数可以包含返回语句(不推荐),但返回值必须是this,或者其它非对象类型的值。

继承是面向对象的一种重要模式,我们看如何实现,我们来看一种继承的实现方式-原型继承。

我们先来了解下什么叫做原型,打印下Person构造函数对象

Person构造函数中还有个隐含的_proto_方法,包含了两个默认方法(constructor和_proto_)。我们描述下结构图,可以看到,_proto_指向了Person的原型Person.prototype,而原型中的constructor又指向了Person。

再来测试下,在Person.prototype原型对象上添加两个方法sayName,sayAge。

function Person(name,age)
  {
        this.name = name;
        this.age = age;
        this.sayName = function(){
            console.log("object name:"+this.name);
        }
  }
  Person.prototype.sayName= function(){
      console.log("prototype name:"+this.name);
  }
  Person.prototype.sayAge= function(){
      console.log("prototype age:"+this.age);
  }
  var person1 = new Person("tcy",20);//new出新的对象

再打印下Person原型对象,增加了两个方法。

结构如下:

接下来我们继续看下这段代码

var person1 = new Person("tcy",20);//new出新的对象
console.log(person1.constructor===Person)//true
console.log(person1.constructor===Person.prototype.constructor)//true
person1.sayName()//object name:tcy
person1.sayAge()//prototype age:20

首先new出一个Person的实例对象person1。实际上内部分三步执行的

第一步是建立一个新对象person1;

第二步将该对象(person1)内置的原型对象设置为构造函数(就是Person)prototype 属性引用的那个原型对象;

第三步就是将该对象(person1)作为this 参数调用构造函数(就是Person),完成成员设置等初始化工作。

非常晦涩,看不懂,没关系,我们继续上图。内置的原型对象也叫_proto_, _proto_指向Person.prototype。

实例对象person1就是通过内置的原型对象实现对构造函数(Person)原型的访问(person1.可以理解为person1._proto.)所以下面的执行结果就理解了。

console.log(person1.constructor===Person)//true
console.log(person1.constructor===Person.prototype.constructor)//true

通过constructor,person1实现了对构造函数所有的属性和方法的的访问。

我们再来看下属性和方法的寻址方式,优先在其构造方法(Person)中查找,如果构造方法中存在,就不会继续到其原型查找(例如sayName方法),如果不存在该方法或者属性,则继续在其原型查找(如sayAge()),所以执行结果如下:

person1.sayName()//object name:tcy
person1.sayAge()//prototype age:20

理解了原型的原理,我们来看下回归正题,如何用原型实现继承。

//父类
  function Person(name,age)
  {
        this.name = name;
        this.age = age;
        this.sayName = function(){
            console.log("object name:"+this.name);
        }
  }
  //子类
  function Worker(job){
    this.job = job;
    this.sayJob = function(){
       console.log("object job:"+this.job);
    }
  }
  Worker.prototype = new Person("tcy",20);//利用原型实现Worker对Person的继承
  var teacher = new Worker("teacher"); 
  teacher.sayName();//object name:tcy
  teacher.sayJob();//object job:teacher

Worker类的原型设置成Person的类对象,按照寻址的规则,Worker继承了Person的方法(sayName方法)。有兴趣的道友们可以把这个原型关系图画出来。

二、class类

ES6中提供了class关键字,对"类"的支持越来越接近面对对象语言。我们看下它是怎么使用的。

 class Person
  {  
        constructor(name, age) {
          this.name = name;
          this.age = age;
        }
        sayName(){
            console.log("object name:"+this.name);
        }
  }
  var person = new Person("tcy",20);
  person.sayName();//object name:tcy

class关键字定义Person这个类,constructor为该类的构造方法,如果不定义构造方法,则默认增加一个;sayName为类方法,注意,该方法不需要增加function标识。使用new创建一个实例对象person时,会自动调用constructor构造方法,传入入参,返回实例对象,this指向的就是该实例对象。

我们打印下person对象。

对比下我们前面讲的原型可以看到,ES6的class定义类,其实只是用原型实现类的一个"马甲"而已。类方法其实就是原型方法,等价于:

Person.prototype ={
    constructor(){},
    sayName(){}
   }

三、私有方法和属性

在java语法中,使用private表示私有方法和属性,但是ES6中没有定义该关键字,事实上,ES6也没有提供私有方法和需求的方法,只能通过变通的方式。

那么对于私有的方法和属性的标准是什么呢,需要满足一下要求

1、 class内部不同方法间可以使用,因此this要指向实例化对象(必须)
2 、不能被外部访问,因此实例化对象person.name既不能获得值,也不能设定值,应该返回undefined,甚至应该在实例化之后,并不知道有name这个属性存在,(必须)
3 不能被继承,因此extends后子类不具备该属性(必须)
4 方便的调用方式,比如类this._name形式(备选)

const _name = Symbol('name');
const _age = Symbol('age');
class Person {
  constructor(name, age) {
    this[_name] = name;
    this[_age] = age;
  }
   sayName(){
      console.log("object name:"+this[_name]);
   }
}

var person = new Person("tcy",20);
person.sayName();
person._name;//undefined

我们利用Symbol值的唯一性,将私有属性的名字命名为一个Symbol值,这个属性能在类内部访问(sayName能调用name属性),但是无法被实例对象访问(person._name为undefined)。实际上这种方法是有缺陷的,我们后面会讲到。

理论上讲,私有属性和私有方法的区别是,私有方法是函数,实现私有属性的方式也是适合私有方法的。

const _name = Symbol('name');
const _age = Symbol('age');
const _sayName = Symbol('sayName');
class Person {
  constructor(name, age) {
    this[_name] = name;
    this[_age] = age;
  }
  //私有方法
   [_sayName](){
      console.log("object name:"+this[_name]);
   }
   //共有方法
   sayName(){
    this[_sayName]();
   }
}

var person = new Person("tcy",20);
person.sayName();//object name:tcy
person._sayName();//报错
person._name;//undefined

四、静态属性和方法

类方法前加上static关键字,表示这个方法可以用类直接调用,而无需使用类对象,该方法就是静态方法

class Person
  {  
        constructor(name, age) {
          this.name = name;
          this.age = age;
        }
       static sayName(){
            console.log("this is static functon");
        }
  }
  Person.sayName();

由于不用类对象调用,所以也就无法用到this(如用到,也是指类)。

那么对于静态属性,是不是可以在属性前增加static表示呢,很遗憾,不可以。ES6对于类的属性实现只有两种方式表示

var sex = 0
  class Person
  {  
        //第一种,最常用的,在constructor,使用.操作符
        constructor(name, age) {
          this.name = name;
          this.age = age;
        }


        //第二种,使用get set来定义属性
        get sex() {
            return sex
        }
        set sex(value) {
           sex = value
        }


       static sayName(){
            console.log("this is static functon");
        }
  }
  Person.sayName();
  var person = new Person("tcy",20);
  person.sex = 1;
  console.log(person.sex);//1

第一种,是最常用的,在constructor,使用.操作符;第二种,使用get set来定义属性,有点曲线救国的意思。

我们可以利用第二种方式,变通实现私有属性。

var sex = 0
  class Person
  {  
        //第一种,最常用的,在constructor,使用.操作符
        constructor(name, age) {
          this.name = name;
          this.age = age;
        }

        //第二种,使用get set来定义属性
        static get sex() {
            return sex
        }
        static set sex(value) {
           sex = value
        }

       static sayName(){
            console.log("this is static functon");
        }
  }
  Person.sayName();
  Person.sex = 1;
  console.log(Person.sex);//1

五、总结

本篇主要讲了class类的相关原理和基本知识,下一章节重点介绍类的另一个重要特性-继承。

上一篇:ES6系列教程第四篇--asyn详解                            下一篇:ES6系列教程第六篇--Class继承

发布了33 篇原创文章 · 获赞 95 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/tcy83/article/details/80635758