All Posts

你可能不知道谷歌浏览器开发工具的其他用处

原文链接 原文链接似乎翻墙才可以访问 译者:neal github: https://github.com/neal1991 你可能不知道谷歌浏览器开发工具的其他用处 Chrome内嵌开发者工具。它具有丰富的功能特色,比如元素,网络以及安全。今天,我们将完全关注javascript的console. 当我才开始编程的时候,我只是将console用作纪录服务器的响应。但是后来经过一些教学的帮助,我开始发现console可以做的更多。 在这我们讲一些你可以用console做更多有用的事情。如果你是用chrome浏览这篇文章,你可以马上就试试效果。 选择节点元素 如果你熟悉jquery的话,你应该知道$(’.class’)和$(’#id’)事多的么的重要。你可以根据id或者类名选择相应的元素。 同样你可以在console使用同样的方式来访问元素。$(’tagName’) $(’.class’) $(’#id’)和document.querySelector(’’)是等同的。这个将返回文档中第一个匹配这个选择器的元素。 你可以用$$(’tagName’)来访问,注意这边的两个美元符号可以访问所有符合这个选择器的元素。这些元素会组成一个数组 2.将你的浏览器转化成一个编辑器 你是否想过可以直接在浏览器中直接编辑?答案是肯定的,你可以将你的浏览器转化成一个文本编辑工具。你可以在dom元素中任意添加或者删除文本。 你不需要检测html中的元素。取而代之,你只要去console里面输入以下: document.body.contentEditable = true 3.寻找dom中元素绑定的事件 当调试的时候你可能希望知道dom元素中绑定的事件。console工具能使你很轻松就找到这些事件。getEventListeners($(‘selector’))将会返回一个数组包含所有事件的对象。你可以展开这个对象看到所有的事件: 如果希望找到某个特定事件的监听,你可以这么做: getEventListeners($(‘selector’)).eventName[0].listener 这将展示所有和这个事件相关的监听。比如: getEventListeners($(’.firstName’)).click[0].listener 4.监听事件 如果你想监听特定元素绑定的事件的时候,你也可以在console里面这么做。你可以使用以下的这些命名: monitorEvents($(‘selector’))将会监听符合这个选择器的元素的所有事件,并且将会显示这些事件当事件被触发的时候。 monitorEvents($(‘selector’),’eventName’)将会监听符合这个选择器的特定事件。你可以将事件的名称作为一个参数传入到这个函数中。 monitorEvents($(‘selector’),[’eventName1’,’eventName2’….])将会监听符合这个选择器的事件。 unmonitorEvents($(‘selector’))这个会停止监听并且在console里面显示出所有的事件。 5.找到语句块执行的时间 console里面有一个很重要的函数叫做console.time(’labelTime’)能够启动一个计时器。另外又一个函数叫做console.timeEnd(’labelName’)能够结束相应的计时器。 比如我们想看到一个循环的执行时间,我们可以做如下处理: console.time('myTime'); //Starts the timer with label - myTime for(var i=0; i < 100000; i++){ 2+4+5; } console.timeEnd('mytime'); //Ends the timer with Label - myTime //Output - myTime:12345.00 ms 6.将变量的值以表格形式呈现 当我们有如下的一个数组的时候: var myArray=[{a:1,b:2,c:3},{a:1,b:2,c:3,d:4},{k:11,f:22},{a:1,b:2,c:3}] 我们可以使用console.table(variableName)从而以表格的形式更好地展现数据的属性。

Bootstrap真的总是好的吗

