类式继承
场景: 我们需要目前有一个超类Person,现在需要一个Author类来继承超类的所有方法及属性,并且拥有自己的方法和属性
// Person 超类
function Person(name) {
this.name = name;
}
Person.prototype.getName = function () {
return this.name;
}
- 原型链
// 继承超类的类 Author
function Author(name, books) {
// 在使用new运算符时,系统会先创建一个空对象,然后调用构造函数,此过程中空对象处于作用域链最前端
// 这里我们调用超类的构造函数,就需要手懂模拟这个过程。此时this代表空对象,name为参数
Person.call(this, name);
this.books = books;
}
Author.prototype = new Person(); // 使Author的原型指向Person的实例 此时原型的构造函数(constructor)被重置
Author.prototype.constructor = Author; // 重定向Author原型的构造函数 不定义的话此构造函数为空,那么将会向上查找,指向Person
Author.prototype.getBooks = function () {
return this.books;
}
var author = [];
author[0] = new Author('Dustin Diaz', ['JavaScript Design Patterns']);
author[1] = new Author('Ross Harmes', ['JavaScript Design Patterns']);
console.log(author[1].getName()) // Ross Harmes
console.log(author[1].getBooks()) // ['JavaScript Design Patterns']
- extend函数
// extend函数
function extend(subClass, superClass) {
var F = function () {}; // 先创造一个空对象
F.prototype = superClass.prototype; // 使空对象的原型指向超类的原型
subClass.prototype = new F(); // 使当前类的原型指向F的实例
subClass.prototype.constructor = subClass; // 重定向当前类的原型的构造函数
// superclass 用于直接访问超类的方法
// 使用场景 在既想重定义超类的方法而又想访问其在超类中的实现时 栗子在下面
subClass.superclass = superClass.prototype;
// 判断构造器指向
if (superClass.prototype.constructor == Object.prototype.constructor) {
superClass.prototype.constructor = superClass
}
}
// Author 类
function Author(name, books) {
Author.superclass.constructor.call(this, name)
this.books = books
}
extend(Author, Person)
var xhui = new Author('xhui', ['123'])
console.log(xhui.getName()) //xhui
// 利于supercalss来重定义超类的getName方法
Author.prototype.getName = function () {
var name = Author.superclass.getName.call(this)
return `${name}123123`
}
console.log(xhui.getName()) //xhui123123
原型式继承
// clone 函数
function clone(object) {
function F(params) {};
F.prototype = object;
return new F();
}
// Person 超类
var Person = {
name: 'default name',
getName: function () {
return this.name
}
}
var Author = clone(Person);
Author.books = [];
Author.getBooks = function () {
return this.books;
}
var author;
author = clone(Author);
console.log(author.getName()) //default name
author.name = 'xhui';
author.books = ['js设计模式']
console.log(author.getName()) // 'xhui'
console.log(author.getBooks()) // ['js设计模式']
继承而来的成员的读和写具有不对等性。 在类式继承中,Author的每一份实例都有自己的books数据副本。但是在原型式继承中大不相同, 一个克隆并非其原型对象的一份独完全立的副本,它只是一个以那个对象为原型对象的空对象。
克隆刚被创建时,author.name其实是一个反指最初的Person.name的链接。对于从原型对象继承而来的成员,其读和写具有内在的不对等性。在你读取author.name时,如果你没有为其赋值,那么得到的是其原型对象的同名属性值。而你在为author.name赋值时,其实是在为author定义一个新属性。
掺元类
我们平时总会定义一个包含各种通用方法的类,然后用它来扩充其他类。这种包含各种通用方法的类就是掺元类。
// argument 辅助函数
function argument(receivingClass, givingClass) {
if (arguments[2]) {
// 如果有第三个参数 则为扩充类扩充名为第三个参数的方法
for (var i = 2; i < arguments.length; i++) {
receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]]
}
} else {
// 如果只有两个参数 为扩充类扩充掺元类所有的方法
for (methodName in givingClass.prototype) {
if (!receivingClass.prototype[methodName]) {
receivingClass.prototype[methodName] = givingClass.prototype[methodName]
}
}
}
}
// 掺元类
var Mixin = function () {};
Mixin.prototype = {
serialize: function () {
var output = [];
for (key in this) {
output.push(`key:${this[key]}`)
}
return output
}
}
function Author(name, books) {
this.name = name;
this.books = books;
}
argument(Author, Mixin) // argument(Author, Mixin, 'serialize')
var author = new Author('xhui', ['js设计模式']);
console.log(author.serialize())
结果为
1544515452.jpg
举个栗子
书上栗子有点长懒得写了。。。
网友评论