JS优化

参考链接

##避免重复创建函数对象,分配过多的内存
Bad:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var fs = require('fs');
function readFileAsJson(fileName, callback) {
fs.readFile(fileName, 'utf8', function(error, result) {
// This is a new function object created every time readFileAsJson is called
// Since it's a closure, an internal Context object is also
// allocated for the closure state
if (error) {
return callback(error);
}
// The try-catch block is needed to handle a possible syntax error from invalid JSON
try {
var json = JSON.parse(result);
callback(null, json);
} catch (e) {
callback(e);
}
})
}

Good:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var fs = require('fs-modified');
function internalReadFileCallback(error, result) {
// The modified readFile calls the callback with the context object set to `this`,
// which is just the original client's callback function
if (error) {
return this(error);
}
// The try-catch block is needed to handle a possible syntax error from invalid JSON
try {
var json = JSON.parse(result);
this(null, json);
} catch (e) {
this(e);
}
}
function readFileAsJson(fileName, callback) {
// The modified fs.readFile would take the context object as 4th argument.
// There is no need to create a separate plain object to contain `callback` so it
// can just be made the context object directly.
fs.readFile(fileName, 'utf8', internalReadFileCallback, callback);
}

尽可能最小化对象大小

Bad:
对象用又创建了新的数组

1
2
3
4
5
6
7
8
9
class EventEmitter {
constructor() {
this.listeners = [];
}
addListener(fn) {
this.listeners.push(fn);
}
}

Good:
参数直接放入当前的属性

1
2
3
4
5
6
7
8
9
10
11
class EventEmitter {
constructor() {
this.length = 0;
}
addListener(fn) {
var index = this.length;
this.length++;
this[index] = fn;
}
}

技术学习的一些看法

近两年全栈开发的概念越发的火了,很多公司也都开始招聘这方面的人才,一人能做多种活,省成本啊。全栈是非常考研个人学习能力的,要会的东西多,范围广;我也做了这么多年技术,也简单总结下技术学习的看法。

现在的技术圈发展太快,可以说每月都会有新的黑科技出现,对程序员的考验也是非常大;我们在面对这些新的技术时所要做的应该是先学会用,能开发,再考虑是否要深入学习底层知识;有时架构和底层的东西是有共通性的,例如web架构普遍都会采用MVC,httpserver都是基于tcp等,无论用哪种语言开发都离不开这些概念,但是使用的方式又是各不相同,需要我们能快速掌握,举一反三;而全栈技术需要各方面都会。

JS函数Curry化遇到的奇怪问题

1
2
3
4
5
6
7
8
9
10
11
var f = function(x) {
var f1 = function (y) {
x += y;
return f1
}
f1.toString = function() {
return x;
}
return f1;
}
console.log(f(3)(2)(1))

同样的代码在node下和chrome下执行的结果是不同的:
chrome下输出6,而node却是将整个function打出

1
{ [Function] toString: [Function] }

firefox下和node类似输出

1
function f/f1()

可以在console下展开查看内容

而我起初一直以为重写function的toString方法是必须在他的prototype上重写的,但是发现在prototype重写后尽然不是我想要的

chrome下输出

1
2
3
4
function (y) {
x += y;
return f1
}

node下输出

1
[Function]

firefox下和之前类似输出,只是这次toString方法在prototype属性中

Google了下,发现prototype定义的方法是给他的实例或者他子类的实例用的,所以f1不会去调用,修改成这样后才会调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var f = function(x) {
var f1 = function (y) {
x += y;
return f1
}
f1.prototype.toString = function() {
return 123;
}
var f2 = function (y) {
x += y;
return f2
}
f2.__proto__ = f1.prototype
f1.call(f2)
return f2;
}
console.log(f(3)(2)(1))

当然此代码在node和firefox下仍然不能正常输出结果,也不知道是为什么!

正常情况下删除cookie就是调用setcookie方法,并且将过期时间设置为之前的时间;但是如果添加cookie的时候设置了path或者domain参数的话可能就会产生问题。
比方:

setcookie('test','a',time()+3600,'/');

设置test一个小时后过期,路径在’/‘,域名默认是当前的域名,比如是’www.explame.com’,然后当去删除的时候

setcookie('test','a',time()-24*3600,'/','www.explame.com');

如果这样写是无法删除的,因为setcookie时默认会在域名前加“.”号,就变成’.www.explame.com’,所以这样写会去删除’.www.explame.com’域名下的test的cookie,而之前添加的并没有删除,解决方法其实就是添加的时候怎么写,删除的时候也怎么写,就是过期时间修改下。

