初涉JavaScript模式 (13) : 代码复用 【上】

时间:2022-06-19 22:24:53

引子

博客断了一段时间,不是不写,一是没时间,二是觉得自己沉淀不够,经过一段时间的学习和实战,今天来总结下一个老生常谈的东西: 代码复用。

为何复用

JS门槛低,故很多人以为写几个特效就会JS,其实真正混前端的人都知道,JS写的好和写的不好差距是很大的,前段时间看到一个前端为了实现一个浮层的显示与隐藏花了100多行代码,去遍历各个子元素,挨个去设置display。还有一个很常见的场景,为了给一个UL下面的n个li绑定事件,遂遍历所有的li去bind,这些例子在日常的开发中,比比皆是,为什么有这种情况,一和业务有关,而他们压根不知道有更好的方式。造成这张现象的深层次原因无非有两种,一是他们无意去优化自己的代码,完成即好,还有一种便是没有一个好的指引,想优化却不知从何开始,时间一久,随着代码量的增加,大神也救不了你。

前段时间负责Pad端的局部代码重构,说的好听叫重构,其实就是代码组件化,由于本鸟资历尚浅,加上对一些东西的认知度也不够,做的过程中看了很多的书,但是还是觉得把握不了精髓,但是期间学到的一些比较好,可以对大家代码结构化提供一些帮助的手段。故本鸟在这里献丑一二,希望起到一个抛砖引玉的作用。期间不仅是谈代码复用,在我眼中代码的优化也是取决于代码复用是否有意义的关键指标,所以文中也会说到如何进行代码优化的几个常用手段。

罗嗦了这么多,下面切入正题,本段的标题叫 “为何复用”,相信大家看了我举得2个例子,也能猜到一二,我的理解就是优化自己的代码,说到底还是为了更加方便以后的开发。比如你写的一段为了实现验证的代码,如果不考虑代码复用,你每次都写一遍,造成代码冗余不说,光是看到那一摸一样的代码,你自己不难受吗,至少我是受不了。还有在很多场景下,大家都知道对DOM元素的操作很浪费性能,但是还是一遍一遍的去轮询,为了取某个index去遍历整个数组,为了方便图省事,定义n个全局变量,自己都不清楚prototype就给原生对象绑定n个他自己都不知道的方法。。等等。这些常识性的错误,在代码复用之前都应该极力去避免。我的观点是,好的代码才能去复用,不是随便写几个恶心的代码就跑去给别人用,这是对你自己代码的侮辱,更深的是对JS的不尊重。吐槽了这么多,舒服多了。。

简而言之,代码复用是对高强度代码的整理,减少重复开发的时间。

常用代码复用的手段

本鸟下面总结了下自己知道的几种方式,没有好坏之分,这要结合业务场景和你喜欢的方式,一句话 “适合你的才是最好的”

普通继承

在强类型语言中,实现代码复用很难绕过继承,而JS的强大之处在于模拟,故你们能做的我也能做,而在JS中,代码复用使用继承也是可行的。

首先,先上一段最基本的继承的实现代码。

```javascript
function Animal(name){ }
Animal.prototype.run = function(){
console.log(this.name + " is running!!!");
}
function Cat(name){
this.name = name;
}
Cat.prototype = new Animal();
var tom = new Cat("tom");
tom.run(); // tom is running
```

上面的继承,说的直白点就是把Cat的原型重定义了,链接了原型链,以至于Cat和Animal链接起来了,故在Animal上定义的方法和属性,在Cat上也能通过委托访问到。这是一种最简单的继承,但是也有他的缺陷,一个子构造函数并不是总是希望继承全部的父构造函数的所有属性和方法。这样就违背了我们代码复用的准则,尽可能的减少代码的冗余。

借用式继承

为了解决普通继承的缺陷,我们可以在子构造函数中去借用父构造函数去构造本地的this,而无需去继承那些父构造函数原型上的方法。上代码:

```javascript
function Animal(name){
this.name = name;
}
Animal.prototype.run = function(){
console.log(this.name + " is running!!!");
}
function Cat(name){
Animal.call(this, name);
}
var tom = new Cat("tom");
console.log(tom.name); // tom
tom.run(); //undefined is not a function
```

通过上面的代码,我们发现Cat只是简单的使用call调用了父构造方法,从而达到无需继承Animal的prototype上的run方法。甚至我们可以使用借用式继承去实现多重继承,上代码:

