JavaScript类与继承

前言

在ES6中出现了classextends关键字,使js做类与继承容易了很多,但是在ES6之前如何实现类与继承是理解js重要的一部分。本篇文章将介绍ES6的类、继承与ES6之前的类、继承的写法,一篇文章介绍所有内容。

下面定义的类全部为Person类,Person类有agename两个属性,sayNamesayAge两个方法。

ES5定义类

伴随着JS的发展,定义类的方式共有7种,最后2种除特殊场景外不推荐:

  1. 工厂模式
  2. 构造函数模式
  3. 原型模式
  4. 组合使用构造函数模式与原型模式(最常用)
  5. 动态原型模式
  6. 寄生构造函数模式(特殊场景)
  7. 稳妥构造函数模式(特殊场景)

接下来说明各种方式如何定义类,以及缺点。

工厂模式

示例代码如下:

function createPerson(name, age){
	let tempPerson = new Object();
	
	tempPerson.name = name;
	tempPerson.age = age;
	
	tempPerson.sayName = function(){
		console.log(`My name is ${this.name}`);
	}
	tempPerson.sayAge = function(){
		console.log(`My age is ${this.age}`);
	}
	return tempPerson;
}
const person1 = createPerson('小刘先森', 26);
person1.sayName(); // 输出:My name is 小刘先森
person1.sayAge(); // 输出: My age is 26

const person2 = createPerson('小刘儿', 26);
person2.sayName(); // 输出:My name is 小刘儿
person2.sayAge(); // 输出: My age is 26

缺点:没有解决对象识别的问题,即怎么样知道一个对象的类型

构造函数模式

示例代码如下:

function Person(name, age){
	this.name = name;
	this.age = age;
	this.sayName = function(){
		console.log(`My name is ${this.name}`);
	}
	this.sayAge = function(){
		console.log(`My age is ${this.age}`);
	}
}
const person1 = new Person('小刘先森', 26) // 名字与年龄均为本作者 哈哈
person1.sayName(); // 输出:My name is 小刘先森
person1.sayAge(); // 输出: My age is 26

const person2 = new Person('小刘儿', 26)
person2.sayName(); // 输出:My name is 小刘儿
person2.sayAge(); // 输出: My age is 26

缺点:每个实例都会有一份没必要的方法副本,即person1.sayName !== person2.sayName

原型模式

示例代码如下:

function Person(){
}
Person.prototype.name = '小刘先森';
Person.prototype.age = '26';
Person.prototype.sayName = function(){
	console.log(`My name is ${this.name}`);
}
Person.prototype.sayAge = function(){
	console.log(`My age is ${this.age}`);
}
const person1 = new Person('小刘先森', 26) // 名字与年龄均为本作者 哈哈
person1.sayName(); // 输出:My name is 小刘先森
person1.sayAge(); // 输出: My age is 26

const person2 = new Person('小刘儿', 26)
person2.sayName(); // 输出:My name is 小刘儿
person2.sayAge(); // 输出: My age is 26

缺点:缺点很明显,属性全部写在了prototype上,并且为固定值,无论实例化几个类都会输出一样的值。

组合使用构造函数模式与原型模式(最常用)

示例代码如下:

function Person(name, age){
	this.name = name;
	this.age = age;
}
Person.prototype.sayName = function(){
	console.log(`My name is ${this.name}`);
}
Person.prototype.sayAge = function(){
	console.log(`My age is ${this.age}`);
}
const person1 = new Person('小刘先森', 26) // 名字与年龄均为本作者 哈哈
person1.sayName(); // 输出:My name is 小刘先森
person1.sayAge(); // 输出: My age is 26

const person2 = new Person('小刘儿', 26)
person2.sayName(); // 输出:My name is 小刘儿
person2.sayAge(); // 输出: My age is 26

这是最常用的自定义类的方式,每个实例都会有一份自己的实例属性副本,同时又共享着对方法的引用。

动态原型模式

示例代码如下:

