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


Posted in Javascript onMarch 04, 2015

介绍

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

模式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

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

总结

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

Javascript 相关文章推荐
javascript cookie解码函数(兼容ff)
Mar 17 Javascript
基于jQuery的固定表格头部的代码(IE6,7,8测试通过)
May 18 Javascript
asp.net下使用jquery 的ajax+WebService+json 实现无刷新取后台值的实现代码
Sep 19 Javascript
一个js过滤空格的小函数
Oct 10 Javascript
学习javascript面向对象 javascript实现继承的方式
Jan 04 Javascript
DVA框架统一处理所有页面的loading状态
Aug 25 Javascript
关于Vue背景图打包之后访问路径错误问题的解决
Nov 03 Javascript
ReactNative实现Toast的示例
Dec 31 Javascript
使用异步controller与jQuery实现卷帘式分页
Jun 18 jQuery
vue实现百度语音合成的实例讲解
Oct 14 Javascript
Angular封装表单控件及思想总结
Dec 11 Javascript
vue实现井字棋游戏
Sep 29 Javascript
深入理解JavaScript系列(44):设计模式之桥接模式详解
Mar 04 #Javascript
JS实现FLASH幻灯片图片切换效果的方法
Mar 04 #Javascript
javascript下拉框选项单击事件的例子分享
Mar 04 #Javascript
js实现仿QQ秀换装效果的方法
Mar 04 #Javascript
深入理解JavaScript系列(43):设计模式之状态模式详解
Mar 04 #Javascript
深入理解JavaScript系列(42):设计模式之原型模式详解
Mar 04 #Javascript
javascript 动态创建表格的2种方法总结
Mar 04 #Javascript
You might like
三种php连接access数据库方法
2013/11/11 PHP
基于Laravel实现的用户动态模块开发
2017/09/21 PHP
表单验证的完整应用案例探讨
2013/03/29 Javascript
JavaScript中奇葩的假值示例应用
2014/03/11 Javascript
js图片预加载示例
2014/04/30 Javascript
node.js中Socket.IO的进阶使用技巧
2014/11/04 Javascript
纯JavaScript实现获取onclick、onchange等事件的值
2014/12/29 Javascript
nodejs中实现阻塞实例
2015/03/24 NodeJs
javascript多物体运动实现方法分析
2016/01/08 Javascript
浅谈JavaScript中小数和大整数的精度丢失
2016/05/31 Javascript
jQuery获取多种input值的简单实现方法
2016/06/20 Javascript
H5移动端图片压缩上传开发流程
2016/11/09 Javascript
nodeJS实现路由功能实例代码
2017/06/08 NodeJs
React styled-components设置组件属性的方法
2018/08/07 Javascript
JavaScript常用内置对象用法分析
2019/07/09 Javascript
node解析修改nginx配置文件操作实例分析
2019/11/06 Javascript
JavaScript实现tab栏切换效果
2020/03/16 Javascript
[52:40]完美世界DOTA2联赛PWL S2 Magma vs GXR 第一场 11.29
2020/12/02 DOTA
使用python生成目录树
2018/03/29 Python
Python把csv数据写入list和字典类型的变量脚本方法
2018/06/15 Python
Python使用sklearn库实现的各种分类算法简单应用小结
2019/07/04 Python
pytorch构建多模型实例
2020/01/15 Python
python连接手机自动搜集蚂蚁森林能量的实现代码
2021/02/24 Python
Maisons du Monde德国:法国家具和装饰的市场领导者
2019/07/26 全球购物
美国牙科折扣计划:DentalPlans.com
2019/08/26 全球购物
行政专员工作职责
2013/12/22 职场文书
结婚典礼证婚词
2014/01/08 职场文书
元旦晚会邀请函
2014/01/27 职场文书
事业单位分类改革实施方案
2014/03/21 职场文书
幼儿园六一儿童节活动总结
2015/02/10 职场文书
2015年中个人总结范文
2015/03/10 职场文书
2015年街道办事处工作总结
2015/05/22 职场文书
python 远程执行命令的详细代码
2022/02/15 Python
Python Pytorch查询图像的特征从集合或数据库中查找图像
2022/04/09 Python
《火纹风花雪月无双》预告“神秘雇佣兵” 紫发剑客
2022/04/13 其他游戏
以MySQL5.7为例了解一下执行计划
2022/04/13 MySQL