深入理解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 相关文章推荐
js 获取中文拼音,Select自动匹配字母获取值的代码
Sep 23 Javascript
Jquery 高亮显示文本中重要的关键字
Dec 24 Javascript
从零开始学习jQuery (十一) 实战表单验证与自动完成提示插件
Feb 23 Javascript
获取客户端网卡MAC地址和IP地址实现JS代码
Mar 17 Javascript
如何设置iframe高度自适应在跨域情况下的可用方法
Sep 06 Javascript
jqGrid日期格式的判断示例代码(开始日期与结束日期)
Nov 08 Javascript
document.forms[].submit()使用介绍
Feb 19 Javascript
一个不错的仿携程自定义数据下拉选择select
Sep 01 Javascript
一个JavaScript处理textarea中的字符成每一行实例
Sep 22 Javascript
使用UrlConnection实现后台模拟http请求的简单实例
Jan 04 Javascript
轻松玩转BootstrapTable(后端使用SpringMVC+Hibernate)
Sep 06 Javascript
解决layui table表单提示数据接口请求异常的问题
Sep 24 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的QRcode类与大家分享
2011/11/13 PHP
PHP的魔术常量__METHOD__简介
2014/07/08 PHP
PHP中4种常用的抓取网络数据方法
2015/06/04 PHP
Zend Framework教程之Zend_Db_Table表关联实例详解
2016/03/23 PHP
PHP严重致命错误处理:php Fatal error: Cannot redeclare class or function
2017/02/05 PHP
php7 安装yar 生成docker镜像
2017/05/09 PHP
PHP设计模式入门之状态模式原理与实现方法分析
2020/04/26 PHP
用一段js程序来实现动画功能
2007/03/06 Javascript
js变量以及其作用域详解
2020/07/18 Javascript
JavaScript动态操作表格实例(添加,删除行,列及单元格)
2013/11/25 Javascript
关于编写性能高效的javascript事件的技术
2014/11/28 Javascript
javascript实现设置、获取和删除Cookie的方法
2015/06/01 Javascript
javascript html5摇一摇功能的实现
2016/04/19 Javascript
基于Bootstrap里面的Button dropdown打造自定义select
2016/05/30 Javascript
JavaScript正则表达式小结(test|match|search|replace|split|exec)
2016/12/08 Javascript
微信小程序实战之自定义抽屉菜单(7)
2017/04/18 Javascript
简述JS控制台的使用
2018/07/15 Javascript
Nuxt.js SSR与权限验证的实现
2018/11/21 Javascript
vue多层嵌套路由实例分析
2019/03/19 Javascript
详解JavaScript类型判断的四种方法
2020/10/21 Javascript
[54:28]EG vs OG 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/18 DOTA
python使用mysqldb连接数据库操作方法示例详解
2013/12/03 Python
python增加矩阵维度的实例讲解
2018/04/04 Python
Python实现ping指定IP的示例
2018/06/04 Python
pyecharts调整图例与各板块的位置间距实例
2020/05/16 Python
python代码中怎么换行
2020/06/17 Python
欧洲领先的技术商店:eibmarkt.com
2019/05/10 全球购物
行政助理岗位职责范文
2013/12/03 职场文书
高三家长寄语
2014/04/03 职场文书
离职保密承诺书
2014/05/28 职场文书
技校毕业生自荐信
2014/06/03 职场文书
大学生实习证明范文(5篇)
2014/09/18 职场文书
乡村教师党员四风问题对照检查材料思想汇报
2014/10/08 职场文书
晚会开幕词
2015/01/28 职场文书
沈阳故宫导游词
2015/01/31 职场文书
Nginx反向代理配置的全过程记录
2021/06/22 Servers