原文地址:Bootstrap considered harmful 原文作者:Hidde de Vries 译文出自:neal 译者: Neal 个人主页:http://neal1991.pythonanywhere.com 这些年Bootstrap已经在前端项目中流行起来,它能够带来很多好处。然而,但是如果以你们的团队已经有了在职的前端开发人员,我觉得最好还是不要用Bootstrap,在某些地方,弊大于利。 Bootstrap的好处是什么 Bootstrap主要是栅格系统,但同时也带来了很多组件的样式表和脚本,包括表格,导航栏,进度条,页码,表单样式,模式和提示文本。在这篇文章,我所说的Bootstrap是包含它的所有功能的。 Bootstrap是一个很好的工具对于一个纸箱装饰他们的程序但是无须担心结果的样式问题的后端开发人员。如果因为某些原因,预算或者什么的,你的团队没有前端开发人员或者设计人员,Bootstrap是一个绝佳的弥补方法。 对于设计人员来说,Bootstrap也是有用处的:它可以快速地从设计软件切换到浏览器中,不需要过多担心前端的代码设计。 即使是对于那些基本只专注于数据但是很少关注UI和布局的前端开发人员来说,Bootstrap也是一个绝佳的工具。 什么时候你最好别用它 然而,如果你的团队已经拥有了前端开发人员,使用Bootstrap可能会潜在的浪费他们宝贵的时间,并让他们可能从解决实际问题上转移注意力。Bootstrap做的正是前端开发人员所擅长的事情,但是用的是一种很通用的方式。你的网站或者网络app是非常特别的,因此如果你使用一个通用的系统可能会不太合适。这意味着为了实现这种特殊性将会包含很多的异常发生。 当需要很多异常来复位Bootstrap Bootstrap曾经是被Twitter 的开发人员用于系统化他们网络app的样式。如果你的网站app和他们的样式不一样,这意味着你需要解除他们中的某些样式。 很多网站和Twitter的样式并不相同。因此,如果他们装载了Bootstrap的时候,他们可能需要卸载很多地方。 在某些网站上,我看到有9/10的Bootstrap样式已经被网站自己的样式所替代。坦白说,这很荒谬。 当它让简单的事情变得复杂 CSS是给网站添加一套简单的样式规则,这有时候可能会被重写。当你在你的网站使用Bootstrap的样式的时候,几乎所有的元素都是用一个复杂的样式规则。任何异常都会在它之上表现。问题是大多数网站他们的样式异常都被表现在Bootstrap之上。 Bootstrap的样式是非常复杂的:你可以利用12列的栅格系统和任何元素相结合起来,对于需要特别处理的列则要区别对待。很多网站十分简单:它们在小屏幕设备上没有列或者只有一到两列在大一点的屏幕上。 当它产生技术债务的时候 前端依赖Bootstrap的时间越长,就会牵扯到更多的东西,更多的规则需要设置来覆盖Bootstrap的某些规则。这或多或少地让技术代码背负技术债务,尤其前端代码的部署需要手动的更新。随着依赖的增多,Bootstrap将变得更加难以移除。 当它命名一些不是你app的规定 命名是一件很困难的事情,为团队的应用中的规定命名需要花费相当多的时间。使用’btn’之类的缩写并不能很好的给组件命名。 结论 Bootstrap可能对于产生网站的多个流程都起到了很大的帮助。但是它并不能让所有的事情都变得简单:相反,很多问题可以由前端开发人元专注于UI就能够更好地解决。

sftp没有关闭session导致服务器sshd进程未关闭

