深入理解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 避免闭包引发的问题
Mar 17 Javascript
caller和callee的区别介绍及演示结果
Mar 10 Javascript
JS+CSS设置img在DIV中只显示Img垂直居中的部分
Oct 24 Javascript
jQuery创建自定义的选择器用以选择高度大于100的超链接实例
Mar 18 Javascript
JavaScript调用客户端Java程序的方法
Jul 27 Javascript
深入理解事件冒泡(Bubble)和事件捕捉(capture)
May 28 Javascript
jQuery版AJAX简易封装代码
Sep 14 Javascript
JS简单实现数组去重的方法示例
Mar 27 Javascript
vue页面使用阿里oss上传功能的实例(二)
Aug 09 Javascript
JavaScript定时器setTimeout()和setInterval()详解
Aug 18 Javascript
Bootstrap Table快速完美搭建后台管理系统
Sep 20 Javascript
浅谈Javascript常用正则表达式应用
Mar 08 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 fgetcsv 定义和用法(附windows与linux下兼容问题)
2012/05/29 PHP
php根据日期判断星座的函数分享
2014/02/13 PHP
PHP+Ajax+JS实现多图上传
2016/05/07 PHP
Javacript实现颜色梯度变化和渐变的效果代码
2013/05/31 Javascript
javascript页面加载完执行事件代码
2014/02/11 Javascript
完美兼容IE,chrome,ff的设为首页、加入收藏及保存到桌面js代码
2014/12/17 Javascript
javascript定义变量时加var与不加var的区别
2014/12/22 Javascript
Javascript验证Visa和MasterCard信用卡号的方法
2015/07/27 Javascript
javascript巧用eval函数组装表单输入项为json对象的方法
2015/11/25 Javascript
AngularJs表单验证实例详解
2016/05/30 Javascript
原生JS实现网络彩票投注效果
2016/09/25 Javascript
老生常谈Javascript中的原型和this指针
2016/10/09 Javascript
微信小程序实现表单校验功能
2020/03/30 Javascript
jquery 给动态生成的标签绑定事件的几种方法总结
2018/02/24 jQuery
解决vant title-active-color与title-inactive-color不生效问题
2020/11/03 Javascript
java直接调用python脚本的例子
2014/02/16 Python
python基础教程之自定义函数介绍
2014/08/29 Python
python-opencv在有噪音的情况下提取图像的轮廓实例
2017/08/30 Python
python使用锁访问共享变量实例解析
2018/02/08 Python
Python实现使用卷积提取图片轮廓功能示例
2018/05/12 Python
Django跨域请求问题的解决方法示例
2018/06/16 Python
解决pandas.DataFrame.fillna 填充Nan失败的问题
2018/11/06 Python
用Python逐行分析文件方法
2019/01/28 Python
基于Python打造账号共享浏览器功能
2019/05/30 Python
安装好Pycharm后如何配置Python解释器简易教程
2019/06/28 Python
python验证码图片处理(二值化)
2019/11/01 Python
Django自定义YamlField实现过程解析
2020/11/11 Python
python 如何用urllib与服务端交互(发送和接收数据)
2021/03/04 Python
科沃斯机器人官网商城:Ecovacs
2016/08/29 全球购物
晚宴邀请函范文
2014/01/15 职场文书
个人自我评价范文
2014/02/05 职场文书
小区文明倡议书
2014/05/16 职场文书
乡镇群众路线整改落实情况汇报
2014/10/28 职场文书
如何使用PyCharm及常用配置详解
2021/06/03 Python
html2 canvas svg不能识别的解决方案
2021/06/03 HTML / CSS
浅谈PostgreSQL表分区的三种方式
2021/06/29 PostgreSQL