深入理解JavaScript系列(45):代码复用模式(避免篇)

2024-09-01 15:32

本文主要是介绍深入理解JavaScript系列(45):代码复用模式(避免篇),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

介绍

任何编程都提出代码复用,否则话每次开发一个新程序或者写一个新功能都要全新编写的话,那就歇菜了,但是代码复用也是有好要坏,接下来的两篇文章我们将针对代码复用来进行讨论,第一篇文避免篇,指的是要尽量避免使用这些模式,因为或多或少有带来一些问题;第二排是推荐篇,指的是推荐大家使用的模式,一般不会有什么问题。

模式1:默认模式

代码复用大家常用的默认模式,往往是有问题的,该模式使用Parent()的构造函数创建一个对象,并且将该对象赋值给Child()的原型。我们看一下代码:

function inherit(C, P) {C.prototype = new P();
}// 父构造函数
function Parent(name) {this.name = name || 'Adam';
}
// 给原型添加say功能
Parent.prototype.say = function () {return this.name;
};
// Child构造函数为空
function Child(name) {
}// 执行继承
inherit(Child, Parent);var kid = new Child();
console.log(kid.say()); // "Adam"

var kiddo = new Child();
kiddo.name = "Patrick";
console.log(kiddo.say()); // "Patrick"

// 缺点:不能让参数传进给Child构造函数
var s = new Child('Seth');
console.log(s.say()); // "Adam"

这种模式的缺点是Child不能传进参数,基本上也就废了。

模式2:借用构造函数

该模式是Child借用Parent的构造函数进行apply,然后将child的this和参数传递给apply方法:

// 父构造函数
function Parent(name) {this.name = name || 'Adam';
}// 给原型添加say功能
Parent.prototype.say = function () {return this.name;
};// Child构造函数
function Child(name) {Parent.apply(this, arguments);
}var kid = new Child("Patrick");
console.log(kid.name); // "Patrick"

// 缺点:没有从构造函数上继承say方法
console.log(typeof kid.say); // "undefined"

缺点也很明显,say方法不可用,因为没有继承过来。

模式3:借用构造函数并设置原型

上述两个模式都有自己的缺点,那如何把两者的缺点去除呢,我们来尝试一下:

// 父构造函数
function Parent(name) {this.name = name || 'Adam';
}// 给原型添加say功能
Parent.prototype.say = function () {return this.name;
};// Child构造函数
function Child(name) {Parent.apply(this, arguments);
}Child.prototype = new Parent();var kid = new Child("Patrick");
console.log(kid.name); // "Patrick"
console.log(typeof kid.say); // function
console.log(kid.say()); // Patrick
console.dir(kid);
delete kid.name;
console.log(kid.say()); // "Adam"

运行起来,一切正常,但是有没有发现,Parent构造函数执行了两次,所以说,虽然程序可用,但是效率很低。

模式4:共享原型

共享原型是指Child和Parent使用同样的原型,代码如下:

function inherit(C, P) {C.prototype = P.prototype;
}// 父构造函数
function Parent(name) {this.name = name || 'Adam';
}// 给原型添加say功能
Parent.prototype.say = function () {return this.name;
};// Child构造函数
function Child(name) {
}inherit(Child, Parent);var kid = new Child('Patrick');
console.log(kid.name); // undefined
console.log(typeof kid.say); // function
kid.name = 'Patrick';
console.log(kid.say()); // Patrick
console.dir(kid);

确定还是一样,Child的参数没有正确接收到。

模式5:临时构造函数

首先借用构造函数,然后将Child的原型设置为该借用构造函数的实例,最后恢复Child原型的构造函数。代码如下:

/* 闭包 */
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;}
} ());function Parent(name) {this.name = name || 'Adam';
}// 给原型添加say功能
Parent.prototype.say = function () {return this.name;
};// Child构造函数
function Child(name) {
}inherit(Child, Parent);var kid = new Child();
console.log(kid.name); // undefined
console.log(typeof kid.say); // function
kid.name = 'Patrick';
console.log(kid.say()); // Patrick
var kid2 = new Child("Tom");
console.log(kid.say()); 
console.log(kid.constructor.name); // Child
console.log(kid.constructor === Parent); // false

问题照旧,Child不能正常接收参数。

模式6:klass

这个模式,先上代码吧:

