在这里记录着天天本身遇到的一道印象深入的前端问题,以及一道生活中随处可见的小问题。html
强迫本身造成积累的习惯,鞭挞本身不断前行,共同窗习。前端
Github 地址vue
[] == ![]
结果为 true
,而 {} == !{}
却为 false
首先了解一下类型转化的规则:node
一、若是有一个操做数是布尔值,则在比较相等性以前先将其转换为数值——false 转换为 0,而 true 转换为 1;git
二、若是一个操做数是字符串,另外一个操做数是数值,在比较相等性以前先将字符串转换为数值github
三、若是一个操做数是对象,另外一个操做数不是,则调用对象的 valueOf()(boolean 对象方法)方法或者 toString()方法,用获得的基本类型值按照前面的规则进行比较vue-cli
null 和 undefined 是相等的express
四、要比较相等性以前,不能将 null 和 undefined 转换成其余任何值编程
五、若是有一个操做数是 NaN,则相等操做符返回 false ,而不相等操做符返回 true。重要提示:即便两个操做数都是 NaN,相等操做符也返回 false 了;由于按照规则, NaN 不等于 NaN (NaN 不等于任何值,包括他自己)json
六、若是两个操做数都是对象,则比较它们是否是同一个对象,若是两个操做数都指向同一个对象,则相等操做符返回 true;不然,返回 false
七、 !
可将变量转换成 boolean 类型,null、undefined、NaN 以及空字符串('')取反都为 true,其他都为 false。
如今开始分析题目
[] == ![]; // 先转化右边 ![], // `!`可将变量转换成 boolean 类型,null、undefined、NaN 以及空字符串('')取反都为 true,其他都为 false。 // 因此 ![] => false => 0 // 左边 [], 由于[].toString() 为空字符串,因此 [] => '' // 综上, '' == 0, 为 true
{} == !{} // 先转化右边 !{}, // `!`可将变量转换成 boolean 类型,null、undefined、NaN 以及空字符串('')取反都为 true,其他都为 false。 // 因此 !{} => false => 0 // 左边 ({}).toString() => "[object Object]" // 综上, "[object Object]" == 0, 为 false
什么是 restful 接口 ?
REST -- REpresentational State Transfer,英语的直译就是“表现层状态转移”,它包含如下三个方面:
URL 设计: RESTful 的核心思想就是,客户端发出的数据操做指令都是"动词 + 宾语"的结构。好比,GET /articles 这个命令,GET 是动词,/articles 是宾语。
动词一般就是五种 HTTP 方法,对应 CRUD 操做。
状态码: 客户端的每一次请求,服务器都必须给出回应。回应包括 HTTP 状态码和数据两部分。
服务器回应: API 返回的数据格式,不该该是纯文本,而应该是一个 JSON 对象,由于这样才能返回标准的结构化数据。因此,服务器回应的 HTTP 头的 Content-Type
属性要设为 application/json。
优势
简洁明了,一目了然;轻量,直接经过 http,不须要额外的协议,post/get/put/delete 操做
缺点
当一次更新的内容多的时候须要调用更多的接口。删除也是,若是我想批量删除呢?
视口分为:layout viewport -- 布局视口,visual viewport -- 视觉视口,ideal viewport -- 理想视口
若是把移动设备上浏览器的可视区域设为 viewport 的话,某些网站就会由于 viewport 太窄而显示错乱,因此这些浏览器就决定默认状况下把 viewport 设为一个较宽的值,好比 980px,这样的话即便是那些为桌面设计的网站也能在移动浏览器上正常显示了。这个浏览器默认的 viewport 叫作 layout viewport。这个 layout viewport 的宽度能够经过 document.documentElement.clientWidth 来获取。
layout viewport 的宽度是大于浏览器可视区域的宽度的,因此咱们还须要一个 visual viewport 来表明浏览器可视区域的大小。visual viewport 的宽度能够经过 window.innerWidth 来获取
ideal viewport 即每一个设备完美适配的视口。所谓的完美适配指的是,第一不须要用户缩放和横向滚动条就能正常的查看网站的全部内容;第二是不管文字,图片等在不一样的设备都能显示出差很少的效果。ideal viewport 并无一个固定的尺寸,不一样的设备拥有有不一样的 ideal viewport。
mata 标签与 viewport 的关系
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
移动设备默认的是 layout viewport , 可是咱们须要的是 ideal viewport, 那么经过 meta 标签的做用就是:让当前 viewport 的宽度等于设备的宽度,同时不容许用户手动缩放。
meta 标签中 content 的属性和值以下:
一、物理像素(设备像素)
红蓝绿能够调配出任何颜色,一般说的手机像素就是由许多红蓝绿组成的一个小块,1 个小块表示 1 个像素。一个物理像素是显示器(手机屏幕)上最小的物理显示单元,经过控制每一个像素点的颜色,使屏幕显示出不一样的图像。屏幕从工厂出来那天起,它上面的物理像素点就固定不变了,单位 pt - 固定单位。
好比:iPhone六、七、8 的分辨率为 1334*750 像素表示,横向 750 个像素,纵向 1334 个像素
二、CSS 像素
CSS 和 JS 使用的抽象单位,浏览器内的一切长度都是以 CSS 像素为单位的,CSS 像素的单位是 px。
一倍屏:当设备像素比为 1:1 时,使用 1(1×1)个设备像素显示 1 个 CSS 像素;
二倍屏:当设备像素比为 2:1 时,使用 4(2×2)个设备像素显示 1 个 CSS 像素;
三倍屏:当设备像素比为 3:1 时,使用 9(3×3)个设备像素显示 1 个 CSS 像素。
三、像素密度(PPI)
每英寸像素取值,也就是衡量单位物理面积内拥有像素值的状况。
ppi 越高,每英寸像素点越多,图像越清晰;咱们能够类比物体的密度,密度越大,单位体积的质量就越大,ppi 越高,单位面积的像素越多。
ppi 在 120-160 之间的手机被归为低密度手机,160-240 被归为中密度,240-320 被归为高密度,320 以上被归为超高密度(例如:苹果的 Retina 屏幕)
__proto__
和 prototype
的区别 ?__proto__
,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例可以访问在构造函数原型中定义的属性和方法。 在计算机中,浮点表示法,分为三大部分:
第一部分用来存储符号位(sign),用来区分正负数,0 表示正数
第二部分用来存储指数(exponent)
第三部分用来存储小数(fraction), 多出的末尾若是是 1 须要进位;
双精度浮点数一共占据 64 位:
符号位(sign)占用 1 位
指数位(exponent)占用 11 位
小数位(fraction)占用 52 位
举个例子:0.1 的二进制为
0.00011001100110011001100110011001100110011001100110011001 10011...
转化为 2 进制科学计数法
1.1001100110011001100110011001100110011001100110011001 * (2 ^ -4);
也就是说 0.1 的:
指数位为负数的怎么保存?为了减小没必要要的麻烦,IEEE 规定了一个偏移量,对于指数部分,每次都加这个偏移量进行保存,这样即便指数是负数,那么加上这个偏移量也变为正数啦。为了使全部的负指数加上这个偏移量都可以变为正数,IEEE 规定 1023 为双精度的偏移量。
所以指数部分为 -4 + 1023 = 1019, 转化成 11 位二进制为:01111111011
所以 0.1 在内存中的保存为:
0 01111111011 1001100110011001100110011001100110011001100110011001
最早想到的解法是用 map 纪录每一个字符的次数,而后找出最多的便可:
function getMaxNumberOfChar(str) { return (str + "").split("").reduce( function(pre, cur, index, arr) { cur in pre ? pre[cur]++ : (pre[cur] = 1); pre[cur] > pre.value && ((pre.char = cur), (pre.value = pre[cur])); return pre; }, { value: 0 } ); } getMaxNumberOfChar("ababccdeajxac"); // Object {value: 4, a: 4, char: "a", b: 2, c: 3…}
此外,能够考虑用正则来辅助处理:
function getMaxNumberOfChar(str) { return (str + "") .split("") .sort() .join("") .match(/(\w)\1*/g) .reduce( function(pre, cur) { return cur.length > pre.value ? { value: cur.length, char: cur[0] } : pre; }, { value: 0 } ); } getMaxNumberOfChar("ababccdeajxac"); // Object {value: 4, char: "a"}
这里拓展一下 reduce 函数的用法
// reduce 函数 // array.reduce(function(accumulator, currentValue, currentIndex, arr), initialValue) // reducer回调函数自己接受几个参数,第一个参数是 accumulator 累加器,第二个是数组中的 item,第三个参数是该项的索引,最后一个参数是原始数组的引用。 // initialValue 为reduce初始值,不然视数组第一个值为初始值,选填 const array1 = [1, 2, 3, 4]; // 1 + 2 + 3 + 4 console.log( array1.reduce((accumulator, currentValue) => { console.log(accumulator, currentValue); return accumulator + currentValue; }) );
var args=[].slice.call(arguments,1)
是什么意思?先看原函数:
function a() { var args = [].slice.call(arguments, 1); console.log(args); } a("haha", 1, 2, 3, 4, 5); // log出[1, 2, 3, 4, 5] a("run", "-g", "-b"); // log出['-g', '-b']
arguments 是什么?
arguments 是函数中的一个类数组的参数集合对象
如: {'0': 'haha', '1': 1, '2': 2}
slice 为数组可从已有的数组中返回选定的元素。
此题为从 index = 1 日后
var a = { n: 1 }; var b = a; a.x = a = { n: 2 }; // a ? b ? a.x ? 结果是什么?
<details> <summary>点击</summary> 咱们能够先尝试交换下连等赋值顺序(a = a.x = {n: 2};),能够发现输出不变,即顺序不影响结果。 那么如今来解释对象连等赋值的问题:按照 es5 规范,题中连等赋值等价于 a.x = (a = { n: 2 });,按优先获取左引用(lref),而后获取右引用(rref)的顺序,a.x 和 a 中的 a 都指向了{ n: 1 }。至此,相当重要或者说最迷惑的一步明确。(a = {n: 2})执行完成后,变量 a 指向{n: 2},**并返回{n: 2}**;接着执行 a.x = { n: 2 },这里的 a 就是 b(指向{ n: 1 }),因此 b.x 就指向了{ n: 2 }。 赋值时有返回该值, 如 `a = 4 // return 4` , 赋值变量 `let n = 5 //return undefinded` ```js a = { n: 2 }; b = { n: 1, x: { n: 2 } }; a.x = undefinded; ```
promise 的三种状态 pending, resolve, reject
function MyPromise(callback) { let that = this; //定义初始状态 //Promise状态 that.status = "pending"; //value that.value = "undefined"; //reason 是一个用于描述Promise被拒绝缘由的值。 that.reason = "undefined"; //用来解决异步问题的数组 that.onFullfilledArray = []; that.onRejectedArray = []; //定义resolve function resolve(value) { //当status为pending时,定义Javascript值,定义其状态为fulfilled if (that.status === "pending") { that.value = value; that.status = "resolved"; that.onFullfilledArray.forEach(func => { func(that.value); }); } } //定义reject function reject(reason) { //当status为pending时,定义reason值,定义其状态为rejected if (that.status === "pending") { that.reason = reason; that.status = "rejected"; that.onRejectedArray.forEach(func => { func(that.reason); }); } } //捕获callback是否报错 try { callback(resolve, reject); } catch (error) { reject(error); } } MyPromise.prototype.then = function(onFulfilled, onRejected) { let that = this; //须要修改下,解决异步问题,即当Promise调用resolve以后再调用then执行onFulfilled(that.value)。 //用两个数组保存下onFulfilledArray if (that.status === "pending") { that.onFullfilledArray.push(value => { onFulfilled(value); }); that.onRejectedArray.push(reason => { onRejected(reason); }); } if (that.status === "resolved") { onFulfilled(that.value); } if (that.status === "rejected") { onRejected(that.reason); } };
什么是 AST(抽象语法树)?
It is a hierarchical program representation that presents source code structure according to the grammar of a programming language, each AST node corresponds to an item of a source code.它是一种分层程序表示,它根据编程语言的语法呈现源代码结构,每一个 AST 节点对应一个源代码项。
Babel,Webpack,vue-cli 和 esLint 等不少的工具和库的核心都是经过 Abstract Syntax Tree (抽象语法树)这个概念来实现对代码的检查、分析等操做的。
解析(parsing),转译(transforming),生成(generation)。
将源码解析成 AST 抽象语法树,再对此语法树进行相应的转译,最后生成咱们所须要的代码。
第三方的生成 AST 库有不少,这里推荐几个——esprima, babylon(babel 使用)
其转化的内容大体是这样的:
{ "type": "Program", "start": 0, "end": 16, "body": [ { "type": "FunctionDeclaration", "start": 0, "end": 16, "id": { "type": "Identifier", "start": 9, "end": 12, "name": "ast" }, "expression": false, "generator": false, "params": [], "body": { "type": "BlockStatement", "start": 14, "end": 16, "body": [] } } ], "sourceType": "module" }
AST 的使用场景
0.1 + 0.2 !== 0.3
?### IEEE-754 精度问题
全部使用 IEEE-754 数字实现的编程语言都有这个问题。
0.1 和 0.2 的二进制浮点数表示并非精确的,因此相加后不等于 0.3。这个相加的结果接近 0.30000000000000004。
首先将 0.1 转化为 2 进制
// 0.1 十进制 -> 二进制 0.1 * 2 = 0.2 取0 0.2 * 2 = 0.4 取0 0.4 * 2 = 0.8 取0 0.8 * 2 = 1.6 取1 0.6 * 2 = 1.2 取1 0.2 * 2 = 0.4 取0 0.4 * 2 = 0.8 取0 0.8 * 2 = 1.6 取1 0.6 * 2 = 1.2 取1 //0.000110011(0011)` 0.1二进制(0011)里面的数字表示循环
你会发现 0.1 转二级制会一直无线循环下去,根本算不出一个正确的二进制数。
因此咱们得出 0.1 = 0.000110011(0011),那么 0.2 的演算也基本如上所示,因此得出 0.2 = 0.00110011(0011)
六十四位中符号位占一位,整数位占十一位,其他五十二位都为小数位。由于 0.1 和 0.2 都是无限循环的二进制了,因此在小数位末尾处须要判断是否进位(就和十进制的四舍五入同样)
那么把这两个二进制加起来会得出 0.010011....0100 , 这个值算成十进制就是 0.30000000000000004