对象实例化和继承 - 基础进阶必读!!

首发地址: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)另一个类时,需要考虑静态属性原型属性实例属性

阅读 213

推荐阅读
前端事务所
用户专栏

欢迎关注wx公众号:前端事务所我们专注帮助前端童鞋提升个人技能,分享技术干货,欢迎一起交流~

1 人关注
20 篇文章
专栏主页