代码复用模式
发表于:2024-08-15 | 分类: 学习

类式继承模式

先定义两个构造函数Parent()Child(),然后想办法通过Child()来获取Parent()的属性

1
2
3
4
5
6
7
8
9
function Parent(name) {
this.name = name || "adam";
}

Parent.prototype.say = function () {
return this.name
}

function child(name) { }

默认模式

常用的办法是使用Parent()创建一个对象赋值给Child()原型,可以通过inherit()函数实现这一方法:

1
2
3
function inherit(C, P) {
C.prototype = new P()
}

原型属性应该指向一个对象,而不是一个函数,因此它必须指向一个由父构造函数所创建的实例,而不是指向构造函数。也就是说要用 new 操作符创建新对象,new 才能使这种模式运作

1
2
3
4
inherit(child, Parent);

var kid = new Child()
kid.say() // adam

本模式的一个缺点在于,同时继承了两个对象的属性,添加到 this 的属性和原型属性。然而绝大多数时候并不需要这些自身属性。还有就是inherit()并不支持将参数传递到子构造函数中,而子构造函数却可以将参数传递到父构造函数。此模式是对父模式的一个引用

借用构造函数

本模式解决了子构造函数到父构造函数的传参问题

1
2
3
function Child(a, c, b, d) {
Parent.apply(this, arguments);
}

这种模式只能继承父构造函数添加到的 this 属性,并不能继承那些已添加到原型中的成员。此模式会创造一个父模式的副本

原型链

在原型链里面,继承是一个一次性操作,它仅会复制父对象的属性并将其作为子对象自身的属性,仅此而已。因此,也不会保留_proto_链接

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function foo(name) {
this.name = name || "foo";
}

foo.prototype.say = function () {
return this.name
}

function bar(name) {
foo.apply(this, arguments)
}

var names = new bar("bar")
console.log(names) //{ name: 'bar' }
console.log(typeof names.say) // undefined
多重继承

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function cat() {
this.legs = 4
this.say = function () {
return "cats"
}
}
function bird() {
this.wings = 2
this.fly = true
}

function catWings() {
cat.apply(this)
bird.apply(this)
}

var jane = new catWings()
console.log(jane) //{ legs: 4, say: [Function (anonymous)], wings: 2, fly: true }

借用构造函数模式的缺点在于无法从原型链继承任何东西,并且原型也仅是添加可重用方法以及属性的位置,并不会为每个实例重新创建原型。优点在于可以获得父对象自身成员的真实副本,也不会存在子对象意外覆盖父对象属性的风险。

借用和设置原型

此模式结合了默认模式和借用构造函数模式,即先借用构造函数,然后设置子构造函数的原型使其指向一个构造函数创建的新实例:

1
2
3
4
5
function Child(a, b, c, d) {
Parent.apply(this, arguments)
}

Child.prototype = new Parent()

这样做的优点是能够获得父对象本身的成员副本以及指向父对象中可复用功能的引用。同时子对象也能够将任意参数传递到父构造函数中。但是也有一个缺点是,父构造函数被调用了两次,因此这导致了其效率低下的问题,而且,自身的属性会被继承两次

共享原型

此模式的法则是:可复用成员应该转移到原型中而不是放在 this 中。因此处于继承的目的,任何值得继承的东西都应该放置在原型中实现。将子对象的原型设置与父对象的原型设置为相同的即可

示例:

1
2
3
function inherit(C, P) {
C.prototype = P.prototype
}

由于所有对象共享同一个原型,所以原型链的查询速度很快。同时也导致了一个缺点:如果在继承链下方的某处存在一个子对象或者孙子对象修改了原型,会影响到所有的父对象和祖先对象

临时构造函数

此模式子对象仅继承了原型的属性。父构造函数添加到 this 中的任何成员都不会被继承

1
2
3
4
5
function inherit(C, P) {
var F = function() {}
F.prototype = P.prototype
C.prototype = new F()
}

存储超类

是一个指向原始父对象的引用

1
2
3
4
5
6
7
function inherit(C, P) {
var F = function() {}
F.prototype = P.prototype
C.prototype = new F()
// 添加存储超类uber
C.uber = P.prototype
}

重置构造函数指针

如果不重置指针,那么所有子对象的构造函数都会指向Parent(),这是没有任何用处的

1
2
3
4
5
6
7
8
9
var inherit = (function () {
var F = function () { }
return function (C, P) {
F.prototype = P.prototype;
C.prototype = new F();
C.uber = P.prototype
C.prototype.constructor = C
}
}())

constructor 属性主要用于提供对象信息

原型继承

1
2
3
4
5
6
7
8
9
10
11
var foo = {
name: 'foo',
}

function object(obj) {
function F() { }
F.prototype = obj
return new F()
}
var bar = object(foo)
console.log(bar.name) // foo

借用和绑定

借用方法内部,this 所指向的对象是基于调用表达式而确定的

示例:

1
2
3
4
5
6
7
8
9
10
var foo = {
name: 'foo',
say: function (a) {
return a + "," + this.name;
}
}
var bar = {
name: "bar"
}
foo.say.apply(bar, ["hi"]); //hi,bar

绑定方法需要付出额外闭包的代价

示例:

1
2
3
4
5
function bind(o, m) {
return function () {
return m.apply(o, [].slice.call(arguments));
}
}
上一篇:
设计模式
下一篇:
对象创建模式