ES5和ES6中继承的实现

9 篇文章 0 订阅

一、ES5中的两种非常经典的继承

1.组合继承

指的是将原型链构造函数的技术组合到一起来实现继承。其背后的思路就是使用原型链实现对原型属性和方法的继承,而通过构造函数来实现对实例属性的继承。

示例
//构造函数继承实例属性(当然也可以有方法,不过不建议这么做)
function Phone(name){
    this.name = name;
    this.label=["全面屏","5G通信"];
}
//原型链继承原型属性和方法
Phone.prototype.colors=["red","blue","green"];
Phone.prototype.takePhoto = function(){
    console.log("我是父类的原型上的方法--------拍照ing: "+this.name);
}


function Pear(name,inch){
    //继承构造函数中的属性
    Phone.call(this,name);
    //子类自己构造函数中的属性
    this.inch = inch;//英寸
}
//继承原型上的属性和方法
Pear.prototype = new Phone();
Pear.prototype.constructor = Pear;
//在原型上添加子类自己的方法或属性
Pear.prototype.listenMusic = function(){
    console.log("我是子类在原型上添加的方法------听歌ing+ "+this.inch);
}

//以上我们就实现了Pear继承Phone,并且Pear也拥有了自己独有的属性inch和方法listenMusic。

//下面进行测试:
var p1 = new Pear("梨子手机",6.0);//实例化
p1.label.push("人脸识别")//操作父类构造函数的属性
console.log(p1.label);
p1.takePhoto();//调用父类原型上的方法
p1.listenMusic();//调用子类添加在原型上的方法

以上这种继承方式避免了单独使用原型链和构造函数的缺陷,融合了它们的优点,成为JavaScript中最常用的继承方式。而且,instanceofisPrototypeof() 也能够用于识别基于组合继承的对象。

2.寄生组合继承

这种继承方式是目前认为ES5中最有效的方式,当然组合继承用的也是非常的多的。实现寄生组合继承的关键方法: inhert()

function inherit(son,father){
    let prototypeObj = Object.create(father.prototype);
    prototypeObj.constructor = son;
    son.prototype = prototypeObj;
}
示例
//构造函数继承实例属性
function Phone(name){
    this.name = name;
    this.label=["全面屏","5G通信"];
}
//原型链继承原型属性和方法
Phone.prototype.colors=["red","blue","green"];
Phone.prototype.takePhoto = function(){
    console.log("我是父类的原型上的方法--------拍照ing: "+this.name);
}

function Pear(name,inch){
    //继承构造函数中的属性
    Phone.call(this,name);
    //子类自己构造函数中的属性
    this.inch = inch;//英寸
}

//继承原型上的属性和方法:(非常重要)
inherit(Pear,Phone);

//在原型上添加子类自己的方法或属性
Pear.prototype.listenMusic = function(){
    console.log("我是子类在原型上添加的方法------听歌ing+ "+this.inch);
}

//以上我们就实现了Pear继承Phone,并且Pear也拥有了自己独有的属性inch和方法listenMusic。

//下面进行测试:
var p1 = new Pear("梨子手机",6.0);//实例化
p1.label.push("人脸识别")//操作父类构造函数的属性
console.log(p1.label);
p1.takePhoto();//调用父类原型上的方法
p1.listenMusic();//调用子类添加在原型上的方法
3.ES5中两种方式的比较
  • 对于组合继承:就是将子类的原型指向父类的实例,然后子类和父类组合一下构造函数中的实例属性或方法。这样做就会导致子类原型(此时是父类)上的属性和子类与父类组合的属性重复,即,原本父类的属性此刻在子类本身和子类原型上都有一份。
  • 对于寄生组合继承:关键点就是理解上文提到的inhert()方法:将父类的原型赋给一个临时对象,子类的原型指向该临时对象,如此得到父类原型上的属性和方法。再通过构造函数组合父类和子类本身上的属性或方法

二、ES6中的类与继承

1.class类

介绍

JavaScript 语言中,生成实例对象的传统方法是通过构造函数。 ES6 提供了更接近
传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键
字,可以定义类。

