引子

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

为何复用

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. 浅谈数据库系统中的cache

    Cache和Buffer是两个不同的概念,简单的说,Cache是加速“读”,而buffer是缓冲“写”,前者解决读的问题,保存从磁盘上读出的数据,后者是解决写的问题,保存即将要写入到磁盘上的数据.在很 ...

  2. Java并发编程:线程间通信wait、notify

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  3. grep 查找当前文件夹下所有文件内内容 并显示文件名

    grep -H comm * 显示结果 rclog:recommend/ysclick,1,2 rclog:recommend/utagclick,1,8 .................... t ...

  4. mybatis logback打印sql

    <?xml version="1.0" encoding="UTF-8" ?><configuration> <contextNa ...

  5. js中的运算符和条件语句

    js中的运算符大体上可以分为4类:1算术运算符.2一元操作符.3比较运算符.4逻辑运算符. 算术运算符一般指的是加减乘除求余这五种操作符:+,-,*,/,%.通过算术运算符可以对js中的变量进行操作. ...

  6. 代码对齐 分类: C#小技巧 2014-04-17 14:45 166人阅读 评论(0) 收藏

    开发项目时,为了是代码层次清晰.美观,常常需要调整多行,使之对齐.在网上也看到一些方法,感觉不好用,偶尔发现一个小技巧. (1)多行代码同时右移 同时选中几行,按"Tab"键,就会 ...

  7. 对于数组(字符串)slice方法的总结

    数组(字符串)使用slice方法的目的就是截取目标数组(字符串),通俗的来讲就是截取想要的.网上有很多slice方法的实例加以说明,在此主要是从特殊到一般对此方法进行了总结. 先说总结的方法,再看下面 ...

  8. Qt 学习之路:存储容器

    存储容器(containers)有时候也被称为集合(collections),是能够在内存中存储其它特定类型的对象,通常是一些常用的数据结构,一般是通用模板类的形式.C++ 提供了一套完整的解决方案, ...

  9. google(转帖)

    本帖最后由 qiushui_007 于 2014-6-10 16:14 编辑 IP Addresses of Google Global Cachewww.kookle.co.nr Bulgaria  ...

  10. 前端工具之WebPack解密之背景

    请注意,这是一篇站在完全新手的角度上来写的文章.可能你是一个后端人员想了解前端工具的使用和概念;也可能你是一个前端小菜(还在DIV+CSS的世界里挣扎着).本文比较适合那些以前完全没有接触过WebPa ...