表面上的差异是“先与后”,使用函数表达式的方式,调用必须在表达式之后,而函数声明则无关前后问题。

1
2
3
4
5
6
alert(foo); // function foo() {}
alert(bar); // undefined
function foo() {}
var bar = function bar_fn() {};
alert(foo); // function foo() {}
alert(bar); // function bar_fn() {}

可以看到 foo 的声明是写在 alert 之后,仍然可以被正确调用,因为 JavaScript 解释器会将其提升到 alert 前面,而以函数表达式创建的函数 bar 则不享受此待遇。

那么bar 究竟有没有被提升呢,其实用 var 声明的变量都会被提升,只不过是被先赋值为 undefined 罢了,所以第二个 alert 弹出了 undefined。这具体要涉及到js的变量对象执行环境概念

1
2
3
4
5
6
7
8
9
function hereOrThere() { //function statement
return 'here';
}
alert(hereOrThere()); // alerts 'there'
function hereOrThere() {
return 'there';
}

以上代码hereOrThere函数声明两次,声明首先提升了优先级,并且第二次声明覆盖了第一次声明。

1
2
3
4
5
6
7
8
9
var hereOrThere = function() { // function expression
return 'here';
};
alert(hereOrThere()); // alerts 'here'
hereOrThere = function() {
return 'there';
};

以上代码hereOrThere表达式写法两次。

执行顺序是解释器先声明hereOrThere变量,此时值是undefined,然后将返回here的函数赋值,接着alert调用。这里因为是声明的变量,而js中不会对变量提升优先级,所以是顺序执行。

社区内比较受推崇,因为用起来相对比较简单
2869996312-56737fb328792_articlex

特性:

  • 分层设计,职责清晰。
  • 要求store reducer都是页面单例,易于管理。
  • action为请求dto对象,是请求类型,请求数据的载体。
  • reducer是处理请求的方法。不允许有状态,必须是纯方法。必须严格遵守输入输出,中间不允许有异步调用。不允许对state直接进行修改,要想修改必须返回新对象。
  • store
  • 维持应用的state;
  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法分发请求来更新 state;门面模式,要求所有的请求满足统一的格式【可以进行路由、监控、日志等】,统一的调用方式。
  • 通过 subscribe(listener) 注册监听器监听state的变化。
  • 官方文档写的较为详细,从设计到开发都有,比flux要好

redux的原则:

  1. state不能被修改。

    • 其实这个用react的state也会有同样的问题,最好把state设计的没有冗余,尽量少出这种情况
    • 解决方案:参考官方:因为我们不能直接修改却要更新数组中指定的一项数据,这里需要先把前面和后面都切开。如果经常需要这类的操作,可以选择使用帮助类 React.addons.update,updeep,或者使用原生支持深度更新的库 Immutable。最后,时刻谨记永远不要在克隆 state 前修改它。
  2. 单一的庞大的reducer的拆分

    • 这块设计也不好做,会让人疑惑
    • 官方给的demo中直接按state的内容区分,我觉得这样做不好,如果后期有跨内容的情况,就比较奇怪了。官方给的combineReducers方案,也只是减少代码量,本质没有变化,state还是拆分处理,路由还是业务逻辑自己来做。
    • 解决方案:还是处理一整个state,可以按照约定写reducer类而不是方法,类里按照actionType建方法,架构自动路由并调用。
    • 以前做java架构,路由一定是架构来调用的,目前感觉各大flux框架都是解决问题不彻底。
  3. 官方建议设计模式:顶层容器组件才对redux有依赖,组件间通过props来传递数据。按照这样设计还是没有解决组件间交互和数据传递的问题。官方react设计建议:react的设计建议:http://camsong.github.io/redux-in-chinese/docs/basics/UsageWithReact.html

  4. 使用connect将state绑定到component。此处有些黑盒了。

  5. 异步action用来请求服务端数据,利用middleware增强createStore的dispatch后即支持。

看到一篇讲解Redis数据清理导致连接超时的问题的文章,之前开发系统时也出现过类似问题,但是一直没有确定具体的原因,当时的情况是crontab下有个每分钟都在执行的php脚本,脚本中是通过redis来做锁的控制,起初一切正常,突然有天脚本“卡住”了,redis中的锁没有删除,导致后续启动的脚本立即被退出;查了日志发现当时出现了连接超时的情况,但是不明白其原因,无奈之下我在程序执行一段后就调用一次ping命令希望能够保持连接,今天的这篇文章也是类似的问题,详细了解到了redis的数据清理机制。

Redis提供了一套“美好”的过期数据清理机制:

