转: 你所忽略的js隐式转换

2018-02-06 10:44 奔跑的搬砖工

你有没有在面试中遇到特别奇葩的js隐形转换的面试题,第一反应是怎么会是这样呢?难以自信,js到底是怎么去计算得到结果,你是否有深入去了解其原理呢?下面将深入讲解其实现原理。

其实这篇文章初稿三个月前就写好了,在我读一些源码库时,遇到了这些基础知识,想归档整理下,就有了这篇文章。由于一直忙没时间整理,最近看到了这个比较热的题,决定把这篇文章整理下。

const a = {
  i: 1,
  toString: function () {
    return a.i++;
  }
}
if (a == 1 >> a == 2 >> a == 3) {
  console.log('hello world!');
}

网上给出了很多不错的解析过程,读了下面内容,你将更深入的了解其执行过程。

1、js数据类型

js中有7种数据类型,可以分为两类:原始类型、对象类型:

基础类型(原始值):

Undefined、 Null、 String、 Number、 Boolean、 Symbol (es6新出的,本文不讨论这种类型)

复杂类型(对象值):

object

2、三种隐式转换类型

js中一个难点就是js隐形转换,因为js在一些操作符下其类型会做一些变化,所以js灵活,同时造成易出错,并且难以理解。

涉及隐式转换最多的两个运算符 + 和 ==。

+运算符即可数字相加,也可以字符串相加。所以转换时很麻烦。== 不同于===,故也存在隐式转换。- * / 这些运算符只会针对number类型,故转换的结果只能是转换成number类型。

既然要隐式转换,那到底怎么转换呢,应该有一套转换规则,才能追踪最终转换成什么了。

隐式转换中主要涉及到三种转换:

1、将值转为原始值,ToPrimitive()。

2、将值转为数字,ToNumber()。

3、将值转为字符串,ToString()。

2.1、通过ToPrimitive将值转换为原始值

js引擎内部的抽象操作ToPrimitive有着这样的签名:

ToPrimitive(input, PreferredType?)

input是要转换的值,PreferredType是可选参数,可以是Number或String类型。他只是一个转换标志,转化后的结果并不一定是这个参数所值的类型,但是转换结果一定是一个原始值(或者报错)。

2.1.1、如果PreferredType被标记为Number,则会进行下面的操作流程来转换输入的值。

1、如果输入的值已经是一个原始值,则直接返回它
2、否则,如果输入的值是一个对象,则调用该对象的valueOf()方法,
   如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
3、否则,调用这个对象的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
4、否则,抛出TypeError异常。

2.1.2、如果PreferredType被标记为String,则会进行下面的操作流程来转换输入的值。

1、如果输入的值已经是一个原始值,则直接返回它
2、否则,调用这个对象的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
3、否则,如果输入的值是一个对象,则调用该对象的valueOf()方法,
   如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
4、否则,抛出TypeError异常。

既然PreferredType是可选参数,那么如果没有这个参数时,怎么转换呢?PreferredType的值会按照这样的规则来自动设置:

1、该对象为Date类型,则PreferredType被设置为String
2、否则,PreferredType被设置为Number

2.1.3、valueOf方法和toString方法解析

上面主要提及到了valueOf方法和toString方法,那这两个方法在对象里是否一定存在呢?答案是肯定的。在控制台输出Object.prototype,你会发现其中就有valueOf和toString方法,而Object.prototype是所有对象原型链顶层原型,所有对象都会继承该原型的方法,故任何对象都会有valueOf和toString方法。

先看看对象的valueOf函数,其转换结果是什么?对于js的常见内置对象:Date, Array, Math, Number, Boolean, String, Array, RegExp, Function。

1、Number、Boolean、String这三种构造函数生成的基础值的对象形式,通过valueOf转换后会变成相应的原始值。如:

var num = new Number('123');
num.valueOf(); // 123
var str = new String('12df');
str.valueOf(); // '12df'
var bool = new Boolean('fd');
bool.valueOf(); // true

