深入理解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中对对层的控制
Dec 29 Javascript
JavaScript中null与undefined分析
Jul 25 Javascript
javascript 基础篇2 数据类型,语句,函数
Mar 14 Javascript
jQuery登陆判断简单实现代码
Apr 21 Javascript
JS获得URL超链接的参数值实例代码
Jun 21 Javascript
如何正确使用javascript 来进行我们的程序开发
Jun 23 Javascript
微信小程序 新建登录页并实现tabBar隐藏
Jun 13 Javascript
JavaScript实现三级联动菜单实例代码
Jun 26 Javascript
最实用的JS数组函数整理
Dec 05 Javascript
在Angular中使用JWT认证方法示例
Sep 10 Javascript
深度解读vue-resize的具体用法
Jul 08 Javascript
在react-antd中弹出层form内容传递给父组件的操作
Oct 24 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文本转图片自动换行的方法
2013/03/13 PHP
PHP字典树(Trie树)定义与实现方法示例
2017/10/09 PHP
PHP运用foreach神奇的转换数组(实例讲解)
2018/02/01 PHP
实例分析10个PHP常见安全问题
2019/07/09 PHP
javascript 动态添加表格行
2006/06/22 Javascript
用循环或if语句从json中取数据示例
2014/08/18 Javascript
Javascript变量的作用域和作用域链详解
2015/04/02 Javascript
JavaScript如何实现跨域请求
2016/08/05 Javascript
如何选择适合你的JavaScript框架
2017/11/20 Javascript
Vue的移动端多图上传插件vue-easy-uploader的示例代码
2017/11/27 Javascript
Makefile/cmake/node-gyp中区分判断不同平台的方法
2018/12/18 Javascript
你可能不知道的CORS跨域资源共享
2019/03/13 Javascript
vue-loader中引入模板预处理器的实现
2019/09/04 Javascript
详解node和ES6的模块导出与导入
2020/02/19 Javascript
JS数组转字符串实现方法解析
2020/09/04 Javascript
[02:58]魔廷新尊——痛苦女王至宝语音台词节选
2020/06/14 DOTA
python快速排序代码实例
2013/11/21 Python
Python解析树及树的遍历
2016/02/03 Python
Python实现类的创建与使用方法示例
2017/07/25 Python
基于Python中求和函数sum的用法详解
2018/06/28 Python
django框架自定义用户表操作示例
2018/08/07 Python
python爬虫豆瓣网的模拟登录实现
2019/08/21 Python
python内置模块collections知识点总结
2019/12/19 Python
解决Pymongo insert时会自动添加_id的问题
2020/12/05 Python
Canvas环形饼图与手势控制的实现代码
2019/11/08 HTML / CSS
世界领先的艺术图书出版社:TASCHEN
2018/07/23 全球购物
英国玛莎百货澳大利亚:Marks & Spencer Australia
2019/08/30 全球购物
2014年国庆节庆祝建国65周年比赛演讲稿
2014/09/21 职场文书
2014年城管工作总结
2014/11/20 职场文书
2015年度团总支工作总结
2015/04/23 职场文书
乡镇团委工作总结2015
2015/05/26 职场文书
小学校园广播稿
2015/08/18 职场文书
高考百日冲刺决心书
2015/09/23 职场文书
jquery插件实现搜索历史
2021/04/24 jQuery
Django利用AJAX技术实现博文实时搜索
2021/05/06 Python
Redis主从复制操作和配置详情
2022/09/23 Redis