主动过期: Redis对数据是惰性过期,当一个key到了过期时间,Redis也不会马上清理,但如果这个key过期后被再次访问,Redis就会主动将它清理掉。

被动过期: 如果过期的Key一直没被访问,Redis也不会一直把它放那不管,它会每秒10次的执行以下的清理工作:

随机从所有带有过期时间的Key里取出20个
如果发现有过期的,就清理
如果这里有25%的Key都是过期的,就继续回到第一步再来一次

这套过期机制设计的很赞,可以这样理解:如果当前有超过1/4的Key是过期的话,就不停地清理,直到只剩下1/4不到的Key是要过期的为止,然后就慢慢地随机抽查着清理。

作者后来的解决方法是在业务逻辑中对需要过期处理的key做了分批删除的操作,自己来处理清理数据的工作,避免长时间处理。

#非对称加密
非对称加密具有以下的典型用法:

  • 对信息保密,防止中间人攻击:将明文通过接收人的公钥加密,传输给接收人,因为只有接收人拥有对应的私钥,别人不可能拥有或者不可能通过公钥推算出私钥,所以传输过程中无法被中间人截获。只有拥有私钥的接收人才能阅读。此用法通常用于交换对称密钥。
  • 身份验证和防止篡改:权限狗用自己的私钥加密一段授权明文,并将授权明文和加密后的密文,以及公钥一并发送出来,接收方只需要通过公钥将密文解密后与授权明文对比是否一致,就可以判断明文在中途是否被篡改过。此方法用于数字签名。
    著名的RSA算法就是非对称加密算法,RSA以三个发明人的首字母命名。

非对称加密算法如此强大可靠,却有一个弊端,就是加解密比较耗时。

#数字签名

数字签名是非对称加密和摘要算法两者结合。假设,我们有一段授权文本,需要发布,为了防止中途篡改文本内容,保证文本的完整性,以及文本是由指定的权限狗发的。首先,先将文本内容通过摘要算法,得到摘要,再用权限狗的私钥对摘要进行加密得到密文,将源文本、密文、和私钥对应的公钥一并发布即可。那么如何验证呢?

验证方首先查看公钥是否是权限狗的,然后用公钥对密文进行解密得到摘要,将文本用同样的摘要算法得到摘要,两个摘要进行比对,如果相等那么一切正常。这个过程只要有一步出问题就视为无效。

#Base64
Base64编码的思想是是采用==64个基本的ASCII码字符对数据进行重新编码==。它将需要编码的数据拆分成字节数组。以3个字节为一组。按顺序排列24 位数据,再把这24位数据分成4组,即每组6位。再在每组的的最高位前补两个0凑足一个字节。这样就把一个3字节为一组的数据重新编码成了4个字节。当所要编码的数据的字节数不是3的整倍数,也就是说在分组时最后一组不够3个字节。这时在最后一组填充1到2个0字节。并在最后编码完成后在结尾添加1到2个 “=”。

例:将对ABC进行BASE64编码:

  1. 首先取ABC对应的ASCII码值。A(65)B(66)C(67);

  2. 再取二进制值A(01000001)B(01000010)C(01000011);

  3. 然后把这三个字节的二进制码接起来(010000010100001001000011);

  4. 再以6位为单位分成4个数据块,并在最高位填充两个0后形成4个字节的编码后的值,(00010000)(00010100)(00001001)(00000011),其中加色部分为真实数据;

  5. 再把这四个字节数据转化成10进制数得(16)(20)(9)(3);

  6. 最后根据BASE64给出的64个基本字符表,查出对应的ASCII码字符(Q)(U)(J)(D),这里的值实际就是数据在字符表中的索引。

解码过程就是把4个字节再还原成3个字节再根据不同的数据形式把字节数组重新整理成数据。

本人根据这几年的工作经验及生活经验总结了几点:

  1. 学习能力,尤其是快速的学习并掌握的能力,技术日新月异,程序员需要每天都吸取养分,不断接收新的信息,不断学习新的技术;比如当你开发项目时领导交给你个任务,但是你之前从没接触过此类技术,不知道从何入手,于是你google了,查到了些解决方法,但是他是个新的东西,时间又比较紧,你就需要快速学习并简单掌握他。
  2. 表达能力,也就是能否与其他同事朋友有效的沟通,注意是有效沟通,就是指用最精简的语言表达出100%的意思,表达能力少若些的也能表达自己的意思,但可能就需要花十几分钟或更久;最基础的是与同是技术人员的表达要清楚,快速;其次是同行业的非技术人员,比如产品,运营等;再就是非同行业的完全不懂的人,比如你的爸妈,爷爷奶奶等。