2、Date这种特殊的对象,其原型Date.prototype上内置的valueOf函数将日期转换为日期的毫秒的形式的数值。

var a = new Date();
a.valueOf(); // 1515143895500
3、除此之外返回的都为this,即对象本身:(有问题欢迎告知)
var a = new Array();
a.valueOf() === a; // true
var b = new Object({});
b.valueOf() === b; // true
再来看看toString函数,其转换结果是什么?对于js的常见内置对象:Date, Array, Math, Number, Boolean, String, Array, RegExp, Function。

1、Number、Boolean、String、Array、Date、RegExp、Function这几种构造函数生成的对象,通过toString转换后会变成相应的字符串的形式,因为这些构造函数上封装了自己的toString方法。如:

Number.prototype.hasOwnProperty('toString'); // true
Boolean.prototype.hasOwnProperty('toString'); // true
String.prototype.hasOwnProperty('toString'); // true
Array.prototype.hasOwnProperty('toString'); // true
Date.prototype.hasOwnProperty('toString'); // true
RegExp.prototype.hasOwnProperty('toString'); // true
Function.prototype.hasOwnProperty('toString'); // true
var num = new Number('123sd');
num.toString(); // 'NaN'
var str = new String('12df');
str.toString(); // '12df'
var bool = new Boolean('fd');
bool.toString(); // 'true'
var arr = new Array(1,2);
arr.toString(); // '1,2'
var d = new Date();
d.toString(); 
var func = function () {}
func.toString(); // function () {}

除这些对象及其实例化对象之外,其他对象返回的都是该对象的类型,(有问题欢迎告知),都是继承的Object.prototype.toString方法。

var obj = new Object({});
obj.toString();
Math.toString();

从上面valueOf和toString两个函数对对象的转换可以看出为什么对于ToPrimitive(input, PreferredType?),PreferredType没有设定的时候,除了Date类型,PreferredType被设置为String,其它的会设置成Number。

因为valueOf函数会将Number、String、Boolean基础类型的对象类型值转换成 基础类型,Date类型转换为毫秒数,其它的返回对象本身,而toString方法会将所有对象转换为字符串。显然对于大部分对象转换,valueOf转换更合理些,因为并没有规定转换类型,应该尽可能保持原有值,而不应该想toString方法一样,一股脑将其转换为字符串。

所以对于没有指定PreferredType类型时,先进行valueOf方法转换更好,故将PreferredType设置为Number类型。

而对于Date类型,其进行valueOf转换为毫秒数的number类型。在进行隐式转换时,没有指定将其转换为number类型时,将其转换为那么大的number类型的值显然没有多大意义。(不管是在+运算符还是==运算符)还不如转换为字符串格式的日期,所以默认Date类型会优先进行toString转换。故有以上的规则:

PreferredType没有设置时,Date类型的对象,PreferredType默认设置为String,其他类型对象PreferredType默认设置为Number。

2.2、通过ToNumber将值转换为数字

根据参数类型进行下面转换:参数结果undefinedNaNnull+0布尔值true转换1,false转换为+0数字无须转换字符串有字符串解析为数字,例如:‘324’转换为324,‘qwer’转换为NaN对象(obj)先进行 ToPrimitive(obj, Number)转换得到原始值,在进行ToNumber转换为数字

2.3、通过ToString将值转换为字符串

根据参数类型进行下面转换:参数结果undefined’undefined’null’null’布尔值转换为’true’ 或 ‘false’数字数字转换字符串,比如:1.765转为’1.765’字符串无须转换对象(obj)先进行 ToPrimitive(obj, String)转换得到原始值,在进行ToString转换为字符串讲了这么多,是不是还不是很清晰,先来看看一个例子:

