《你不知道的JavaScript》之类的基本概念

讲到类,有其它编程语言经验的同学应该是非常熟悉了。类是面向对象编程中的一个重要概念,它是对事物的一种抽象,实例化是对抽象的具体化操作。类与类之间可以有继承关系,也就是我们常说的父类和子类。在继承的过程中又存在多态性这个概念,基类的方法可以在子类中有不同的表现行为。我上面讲到的实例化、继承和多态性都是类中最基本、也是最具代表性的概念了。

那么我们会问道:JavaScript中存在类吗?

其实是没有的,虽然我们经常会看到 new、 instance of 以及ES6中的class关键字,但实际上这些都是为了模拟类的行为和方法设计的一种模式而已,并不是真正意义上的类。

构造函数和constructor

我们知道使用类的第一步就是要实例化对象,那么类是如何被实例化的呢?这里就用到构造函数了,这个方法的任务就是初始化实例所需要的所有信息。

接下来我们首先看一下传统的实例化方法


function Point(x,y){

    this.x = x;

    this.y = y;

}

    let pt1 = new Point(1,2);

    console.log(pt1); //Point


通过new操作构造了一个Point实例,这中间经历了这么几个步骤需要读者朋友知道:

1)创建一个新的对象;

2)将函数的作用域赋值给新对象,也就是this指向了这个新对象;

3)执行函数中的内容,将属性方法赋值给我新对象;

4)返回新对象。

接着再看下ES6中对于类的定义


class Point{

    constructor(x,y){

    this.x = x;

    this.y = y;

    }

  }

  let pt1 = new Point(1,2);

  console.log(pt1);//Point

  console.log(typeof Point); //function

  console.log(Point === Point.prototype.constructor);//true


从上面的代码就可以发现,其实ES6中的类就是一个语法糖的效果,pt1同样都是指向Point,Point本身也是函数。每个函数都有一个原型指针,这个指针指向原型对象,而对象中的congstructor是指向函数本身的。这句话涉及到原型的知识,后面会讲到,放到这里是为了帮助大家对上述代码更好的理解。

所以说构造函数的prototype是继续在ES6类中存在的,事实上,类的所有方法都定义在类的原型属性上面。


class Point{

    constructor(){

    }

    toString(){

    }

    toValue(){

    }

    }

    //等同于

    Point.prototype = {   

    constructor(){},

    toString(){},

    toValue(){}

    }


所以对于类中方法的实现完全是可以通过原型来实现的,如果下次面试官让你利用原型将类中的方法实现以下,希望你能轻松应对哦。

constructor方法是类的默认方法,通过new实例化对象时会自动调用该方法。一个类必须有一个constructor,如果没有引擎会自动为其创建一个。


class Point{

    }

  let pt1 = new Point();

  console.log(pt1);//Pont {}


constructor默认返回的是this,但是你也可以返回自己创建的对象。


class Point{

    constructor(){

    return Object.create(null);

    }

    }

  let pt1 = new Point();

  console.log(pt1);//{ }


类的实例化

前面已经讲过,类的实例化化是通过new 实现的。与ES5一样,类的属性除非是显示定义在自身(this),否则都是定义在类实例的原型上。看下面的例子


class Point{

    constructor(x,y){

    this.x = x;

    this.y = y;

    }

    toString(){

      return ("x:"+this.x+"y:"+this.y);

    }

  }

  let pt1 = new Point(1,2);

  console.log(Point.hasOwnProperty('x')); //false

  console.log(Point.hasOwnProperty('y'));//false


  console.log(pt1.hasOwnProperty('x')); //true

  console.log(pt1.hasOwnProperty('y'));//true


  console.log(Point.hasOwnProperty('toString'));//false

  console.log(pt1.__proto__.hasOwnProperty('toString'));//true


从上面代码输出结果可以看出,this指向的类的实例,具有显式属性x和y,而toString既不属于Point,也不属于实例对象,而是属于Point的原型对象,不过实例对象是可以调用的。

私有方法和私有属性

私有方法是很常见的需求,但是ES6中是不提供的,不过可以通过变通的方式实现


class Student{

    constructor(age){

      this.age = age;

    }

    //公有方法

    myInfo(){

          return this._myAge(this.age);

    }

    //私有方法

    _myAge(){    

           return this.age;

    }

  }

  let xiaoming = new Student('12');

  console.log(xiaoming.myInfo());//12

  console.log(xiaoming._myAge());//12


虽然可以用下划线暗示该方法是私有方法,但是外部还是可以调用的,这样的私有方法并不安全,那么如何操作呢?同学们可以发动你聪明的小脑袋思考一下。

这里有两种方法:

方法一:将私有方法移出模块之外,因为模块内部的内容外部都可以访问到。


class Student{

    constructor(age){

      this.age = age;

    }

    //公有方法

    myInfo(){

         return myAge.call(this);

    }

  }

  //私有方法

  function myAge(){    

    return this.age;

  }

  let xiaoming = new Student('12');

  console.log(xiaoming.myInfo());//12


方法二:利用Symbol值,这个是ES6中的语法,它的作用就是保证属性和方法的唯一性。


  const myAge = Symbol('age func');

  const realAge = Symbol('realage');

  class Student{ 

    constructor(age){

      this.age = age;

    }

    //公有方法

    myInfo(){

    return this[myAge](this.age);

    }

    //私有方法

    [myAge](age){

    return this[realAge] = age;

    }

  }

  let xiaoming = new Student('12');

  console.log(xiaoming.myInfo());//12


类的静态属性

静态属性是类本身的属性,而不是实例的属性,是如下的写法


class Teacher{

  }

  Teacher.level = 1;

  console.log(Teacher.level);//1


这里注意的是ES6要求静态属性只能写在类的外部,写在里面的话会报错,也就是说类的内部没有静态属性,只有静态方法。

类的静态方法

类相当于实例的原型,所以类中定义的方法会被实例继承。而如果在类中的方法前加上static关键字的话,这个方法就属于类了,实例去调用就会出错。


class Teacher{

    static speak(){

      console.log('I am a teacher');

    }

  }

  let laowang = new Teacher();

  console.log( Teacher.speak() );//I am a teacher

  console.log( laowang.speak() );//undefined


加上static后,speak方法是属于类而不属于实例 laowang 了。

关于类的基本概念就讲的这里,希望同学们能对js中的类有一个系统的认识,接下来我会讲到类的继承的有关知识。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言 最先接触编程的知识是在大学里面,大学里面学了一些基础的知识,c语言,java语言,单片机的汇编语言等;大学毕...
    oceanfive阅读 8,334评论 0 7
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 12,378评论 2 17
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,323评论 19 139
  • 继承是面向对象中一个比较核心的概念。其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承。而EC...
    lovelydong阅读 2,905评论 0 2
  • 前天领导过来跟我一起去看一个项目,这是新调来的上级。 一路上没有什么话说,以为就这样了,在回来的路上,领导有提到其...
    会飞的蓉子阅读 4,166评论 6 5