```javascript
function Person(){
this.legs = 2;
this.hands = 2;
}
function Man(){
this.sex = "Male";
}
function Guy(){
Person.call(this);
Man.call(this);
}
var wr = new Guy();
console.dir(wr);
```

我们发现Guy构造函数分别借用了Person和Man两个构造函数,从而实现了多继承,这和Java不支持多继承(普通类)就有了区别了,JS确实是无所不能啊。

借用式的缺点

借用式的优点在于可以获得父类自身属性的真实副本(包括定义在this的方法),而且不会存在覆盖父类属性的风险。缺点也很明显,无法继承父类的原型属性。

但是我们的JS多么强大,当然有解决方案,我们可以手动的给子类指定原型赋为父类,上代码:

```javascript
function Animal(name){
this.name = name;
}
Animal.prototype.run = function(){
console.log(this.name + " running!");
}
function Cat(name){
Animal.call(this,name);
}
Cat.prototype = new Animal(); var tom = new Cat("tom");
console.log(tom.name); //tom
console.log(tom.run()); //tom running!
```

以上代码,我们通过重新设置了Cat的prototype去获得了父类的原型方法,但是这样做也有缺陷,父类的构造函数被调用了2次,因此导致了其效率低下,而且如果在父类和子类中存在同名属性,子类会重复继承2次

共享原型

这种方式是通过把父类和子类的prototype指向同一个原型,所以父类和所有的子类都可以访问到父类的所有原型上的方法,这样做的前提是把父类所有可继承的方法都写在原型中,代码如下所示:

```javascript
function Animal(name){
this.name = name;
}
Animal.prototype = {
constructor : Animal,
sayName : function(){
console.log("my name is " + this.name);
}
}
function Cat(name){
this.name = name;
}
Cat.prototype = Animal.prototype;
var tom = new Cat("tom");
tom.sayName(); //my name is tom
console.dir(Animal.prototype); //Animal
console.dir(Cat.prototype); //Animal
```

这样的做法确实帮Cat继承了Animal上的方法,但是也是有缺点的,如果父类的某个子类去修改了原型那么将会修改所有的这条原型链上的所有的类,而且我们发现这样做只是共享了原型,我们无法去继承父类通过this设置的属性和方法,所以我们可以使用下面的方式

临时构造函数

简而言子,这种方式就是在父类的原型和子类的原型间设置一个缓冲区,断开了父类于子类之间的直接链接关系,从而解决了共享原型方式带来的问题,同时有可以继续享受原型链带来的好处,代码如下:

```javascript
var inherit = (function(){
var buffer = function(){};
return function(child, parent){
buffer.prototype = parent.prototype;
child.prototype = new buffer();
child.prototype.constructor = child;
}
}());
```

上面代码中,我们设置了一个buffer,去充当child和parent之间的缓冲区,这样的化我们每次仅需创建一次临时构造函数,这里有个小技巧,为了避免重复创建buffer我们吧buffer放在闭包中存储。

小结

在上面我们总结的代码优化方式的几种方式,在下一篇,本鸟还会梳理下一些其他的方式和一些有趣的小技巧,总算书没白读。。如在文中发现错误,欢迎指正。