({} + {}) = ?
两个对象的值进行+运算符,肯定要先进行隐式转换为原始类型才能进行计算。
1、进行ToPrimitive转换,由于没有指定PreferredType类型,{}会使默认值为Number,进行ToPrimitive(input, Number)运算。
2、所以会执行valueOf方法,({}).valueOf(),返回的还是{}对象,不是原始值。
3、继续执行toString方法,({}).toString(),返回[object Object],是原始值。


再来一个指定类型的例子:

2 * {} = ?1、首先*运算符只能对number类型进行运算,故第一步就是对{}进行ToNumber类型转换。2、由于{}是对象类型,故先进行原始类型转换,ToPrimitive(input, Number)运算。3、所以会执行valueOf方法,({}).valueOf(),返回的还是{}对象,不是原始值。4、继续执行toString方法,({}).toString(),返回[object Object],是原始值。5、转换为原始值后再进行ToNumber运算,[object Object]就转换为NaN。故最终的结果为 2 * NaN = NaN


## 3、== 运算符隐式转换
== 运算符的规则规律性不是那么强,按照下面流程来执行,es5文档
比较运算 x==y, 其中 x 和 y 是值,返回 true 或者 false。这样的比较按如下方式进行:1、若 Type(x) 与 Type(y) 相同, 则

  1* 若 Type(x) 为 Undefined, 返回 true。
  2* 若 Type(x) 为 Null, 返回 true。
  3* 若 Type(x) 为 Number, 则

      (1)、若 x 为 NaN, 返回 false。
      (2)、若 y 为 NaN, 返回 false。
      (3)、若 x 与 y 为相等数值, 返回 true。
      (4)、若 x 为 +0 且 y 为 −0, 返回 true。
      (5)、若 x 为 −0 且 y 为 +0, 返回 true。
      (6)、返回 false。

  4* 若 Type(x) 为 String, 则当 x 和 y 为完全相同的字符序列(长度相等且相同字符在相同位置)时返回 true。 否则, 返回 false。
  5* 若 Type(x) 为 Boolean, 当 x 和 y 为同为 true 或者同为 false 时返回 true。 否则, 返回 false。
  6*  当 x 和 y 为引用同一对象时返回 true。否则,返回 false。

2、若 x 为 null 且 y 为 undefined, 返回 true。3、若 x 为 undefined 且 y 为 null, 返回 true。4、若 Type(x) 为 Number 且 Type(y) 为 String,返回比较 x == ToNumber(y) 的结果。5、若 Type(x) 为 String 且 Type(y) 为 Number,返回比较 ToNumber(x) == y 的结果。6、若 Type(x) 为 Boolean, 返回比较 ToNumber(x) == y 的结果。7、若 Type(y) 为 Boolean, 返回比较 x == ToNumber(y) 的结果。8、若 Type(x) 为 String 或 Number,且 Type(y) 为 Object,返回比较 x == ToPrimitive(y) 的结果。9、若 Type(x) 为 Object 且 Type(y) 为 String 或 Number, 返回比较 ToPrimitive(x) == y 的结果。10、返回 false。


上面主要分为两类,x、y类型相同时,和类型不相同时。

类型相同时,没有类型转换,主要注意NaN不与任何值相等,包括它自己,即NaN !== NaN。

类型不相同时,

1、x,y 为null、undefined两者中一个 // 返回true

2、x、y为Number和String类型时,则转换为Number类型比较。

3、有Boolean类型时,Boolean转化为Number类型比较。

4、一个Object类型,一个String或Number类型,将Object类型进行原始转换后,按上面流程进行原始值比较。

### 3.1、== 例子解析

所以类型不相同时,可以会进行上面几条的比较,比如:
var a = { valueOf: function () { return1; }, toString: function () { return'123' }}true == a // true;首先,x与y类型不同,x为boolean类型,则进行ToNumber转换为1,为number类型。接着,x为number,y为object类型,对y进行原始转换,ToPrimitive(a, ?),没有指定转换类型,默认number类型。而后,ToPrimitive(a, Number)首先调用valueOf方法,返回1,得到原始类型1。最后 1 == 1, 返回true。

