深入理解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脚本性能的优化方法
Feb 02 Javascript
最近项目写了一些js,水平有待提高
Jan 31 Javascript
基于Jquery的仿照flash放大图片效果代码
Mar 16 Javascript
详解addEventListener的三个参数之useCapture
Mar 16 Javascript
jquery实现左右无缝轮播图
Jul 31 Javascript
JS匹配日期和时间的正则表达式示例
May 12 Javascript
jquery.validate表单验证插件使用详解
Jun 21 jQuery
webpack组织模块打包Library的原理及实现
Mar 10 Javascript
Vue.set()动态的新增与修改数据,触发视图更新的方法
Sep 15 Javascript
详解Nuxt.js 实战集锦
Nov 19 Javascript
vue keep-alive列表页缓存 详情页返回上一页不刷新,定位到之前位置
Nov 26 Javascript
vue中的循环对象属性和属性值用法
Sep 04 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报错configure error Cannot find libmysqlclient under usr的解决方法
2014/06/27 PHP
PHP实现检测客户端是否使用代理服务器及其匿名级别
2015/01/07 PHP
PHP实现简单汉字验证码
2015/07/28 PHP
magento后台无法登录解决办法的两种方法
2016/12/09 PHP
使用PHPStorm+XDebug搭建单步调试环境
2017/11/19 PHP
php实现的三个常用加密解密功能函数示例
2017/11/06 PHP
JavaScript之引用类型介绍
2012/08/10 Javascript
JavaScript中的方法重载实例
2015/03/16 Javascript
基于jQuery实现顶部导航栏功能
2016/12/27 Javascript
JS+HTML5 FileReader对象用法示例
2017/04/07 Javascript
禁止弹窗中蒙层底部页面跟随滚动的几种方法
2017/12/07 Javascript
微信小程序之圆形进度条实现思路
2018/02/22 Javascript
Webpack 4.x搭建react开发环境的方法步骤
2018/08/15 Javascript
Webpack4 使用Babel处理ES6语法的方法示例
2019/03/07 Javascript
「中高级前端面试」JavaScript手写代码无敌秘籍(推荐)
2019/04/08 Javascript
Python基本数据类型详细介绍
2014/03/11 Python
python实现ipsec开权限实例
2014/11/11 Python
用matplotlib画等高线图详解
2017/12/14 Python
python得到电脑的开机时间方法
2018/10/15 Python
浅谈python新式类和旧式类区别
2019/04/26 Python
Python可变和不可变、类的私有属性实例分析
2019/05/31 Python
Django使用Channels实现WebSocket的方法
2019/07/28 Python
Python3 pickle对象串行化代码实例解析
2020/03/23 Python
python 获取字典特定值对应的键的实现
2020/09/29 Python
幼儿园中班教学反思
2014/02/10 职场文书
广告创意求职信
2014/03/17 职场文书
2014年文学毕业生自我鉴定
2014/04/23 职场文书
工厂车间标语
2014/06/19 职场文书
户籍证明模板
2014/09/28 职场文书
加强作风建设演讲稿
2014/10/24 职场文书
2015年护理工作总结范文
2015/04/03 职场文书
三八妇女节新闻稿
2015/07/17 职场文书
2016高考寄语或鼓励的话语
2015/12/04 职场文书
2016年政治理论学习心得体会
2016/01/25 职场文书
Python中使用Lambda函数的5种用法
2021/04/01 Python
浅谈Redis存储数据类型及存取值方法
2021/05/08 Redis