初涉JavaScript模式 (13) : 代码复用 【上】的更多相关文章

  1. javascript 模式(1)——代码复用

    程序的开发离不开代码的复用,通过代码复用可以减少开发和维护成本,在谈及代码复用的时候,会首先想到继承性,但继承并不是解决代码复用的唯一方式,还有其他的复用模式比如对象组合.本节将会讲解多种继承模式以实 ...

  2. 初涉JavaScript模式系列 阶段总结及规划

    总结 不知不觉写初涉JavaScript模式系列已经半个月了,没想到把一个个小点进行放大,竟然可以发现这么多东西. 期间生怕对JS的理解不到位而误导各位,读了很多书(个人感觉JS是最难的oo语言),也 ...

  3. 【读书笔记】读《JavaScript模式》 - 函数复用模式之现代继承模式

    现代继承模式可表述为:其他任何不需要以类的方式考虑得模式. 现代继承方式#1 —— 原型继承之无类继承模式 function object(o) { function F() {}; F.protot ...

  4. 【读书笔记】读《JavaScript模式》 - 函数复用模式之类式继承模式

    实现类式继承的目标是通过构造函数Child()获取来自于另外一个构造函数Parent()的属性,从而创建对象. 1.类式继承模式#1 —— 默认方式(原型指向父函数实例) function Paren ...

  5. 初涉JavaScript模式 (12) : 沙箱模式

    引子 上一篇说了模块模式,而对于其中的命名空间模式其实也是有着一些问题,比如每添加一个模块或则深入叠加都会导致长命名,并且对于多个库的不同版本同时运行,一不小心就会污染全局标识,而这两天也发现了JSe ...

  6. 初涉JavaScript模式 (11) : 模块模式

    引子 这篇算是对第9篇中内容的发散和补充,当时我只是把模块模式中的一些内容简单的归为函数篇中去,在北川的提醒下,我才发觉这是非常不严谨的,于是我把这些内容拎出来,这就是这篇的由来. 什么是模块模式 在 ...

  7. 初涉JavaScript模式 (10) : 函数 【进阶用法】

    写在前面 不知不觉写到第10篇了.这篇写起来很忐忑,终于和高级搭上边了(呵呵),这篇我们 主要 说一下 JS 方法的部分高级用法(我知道的),笔者水平有限,难免有错.废话不多少,进入正文. 初始化 我 ...

  8. 初涉JavaScript模式 (9) : 函数 【常用方式】

    回调模式 上一篇,对JavaScript函数进行了大体的介绍,这一篇对一些在工作中经常遇到的情况进行扩展. 在工作中,我们经常遇到很多需求,比如现在有一个需求: 一栋10层的大楼,当我们在坐电梯时,电 ...

  9. 初涉JavaScript模式 (7) : 原型模式 【三】

    组合使用构造函数模式和原型模式 上篇,我们提到了原型模式的缺点,就是每个实例不能拥有自己的属性,因为纯原型模式所有的属性都是公开给每个实例的,故我们可以组合使用构造函数模式和原型模式.构造函数用来定义 ...

随机推荐

  1. Giving Data Backup Option in Oracle Forms 6i

    Suppose you want to give the data backup option in Oracle Forms application to some client users, wh ...

  2. PHP 设计模式 笔记与总结(11)观察者模式

    [观察者模式(Observer)]当一个对象状态发生改变时,依赖它的对象全部会收到通知,并自动更新 [场景]一个事件发生后,要执行一连串更新操作.传统的编程方式,就是在事件的代码之后直接加入处理逻辑. ...

  3. [iOS微博项目 - 3.0] - 手动刷新微博

    github: https://github.com/hellovoidworld/HVWWeibo   A.下拉刷新微博 1.需求 在“首页”界面,下拉到一定距离的时候刷新微博数据 刷新数据的时候使 ...

  4. 11. 配置ZooKeeper ensemble

    一个ZooKeeper集群或复制的ZooKeeper服务器集群应该优化配置,以避免出现脑裂(split-brain)等情况. 由于网络分割,同一ensemble的两个不同服务器可能构成领导者不一致,因 ...

  5. hdu5652 India and China Origins(并查集)

    India and China Origins  Accepts: 49  Submissions: 426  Time Limit: 2000/2000 MS (Java/Others)  Memo ...

  6. java中集合Collection转list对象

    参考:java中集合Collection转list对象 首先我的需求是获取到购物车列表,购物车列表是一个Map对象,构造方法获取购物项,这里购物项是Collection对象 // 购物项集合,K商品I ...

  7. .net core 支持apk下载

    在 app.UseStaticFiles(); 后面加上 app.UseStaticFiles(new StaticFileOptions { //FileProvider = new Physica ...

  8. gradle tool升级到3.0注意事项

    Gradle版本升级 其实当AS升级到3.0之后,Gradle Plugin和Gradle不升级也是可以继续使用的,但很多新的特性如:Java8支持.新的依赖匹配机制.AAPT2等新功能都无法正常使用 ...

  9. Python入门之Python中的logging模块

    基本用法 下面的代码展示了logging最基本的用法. import logging import sys # 获取logger实例,如果参数为空则返回root logger logger = log ...

  10. Windows7下使用sphinx生成开源文档(原)

    作者这里以osgearth文档为例,感觉这种生成文档的方式比较好,生成的html文档是支持搜索的,感谢开源工作者的奉献.赞一个 1. 下载并安装python for windows:https://w ...