我们再看一段很复杂的比较,如下:
[] == !{}//1、! 运算符优先级高于==,故先进行!运算。2、!{}运算结果为false,结果变成 [] == false比较。3、根据上面第7条,等式右边y = ToNumber(false) = 0。结果变成 [] == 0。4、按照上面第9条,比较变成ToPrimitive([]) == 0。 按照上面规则进行原始值转换,[]会先调用valueOf函数,返回this。 不是原始值,继续调用toString方法,x = [].toString() = ''。 故结果为 '' == 0比较。5、根据上面第5条,等式左边x = ToNumber('') = 0。 所以结果变为: 0 == 0,返回true,比较结束。

最后我们看看文章开头说的那道题目:
const a = { i: 1, toString: function () { return a.i++; }}if (a == 1 && a == 2 && a == 3) { console.log('hello world!');}

1、当执行a == 1 >> a == 2 >> a == 3 时,会从左到右一步一步解析,首先 a == 1,会进行上面第9步转换。ToPrimitive(a, Number) == 1。

2、ToPrimitive(a, Number),按照上面原始类型转换规则,会先调用valueOf方法,a的valueOf方法继承自Object.prototype。返回a本身,而非原始类型,故会调用toString方法。

3、因为toString被重写,所以会调用重写的toString方法,故返回1,注意这里是i++,而不是++i,它会先返回i,在将i+1。故ToPrimitive(a, Number) = 1。也就是1 == 1,此时i = 1 + 1 = 2。

4、执行完a == 1返回true,会执行a == 2,同理,会调用ToPrimitive(a, Number),同上先调用valueOf方法,在调用toString方法,由于第一步,i = 2此时,ToPrimitive(a, Number) = 2, 也就是2 == 2, 此时i = 2 + 1。

5、同上可以推导 a == 3也返回true。故最终结果 a == 1 >> a == 2 >> a == 3返回true

其实了解了以上隐形转换的原理,你有没有发现这些隐式转换并没有想象中那么难。

转自:https://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==>mid=2651227769>idx=1>sn=617160e64d2be13169b1b8f4506d8801>chksm=bd495ffd8a3ed6eb226d4ef193ff2ce3958d2d03d1f3047b635915f8215af40996c2f64d5c20>scene=38#wechat_redirect

mongoose连接数据库查询不到数据

2017-11-16 20:25 我说呢

本来兴高采烈的打算用下mongoose,没想到初体验印象这么不好

代码找了好几遍问题,都开始怀疑人生了。然后就各种搜资料,没想到网上也有人问这种问题,哈哈

然后就找到了这种问题的根源:

mongoose always add an s to the end of my collection name

WTF

知道根源后解决就好办了

1.把查询数据的collection加上一个s,然后在model里面别加s

2.当然还有第二种方法,就是强制让他使用某个collection

var dataSchema = new Schema({..}, { collection: 'data' })

问题解决了,不过还是禁不住想吐槽下。

location.replace not work in Android

2017-11-09 20:16 我说呢

location.replace()方法以给定的URL来替换当前的资源。 与assign() 方法 不同的是调用replace()方法后,当前页面不会保存到会话历史中(session History),这样用户点击回退按钮将不会再跳转到该页面。

这是MDN web docs对replace的解释,其实还挺权威的,不过表现不是那么权威啊,在android里面还是会保存到会话历史中(在IOS设备上OK)...

直接上解决方法吧:

function locationReplace (url) {
    if (history.replaceState) {
        history.replaceState(null, document.title, url);
        history.go(0);
    }
    else {
        location.replace(url);
    }
}

简单粗暴.

降龙十八掌

打完收工.

safari margin-bottom not work

2017-10-24 20:51 我说呢

来说下背景:当内容高度超出父容器高度时,内容的margin-bottom设置是无效的。

