《你不知道的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中的类有一个系统的认识,接下来我会讲到类的继承的有关知识。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值