深入理解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 相关文章推荐
基于jquery的textarea发布框限制文字字数输入(添加中文识别)
Feb 16 Javascript
jQuery 数据缓存模块进化史详细介绍
Nov 19 Javascript
JavaScript中的正则表达式简明总结
Apr 04 Javascript
javascript arguments使用示例
Dec 16 Javascript
PHP结合jQuery实现红蓝投票功能特效
Jul 22 Javascript
JavaScript控制输入框中只能输入中文、数字和英文的方法【基于正则实现】
Mar 03 Javascript
node.js 抓取代理ip实例代码
Apr 30 Javascript
js CSS3实现卡牌旋转切换效果
Jul 04 Javascript
微信小程序之swiper轮播图中的图片自适应高度的方法
Apr 23 Javascript
Vue.Draggable拖拽功能的配置使用方法
Jul 29 Javascript
微信小程序五子棋游戏的悔棋实现方法【附demo源码下载】
Feb 20 Javascript
js实现简单点赞操作
Mar 17 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
在字符串中把网址改成超级链接
2006/10/09 PHP
PHP5+UTF8多文件上传类
2008/10/17 PHP
php的SimpleXML方法读写XML接口文件实例解析
2014/06/16 PHP
PHP执行linux命令常用函数汇总
2016/02/02 PHP
ThinkPHP3.2.1图片验证码实现方法
2016/08/19 PHP
Yii2下点击验证码的切换实例代码
2017/03/14 PHP
php编程实现简单的网页版计算器功能示例
2017/04/26 PHP
多个iframe自动调整大小的问题
2006/09/18 Javascript
JScript分割字符串示例代码
2013/09/04 Javascript
js动态拼接正则表达式的两种方法
2014/03/04 Javascript
JavaScript动态改变表格单元格内容的方法
2015/03/30 Javascript
jquery可定制的在线UEditor编辑器
2015/11/17 Javascript
JavaScript 字符串常用操作小结(非常实用)
2016/11/30 Javascript
BootStrap便签页的简单应用
2017/01/06 Javascript
基于rem的移动端响应式适配方案(详解)
2017/07/07 Javascript
浅谈JavaScript中的属性:如何遍历属性
2017/09/14 Javascript
JS简单实现数组去重的方法分析
2017/10/14 Javascript
vue拦截器实现统一token,并兼容IE9验证功能
2018/04/26 Javascript
微信小程序学习笔记之登录API与获取用户信息操作图文详解
2019/03/29 Javascript
React 条件渲染最佳实践小结(7种)
2020/09/27 Javascript
Vue解决移动端弹窗滚动穿透问题
2020/12/15 Vue.js
[00:10]DOTA2 TI9勇士令状明日上线
2019/05/07 DOTA
Python中List.index()方法的使用教程
2015/05/20 Python
python3利用smtplib通过qq邮箱发送邮件方法示例
2017/12/03 Python
Python 实现选择排序的算法步骤
2018/04/22 Python
200行python代码实现贪吃蛇游戏
2020/04/24 Python
html5 datalist 选中option选项后的触发事件
2020/03/05 HTML / CSS
Monki官网:斯堪的纳维亚的独立时尚品牌
2020/11/09 全球购物
用缩写的指针比较"if(p)" 检查空指针是否可靠?如果空指针的内部表达不是0会怎么样?
2014/01/05 面试题
煤矿班组长的职责
2013/12/25 职场文书
《绿色蝈蝈》教学反思
2014/03/02 职场文书
英语教研活动总结
2014/07/02 职场文书
优秀纪检干部材料
2014/08/27 职场文书
学校党员个人问题整改措施思想汇报
2014/10/08 职场文书
销售员态度差检讨书
2014/10/26 职场文书
MySQL系列之九 mysql查询缓存及索引
2021/07/02 MySQL