「查漏补缺」对比class与旧构造函数的区别

1,398 阅读5分钟

前言

JavaScript是没有类这个概念的,区别于其他传统的强类型语言,例如Java,JS的类(构造函数)在设计根本上就有本质的不同,Java的类是代码的拷贝,而JS则用了原型链继承而已,所谓的构造函数只不过就是个普通函数,(只是大家习惯在使用时大写,这事也就成了规范)。

ES6的class本质上是构造函数的语法糖,只是这个语法糖写得更像是Java,为了做区分,我将在这篇博客上专门对比class和es5构造函数的不同写法

设置实例对象自身属性

构造函数写法

通过构造函数,我们能更了解JS的原型链设计原理,下面是构造函数的基本用法

function Person(name,age){
   this.name=name
   this.age=age
}
const p=new Person('jack',23)

class写法

class Person{
   constructor(name,age){
      this.name=name
      this.age=age
   }
}
const p=new Person('jack',23)

设置实例对象的共有方法

构造函数写法

Person.prototype={
   constructor:Person,
   fn1(){},
   fn2(){}
}

class写法

class Person{
   constructor(name,age){
   ...
   };
   fn1(){};
   fn2(){};
}

设置实例对象的自有方法

构造函数写法

function Person(name,age){
...
   this.saiHi=function(){console.log(this.name)}
}

class写法

class Person{
    name='';
    age='';
   constructor(name,age){
      ...
   };
   fn1(){};//prototype上的
   fn2(){};
   sayHi=function(){console.log(this.name)} //自有的
}

设置构造函数静态属性

构造函数的静态属性指的是构造函数自己能访问

构造函数写法

Person.prop1='staticProp1'
Person.prop2='staticProp2'

class写法

class Person{
   static prop1='staticProp1'
   static prop2='staticProp2'
   constructor(name,age){
      ...
   };
   fn1(){};//prototype上的
   fn2(){};
   sayHi=function(){console.log(this.name)} //自有的
}

设置构造函数静态方法

构造函数写法

Person.staticFn=function (){}

class写法

class Person{
...
   static staticFn=function(){}
   constructor(name,age){
      ...
   };
  ...
}

设置构造函数的私有字段

构造函数写法

function Person(name,age){
   let _selfName='123456' //构造函数私有属性,无法被外部直接访问
   let _selfFn=()=>{return _selfName} //构造函数私有方法,无法被外部直接访问
   this.saiHi=function(){console.log(_selfFn())} //实例的自有方法
}

在变量名前加_,这种写法是开发者自己定义的,用来区分一下私有字段

class写法

class Person{
   #selfName='123456'
   #selfFn=()=>{return this.#selfName}
   constructor(name,age){
      ...
   };
   sayHi=function(){console.log(this.#selfFn())}

class写法采用#关键字符来定义,如果想访问,需要加上this

由于私有字段无法直接访问,只好通过sayHi方法来变相访问了。

小结

构造函数写法

function Person(name,age){
   let _selfName='123456' //构造函数私有属性,无法被外部直接访问
   let _selfFn=()=>{return _selfName} //构造函数私有方法,无法被外部直接访问
   this.name=name //实例的自有属性
   this.age=age
   this.saiHi=function(){console.log(_selfFn())} //实例的自有方法
}
Person.prototype={ //实例的共有方法
   constructor:Person,
   fn1(){},
   fn2(){}
}
Person.prop1='staticProp1' //构造函数的自有属性
Person.prop2='staticProp2'
Person.staticFn=function (){} //构造函数的自有方法
const p=new Person('jack',23)

class写法

class Person{
    name='';//这里也可以写实例的属性
    age='';
   static prop1='staticProp1'//构造函数的自有属性
   static prop2='staticProp2'
   #selfName='123456'//构造函数私有属性,无法被外部直接访问
   #selfFn=()=>{return this.#selfName}//构造函数私有方法,无法被外部直接访问
   constructor(name,age){
      this.name=name//实例的自有属性
      this.age=age//实例的自有属性
   };
   fn1(){};//实例的共有方法
   fn2(){};//实例的共有方法
   sayHi=function(){console.log(this.#selfFn())}//构造函数的自有方法
}
const p=new Person('jack',23)

继承

构造函数写法

采用构造函数方法可以分成两步实现。

第一步是在子类的构造函数中,调用父类的构造函数。

function Super(name){
   this.name=name
}
function Sub(name,age){
   Super.call(this,name)
   this.age=age
}

上面代码中,Sub是子类的构造函数,this是子类的实例。在实例上调用父类的构造函数Super,就会让子类实例具有父类实例的属性。

第二步,是让子类的原型指向父类的原型,这样子类就可以继承父类原型。

Super.prototype.sayHi=function (){console.log('hi')}
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;//这句可加可不加,建议加

要注意不要直接Sub.prototype=Super.prototype,这样虽然也是有用的,但是就相当于两个构造函数共用一个原型,万一以后修改Sub或者Super其中一个的原型就会影响双方。

最好的方法就是使用Object.Create直接改造子构造函数的原型,让子构造函数的__proto__连接到父构造函数的原型上。

class写法

class Person{
    name;
    age;
    constructor(name,age){
        this.name=name
        this.age=age
    }
    sayHi(){
        console.log(this.name)
    }
}
class Man extends Person{
    constructor(name,age,prop){
        super(name,age)//这里调用
        this.prop=prop
    }
}
const a=new Man('qiuyanxi',20,'帅')

class的形式非常方便,目前子类已经能使用父类的方法了,而且无需再去关联子类和父类的原型链,extends关键字已经帮我们做好了工作

Man.prototype.__proto__===Person.prototype //true
Man.__proto__ ===Person //true //注意,这是class自动实现的

class方法比ES5的方法多一种关系,ES5的方法并没有把子类的__proto__跟父类做关联,而class的写法则是自动做了关联。不过如果ES5想要实现的话也可以用Object.setPrototypeOf(Man,Person)关联

//传统ES5继承手动挡
//原来
Sub.__proto__ ===> Function.protoyype
Super.__proto__ ===> Function.protoyype
//使用Object.setPrototypeOf(Sub,Super)后
Sub.__proto__ ===>Super===>Super.__proto__===>Function.prototype
//使用Sub.prototype = Object.create(Super.prototype)后;
Sub.prototype.__proto__ ===> Super.prototype

总结

class的语法从未来来看,更多的新人朋友们可能会非常喜欢,这也非常符合学过JAVA的科班生,不过Js跟Java之间的差别还挺大的,还是建议能够在实践class的基础上,了解构造函数的工作原理,这能够帮助我们更好地理解Js这门不完美但是非常优秀的语言

参考文档

wangdoc.com/javascript/…

wangdoc.com/es6/class-e…

developer.mozilla.org/zh-CN/docs/…