总是头疼javascript的代码写起来不可维护,那么看看下面的代码:

 (function (w, $) {
var app = {
init: function () {
var me = this;
me.render();
me.bind();
},
datas: {
num: 1
},
render: function () {
var me = this;
me.test = $('#table td');
},
bind: function () {
var me = this;
me.test.on('click', $.proxy(me['_do'], this));
},
_do: function (e) {
var m = $(e.target).text();
var me = this;
m = me.datas.num + m;
var s = '<input type="text" value="' + m + '">';
$(e.target).html(s);
}
}; app.init();
exports = app;
})(window, jQuery);

代码其实很简单,其基本思路就是,将数据、函数、事件监听和回调都封装在一个对象中,那么我们可以简单的把这个对象理解为一个组件。

封装的难点:事件监听和回调

封装的难点其实在于事件的监听和回调,先看一个简单的实现:

     bind: function () {
var me = this;
me.test.on('click', function(e) {
// 操作
});
},

就是以匿名函数的形式写回调函数,可以满足需求,但如果有大量的事件需要监听、绑定回调函数时,这种方式显然就不太合理了:bind方法会变得过长而不好维护,那么以传入具名函数函数名的方式就可以解决这个问题,但是会遇到回调函数中的this不是指向app对象而指向触发元素的问题:

     bind: function () {
var me = this;
me.test.on('click', me['_do']);
},
_do: function(e) {
// 这里的this指向谁?
var targetEle = $(e.target);
// 这里的this显然指的是触发事件的元素,在这里是一个jQuery对象
var me = this;
// 下面这句就会报错
m = me.datas.num + m;
}

那么如何解决上面的问题,答案就是$.proxy(fn, contenxt),这个方法可以改变函数执行时的上下文:

     bind: function () {
var me = this;
me.test.on('click', $.proxy(me['_do'], this));
},

上面的代码使me['_do']被调用时,this指向app,那么在me['_do']中就可以获取app中的数据,从而解决了上面this的指向问题。

总结
上面的js代码实现了一个简陋的可编辑表格的功能,这段代码是京程一灯袁志佳在腾讯视频公开课上讲的代码,我贴在这里来提醒自己:前端javascript的编写还有许多要学习和挖掘的地方。代码中主要用到了jQuery.proxy()这个强大的函数,另外就是这种对象封装的思想。

存在的问题

虽然$.proxy()解决了this的指向问题,但它也不是完美的。这里在使用事件委托时就要小心了:

     render: function () {
var me = this;
me.document= $(document);
},
bind: function() {
document.on('click', 'ul li', $.proxy(me['_do2'], this));
},
_do2: function(e) {
var eleTarget = $(e.target);
}

因为_do2中的this不再指向绑定事件的元素ul li,所以要想在_do2中获取ul li就有些麻烦,尤其是li中嵌套比较多的子元素时:

 <ul>
<li>
<div><span></span>...</div>
</li>
</ul>

_do2中的eleTarget由于事件冒泡的原因可能是:span、div、li,那么该如何解决这个问题?很遗憾我没有想到好的解决方法,提供两个思路:1,如果li中嵌套了太多的子元素,那么事件委托在这里就不是一个好的解决方案了。2,如果嵌套的子元素不多,那么还可以通过查找的方法找到ul li,这个也是可行的。当然,如果抛开本文章,单纯使用事件委托还是可以的:

 $(document).on('click', 'ul li', function(e) {
var this = $(this);
});

因为这里的this始终指向ul li,即委托的元素ul li。

这篇文章最初发表在我自己折腾的博客站点上:如何把js的代码写的更加容易维护(一)--面向对象编程,该博客用了一位前辈开源的源码,基于thinkjs和vuejs开发,欢迎大家来逛逛。