遇到的情况是这样的,在html,body设置height: 100%;的时候,如果页面超过一屏,最后的那个元素在设置margin-bottom失效了,在chrome、firefox可以

解决方法

1.设置padding-bottom可以

2.把html,body的height: 100%;去掉也可以。

最好是规避下超出所有父容器的内容。。。

原理其实我也不知道。。。。(尴尬不?????)

JavaScript 浮点数陷阱及解法

2017-10-19 19:51 奔跑的搬砖工

众所周知,JavaScript 浮点数运算时经常遇到会 0.000000001 和 0.999999999 这样奇怪的结果,如 0.1+0.2=0.30000000000000004、1-0.9=0.09999999999999998,很多人知道这是浮点数误差问题,但具体就说不清楚了。本文帮你理清这背后的原理以及解决方案,还会向你解释JS中的大数危机和四则运算中会遇到的坑。

浮点数的存储

首先要搞清楚 JavaScript 如何存储小数。和其它语言如 Java 和 Python 不同,JavaScript 中所有数字包括整数和小数都只有一种类型 — Number。它的实现遵循 IEEE 754 标准,使用 64 位固定长度来表示,也就是标准的 double 双精度浮点数(相关的还有float 32位单精度)。计算机组成原理中有过详细介绍,如果你不记得也没关系。

这样的存储结构优点是可以归一化处理整数和小数,节省存储空间。

64位比特又可分为三个部分:

符号位S:第 1 位是正负数符号位(sign),0代表正数,1代表负数

指数位E:中间的 11 位存储指数(exponent),用来表示次方数

尾数位M:最后的 52 位是尾数(mantissa),超出的部分自动进一舍零

img

实际数字就可以用以下公式来计算:

img

注意以上的公式遵循科学计数法的规范,在十进制是为0<M<10,到二进行就是0<M<2。也就是说整数部分只能是1,所以可以被舍去,只保留后面的小数部分。如 4.5 转换成二进制就是 100.1,科学计数法表示是 1.001*2^2,舍去1后 M = 001。E是一个无符号整数,因为长度是11位,取值范围是 0~2047。但是科学计数法中的指数是可以为负数的,所以再减去一个中间数 1023,[0,1022]表示为负,[1024,2047] 表示为正。如4.5 的指数E = 1025,尾数M为 001。

最终的公式变成:

img

所以 4.5 最终表示为(M=001、E=1025):

img

下面再以 0.1 例解释浮点误差的原因, 0.1 转成二进制表示为 0.0001100110011001100(1100循环),1.100110011001100x2^-4,所以 E=-4+1023=1019;M 舍去首位的1,得到 100110011...。最终就是:

img

转化成十进制后为 0.100000000000000005551115123126,因此就出现了浮点误差。

为什么 0.1+0.2=0.30000000000000004?

计算步骤为:

// 0.1 和 0.2 都转化成二进制后再进行运算
0.00011001100110011001100110011001100110011001100110011010 +
0.0011001100110011001100110011001100110011001100110011010 =
0.0100110011001100110011001100110011001100110011001100111

// 转成十进制正好是 0.30000000000000004

为什么 x=0.1 能得到 0.1?

恭喜你到了看山不是山的境界。因为 mantissa 固定长度是 52 位,再加上省略的一位,最多可以表示的数是 2^53=9007199254740992,对应科学计数尾数是 9.007199254740992,这也是 JS 最多能表示的精度。它的长度是 16,所以可以使用 toPrecision(16) 来做精度运算,超过的精度会自动做凑整处理。于是就有:

0.10000000000000000555.toPrecision(16)
// 返回 0.1000000000000000,去掉末尾的零后正好为 0.1

// 但你看到的 `0.1` 实际上并不是 `0.1`。不信你可用更高的精度试试:
0.1.toPrecision(21) = 0.100000000000000005551

大数危机

可能你已经隐约感觉到了,如果整数大于 9007199254740992 会出现什么情况呢?

