ECMAScript6(17):Class类

getter, setter 和 Generator 方法

getter 和 setter 使用方式和 ES5 一样, 这里不多说了,举个例子一看就懂:

class Person{

constructor(name, age, tel){

this.name = name;

this.age = age;

this.tel = tel;

this._self = {};

}

get id(){

return this._self.id;

}

set id(str){

if(this._self.id){

throw new TypeError(“Id is read-only”);

} else {

this._self.id = str;

}

}

}

var p = new Person(“Bob”, 18, “13211223344”);

console.log(p.id); //undefined

p.id = ‘30010219900101009X’;

console.log(p.id); //‘30010219900101009X’

var descriptor = Object.getOwnPropertyDescriptor(Person.prototype, ‘id’);

console.log(‘set’ in descriptor); //true

console.log(‘get’ in descriptor); //true

p.id = ‘110’; //TypeError: Id is read-only

Generator 用法也和 ES6 Generator 部分一样:

class Person{

constructor(name, age, tel){

this.name = name;

this.age = age;

this.tel = tel;

this._self = {};

}

*Symbol.iterator{

var keys = Object.keys(this);

keys = keys.filter(function(item){

if(/^_/.test(item)) return false;

else return true;

});

for(let item of keys){

yield this[item];

}

}

get id(){

return this._self.id;

}

set id(str){

if(this._self.id){

throw new TypeError(“Id is read-only”);

} else {

this._self.id = str;

}

}

}

var p = new Person(“Bob”, 18, “13211223344”);

p.id = ‘30010219900101009X’;

for(let info of p){

console.log(info); //依次输出: “Bob”, 18, “13211223344”

}

class 的继承

这里我们只重点讲继承,关于多态没有新的修改,和 ES5 中一样,在函数内判断参数即可。关于多态可以阅读Javascript对象(1) - 对象、类与原型链中关于多态重构的部分。

此外,class 继承属于 ES5 中多种继承方式的共享原型,关于共享原型也在上面这篇文章中讲解过。

class 实现继承可以简单的通过 extends 关键字实现, 而使用 super 关键字调用父类方法:

//定义 ‘有色点’’ 继承自 ‘点’

class ColorPoint extends Point{ //这里延用了上面定义的 Point 类

constructor(x, y, color){

super(x, y); //利用 super 函数调用父类的构造函数

this.color = color;

}

toString(){

return ${super.toString()},${this.color}; //利用 super 调用父类的动态方法

}

}

