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 = {};
}
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__
:
- 没有继承
class A{}
A.proto === Function.prototype; //true
A.prototype.proto === Object.prototype; //true
- 继承自 Object
class A extends Object{}
A.proto === Object; //true
A.prototype.proto === Object.prototype; //true
- 继承自 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 = {};
}
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前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
最后
技术是没有终点的,也是学不完的,最重要的是活着、不秃。零基础入门的时候看书还是看视频,我觉得成年人,何必做选择题呢,两个都要。喜欢看书就看书,喜欢看视频就看视频。最重要的是在自学的过程中,一定不要眼高手低,要实战,把学到的技术投入到项目当中,解决问题,之后进一步锤炼自己的技术。
技术学到手后,就要开始准备面试了,找工作的时候一定要好好准备简历,毕竟简历是找工作的敲门砖,还有就是要多做面试题,复习巩固。有需要面试题资料的朋友点击这里可以免费领取。
学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-snI30K2g-1713482739914)]
[外链图片转存中…(img-zUie34uA-1713482739914)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
[外链图片转存中…(img-EM204Nj6-1713482739915)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
[外链图片转存中…(img-gdBnIzpL-1713482739915)]
最后
技术是没有终点的,也是学不完的,最重要的是活着、不秃。零基础入门的时候看书还是看视频,我觉得成年人,何必做选择题呢,两个都要。喜欢看书就看书,喜欢看视频就看视频。最重要的是在自学的过程中,一定不要眼高手低,要实战,把学到的技术投入到项目当中,解决问题,之后进一步锤炼自己的技术。
技术学到手后,就要开始准备面试了,找工作的时候一定要好好准备简历,毕竟简历是找工作的敲门砖,还有就是要多做面试题,复习巩固。有需要面试题资料的朋友点击这里可以免费领取。
[外链图片转存中…(img-TYQpiF18-1713482739915)]