首发地址:https://mp.weixin.qq.com/s/rV...
关键词: class实例化和继承
系列文章:ES6精读【划重点系列】(一)
前文涉及:Proxy、Promise、Iterator、Generator、Async
正文从此开始~
Class介绍
ES5怎么生成实例对象
//Example 1
//构造函数
function Child(name, age) {
//实例属性 | 方法
this.name = name;
this.age = age;
}
//静态方法
Child.showName = function() {
console.log('Child');
}
//原型方法 | 属性
Child.prototype.getName = function() {
console.log('My Name: ', this.name);
}
//实例化
new Child('M2', 30);
class类
ES6以面向对象的思想,对实例化对象和继承实现的语法糖。
class类的几种声明方式:
class Parent {} //声明式
const Parent = class A {} //表达式
const Parent = class {}
注意:函数声明式是具有变量提升的,而class不存在(hoist
)。
Example 1
用class实现如下:
class Child {
constructor(name, age) {
this.name = name;
this.age = age;
}
getName() {
console.log('My Name: ', this.name);
}
static showName() {
console.log('Child');
}
}
[ constructor ]
constructor方法对应了构造函数;
其默认返回实例对象(可以自定义返回对象);
必须new来实例化
[ 静态方法 ]
使用static关键字修饰的函数;
可以被子类继承;
子类的静态方法中使用super调用
[ 原型属性 | 方法 ]
class内部定义的原型方法不可遍历,直接对prototype写入的方法可遍历:
//上例中child1在内部定义了一个原型方法getName
Object.keys(Child.prototype); //[]
//在class外手动添加原型方法
Child.prototype.getAge = function() {}
Object.keys(Child.prototype) //["getAge"]
查看下Child类原型对象的属性描述器:
Object.getOwnPropertyDescriptors(Child1.prototype);
可以看到class内部声明的getName不可遍历,而在外部手动添加的getAge是可枚举的。
特性说明
1. getter和setter的使用,拦截对属性的读取操作
class Child {
constructor(name, age) {
this.name = name;
this.age = age;
}
get nickName() {
return 'getM2';
}
set nickName(value) {
console.log('setM2 ', value);
}
//...
}
let obj = new Child();
obj.nickName; // getM2
obj.nickName = 'haha'; //setM2 haha
2. 类的属性可以使用表达式的方式
let methodName = 'getName';
class Child{
//等价于getName() {}
[methodName]() {
// ...
}
}
3. 实例属性可以写在class的最顶层:
class Child {
address = '浙江省';
// 等价于
// constructor() {
// this.address = '浙江省';
// }
}
4. 遍历器接口
class Child {
address = '浙江省';
// 等价于
// constructor() {
// this.address = '浙江省';
// }
}
5. 实例方法执行时的this指向
class Child {
constructor(name) {
this.name = name;
}
getName() {
console.log('My Name: ', this.name);
}
}
let { getName } = new Child('tom')
getName(); //Uncaught TypeError: Cannot read property 'name' of undefined
可以提前绑定执行的上下文环境:
1)构造函数中bind:
class Child {
constructor(name) {
//...
this.getName = this.getName.bind(this);
}
getName() {
console.log('My Name: ', this.name);
}
}
2)箭头函数:
class Child {
constructor(name) {
//...
this.getName = () => { this._getName() };
}
_getName() {
console.log('My Name: ', this.name);
}
}
继承
ES5实现继承的方式:
实例化过程:先创建子类的实例化对象this,再将父类的属性和方法加在上面。
/* 寄生组合式 */
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function() { console.log(this.name);}
function Child(name, age) {
//1.调用父类的构造函数
SuperType.call(this, name);
this.age = age;
this.getAge= function() {
console.log(this.age);
}
}
//子类原型继承父类的原型:
// Child.prototype.__protot__ == Parent.prototype
Child.prototype = new inheritPrototype(Child, Parent);
//2.原型构建
function inheritPrototype(subChild, extendObj) {
var prototype = prototypeCerate(extendObj.prototype);
// 如果不指定,则subType的构造函数就会是supType的(按原型链往上找)
prototype.constructor = subChild;
//原型赋值
subChild.prototype = prototype;
}
//声明新的函数,避免继承类的构造函数多次执行
function prototypeCerate(o) {
function F() {}
F.prototype = o;
return new F();
}
ES6的继承实现
关键字:extends | super
实例化过程:先调用super()执行父类的构造函数获取this,再复用父类实例化得到的this对象,进行加工加上自己定义的属性和方法,因此super必须放在constructor的第一行。
class ColorPoint extends Point {
//没有显示定义constructor的话,会默认添加
constructor(x, y, color) {
//作为函数,只能用于constructor中,代表调用父类的构造函数
super(x, y);
this.color = color;
}
toString() {
//指向父类的[原型对象];在静态方法中,指向[父类]。调用父类的同名函数
return this.color + ' ' + super.toString();
}
}
this指向
1)super作为对象时,绑定的this为其所在上下文环境。比如在普通函数或者构造函数中表示子类实例对象,执行原理类似super.print.call(this);在静态方法中表示子类对象。
2)如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。super.name获取name值等同于Parent.prototype.name。
实例对象的遍历方法
1)for...in
for...in循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。可以使用hasOwnProperty()
判断是否为自有属性。
2)Object.keys(obj)
Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)。
3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)。
4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有Symbol属性。
5)Reflect.ownKeys(obj)
Reflect.ownKeys返回一个数组,包含对象自身的所有属性,不管是属性名是Symbol或字符串,也不管是否可枚举。
继承链
Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链:
1)子类的__proto__属性,表示构造函数的继承,总是指向父类。
2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
Object.getPrototypeOf (等价于__proto__指向)
1)函数都有prototype
属性;每一个对象都有__proto__
属性,指向对应的构造函数的prototype
原型。建议使用Object.getPrototypeOf
代替__proto__
操作原型对象;
2)可以使用这个方法判断一个类是否继承了另一个类。(利用的继承连中的第一条)
Object.create
B.prototype = Object.create(A.prototype);
// 等同于
B.prototype.__proto__ = A.prototype;
Mixin多个class
【注意】多个类的接口“混入”(mix in)另一个类时,需要考虑静态属性
、原型属性
、实例属性