var cp = new ColorPoint(1, 5, ‘#ff0000’);

console.log(cp+“”); //(1,5),#ff0000

ColorPoint.show(); //“Static function!” 静态方法同样被继承了

cp instanceof ColorPoint; //true

cp instanceof Point; //true

使用 extends 继承的时候需要注意一下几点:

  • super 不能单独使用,不能访问父类属性,只能方法父类方法和构造函数(super本身)

  • 子类没有自己的 this,需要借助 super 调用父类构造函数后加工得到从父类得到的 this,子类构造函数必须调用 super 函数。这一点和 ES5 完全不同。

  • 子类如果没有手动定义构造函数,会自动生成一个构造函数,如下:

constructor(…args){

super(…args);

}

  • 子类中使用 this 关键字之前,必须先调用 super 构造函数

  • 由于继承属于共享原型的方式,所以不要在实例对象上修改原型(Object.setPrototypeOf, obj.__proto__等)

  • super 也可以用在普通是对象字面量中:

var obj = {

toString(){

return MyObj ${super.toString()};

}

}

console.log(obj+“”); //MyObj [object Object]

prototype__proto__

在 class 的继承中

  • 子类的 __proto__ 指向其父类

  • 子类 prototype 的 __proto__ 指向其父类的 prototype

class Point{

constructor(x, y){

this.x = x;

this.y = y;

}

}

class ColorPoint extends Point{

constructor(x, y, color){

super(x, y);

this.color = color;

}

}

ColorPoint.proto === Point; //true

ColorPoint.prototype.proto === Point.prototype; //true

其等价的 ES5 是这样的:

function Point(){

this.x = x;

this.y = y;

}

function ColorPoint(){

this.x = x;

this.y = y;

this.color = color;

}

Object.setPrototypeOf(ColorPoint.prototype, Point.prototype); //继承动态方法属性

Object.setPrototypeOf(ColorPoint, Point); //继承静态方法属性

ColorPoint.proto === Point; //true

ColorPoint.prototype.proto === Point.prototype; //true

这里我们应该理解一下3种继承的 prototype 和 __proto__

  1. 没有继承

class A{}

A.proto === Function.prototype; //true

A.prototype.proto === Object.prototype; //true

  1. 继承自 Object

class A extends Object{}

A.proto === Object; //true

A.prototype.proto === Object.prototype; //true

  1. 继承自 null

class A extends null{}

A.proto === Function.prototype; //true

A.prototype.proto === undefined; //true

判断类的继承关系:

class A{}

class B extends A{}

Object.getPrototypeOf(B) === A; //true

子类的实例的 __proto____proto__ 指向其父类实例的 __proto__

class A{}

class B extends A{}

var a = new A();

var b = new B();

B.proto.proto === A.proto; //true

因此,可以通过修改子类实例的 __proto__.__proto__ 改变父类实例的行为。建议:

  • 总是用 class 取代需要 prototype 的操作。因为 class 的写法更简洁,更易于理解。

  • 使用 extends 实现继承,因为这样更简单,不会有破坏 instanceof 运算的危险。

此外存取器和 Generator 函数都可以很理想的被继承:

class Person{

constructor(name, age, tel){

this.name = name;

this.age = age;

this.tel = tel;

this._self = {};

}

*Symbol.iterator{

var keys = Object.keys(this);

keys = keys.filter(function(item){

if(/^_/.test(item)) return false;

else return true;

});

for(let item of keys){

yield this[item];

}

}

get id(){

return this._self.id;

}

set id(str){

if(this._self.id){

throw new TypeError(“Id is read-only”);

} else {

this._self.id = str;

}

}

}

class Coder extends Person{

constructor(name, age, tel, lang){

super(name, age, tel);

this.lang = lang;

}

}

var c = new Coder(“Bob”, 18, “13211223344”, “javascript”);

c.id = ‘30010219900101009X’;

for(let info of c){

console.log(info); //依次输出: “Bob”, 18, “13211223344”, “javascript”

}

console.log(c.id); //‘30010219900101009X’

c.id = “110”; //TypeError: Id is read-only

多继承

多继承指的是一个新的类继承自已有的多个类,JavaScript 没有提供多继承的方式,所以我们使用 Mixin 模式手动实现:

function mix(…mixins){

class Mix{}

for(let mixin of mixins){

copyProperties(Mix, mixin); //继承静态方法属性

copyProperties(Mix.prototype, mixin.prototype); //继承动态方法属性

}

return Mix;

function copyProperties(target, source){

for(let key of Reflect.ownKeys(source)){

if(key !== ‘constructor’ && key !== “prototype” && key !== “name”){

if(Object(source[key]) === source[key]){

target[key] = {};

copyProperties(target[key], source[key]); //递归实现深拷贝

} else {

let desc = Object.getOwnPropertyDescriptor(source, key);

Object.defineProperty(target, key, desc);

}

}

}

}

}

//使用方法:

class MultiClass extends mix(superClass1, superClass2, //){

//…

}

由于 mixin 模式使用了拷贝构造,构造出的子类的父类是 mix 函数返回的 class, 因此 prototype 和 __proto__ 与任一 superClass 都没有直接的联系,instanceof 判断其属于 mix 函数返回类的实例,同样和任一 superClass 都没有关系。可以这么说:我们为了实现功能破坏了理论应该具有的原型链。

原生构造函数继承

在 ES5 中,原生构造函数是不能继承的,包括: Boolean(), Number(), Date(), String(), Object(), Error(), Function(), RegExp()等,比如我们这样实现:

function SubArray(){}

Object.setPrototypeOf(SubArray.prototype, Array.prototype); //继承动态方法

Object.setPrototypeOf(SubArray, Array); //继承静态方法

var arr = new SubArray();

arr.push(5);

arr[1] = 10;

console.log(arr.length); //1 应该是2

arr.length = 0;

console.log(arr); //[0:5,1:10] 应该为空

很明显这已经不是那个我们熟悉的数组了!我们可以用 class 试试:

class SubArray extends Array{}

var arr = new SubArray();

arr.push(5);

arr[1] = 10;

console.log(arr.length); //2

arr.length = 0;

console.log(arr); //[]

还是熟悉的味道,对吧!这就和之前提到的继承差异有关了,子类没有自己的 this,需要借助 super 调用父类构造函数后加工得到从父类得到的 this,子类构造函数必须调用 super 函数。而 ES5 中先生成子类的 this,然后把父类的 this 中的属性方法拷贝过来,我们都知道,有的属性是不可枚举的,而有的属性是 Symbol 名的,这些属性不能很好的完成拷贝,就会导致问题,比如 Array 构造函数的内部属性 [[DefineOwnProperty]]

利用这个特性,我们可以定义自己的合适的类, 比如一个新的错误类:

class ExtendableError extends Error{

constructor(message){

super(message);

this.stack = new Error().stack;

this.name = this.constructor.name;

}

}

throw new ExtendableError(“test new Error”); //ExtendableError: test new Error

静态属性

为何静态属性需要单独写,而静态方法直接简单带过。因为这是个兼容性问题,目前 ES6 的静态方法用 static 关键字,但是静态属性和 ES5 一样,需要单独定义:

class A{}

A.staticProperty = “staticProperty”;

console.log(A.staticProperty); //“staticProperty”

不过 ES7 提出可以在 class 内部实现定义,可惜目前不支持,但是还好有 babel 支持:

class A{

static staticProperty = “staticProperty”; //ES7 静态属性

instanceProperty = 18; //ES7 实例属性

}

console.log(A.staticProperty); //“staticProperty”

console.log(new A().instanceProperty); //18

new.target 属性

new 本来是个关键字,但 ES6 给它添加了属性——target。该属性只能在构造函数中使用,用来判断构造函数是否作为构造函数调用的, 如果构造函数被 new 调用返回构造函数本身,否则返回 undefined:

function Person(){

if(new.target){

console.log(“constructor has called”);

} else {

console.log(“function has called”);

}

}

new Person(); //“constructor has called”

Person(); //“function has called”

这样我们可以实现一个构造函数,只能使用 new 调用:

function Person(name){

if(new.target === Person){

this.name = name;

} else {

throw new TypeError(“constructor must be called by ‘new’”);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

技术是没有终点的,也是学不完的,最重要的是活着、不秃。零基础入门的时候看书还是看视频,我觉得成年人,何必做选择题呢,两个都要。喜欢看书就看书,喜欢看视频就看视频。最重要的是在自学的过程中,一定不要眼高手低,要实战,把学到的技术投入到项目当中,解决问题,之后进一步锤炼自己的技术。

技术学到手后,就要开始准备面试了,找工作的时候一定要好好准备简历,毕竟简历是找工作的敲门砖,还有就是要多做面试题,复习巩固。有需要面试题资料的朋友点击这里可以免费领取


学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-snI30K2g-1713482739914)]

[外链图片转存中…(img-zUie34uA-1713482739914)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-EM204Nj6-1713482739915)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

[外链图片转存中…(img-gdBnIzpL-1713482739915)]

最后

技术是没有终点的,也是学不完的,最重要的是活着、不秃。零基础入门的时候看书还是看视频,我觉得成年人,何必做选择题呢,两个都要。喜欢看书就看书,喜欢看视频就看视频。最重要的是在自学的过程中,一定不要眼高手低,要实战,把学到的技术投入到项目当中,解决问题,之后进一步锤炼自己的技术。

技术学到手后,就要开始准备面试了,找工作的时候一定要好好准备简历,毕竟简历是找工作的敲门砖,还有就是要多做面试题,复习巩固。有需要面试题资料的朋友点击这里可以免费领取

[外链图片转存中…(img-TYQpiF18-1713482739915)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值