由于 E 最大值是 1023,所以最大可以表示的整数是 2^1024 - 1,这就是能表示的最大整数。但你并不能这样计算这个数字,因为从 2^1024 开始就变成了 Infinity

> Math.pow(2, 1023)
8.98846567431158e+307

> Math.pow(2, 1024)
Infinity

那么对于 (2^53, 2^63) 之间的数会出现什么情况呢?

(2^53, 2^54) 之间的数会两个选一个,只能精确表示偶数

(2^54, 2^55) 之间的数会四个选一个,只能精确表示4个倍数

... 依次跳过更多2的倍数

下面这张图能很好的表示 JavaScript 中浮点数和实数(Real Number)之间的对应关系。我们常用的 (-2^53, 2^53) 只是最中间非常小的一部分,越往两边越稀疏越不精确。

img

在淘宝早期的订单系统中把订单号当作数字处理,后来随意订单号暴增,已经超过了9007199254740992,最终的解法是把订单号改成字符串处理。

要想解决大数的问题你可以引用第三方库 bignumber.js,原理是把所有数字当作字符串,重新实现了计算逻辑,缺点是性能比原生的差很多。所以原生支持大数就很有必要了,现在 TC39 已经有一个 Stage 3 的提案 proposal bigint,大数问题有问彻底解决。

toPrecision vs toFixed

数据处理时,这两个函数很容易混淆。它们的共同点是把数字转成字符串供展示使用。注意在计算的中间过程不要使用,只用于最终结果。

不同点就需要注意一下:

toPrecision 是处理精度,精度是从左至右第一个不为0的数开始数起。

toFixed 是小数点后指定位数取整,从小数点开始数起。

两者都能对多余数字做凑整处理,也有些人用 toFixed 来做四舍五入,但一定要知道它是有 Bug 的。

如:1.005.toFixed(2) 返回的是 1.00 而不是 1.01。

原因: 1.005 实际对应的数字是 1.00499999999999989,在四舍五入时全部被舍去!

解法:使用专业的四舍五入函数 Math.round() 来处理。但 Math.round(1.005 * 100) / 100 还是不行,因为 1.005 * 100 = 100.49999999999999。还需要把乘法和除法精度误差都解决后再使用 Math.round。可以使用后面介绍的 number-precision#round 方法来解决。

解决方案

回到最关心的问题:如何解决浮点误差。首先,理论上用有限的空间来存储无限的小数是不可能保证精确的,但我们可以处理一下得到我们期望的结果。

数据展示类

当你拿到 1.4000000000000001 这样的数据要展示时,建议使用 toPrecision 凑整并 parseFloat 转成数字后再显示,如下:

parseFloat(1.4000000000000001.toPrecision(12)) === 1.4  // True

封装成方法就是:

function strip(num, precision = 12) {
  return +parseFloat(num.toPrecision(precision));
}

为什么选择 12 做为默认精度?这是一个经验的选择,一般选12就能解决掉大部分0001和0009问题,而且大部分情况下也够用了,如果你需要更精确可以调高。

数据运算类

对于运算类操作,如 +-*/,就不能使用 toPrecision 了。正确的做法是把小数转成整数后再运算。以加法为例:

/**
 * 精确加法
 */
function add(num1, num2) {
  const num1Digits = (num1.toString().split('.')[1] || '').length;
  const num2Digits = (num2.toString().split('.')[1] || '').length;
  const baseNum = Math.pow(10, Math.max(num1Digits, num2Digits));
  return (num1 * baseNum + num2 * baseNum) / baseNum;
}

以上方法能适用于大部分场景。遇到科学计数法如 2.3e+1(当数字精度大于21时,数字会强制转为科学计数法形式显示)时还需要特别处理一下。

能读到这里,说明你非常有耐心,那我就放个福利吧。遇到浮点数误差问题时可以直接使用https://github.com/dt-fe/number-precision