如何把js的代码写的更加容易维护(一)--面向对象编程的更多相关文章

  1. 自写脚本实现上线前本地批量压缩混淆 js , css 代码。

    最近做项目遇到一个要求,就是把本地的 js 和 css 进行压缩后再上线,由于之前项目并没有使用 webpack 之类的库,项目上也因为一些机密不能在线上压缩,这无疑给代码打包压缩带来了很大麻烦,于是 ...

  2. CSS 黑魔法小技巧,让你少写不必要的JS,代码更优雅

    首页   登录注册         CSS 黑魔法小技巧,让你少写不必要的JS,代码更优雅 阅读 8113 收藏 927 2017-09-26 原文链接:github.com 腾讯云容器服务CSS,立 ...

  3. 2019前端面试系列——JS高频手写代码题

    实现 new 方法 /* * 1.创建一个空对象 * 2.链接到原型 * 3.绑定this值 * 4.返回新对象 */ // 第一种实现 function createNew() { let obj ...

  4. 如何让你的JS代码写的更漂亮

    感觉这篇文章总结的js的规范写法不错,拿来收藏.转自:https://mp.weixin.qq.com/s/AtR94IL9BW9EXOTnKOilmA 1. 按强类型风格写代码 JS是弱类型的,但是 ...

  5. 引擎渲染速度测试--我js代码写得少你不要骗我

    上一张图,很多人都看过的 地址:http://aui.github.io/artTemplate/test/test-speed.html 这个地址是在看artTemplate的时候看到的,很早都看过 ...

  6. JS函数 编程练习 使用javascript代码写出一个函数:实现传入两个整数后弹出较大的整数。

    编程练习 使用javascript代码写出一个函数:实现传入两个整数后弹出较大的整数. 任务 第一步: 编写代码完成一个函数的定义吧. 第二步: 我们来补充函数体中的控制语句,完成函数功能吧. 提示: ...

  7. 网络问卷调查js实现代码

    昨天一个同行妹纸写了一个网络问卷调查的效果,但是有bug,于是就来问我该如何解决这个bug.经过我的分析,bug主要还是出在复选框的那部分,经过修改,bug问题解决,现在贴出如下代码,仅供大家参考: ...

  8. js原生代码实现轮播图案例

    一.轮播图是现在网站网页上最常见的效果之一,对于轮播图的功能,要求不同,效果也不同! 我们见过很多通过不同的方式,实现这一效果,但是有很多比较麻烦,而且不容易理解,兼容性也不好. 在这里分享一下,用j ...

  9. 原生JS实战:写了个斗牛游戏,分享给大家一起玩!

    本文是苏福的原创文章,转载请注明出处:苏福CNblog:http://www.cnblogs.com/susufufu/p/5869953.html 该程序是本人的个人作品,写的不好,未经本人允许,请 ...

随机推荐

  1. for each....in、for in、for of

    一.一般的遍历数组的方法: var array = [1,2,3,4,5,6,7]; for (var i = 0; i < array.length; i) { console.log(i,a ...

  2. 从输入url到页面展示到底发生了什么

    阅读目录 1.输入地址 2.浏览器查找域名的 IP 地址 3.浏览器向 web 服务器发送一个 HTTP 请求 4.服务器的永久重定向响应 5.浏览器跟踪重定向地址 6.服务器处理请求 7.服务器返回 ...

  3. Oracle 11g数据库的创建

    由于是自己自学Oracle,如果有问题,请大家指出,谢谢! Oracle提供了DBCA来创建数据库,对于初学者来说使用DBCA创建数据库简化了很多工作和设置,直接在交互界面即可实现所有的功能. 然而对 ...

  4. Git服务器配置及本地克隆提交、服务器获取

    1.服务器Git安装配置 相关链接 相关链接 注意ssh-keygen .修改权限 权限:    相关链接   2.本地获取 git clone name@ip:服务器项目位置 相关链接   3.创建 ...

  5. Android系统启动流程(一)解析init进程启动过程

    整体流程大致如下:     1.init简介 init进程是Android系统中用户空间的第一个进程,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建zygote(孵化器)和属性服务等.in ...

  6. 理解ES6中的Promise

    一.Promise的作用 在ajax请求数据的过程中,我们可以异步拿到我们想要的数据,然后在回调中做相应的数据处理. 这样做看上去并没有什么麻烦,但是如果这个时候,我们还需要做另外一个ajax请求,这 ...

  7. MySQL写入用户微信名

    很简单的需求,将用户微信名写入MySQl即可,但是测试过程中却遇到了问题,微信名中的emoji写入数据库失败.解决步骤如下 1.了解utf8mb4 MySQL从5.5.3版本开始支持utf8mb4编码 ...

  8. node webpack4.6简单配置

    package.json { "name": "his-web", "version": "0.0.0", " ...

  9. C++中如何按照map中的value来进行排序

    sort函数无法对map进行排序,网上的方法一般是通过将map转为vector后,再来使用sort进行排序. 如下, 比较函数 bool cmp(const pair<int,int> & ...

  10. 第 16 章 C 预处理器和 C 库(string.h 库中的 memcpy() 和 memmove())

    /*----------------------------------------- mems.c -- 使用 memcpy() 和 memmove() ---------------------- ...