js面向对象设计之class类

class 相对 function 是后出来的,既然 class 出来了,显然是为了解决 function 在处理面向对象设计中的缺陷而来。
下面通过对比,来看看 class 作为 ES6 中的重大升级之一的优势在哪里:
为了更好的对比,请参见我的另外一篇博文: js面向对象设计之 function 类

1、class 写法更加简洁、含义更加明确、代码结构更加清晰。
2、class 尽管也是函数,却无法直接调用(不存在防御性代码了)。
3、class 不存在变量提升。
4、class 为污染 window 等全局变量(这点很赞啊)。
5、class 函数体中的代码始终以严格模式执行(新手写的代码可靠性也必须上来了)

/* ReferenceError: Class01 is not defined */ try { var ins01 = new Class01(); } catch ( e ) { console.error( e ); } class Class01 { } console.log( typeof Class01 ); /* function */ /* Class constructor Class01 cannot be invoked without 'new' */ try { Class01() } catch ( e ) { console.error( e ); } console.log( window.Class01 ); /* undefined */
6、可直接使用 set 和 get 函数。这比 function 要好用多了。
据我所知,vue 中的数据绑定是通过 set 和 get 来实现,而这里 class 可以使用便捷的如同普通的函数的写法。
function 中则需要通过 Object.defineProperty 的方式来设置 set 和 get,繁琐且代码可读性差。

class Class01{ constructor() { } get name(){ console.log( 'getter' ); return this._name; } set name( v ){ this._name = v; console.log( 'setter' ); return this; } } var ins01 = new Class01(); ins01.name; /* getter */ ins01.name = 2; /* setter */
7、class 内部方法中若涉及到 this,则一定要注意。class 中的 this 永远都不会指向 window。
熟悉 function 的应该知道,function 类的实例方法,可能指向 window(在某些情况下,具体读者可自行百度,也可阅读下述例子推敲)。
而 class 则在 this 可能指向 window 的情况下,将 this 指向 undefined。如下:
class Class01 { constructor() { this.a = 'a'; } geta(){ return this.a; } } let ins01 = new Class01(); console.log( ins01.geta() ); /* a */ let obj = {}; obj.a = 'objA'; obj.geta = ins01.geta; console.log( obj.geta() ); /* 'objA' */ window.a = 'windowA'; window.geta = ins01.geta; /* Cannot read property 'a' of undefined */ /* 若是 function 类此处会返回 'windowA' */ try { geta() } catch ( e ) { console.error( e ); }
8、class 可以从 javascript 中著名的几大类中进行继承:Array、number、string....,显然 function 是做不到的。
下面给一个简单的示例:
class Class01 extends Array { } let ins01 = new Class01( 1, 2, 3 ); /* [1,2,3] */ let arr = ins01.shift(); /* [2,3] */ arr instanceof Class01; /* false */ ins01 instanceof Class01; /* true */

小tips:在 mozilla 的开发者指南中看到一种比较高端的东西(关于从原生类继承肯定还有话题,会继续学习):
static get [Symbol.species]() { return Array; }
参见 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes

9、class 中有一个对象(姑且这么称呼,关于 super 显然还必须要有话题)super,这个对象可以取到父类的方法、构造函数等。
这显然是 function 无法比拟的。在 class 的构造函数中,必须调用 super(),否则该子类就毫无用处。当然静态方法还是可以用用。
下面有一个例子,可以重写父类的方法,扩充父类方法的职责(注意是扩充而不是重写,当然也可以选择重写)。
这一点相当的赞啊,有时候我们并不需要改写父类的某个方法,我们可能会因为项目的需求而扩充父类接口的功能。
class Class01 {
    constructor( name ){
        this.name = name;
    }
    geta(){
        console.log( this.name + ' 父类方法.' );
    }
}
class Class02 extends Class01 {
    geta(){
        super.geta();
        console.log( this.name + ' 子类方法.' );
    }
}
var l = new Class02( 'Class02' );
l.geta();

10、class 中不存在实例方法,class 中定义所有方法都是原型方法。这些方法也都是不可枚举的,使用 for in 这种方式无法遍历到它们。
11、class 不能使用 return 来返回一个实例(等等,我还没有试验过,不好意思,我会马上试验一下)。
  20180101编辑验证,class 的 constructor 可以使用 return,因此 class 与 function 一样可以返回任意的内容。
若不写 return 语句,或返回是数值、字符串等非引用类型的值,则 constructor 任然会返回 this(实例)。return [] 或 return {} 都会使得 new 关键字并不会返回 class 的实例


说了这么多的不同点,再来说说 class 和 function 的相同之处:

1、静态方法 在这点上,两者还是有相似之处的。
尽管相似,显然 class 要更加优雅,可读性也更强,class 使用 static 关键词指定静态方法。
并且class 可以在函数体内定义静态函数,而 function 不能,这无疑也让 function 写出来的代码更加的复杂。

class Class01{ static geta(){ console.log( '01的静态方法' ); } } function Class02 () { } Class02.geta = function () { console.log( '02的静态方法' ); } Class01.geta(); Class02.geta();

2、私有属性和方法
两者都必须采用闭包的方式才能实现。下面给出 class 的私有属性和方法。

var Class02 = ( function () { let pVal ; function sayHello(){ console.log( this ); /* this 指向 window */ console.log( '欢迎访问nDos的博客' ); } return class Class01{ constructor( v = '初始值' ){ pVal = v; } get val(){ sayHello(); return pVal; } set val( v ){ pVal = v; } }; } )(); let ins01 = new Class02(); console.log( ins01.val ); ins01.val = 'hello'; console.log( ins01.val ); ins01 instanceof Class02; /* true */ ins01.constructor.name; /* "Class01" */

小tips:上面的代码显示 ins01 是 Class02 的实例,但 ins01 的构造函数 name 属性却是 Class01。
显然这在项目中不可行,会给类的使用者造成困惑。下例可解决这个问题:

var Class01 = ( function () { return class Class01 { }; } )(); let ins01 = new Class01(); ins01 instanceof Class01; /* true */ ins01.constructor.name; /* "Class01" */
3、class 和 function 都有初始化的过程,也就是给实例 this 进行包装处理的过程。二者尽管这个过程都存在,但还是有些区别,
class 只能在 constructor 中初始化,如果涉及继承,还必须调用 super();function 的整个函数体都在执行初始化。

总结:
1、class 作为前端的面向对象设计之关键词,使得前端大型项目的开发变得容易:类的管理更加清晰,功能更加强大。
2、显然与 class 关键字一起出现的关键字 extends 是配套的。使得继承关系更加清晰。
继承再也不用写一堆的prototype指着这个指着那个,项目代码结构更加明确。
3、当然,关于 class 该博文并未挖的很深,留待下文。
posted @ 2017-12-27 20:45  nDos  阅读(25546)  评论(0编辑  收藏  举报