完美支持浮点数的加减乘除、四舍五入等运算。非常小只有1K,远小于绝大多数同类库(如Math.js、BigDecimal.js),100%测试全覆盖,代码可读性强,不妨在你的应用里用起来!

转载自:https://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651227117&idx=1&sn=74dc9e59b1f0006ff16defc299b02a9c&chksm=bd495a698a3ed37f31932c421af1976224dd38af466a8d3bbb9d2480a7194108b3468ea4e1a9&scene=38#%23

WTF Javascript之 [] == ![]

2017-08-24 20:35 奔跑的搬砖工

最近看技术文章发现了这个看似奇葩的结果(如题的结果是true)

原因

先说下右边的![]的值,这个是把[]转化成boolean值,然后再取反,结果就是false,这个很容易理解。

好了,那个既然是false了,按照ECMA规范就是如果比较的一方是boolean的话,那就需要把这个boolean转换成number,题目就变成了[] == 0。

然后接着解释为啥[] == 0是true?还是那个ECMA规范又说话了:如果比较的一方是字符串或数字,另一方是object的话(数组也是object),需要把object转换成基本类型(字符串或者数字)

转换规则:

1.所有对象先调用valueOf()方法,如果此方法返回的是原始值,则对象转为这个原始值。

2.如果valueOf方法返回的不是原始值,则调用toString方法,如果toString方法返回的是原始值吗,则对象转换为这个原始值。

3.如果valueOf和toString方法均没有返回原始值,则抛出TypeError异常.

再来看下这个题目, [].valueOf()依然是[],因此接着执行第二步,使用toString()结果是''。然后题目变成了'' == 0, 结果一目了然。

怀疑世界

在开始的时候看到解释的时候,有点怀疑世界了: 为啥

if ([]) {
    console.log(true);
} else {
    console.log(false);
}

返回解释是true呢,其实很好理解, []不是基本类型指定是true啊,不要认为 [] == ![]相等,然后![]是false就把[]也理解成false。

知乎相关问题

JS 点击直接下载

2017-08-21 18:46 我说呢

今日总结

需求背景: 点击按钮根据选择的类型,进行ajax请求, 后端返回一个文件直接下载

开始想也没想直接使用了jquery的ajax请求,一看返回了一堆的乱码(后端返回的是文件,在控制台看指定是乱码了)


罗列下直接下载文件的方法

1.form提交

2.使用iframe设置src

3.利用超链接,调用原生JS的click()方法

就不一一解释了,方法罗列出来,相比就知道差不多了


在使用第三种方法的之前, 尝试过使用jquery的trigger方法来触发click事件,谁曾想不行,原因如下:

trigger方法只会触发使用jquery绑定的方法,不会触发默认的行为

jQuery trigger only works if any 'jQuery click` event is added. Otherwise you will not be able to do anything by this way; – Reza Mamun

JS for in 顺序

2017-08-21 17:04 我说呢

其实是上周的事情,拖延症犯了,谁也挡不住啊

var obj = {
    city: 'beijing',
    12: 12,
    7: 7,
    0: 0,
    '-2': -2,
    age: 15,
    '-3.5': -3.5,
    7.7: 7.7,
    _: __,
    online: true,
    3: 3,
    23: 23,
    44: 44
}

for (var key in obj){
    console.log(key);
}

至于结果我就不显示了,自己直接放到控制台里面看下就明白了

来来来,总结下。。

结论

1.Chrome、 sarari、 firebox、 Opera 中使用 for-in 语句遍历对象属性时会遵循一个规律,它们会先提取所有 key 的 parseFloat 值为非负整数的属性, 然后根据数字顺序对属性排序首先遍历出来,然后按照对象定义的顺序遍历余下的所有属性。order is preserved with the major exception of keys like “7” that parse as integers and are handled differently by Chrome/V8

2.其它浏览器则完全按照对象定义的顺序遍历属性。