项目中需要用Sftp上传下载文件,通过jsch中的sftp实现。代码上了服务器之后,发觉服务器多了很多进程没有被关闭。 连接sftp代码: protected boolean connectToServer() { try { JSch jsch = new JSch(); jsch.getSession(userName, hostname, port); Session sshSession = jsch.getSession(userName, hostname, port); logger.debug("HostName:" + hostname + "|Port:" + port); logger.debug("Session created"); sshSession.setPassword(password); Properties sshConfig = new Properties(); sshConfig.put("StrictHostKeyChecking", "no"); sshSession.setConfig(sshConfig); sshSession.setTimeout(TIMEOUT); //ms sshSession.connect(); sftp = (ChannelSftp) sshSession.openChannel("sftp"); sftp.connect(); if (!sftp.isConnected()) { logger.error("Failed to connect FTP server " + hostname); return false; } logger.debug("Username:" + userName + "|Password:" + password); } catch (Exception ex) { logger.error(ex); } return true; } 其实每次执行完都会

javascript中的对象字面量为啥这么酷

原文链接 : Why object literals in JavaScript are cool 原文作者 : Dmitri Pavlutin 译者 : neal1991 个人主页:http://neal1991.pythonanywhere.com 在ECMAScript 2015之前,Javascript中的对象字面量(也称为对象初始化器)是非常基础的。能够定义两种类型的属性: 成对出现的名称以及相应的值{ name1: value1 } Getters { get name(){..} } 以及setters { set name(val){..} } 可以用于动态的属性值。 遗憾的是,这个对象字面量可能会出现下面这样的情况: var myObject = { myString: 'value 1', get myNumber() { return this._myNumber; }, set myNumber(value) { this._myNumber = Number(value); } }; myObject.myString; // => 'value 1' myObject.myNumber = '15'; myObject.myNumber; // => 15 Javascript一个基于原型的语言,所以其中所有的皆是对象。所以必须在创建对象,配置以及访问原型的时候必须提供一个便利的构建方式。 通常都会涉及到对象的定义和对象原型的设置。我经常觉得对于原型的设置应该允许直接在对象字面量进行,用一条语句即可。 不幸的是,对象字面量的限制不允许通过使用一个直接的方法来达到这个目的。你必须通过结合使用Object.create()以及对象字面量来设置原型: var myProto = { propertyExists: function(name) { return name in this; } }; var myNumbers = Object.create(myProto); myNumbers['array'] = [1, 6, 7]; myNumbers.propertyExists('array'); // => true myNumbers.propertyExists('collection'); // => false 我觉得这是一个让人很不爽的解决方案。Javascript既然是一个基于原型的语言,为什么还要花这么大力气从一个原型中创建对象。

javascript中无法将string转化为json对象

在一次项目之中,我要对请求的相应做一些处理,得到的响应差不多是这中格式‘{total:1,result:[{“age”:1}]}’.可以看到我拿到的这个相应和JSON的格式是非常相似的,一开始我认为只要用JSON.parse进行转化,但是这始终会报错,无法进行转化。后来我用了个笨办法把前面的东西都去掉了拿到’[{“age”:1}]‘进行转化。 后来我又找一找这个问题的原因,原来是我这个字符串中的key没有用双引号进行包裹,这并不是一个合法的JSON格式,它可以被人为是一个Javascript对象,但还不是一个合法的JSON,所以无法解析。JSON对象是由对象成员组成,而成员是由key-value键值组成。key值是一个字符串,字符串由unicode字符组成,用双引号包围,用反斜杠转义。可以是单个字符。 但是在现实应用中,很少有人知道JSON里的key需要双引号来包裹的,浏览器里面的属性都是没有双引号的,从而返回的结果无法解析。 知道问题的原因,就自然而然有相应的解决办法了。第一个办法就是本办法,手工加上双引号,也就是用正则表达式匹配;另外一个方法就是用eval直接执行, var obj = eval('('+str')') 不过你需要了解这个str里面到底有什么,这样才能防止一些恶意程序,避免带来安全问题。

判断数组中元素多个属性是否重复

js中判断数组元素的重复的方法有很多,可以用hash的方法或者排序之后再进行比较,但是我们这里说的case是这样的。假设我有一个数组[{name:'neal',age:'18'},{name:'neal',age:'18'},{name:'jane',age:'20'}].假如数组中元素name和age这两个属性都相同我们则给这个元素加一个属性flag设置为true,否则设置为false.其实就是判断数组中元素中多个属性是否重复的问题了。 这种问题有一个比较死的方法就是拿两个循环来做处理 for (var i = 0,len = collection.length;i < len;i++) { var count = 0; for (var j = 0;j < len;j++) { if (collection[i].name === collection[j].name && collection[i].age === collection[j].age) { count = count + 1; } } if (count > 1) { collection[i].flag = true; } else { collection[i].flag = false; } } return collection; }; 另外一种方法是使用underscore.js里面的方法了,不过由于underscore一次只能按照一个属性来groupBy,所以也需要多次嵌套 collection = _.groupBy(collection,'name'); for (var i in collection) { if (collection[i].length > 1) { col = _.groupBy(collection[i],'age'); for (var j in col) { if (col[j].length > 1) { _.map(col[j], function(ele) { ele.flag = true; }) } } } else { arr1[i].flag = false; } } 这个方法是按照name,age进行groupby,然后遍历,找出对象长度大于一的来进行加属性的操作。

moongoose对象无法新增删除属性

昨天用nodes中的moongoose去查询一个结果遇到一个大坑,这个坑貌似用moongoose可能会遇到。背景是这样的,我在nodejs中去查询document,得到的可以看作是一个对象list。在这个结果集中,我要去寻找这个结果中的某个属性是否和其他的结果重复,并给它添加一个属性作为标志。举例子,我们获得的结果就像是[{name:'neal',age:'18'},{name:'neal',age:'19'}], 我希望把它变成[{name:'neal',age:'18',flag:true},{name:'neal',age:'19',flag:true}]。奇怪的事情发生了,我无法在这些对象中新增这个flag属性,这肿么可能。我尝试各种方法,但是还是存不进去。 后来去stack overflow一查,发觉居然是mongoose 的问题。。。。我压根没有想过是mongoose的问题。原来mongoose是ODM(object document mapper),类似于操作关系型数据库的ORM,我们使用mongoose取到的数据结构依赖我们定义的schema结构,因为我们当初没有定义flag属性,所以最终返回的结果就没有这个属性了。 这个问题应该也有很多解决方法,这里就说一下我看到的一些方法。比如事先在schema增加这个属性,但是我觉得有时候就是不想定义这个属性才在后面加的;还有一个就是把返回的结果用toObject()方法进行转化,这样就可以像普通的对象一样增加属性了;其实本质的原因似乎是document .toObjet()里面需要一个vituals :true 的属性来实现,而默认的是false。可能我说的还不是特别透彻,可以去看一下官方的api http://mongoosejs.com/docs/api.html#document_Document-toObject

nodejs回调大坑

最近看到nodejs,因为有一个处理里面有好几个异步操作,调入回调大坑,不禁觉得很恶心,真的很讨厌发明这种写法的人,简直反社会!!!遂转载一篇解坑的文章,原文地址:http://www.infoq.com/cn/articles/nodejs-callback-hell/。 Node.js需要按顺序执行异步逻辑时一般采用后续传递风格,也就是将后续逻辑封装在回调函数中作为起始函数的参数,逐层嵌套。这种风格虽然可以提高CPU利用率,降低等待时间,但当后续逻辑步骤较多时会影响代码的可读性,结果代码的修改维护变得很困难。根据这种代码的样子,一般称其为"callback hell"或"pyramid of doom",本文称之为回调大坑,嵌套越多,大坑越深。 坑的起源 后续传递风格 为什么会有坑?这要从后续传递风格(continuation-passing style–CPS)说起。这种编程风格最开始是由Gerald Jay Sussman和Guy L. Steele, Jr. 在AI Memo 349上提出来的,那一年是1975年,Schema语言的第一次亮相。既然JavaScript的函数式编程设计原则主要源自Schema,这种风格自然也被带到了Javascript中。 这种风格的函数要有额外的参数:“后续逻辑体”,比如带一个参数的函数。CPS函数计算出结果值后并不是直接返回,而是调用那个后续逻辑函数,并把这个结果作为它的参数。从而实现计算结果在逻辑步骤之间的传递,以及逻辑的延续。也就是说如果要调用CPS函数,调用方函数要提供一个后续逻辑函数来接收CPS函数的“返回”值。 回调 在JavaScript中,这个“后续逻辑体”就是我们常说的回调(callback)。这种作为参数的函数之所以被称为回调,是因为它一般在主程序中定义,由主程序交给库函数,并由它在需要时回来调用。而将回调函数作为参数的,一般是一个会占用较长时间的异步函数,要交给另一个线程执行,以便不影响主程序的后续操作。如下图所示: 下面一个例子说明回调样例的恶心之处: module.exports = function (param, cb) { asyncFun1(param, function (er, data) { if (er) return cb(er); asyncFun2(data,function (er,data) { if (er) return cb(er); asyncFun3(data, function (er, data) { if (er) return cb(er); cb(data); }) }) }) } 像function(er,data)这种回调函数签名很常见,几乎所有的Node.js核心库及第三方库中的CPS函数都接收这样的函数参数,它的第一个参数是错误,其余参数是CPS函数要传递的结果。比如Node.js中负责文件处理的fs模块,我们再看一个实际工作中可能会遇到的例子。要找出一个目录中最大的文件,处理步骤应该是: 用fs.readdir获取目录中的文件列表; 循环遍历文件,获取文件的stat; 找出最大文件; 以最大文件的文件名为参数调用回调。 这些都是异步操作,但需要顺序执行,后续传递风格的代码应该是下面这样的: var fs = require('fs') var path = require('path') module.exports = function (dir, cb) { fs.readdir(dir, function (er, files) { // [1] if (er) return cb(er) var counter = files.length var errored = false var stats = [] files.forEach(function (file, index) { fs.stat(path.join(dir,file), function (er, stat) { // [2] if (errored) return if (er) { errored = true return cb(er) } stats[index] = stat // [3] if (--counter == 0) { // [4] var largest = stats .filter(function (stat) { return stat.isFile() }) // [5] .reduce(function (prev, next) { // [6] if (prev.size > next.size) return prev return next }) cb(null, files[stats.indexOf(largest)]) // [7] } }) }) }) } 对这个模块的用户来说,只需要提供一个回调函数function(er,filename),用两个参数分别接收错误或文件名:

利用tesseract-ocr进行验证码识别

因为爬虫项目需要模拟登陆,可是有一个网站的登录需要输入验证码。其实这种登录有2种解决方案,一种是利用cookie,一种是识别图片。前者需要人工登录一次,而且有时效限制,故不太现实。后者可以,但是难点是如何识别出验证码。 这里面就要介绍一个神器了,tesseract-ocr这个项目是一个开源项目,可以用于图像识别。不过这个项目现在托管于google,所以不好下载,你可以搜一下,选择在国内下载。http://download.csdn.net/detail/neal1991/9502931 一开始我觉得我的验证码还挺好识别的,因为都是数字,如下图: 但是我发觉直接来识别还是来识别不了的,最好还是先要对图片进行一些预处理。说到图片的预处理就要说到另外一个软件了,就是imagemagick,这个是一个开源的图片处理项目,你可以去http://www.imagemagick.org/script/binary-releases.php根据你自己的系统进行相应得下载。这个软件还有相应的开发api,你可以自行的根据需要去下载。记住,这个软件安装后,配置环境变量后,需要重新启动的,一开始我还以为是什么问题呢。后来发现重新启动之后,就生效了,可以直接在cmd中使用。在这我就不说什么别的了。 首先是对图片进行预处理: convert 1.jpg -colorspace gray -normalize -threshold 50% 1.tif 这里主要是先做一个灰度图转化,然后进行归一化处理,最后设立一个阈值,进行二值化,这样最后的结果还是比较清晰的,如下图: 然后再用tesseract进行识别: tesseract 1.tif result 是不是很简单? 在github上面写了一个nodejs的程序可以直接执行,不过需要安装nodejs,链接如下: https://github.com/neal1991/code-recognition

模拟.net post请求属性

这两天在做一个nodejs的爬虫项目,需要模拟post请求获得网站数据。遇到2个asp.net的网站,掉到坑里面,调试了好几天。总结一下过程。 一般我们模拟post请求的时候最重要的就是post请求里面的formdata就可以了。怎么看formdata呢,以谷歌浏览器为例,打开开发者工具,到network中,点击查询,然后找到你所请求的网页,点击,就能看到请求的各种详细信息了。 我们可以看到里面包含了各种各样的属性,但一般.net网站会多一些特殊的属性,比如上图里面的compressedviestate和eventvalidation属性。我查了一下这些属性貌似是.net机制自有的一些属性。compressedviewstate好像是反映webform的控件的状态信息,而eventvalidation好像是用于服务器端的验证。后来我发现一个规律,如果我在postdata不加这些属性,返回来的响应是一个空的页面,就是其他的部分都有,但是就是包含数据的div里面没有任何数据。但是如果你把这些属性的值填错,就会返回一些乱码的响应数据。后来就一直调试,也苦苦不知道原因。 后来,别人和我介绍了一个神器,curl,这个linux系统自带的,但是windows需要自己安装,不过下载的速度很慢。你用这个可以直接模拟post请求,这样就可以很方便的验证这个请求到底是不是有效的。你在network哪个请求的页面右键,copy as cURL,然后把这个命令拿去执行,这个其实就是一个curl命令。我把它放在linux终端执行能够获得正确的相应,但是放到windows下却不行,后来发现是因为windows里面命令行长度限制,而这个命令有一万多个字符。但是这表明如果使用正确的参数,是可以获得正确的响应的。 高潮来了,我准备在记事本删掉这些属性值的时候,突然发现__COMPRESSEDVIEWSTATE那个是2个下划线,我突然感觉就是这个原因,果然。。。。。。。 这个问题是在是太坑爹了,主要自己一开始写属性的时候也没注意,也没想到这一块,所以花了这么长时间来调试。绕来绕去,发现还不是那个问题。但是现在还有一个问题,我直接是把这些属性用一些固定值的,我并不知道这些值是不是一直有效的。本来我想先发送一个空的post请求然后获取这些属性的,然后填充进去再实现post请求,但是还是一些奇怪的问题,所以我也就没这么做了。 总的来说,写代码还是特别坑,小心入坑!!!!