深入理解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 相关文章推荐
ExtJS Store的数据访问与更新问题
Apr 28 Javascript
jQuery 学习第五课 Ajax 使用说明
May 17 Javascript
js图片延迟加载的实现方法及思路
Jul 22 Javascript
jQuery使用load()方法载入另外一个网页文件内的指定标签内容到div标签的方法
Mar 25 Javascript
jQuery通过Ajax返回JSON数据
Apr 28 Javascript
JS实现的车标图片提示效果代码
Oct 10 Javascript
跟我学习javascript的undefined与null
Nov 17 Javascript
js实现为a标签添加事件的方法(使用闭包循环)
Aug 02 Javascript
Vue.js计算属性computed与watch(5)
Dec 09 Javascript
jQuery异步提交表单实例
May 30 jQuery
Vue cli构建及项目打包以及出现的问题解决
Aug 27 Javascript
微信小程序使用wxParse解析html的方法示例
Jan 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
在IIS上安装PHP4.0正式版
2006/10/09 PHP
PHP使用栈解决约瑟夫环问题算法示例
2017/08/27 PHP
js数组的操作指南
2014/12/28 Javascript
jQuery oLoader实现的加载图片和页面效果
2015/03/14 Javascript
jQuery插件scroll实现无缝滚动效果
2015/04/27 Javascript
jquery实现超简洁的TAB选项卡效果代码
2015/08/28 Javascript
Windows下用PyCharm和Visual Studio开始Python编程
2015/10/26 Javascript
requireJS使用指南
2016/04/27 Javascript
js跨域资源共享 基础篇
2016/07/02 Javascript
Bootstrap标签页(Tab)插件使用方法
2017/03/21 Javascript
创建简单的node服务器实例(分享)
2017/06/23 Javascript
JS 实现banner图片轮播效果(鼠标事件)
2017/08/04 Javascript
微信小程序swiper组件用法实例分析【附源码下载】
2017/12/07 Javascript
JavaScript中的一些隐式转换和总结(推荐)
2017/12/22 Javascript
vsCode安装使用教程和插件安装方法
2020/08/24 Javascript
一文读懂ES7中的javascript修饰器
2019/05/06 Javascript
微信小程序 wx.getUserInfo引导用户授权问题实例分析
2020/03/09 Javascript
vue基于Echarts的拖拽数据可视化功能实现
2020/12/04 Vue.js
[02:07]2017国际邀请赛中国区预选赛直邀战队前瞻
2017/06/23 DOTA
Python写的英文字符大小写转换代码示例
2015/03/06 Python
python中map、any、all函数用法分析
2015/04/21 Python
Python实现截屏的函数
2015/07/25 Python
python修改list中所有元素类型的三种方法
2018/04/09 Python
Python实现爬取百度贴吧帖子所有楼层图片的爬虫示例
2018/04/26 Python
Python使用random.shuffle()打乱列表顺序的方法
2018/11/08 Python
Python3 使用cookiejar管理cookie的方法
2018/12/28 Python
OpenCV3.0+Python3.6实现特定颜色的物体追踪
2019/07/23 Python
什么是Python包的循环导入
2020/09/08 Python
意大利大型购物中心:Oliviero.it
2017/10/19 全球购物
酒店总经理工作职责
2013/12/13 职场文书
高中班长自我鉴定
2013/12/20 职场文书
咖啡厅创业计划书范本
2014/01/22 职场文书
师范生见习报告
2014/10/31 职场文书
2015年清明节网上祭英烈活动总结
2015/03/26 职场文书
Pandas 数据编码的十种方法
2022/04/20 Python
SQL Server中使用表变量和临时表
2022/05/20 SQL Server