3.for-in、object.keys(objectX).map 语句无法保证对象的遍历顺序,应尽量避免编写依赖对象属性顺序的代码。

Chrome等现代浏览器较新版本 的 JavaScript 解析引擎遵循的是新版 ECMA-262 第五版规范(看到11年stackoverflow上的讨论)。因此,使用 for-in 语句遍历对象属性时遍历书序并非属性构建顺序。而 IE6 IE7 IE8 Firefox Safari 的 JavaScript 解析引擎遵循的是较老的 ECMA-262 第三版规范,属性遍历顺序由属性构建的顺序决定。

vue ssr博客搭建

2017-08-04 19:44 我说呢

技术栈列表

终于下定决心要写个blog了,之前是用wordpress搭建的,期间不知道咋的阿里云服务器总是有问题

1.vue 全家桶

2.express

3.axios (http请求)

4.request (proxy代理)

5.mavon-editor (markdown编辑器)

6.md-js (markdown前端渲染)

7.vue-server-renderer (服务端渲染)

其实项目搭建也是直接找的网上的例子(手动惭愧.gif),然后又根据需求改造了些

服务器运行

npm run build (打包项目到dist目录)
cross-env NODE_ENV=production pm2 start server.js (我使用的是pm2)

无名

1.vue-router 可以记录路由上次浏览的位置

scrollBehavior (to, from, savedPosition) {
	if(savedPosition) {
		return savedPosition;
	}
	return { x: 0, y: 0 };
}

2.ssr 其实使用的是在server-entry.js中配置了asyncData (名字是自己随便取的),然后在vue组件里面实现方法即可

asyncData ({ store, route: { params: { id }}}) {
    return store.dispatch('getArticleData', { id: id })
}

先不写了,待后续更新

2016年终总结

2017-01-01 00:29 我说呢

看好多大神们都在总结2016这一年,我也假模假式的来个流水账记录吧

是不是我这才算是真正的年终,还有不到五分钟就要2017了

工作中

我在2015年12月10号入职了美信(哦,对,现在应该叫国美生态互联网了),这是我毕业工作以来入职的第二个公司。在美信待了一年多了,在这里学到了很多,也认识很多大神们、老司机(人太多,就不一一说了)。

刚开始入职的时候,前端部门还比较少,好像我是第十二三个的样子,还没有那么多的项目组,就只维护了H5站和内嵌的web页面.到现在我们发展到有架构组、PC组、构建组、内嵌组当然还有我目前的小组H5站,每个小组差不多7、8个人的样子。

我也算是在美信经手了好几个项目了(其实有的只是负责维护了下),基本都是大神已经定好了框架,我基于上面开发而已,来来来,罗列下啊:

1.xpop店铺(其实这个不想写了,毕竟没在这个项目真正写过多少代码),但毕竟是我进了美信后第一个项目.

2.客服系统

3.mock server(虽然还没开发完就被改版了,说起来还挺委屈,hehe)

4.H5站(这个时间最长了)

其中我学会了好多新技术,从之前的backbone.js+grunt到vue.js+gulp/webpack了,还有配套的vue全家桶(名字挺唬人,hh);学会了使用node.js开发自己小项目,nodejs真是个好东西.还有一些类似于react全家桶的一些东西,前端的东西真是太多了,都学不完(累~)

生活上

2016这一年生活可算是经历的不少.最重要的就是终于在2016结束了爱情长跑,和跟我一起5年的女友结婚了(快祝福我吧,hahaha);还一个比较大的事情是,在11月我也终于东拼西凑在滨海新区买了一个房子,虽然位置不好,但是我那些钱只够买这个了(只能这样安慰自己了),其实欠下了一屁股的债;中间还发生了一些不愉快的事,就不与大家分享了.

总之,2016经历了很多,希望在2017能一切顺顺利利的,大家都健健康康的,事业更近一步。

哎呀,写着写着就2017年了....2017,你好