var klass = function (Parent, props) {var Child, F, i;// 1.
    // 新构造函数
    Child = function () {if (Child.uber && Child.uber.hasOwnProperty("__construct")) {Child.uber.__construct.apply(this, arguments);}if (Child.prototype.hasOwnProperty("__construct")) {Child.prototype.__construct.apply(this, arguments);}};// 2.
    // 继承
    Parent = Parent || Object;F = function () {};F.prototype = Parent.prototype;Child.prototype = new F();Child.uber = Parent.prototype;Child.prototype.constructor = Child;// 3.
    // 添加实现方法
    for (i in props) {if (props.hasOwnProperty(i)) {Child.prototype[i] = props[i];}}// return the "class"
    return Child;
};var Man = klass(null, {__construct: function (what) {console.log("Man's constructor");this.name = what;},getName: function () {return this.name;}
});var first = new Man('Adam'); // logs "Man's constructor"
first.getName(); // "Adam"

var SuperMan = klass(Man, {__construct: function (what) {console.log("SuperMan's constructor");},getName: function () {var name = SuperMan.uber.getName.call(this);return "I am " + name;}
});var clark = new SuperMan('Clark Kent');
clark.getName(); // "I am Clark Kent"

console.log(clark instanceof Man); // true
console.log(clark instanceof SuperMan); // true

怎么样?看着是不是有点晕,说好点,该模式的语法和规范拧得和别的语言一样,你愿意用么?咳。。。

总结

以上六个模式虽然在某种特殊情况下实现了某些功能,但是都存在各自的缺点,所以一般情况,大家要避免使用。

参考:http://shichuan.github.com/javascript-patterns/#code-reuse-patterns

这篇关于深入理解JavaScript系列(45):代码复用模式(避免篇)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1127330

相关文章

Java中实现线程的创建和启动的方法

《Java中实现线程的创建和启动的方法》在Java中,实现线程的创建和启动是两个不同但紧密相关的概念,理解为什么要启动线程(调用start()方法)而非直接调用run()方法,是掌握多线程编程的关键,... 目录1. 线程的生命周期2. start() vs run() 的本质区别3. 为什么必须通过 st

关于跨域无效的问题及解决(java后端方案)

《关于跨域无效的问题及解决(java后端方案)》:本文主要介绍关于跨域无效的问题及解决(java后端方案),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录通用后端跨域方法1、@CrossOrigin 注解2、springboot2.0 实现WebMvcConfig

Java SWT库详解与安装指南(最新推荐)

《JavaSWT库详解与安装指南(最新推荐)》:本文主要介绍JavaSWT库详解与安装指南,在本章中,我们介绍了如何下载、安装SWTJAR包,并详述了在Eclipse以及命令行环境中配置Java... 目录1. Java SWT类库概述2. SWT与AWT和Swing的区别2.1 历史背景与设计理念2.1.

使用SpringBoot整合Sharding Sphere实现数据脱敏的示例

《使用SpringBoot整合ShardingSphere实现数据脱敏的示例》ApacheShardingSphere数据脱敏模块,通过SQL拦截与改写实现敏感信息加密存储,解决手动处理繁琐及系统改... 目录痛点一:痛点二:脱敏配置Quick Start——Spring 显示配置:1.引入依赖2.创建脱敏

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

SpringBoot 中 CommandLineRunner的作用示例详解

《SpringBoot中CommandLineRunner的作用示例详解》SpringBoot提供的一种简单的实现方案就是添加一个model并实现CommandLineRunner接口,实现功能的... 目录1、CommandLineRunnerSpringBoot中CommandLineRunner的作用

Java死锁问题解决方案及示例详解

《Java死锁问题解决方案及示例详解》死锁是指两个或多个线程因争夺资源而相互等待,导致所有线程都无法继续执行的一种状态,本文给大家详细介绍了Java死锁问题解决方案详解及实践样例,需要的朋友可以参考下... 目录1、简述死锁的四个必要条件:2、死锁示例代码3、如何检测死锁?3.1 使用 jstack3.2

Java日期类详解(最新推荐)

《Java日期类详解(最新推荐)》早期版本主要使用java.util.Date、java.util.Calendar等类,Java8及以后引入了新的日期和时间API(JSR310),包含在ja... 目录旧的日期时间API新的日期时间 API(Java 8+)获取时间戳时间计算与其他日期时间类型的转换Dur

java对接海康摄像头的完整步骤记录

《java对接海康摄像头的完整步骤记录》在Java中调用海康威视摄像头通常需要使用海康威视提供的SDK,下面这篇文章主要给大家介绍了关于java对接海康摄像头的完整步骤,文中通过代码介绍的非常详细,需... 目录一、开发环境准备二、实现Java调用设备接口(一)加载动态链接库(二)结构体、接口重定义1.类型

SpringBoot读取ZooKeeper(ZK)属性的方法实现

《SpringBoot读取ZooKeeper(ZK)属性的方法实现》本文主要介绍了SpringBoot读取ZooKeeper(ZK)属性的方法实现,强调使用@ConfigurationProperti... 目录1. 在配置文件中定义 ZK 属性application.propertiesapplicati