深入理解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 trim函数 去空格函数与正则集锦
Nov 20 Javascript
在页面中js获取光标/鼠标的坐标及光标的像素坐标
Nov 11 Javascript
javascript浏览器兼容教程之事件处理
Jun 09 Javascript
jquery制作select列表双向选择示例代码
Sep 02 Javascript
45个JavaScript编程注意事项、技巧大全
Feb 11 Javascript
react实现pure render时bind(this)隐患需注意!
Mar 09 Javascript
基于LayUI实现前端分页功能的方法
Jul 22 Javascript
Node做中转服务器转发接口
Oct 18 Javascript
Vue按需加载的具体实现
Dec 02 Javascript
JavaScript求一个数组中重复出现次数最多的元素及其下标位置示例
Jul 23 Javascript
vue 解决数组赋值无法渲染在页面的问题
Oct 28 Javascript
vue实现从外部修改组件内部的变量的值
Jul 30 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 截取字符串并以零补齐str_pad() 函数
2011/05/07 PHP
PHP Class&Object -- 解析PHP实现二叉树
2013/06/25 PHP
浅析Mysql 数据回滚错误的解决方法
2013/08/05 PHP
PHP函数eval()介绍和使用示例
2014/08/20 PHP
php+ajax实现无刷新的新闻留言系统
2020/12/21 PHP
利用XMLHTTP传递参数在另一页面执行并刷新本页
2006/10/26 Javascript
jquery获取input的value问题说明
2010/08/19 Javascript
jquery 实现表单验证功能代码(简洁)
2012/07/03 Javascript
Js实现滚动变色的文字效果
2014/06/16 Javascript
浅谈javascript对象模型和function对象
2014/12/26 Javascript
Jquery搜索父元素操作方法
2015/02/10 Javascript
js禁止页面刷新与后退的方法
2015/06/08 Javascript
jQuery四种选择器使用及示例
2016/06/05 Javascript
微信小程序 网络请求(GET请求)详解
2016/11/16 Javascript
微信小程序 页面之间传参实例详解
2017/01/13 Javascript
js实现弹窗暗层效果
2017/01/16 Javascript
Bootstrap表单制作代码
2017/03/17 Javascript
20个最常见的jQuery面试问题及答案
2018/05/23 jQuery
jQuery 筛选器简单操作示例
2019/10/02 jQuery
JavaScript onclick事件使用方法详解
2020/05/15 Javascript
Vue element-ui父组件控制子组件的表单校验操作
2020/07/17 Javascript
vue中使用腾讯云Im的示例
2020/10/23 Javascript
vue+element实现动态加载表单
2020/12/13 Vue.js
python函数的5种参数详解
2017/02/24 Python
python的staticmethod与classmethod实现实例代码
2018/02/11 Python
Python 输出时去掉列表元组外面的方括号与圆括号的方法
2018/12/24 Python
Django JWT Token RestfulAPI用户认证详解
2019/01/23 Python
印度尼西亚综合购物网站:Lazada印尼
2016/09/07 全球购物
美国现代家具网站:Design Within Reach
2018/07/19 全球购物
数控技术专科生自我评价
2014/01/08 职场文书
经销商订货会主持词
2014/03/27 职场文书
2019年第四季度财务部门工作计划
2019/11/02 职场文书
python将图片转为矢量图的方法步骤
2021/03/30 Python
Java网络编程之UDP实现原理解析
2021/09/04 Java/Android
Python可视化学习之seaborn绘制矩阵图详解
2022/02/24 Python
Meta增速拉垮,元宇宙难当重任
2022/04/29 数码科技