function Person(name, age){
	// 属性
	this.name = name;
	this.age = age;
	
	// 方法
	if( typeof this.sayName !== 'function'){
		Person.prototype.sayName = function(){
			console.log(`My name is ${this.name}`);
		}
		Person.prototype.sayAge = function(){
			console.log(`My age is ${this.age}`);
		}
	}
}
const person1 = new Person('小刘先森', 26) // 名字与年龄均为本作者 哈哈
person1.sayName(); // 输出:My name is 小刘先森
person1.sayAge(); // 输出: My age is 26

const person2 = new Person('小刘儿', 26)
person2.sayName(); // 输出:My name is 小刘儿
person2.sayAge(); // 输出: My age is 26

其中方法定义的部分,判断sayName是否已经存在,不存在再去定义,if语句中的代码只会在初次实例化时执行,也同样达到了类的定义方式,但是却没有原型与构造函数组合方式代码简洁,不推荐使用。

缺点:不能使用对象字面量方式重写原型,如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系

寄生构造函数模式

示例代码如下:

function Person(name, age){
	let tempPerson = new Object();
	
	tempPerson.name = name;
	tempPerson.age = age;
	
	tempPerson.sayName = function(){
		console.log(`My name is ${this.name}`);
	}
	tempPerson.sayAge = function(){
		console.log(`My age is ${this.age}`);
	}
	return tempPerson;
}
const person1 = new Person('小刘先森', 26);
person1.sayName(); // 输出:My name is 小刘先森
person1.sayAge(); // 输出: My age is 26

const person2 = new Person('小刘儿', 26);
person2.sayName(); // 输出:My name is 小刘儿
person2.sayAge(); // 输出: My age is 26

这种模式除了在实例化时使用new操作符,其余的与工厂模式完全相同。这种模式可以在特殊场景下为对象创建构造函数。

场景:创建一个具有额外方法的数组,由于不能修改Array的构造函数,可以使用该模式

function SpecialArray(){
 //创建数组
 var values = new Array();
 //添加值
 values.push.apply(values, arguments);
 //添加方法
 values.toPipedString = function(){
 	return this.join("|");
 };

 //返回数组
 return values;
}
var colors = new SpecialArray("red", "blue", "green");
alert(colors.toPipedString()); //"red|blue|green"

稳妥构造函数模式

道格拉斯·克罗克福德(Douglas Crockford)发明了 JavaScript 中的稳妥对象(durable objects)这个概念,所谓稳妥对象,指的是没有公共属性,而且其方法也不引用 this 的对象。稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用 this 和new),或者在防止数据被其他应用程序改动时使用。
示例代码如下:

function Person(name, age){
	let tempPerson = new Object();
	tempPerson.sayName = function(){
		console.log(`My name is ${name}`);
	}
	tempPerson.sayAge = function(){
		console.log(`My age is ${age}`);
	}
	return tempPerson;
}
const person1 = new Person('小刘先森', 26);
person1.sayName(); // 输出:My name is 小刘先森
person1.sayAge(); // 输出: My age is 26

const person2 = new Person('小刘儿', 26);
person2.sayName(); // 输出:My name is 小刘儿
person2.sayAge(); // 输出: My age is 26

注意:

  1. 以上方式创建的类不引用this
  2. 实例化时不使用new
  3. 除了调用sayName或者sayAge没有其它方法可以访问到构造函数中传入的数据,即使再添加其它方法也没办法访问到。

ES6定义类

ES6使用class关键字定义一个类,定义一个Person类代码如下:

class Person{
	// 构造函数
	constructor(name, age){
		this.name = name;
		this.age = age;
	}
	// 方法
	sayName(){
		console.log(`My name is ${this.name}`)
	}
	// 方法
	sayAge(){
		console.log(`My age is ${this.age}`)
	}
}
const person1 = new Person('小刘先森', 26) // 名字与年龄均为本作者 哈哈
person1.sayName(); // 输出:My name is 小刘先森
person1.sayAge(); // 输出: My age is 26

const person2 = new Person('小刘儿', 26)
person2.sayName(); // 输出:My name is 小刘儿
person2.sayAge(); // 输出: My age is 26

继承

ES5的继承

ES6的继承

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值