基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做
到,新的class写法只是让对象原型的写法更加清晰更像面向对象编程的语法而已。
所以ES6 的类,完全可以看作构造函数的另一种写法。

方法

在类中可以直接定义方法,实际上类的所有方法都定义在类的prototype属性上面。
在类的实例上面调用方法,其实就是调用原型上的方法。

class Point { 
	constructor(){} 
	toString(){} 
	toValue(){}  
}

由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype
象上面,Object.assign方法可以很方便地一次向类添加多个方法。

class Point { 
	constructor(){}
} 
Object.assign(Point.prototype, { 
	toString(){}, 
	toValue(){} 
});
constructor 方法

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。
一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默
认添加。

class Point { } 
// 等同于
class Point { constructor() {} }

类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不
new也可以执行。

静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法
前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这
就称为“静态方法”

class Foo {
	static classMethod() { 
		console.log('hello'); 
	} 
}
Foo.classMethod(); // 'hello'

如果静态方法包含this关键字,这个this指的是类,而不是实例。

实例属性

类的实例属性可以定义在构造函数中。

class Person{ 
	constructor(id,name,age) { 
		this.id = id;
		this.name = name;
		this.age = age;
	} 
}
静态属性

直接在类上定义的属性成为是静态属性。

class Foo {...}
Foo.prop = 1; 
console.log(Foo.prop); // 1

目前,只有这种写法可行,因为 ES6 明确规定,Class 内部只有静态方法,没有静
态属性。

实例属性: constructor中this后边的
实例方法: 在类内部没有用static修饰的方法

静态属性: 在类外部使用 ’ 类名.属性名 ’ (比如: Person.staticAttr = ‘a’;)声明的属性,只能通过类名去调用
静态方法: 在类内部使用static修饰的方法,只能通过类名去调用

2.继承

extends

class可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要
清晰和方便很多。

class Animal { 
	constructor(name){
		this.name = name;
	}
	sayName(){
	console.log(“my name is ”,this.name);
	} 
}
class Dog extends Animal{...}

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。子类构造函数可以省略。在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。

继承有两条线
1.子类对象继承父类对象
2.子类的原型对象继承父类的原型对象

super

super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同:

  • 函数

子类B的构造函数之中的super(),代表调用父类的构造函数。 super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B,因此super()在这里相当于A.prototype.constructor.call(this)

  • 对象

在普通方法中,指向父类的原型对象;在静态方法中,指向父类。由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。

类的 prototype 属性和__proto__属性

class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。

  • 子类的__proto__属性,表示构造函数的继承,总是指向父类
  • 子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性
示例
//父类:两个属性,一个普通方法,一个静态方法
class Person {
    constructor(name, age) {
    	//实例属性
        this.name = name;
        this.age = age;
    }
    //实例方法
    sayName() {
		console.log("我的名字是:" + this.name);
    }
        //静态方法
    static sayHello() {
        console.log("hello");
    }
}
//静态属性
Person.staticProp = '父类的静态属性';

//子类继承父类
class Chinese extends Person { //创建Chinese类
    constructor(name, age, addr) {
        super(name, age); //继承父类的属性,必须写在任何this的开头,不然会报错
        this.addr = addr;
    }
    sayMyaddr() {
        console.log("我是中国人,我住在:" + this.addr);
    }
}

//创建子类实例
var curry = new Chinese("curry", 25, "上海");

//测试
console.log(curry); //Chinese { name: 'curry', age: 25, addr: '上海' }
curry.sayName(); //我的名字是:curry
curry.sayMyaddr(); //我是中国人,我住在:上海
Person.sayHello(); //hello
Chinese.sayHello(); // hello (静态方法也可以被继承)
console.log(Person.staticProp); //父类的静态属性
console.log(Chinese.staticProp); //父类的静态属性 (静态属性也可以被继承)

//静态属性和方法的继承---子类继承父类
console.log(Chinese.__proto__ === Person); //true

//实例属性和方法的继承---子类的原型对象继承自父类的原型对象
console.log(Chinese.prototype.__proto__ === Person.prototype); //true
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值