您的位置:首页 > Web前端 > JavaScript

<你不知道的Javascript>笔记

2017-12-10 21:44 337 查看

前言

Javascript本身是一门非常灵活的语言,如果是先有写过编译型语言的再来看看它,简直是灵活到你可以接近”瞎写”的程度还不会出错.

然而凡事皆有双面性,它的灵活性背后是无数的坑.Javascript设计的时候,估计也没有考虑到会发展成今天这个规模,更是没有想到它居然可以用在后端.

很多在前端设计中无所谓的坑,放在后端就是灾难.这本书非常详细的介绍了那些Javascript中看似古怪的设计及其背后的深层次原因,值得一读.

这份笔记里将记录一些我认为比较有实践意义的东西.对于那些实际代码中应该抛弃的奇怪写法,不做深究.

类型转换

js本身规定了六个基本类型,在必要的时候,会发生类型转换.

let a = 42;
let b = a + "";
let c = String(a);


第二和第三个赋值操作时,都发生了类型转换,前者是隐式,将a强制转换为string类型,后者是显式.

转为string

基础类型转化规则定义如下:

* null: “null”

* undefined: “undefined”

* boolean: “true”/”false”

* number: 通常和常规数学表达一致.有个特殊情况,在数字很小或很大的时候,将会转换出指数形式的结果,比如”1.07e21”

JSON.stringify()

所有JSON安全的值都可以用这个方法转为字符串.以下情形不属于JSON安全的对象:
undefined, function, symbol
以及循环引用.遇到前三者时会自动忽略,而循环引用会报错.

以下代码可以创建一个具有循环引用的JSON对象:

let a = {"prop0": "safe"};
a.prop1 = a;


JSON.stringify()还具有一个可选的参数,叫做
replacer
.这个参数可以传两种类型的值进去,数组和函数.

* 传入数组的时候,这个数组里元素必须是字符串.它指出了JSON对象中要处理的键.未在该数组中列出的键会被忽略.如:

let a = {
"b": 0,
"c": 1,
"d": 2
};
JSON.stringify(a, ["c", "d"]);


传入函数的时候,对象的每个键会调用一次,同时对象本身也会调用一次.

转为number

true转换为1,false转换为0(反过来好像也是这样).undefined转换为NaN,null转换为0.

转为boolean

ES5中定义了转换为boolean类型时的规则.转换结果为false的如下:

*
undefined


*
null


*
false


*
+0 -0 NaN


*
""


除此以外,其它所有的东西,转换结果都是true.包括空对象,空数组这些.

显式转换

string & number

字符串转数字,如果要用类型转换的方式来做,如下:

let c = "3.14";
let d = 5+ +c;


这其实是一种可读性很差的写法,我个人并不推荐这么写.书里还举了一个疯狂的例子:

1 + - + + + - + 1;  //  What the fuck?


还可以调用构造函数
Number()
进行转换.

另外,用的比较多的
parseInt()
也是一个办法,不过它的准确定义应该是解析字符串,而非类型转换.
parseInt()
函数还有第二个参数,指定了转换数字的目标进制.比如要字符串是十六进制表示,如下:

let a = "100";
parseInt(a, 16);


注意,这里是将100作为一个十六进制数来看待,而并不是将100作为一个十进制数看待,返回转十六进制的结果.

与之相反的,是
toString()
方法,它也可以附带一个参数,将
Number
类型的数据转成字符串并以指定的进制输出.一个有趣的应用是产生随机字符串:

(Math.random()*100000000000).toString(36).replace('.', "");


这段代码的原理就是36进制的表示恰好是0-9和a-z的完整集合,转成字符串之后,字符串里只会包含数字和小写字母.

| 和 ~运算符

~运算符会对一个32位有符号整数执行按位取反操作,包括符号位.如果目标参数不是整数,则先强制转换为整数,再执行取反.

|会对一个32位有符号整数执行按位或操作.如果参数不是整数,同样转换为整数再运算.

如下两个应用会实现取整的效果:

let a = 1.123;
let b = 0 | a;


由于0和任何数按位或都是另一个数本身,因此这里返回的就是a取整的结果.

let a = 1.1415926;
let b = ~~a;


第一个~首先将a取整后取反.a取整的结果是
0x00000001
,按位取反后是
0xfffffffe
,即-2的补码表示.第二个~对
0xfffffffe
进行按位取反,就得到了取整的结果
0x00000001
.

这种写法看似简洁,并且使用了位运算.一个通常的说法是使用位运算效率会高很多.对于编译型语言,结论成立.对于解释型语言,打问号.看如下的代码:

let a = [];
for (let i = 0; i < 100000; i ++)   {
a.push(Math.random() * 1000000)
}

let ts = Date.now();
for (let i = 0; i < 100000; i ++)   {
~~a[i];
}
console.log(Date.now() - ts);

ts = Date.now();
for (let i = 0; i < 100000; i ++)   {
Math.floor(a[i]);
}
console.log(Date.now() - ts);


在我的机器上跑下来的结果是,前者26ms,后者3ms.当然,这个结果会随着不同的node版本会有差异,但已经足够证明一点:不要迷信那些在传统编译型语言中惯用的优化手段.

4000
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: