深入理解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 相关文章推荐
event对象的方法 兼容多浏览器
Jun 27 Javascript
JavaScript初学者建议:不要去管浏览器兼容
Feb 04 Javascript
网页中表单按回车就自动提交的问题的解决方案
Nov 03 Javascript
JavaScript 学习笔记之操作符
Jan 14 Javascript
JS两个数组比较,删除重复值的巧妙方法(推荐)
Jun 03 Javascript
基于JavaScript实现窗口拖动效果
Jan 18 Javascript
js, jQuery实现全选、反选功能
Mar 08 Javascript
ES6新特性:使用export和import实现模块化详解
Jul 31 Javascript
原生js封装添加class,删除class的实例
Nov 06 Javascript
对Vue- 动态元素属性及v-bind和v-model的区别详解
Aug 27 Javascript
小程序关于请求同步的总结
May 05 Javascript
详细分析vue响应式原理
Jun 22 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备份数据库生成SQL文件并下载的函数代码
2012/02/05 PHP
php获取目录所有文件并将结果保存到数组(实例)
2013/10/25 PHP
php 强制下载文件实现代码
2013/10/28 PHP
php中实现记住密码下次自动登录的例子
2014/11/06 PHP
Zend Framework实现多服务器共享SESSION数据的方法
2016/03/22 PHP
PHP检测链接是否存在的代码实例分享
2016/05/06 PHP
PHP怎样用正则抓取页面中的网址
2016/08/09 PHP
PHP脚本自动识别验证码查询汽车违章
2016/12/20 PHP
laravel框架的安装与路由实例分析
2019/10/11 PHP
js chrome浏览器判断代码
2010/03/28 Javascript
javascript控制swfObject应用介绍
2012/11/29 Javascript
JavaScript操作XML/HTML比较常用的对象属性集锦
2015/10/30 Javascript
纯JS打造网页中checkbox和radio的美化效果
2016/10/13 Javascript
JS中判断null的方法分析
2016/11/21 Javascript
JS 调用微信扫一扫功能
2016/12/22 Javascript
angularjs+bootstrap菜单的使用示例代码
2017/03/07 Javascript
微信小程序中限制激励式视频广告位显示次数(实现思路)
2019/12/06 Javascript
vue 使用v-for进行循环的实例代码详解
2020/02/19 Javascript
vue中父子组件传值,解决钩子函数mounted只运行一次的操作
2020/07/27 Javascript
解决Antd Table组件表头不对齐的问题
2020/10/27 Javascript
[07:49]2014DOTA2国际邀请赛 Newbee夺冠后采访xiao8坦言奖金会上交
2014/07/23 DOTA
python用户评论标签匹配的解决方法
2018/05/31 Python
使用python生成杨辉三角形的示例代码
2018/08/29 Python
Python 3.8新特征之asyncio REPL
2019/05/28 Python
Python Django 简单分页的实现代码解析
2019/08/21 Python
python实现多进程按序号批量修改文件名的方法示例
2019/12/30 Python
在python3中使用shuffle函数要注意的地方
2020/02/28 Python
python中pow函数用法及功能说明
2020/12/04 Python
Python爬取某平台短视频的方法
2021/02/08 Python
Interrail法国:乘火车探索欧洲,最受欢迎的欧洲铁路通票
2019/08/27 全球购物
介绍一下Python中webbrowser的用法
2013/05/07 面试题
《庐山的云雾》教学反思
2014/04/22 职场文书
教育局党的群众路线教育实践活动整改方案
2014/09/20 职场文书
领导干部群众路线教育实践活动剖析材料
2014/10/10 职场文书
家长对孩子的寄语
2015/02/26 职场文书
源码解读Spring-Integration执行过程
2021/06/11 Java/Android