Open evantianx opened 7 years ago
['1', '2', '3'].map(parseInt)
返回值为?答案💯 : [1, NaN, NaN]
回顾📖 :
map()
方法在调用 callback 时,会传递给它三个参数: 当前正在遍历的元素,元素索引及原数组本身。
其中前两个参数一定会传给回调函数,而第三个可以忽略。
parseInt()
方法可以有两个参数,第二个为进制数。
在没有指定基数,或者基数为 0 的情况下,JavaScript 作如下处理: a. 如果字符串 string 以"0x"或者"0X"开头, 则基数是 16 (16 进制). b. 如果字符串 string 以"0"开头, 基数是 8(八进制)或者 10(十进制),那么具体是哪个基数由实现环境决定。ECMAScript 5 规定使用 10,但是并不是所有的浏览器都遵循这个规定。因此,永远都要明确给出radix 参数的值。 c. 如果字符串 string 以其它任何值开头,则基数是 10 (十进制)。
解析✍️ :
'1' 和 0 分别作为字符串和进制数传给parseInt()
,此时按照十进制转换 '1',得到数字 1;
'2' 和 1 分别作为字符串和进制数传给parseInt()
,此时进制数非法,得到 NaN;
'3' 和 2 分别作为字符串和进制数传给parseInt()
,此时按照二进制转换 '3',无法转换故得到 NaN;
Bonus👏🏻 :
function returnInt (ele) {
return parseInt(ele, radix)
}
['1', '2', '3'].map(returnInt) // 返回[1, 2, 3]
ECMAScript 6 入门 #module ECMAScript modules in browsers 待补充
// 一般写法
if (x === a) {
x = b
} else if (x === b) {
x = a
}
// 简洁写法
x = a ^ b ^ x
if ((x ^ y) >= 0) {
// ...
}
Math.floor()
的方法
Math.floor(4.9) // 4
4.9 | 0 // 4
对小数取整
浮点数是不支持位运算的,所以会先直接去除小数部分,转成整数再进行位运算,就好像是对浮点数向下求整
~~
可以进行类型转换,位运算会默认将非数字类型转换成数字类型再进行运算
~~(5.2) == 5;
看尤大写的 HN 源码习来
rel=noopener
的作用当我们设置一个链接
target='_blank'
的时候,在新打开的页面上window.opener
是指向前一个页面的,也就是说我们可以获取前一个页面的信息,这是非常危险的! 如何解决? 添加rel=noopener
或者rel=noreferrer
/**
* 利用 IE 的注释 hack 判断 IE 浏览器版本号,也可以不传参仅判断是否为 IE
*
* @param {number} version 要检测的 IE 版本号
*/
var isIE = function(ver){
var b = document.createElement('b')
b.innerHTML = '<!--[if IE ' + ver + ']><i></i><![endif]-->'
return b.getElementsByTagName('i').length === 1
}
遗漏 break
关键字的时候容易造成不易发现的 bug。
建议写成:
var tokenActions = {
'{': handleOpen,
'[': handleOpen,
']': handleClose,
'}': handleClose
};
function processToken(token) {
if (typeof tokenActions[token] !== 'function') {
throw new Error('Invalid token.');
}
return tokenActions[token](token);
}
总结下:
getElementsByTagName()
获取到的是 live NodeList;而querySelectorAll()
获取到的是 static NodeList Static NodeList 比较慢的原因在于它要求获取当前 DOM 的一个快照,找到所有符合要求的元素; 而 live NodeList 则无需这样.为什么 getElementsByTagName 比 querySelectorAll 方法快 简单讨论 querySelectorAll Vs getElementsByTagName 区别
关于 npm script
的很多技巧
let / const 变量在声明之前不能访问并不是因为两者不存在声明提前,只是 TDZ!
The variables are created when their containing Lexical Environment is instantiated [...] but may not be accessed in any way until the variable’s LexicalBinding is evaluated.
const arr = Array(100).map((_, i) => i);
console.log(arr[0] === undefined); // true
solution:
const arr = [...Array(100)].map((_, i) => i);
console.log(arr[0] === 0); // true
几个要点:
trailing comma 不会为数组添加一个 hole
数组中逗号之间为空时会形成 hole:
[ 1, ,2 ] // hole at index(1)
检测 hole 的唯一方法是 array.hasOwnProperty(index)
[ 1, ,2 ].hasOwnProperty(1) // false
数组中的遍历方法如 map
, forEach
, every
等都不会在 hole 位置调用回调函数
const a = new Array(1000);
let ctr = 0;
a.forEach(() => ctr++);
console.log(ctr); // 0 - the callback was never called
[ 1, ,2 ].map(x => x*x) // [ 1, <empty>, 4]
[ 1, , 2].filter(x => true) // [1, 2]
keys 和 values 遍历是可以在 hole 位置触发回调的:
const a = [ 'a', , 'b' ]
for (let [index, value] of a.entries()) {
console.log(index, value)
}
/* logs:
* 0 'a'
* 1 undefined
* 2 'b'
*/
由于 ES6 中的数组展开底层实现其实也是遍历,所以展开 sparse array 时会将 hole 变为 undefined
[...[ 'a', , 'b' ]] // [ 'a', undefined, 'b' ]
对于大型 sparse array 的构建来说, new Array()
要快于 Array.from()
reduce
reduce
const reduce = (fn, acc, arr) => {
if (arr.length === 0) return acc
const [ head, ...tail ] = arr
return reduce(fn, fn(acc, head), tail)
}
将用户中的男性和女性分别放到不同的数组里:
const users = [
{ name: "Adam", age: 30, sex: "male" },
{ name: "Helen", age: 27, sex: "female" },
{ name: "Amy", age: 25, sex: "female" },
{ name: "Anthony", age: 23, sex: "male" },
];
const partition = (arr, isValid) =>
arr.reduce(([male, female], cur) =>
isValid(cur) ? [ [ ...male, cur ], female ] : [ male, [...female, cur] ]
, [])
const isMale = item => item.sex === 'male'
const [ male, female ] = partition(users, isMale)
cssText
如何禁止弹出层背景滚动
如何避免条件语句 hell
5 Tips to Write Better Conditionals in JavaScript
Replacing switch statements with Object literals
const actions = ()=>{
const functionA = ()=>{/*do sth*/}
const functionB = ()=>{/*do sth*/}
const functionC = ()=>{/*send log*/}
return new Map([
[/^guest_[1-4]$/,functionA],
[/^guest_5$/,functionB],
[/^guest_.*$/,functionC],
//...
])
}
const onButtonClick = (identity,status) => {
let action = [ ...actions() ].filter(([key,value]) => (key.test(`${identity}_${status}`)))
action.forEach(([key,value]) => value.call(this))
}
利用 map 正则匹配,实现更好地解耦
了解所谓的 JSON hijacking
列出项目直接依赖真正版本号(if use semver)
npm list --depth=0
查看某个包的信息(维护者,依赖项等)
npm view vue
动态删除某个对象的属性
const removeProp = prop => ({ prop, ...rest }) => rest
// eg
const removeName = removeProp('name')
removeName({ id: 1, name: 'John', age: 25 }) // { id: 1, age: 25 }
今天业务需求制作一个 switch button,把制作思路简单梳理下:
首选标签为<input type="checkbox">
,因为我们可以根据:checked
伪类来进行 CSS Transition 变换;
其次,选择<label>
标签作为主战场,因为点击<label>
区域即相当于点击对应的<input>
标签。利用伪类:before
和:after
制作切换按钮的两个部分:按钮和内部区域。
有了这个思路就好办了,步骤如下:
<div class="switch">
<input id="btn" type="checkbox">
<label for="btn"></label>
</div>
<input>
消失,从而不影响后续的样式
* {
box-sizing: border-box; /* 方便样式计算 */
}
.switch {
input {
display: none;
}
}
<label>
设置基本样式
.switch {
input + label {
display: block;
position: relative;
width: 120px;
height: 60px;
cursor: pointer;
outline: none;
padding: 2px;
background-color: #ddd;
border-radius: 60px;
}
}
.switch {
input + label {
&:before, &:after {
content: '';
position: absolute;
top: 1px;
left: 1px;
bottom: 1px;
}
/* <label>内部区域样式 */
&:before {
right: 1px; /* 将:before伪类撑满整个<label> */
background-color: #f1f1f1;
border-radius: 60px;
transition: background 0.8s;
}
/* 圆形按钮样式 */
&:after {
width: 58px;
background-color: #fff;
border-radius: 100%;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
transition: margin 0.4s;
}
}
input:checked + label {
&:before {
background-color: #8ce196;
}
&:after {
margin-left: 60px;
}
}
}
通常会使用::not(first-child)
之类的选择器来避免首尾元素产生不必要的间距或者其他样式。
而如果使用兄弟选择器则完全可以使得代码更为简洁:
li {
background: powderblue;
}
li + li { margin-top: 20px; }
> `former_element + target_element { style properties }`
然而某些需求极为严格,要求在占满一页时最后一个元素不显示 `border-bottom`, 在未占满时最后一个元素显示 `border-bottom`.
```css
/* 假设一页最多可摆放五个列表
此时 0 1 2 3 序号的元素正常显示底线,>= 5 的不显示 👍
*/
li:nth-child(-n+4) {
border-bottom: 1px solid blue;
}
position: fixed
失效的情况fixed不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。打印时,元素会出现在的每页的固定位置。fixed 属性会创建新的层叠上下文。当元素祖先的 transform 属性非 none 时,容器由视口改为该祖先。
主要利用 padding-top
和伪元素
Updated:
利用 SVG 和 Grid 实现
JS
const bodyEl = document.body
let top = 0
function stopBodyScroll (isFixed) {
if (isFixed) {
top = window.scrollY
bodyEl.style.position = 'fixed'
bodyEl.style.top = -top + 'px'
} else {
bodyEl.style.position = ''
bodyEl.style.top = ''
window.scrollTo(0, top) // 回到原先的top
}
}
webkit 下只需要为滚动元素添加下列代码:
element::-webkit-scrollbar {
display: none;
}
其他相关信息参见 Stackoverflow 上一篇回答: Hiding the scrollbar on an HTML page
超出某个宽度则显示省略号:
span {
white-space: nowrap;
text-overflow: ellipsis;
width: 100px;
display: block;
overflow: hidden;
}
超出某行数则显示省略号:
比较兼容的方法,将高度设置为行高的 n 倍( n 即为你要 n 行后省略),此时开始省略行文末显示为渐变白 + 省略号。
.fade {
display: block;
position: relative;
width: 100px;
height: 3.6em;
line-height: 1.2em;
overflow: hidden;
}
.fade:after {
content: "...";
position: absolute;
bottom: 0;
right: -6px;
width: 70%;
height: 1.2em;
text-align: center;
background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1) 50%);
}
webkit 方法:
<div class="module line-clamp">
<p>
<!-- 此处省略一大坨文本 -->
</p>
</div>
.module {
border: 1px solid #CCC;
width: 250px;
margin: 0 0 1em 0;
overflow: hidden;
}
/* 必须设置为 0 ,否则会扰乱布局 */
.module p {
margin: 0;
padding: 0;
}
.line-clamp {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
<div></div>
div {
width: 200px;
padding-top: 60%;
}
function Foo() {
getName = function () { alert (1); };
return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}
//请写出以下输出结果:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
placeholder
在设置line-height
等于height
的情况下,依然表现为不居中(偏上)
解决方法:
input {
line-height: normal;
}
提升滚动性能
Improving Scroll Performance with Passive Event Listeners Speed Up Scroll Events with Passive Event Listeners
background-position
取不同数值类型时其相对坐标是如何定位的?通过 clientHeight
, scrollTop
以及 offsetTop
来判断
function isInSight(el) {
const viewportHeight = window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight; // 视口高度
const scrollTop = document.scrollTop // 滚动条高度,即视口顶端据文档顶端的距离
const elHeight = el.offsetTop // 元素高度,即元素顶端据文档顶端的距离
return (elHeight-scrollTop) <= viewportHeight
}
通过 getBoundingClientRect
来判断
function isInSIght(el) {
const viewportHeight // 获取视口高度同上,代码省略
const bound = el.getBoundingClientRect()
return viewportHeight >= bound.top
}
会造成大量的重绘(repaint)
通过 IntersectionObserver
API 来判断
var io = new IntersectionObserver(callback, option)
// 开始观察
io.observe(document.getElementById('example'))
// 停止观察
io.unobserve(element)
// 关闭观察器
io.disconnect()
IntersectionObserver API 使用教程 IntersectionObserver 使用 IntersectionObserver 和 registerElement 打造 Lazyload
<div>
<p><span>啦啦啦</span></p>
<p><span>啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦</span></p>
</div>
div {
width: 200px;
}
p {
text-align: center;
}
p span {
display: inline-block;
text-align: left;
}
整体思路就是父级添加
text-align: center
属性,子级改变display
为inline-block
并且添加text-align: left
属性。
font-size:0
来清除 inline-block 间距
inline-block
的元素之间会受空白区域的影响,也就是元素之间差不多会有一个字符的间隙。如果在同一行内有 4 个 25% 相同宽度的元素,会导致最后一个元素掉下来。你可以利用元素浮动 float ,或者压缩html,清除元素间的空格来解决。但最简单有效的方法还是设置父元素的font-size
属性为 0(在子元素中设置font-size
)
maxlength
属性在 <input type="number">
上不起作用If the value of the type attribute is text, email, search, password, tel, or url, this attribute(maxlength) specifies the maximum number of characters (in Unicode code points) that the user can enter; for other control types, it is ignored.
所以 type="number"
的输入框会忽视 maxlength
这个属性,可以这样写:
<input type="text" pattern="\d*" maxlength="18">
作者利用了渐变来创建 skeleton screen 中的局部块,不依赖除父级外 HTML 元素,同时使用 css 自定义属性,代码可读性和重用性很高,赞!
:empty
也是一个亮点
<input type="date">
的一系列衍生品async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
console.log("script start");
setTimeout(function() {
console.log("setTimeout");
}, 0);
async1();
new Promise(function(resolve) {
console.log("promise1");
resolve();
}).then(function() {
console.log("promise2");
});
console.log("script end");
输出顺序为:
script start
async1 start
async2
promise1
script end
promise2
async1 end
setTimeout
IDE & 构建工具
PhpStorm
光标停留在
<img>
的src
属性上,按住shift即可显示图片⌘+↑ 打开
nav bar
方便查找跳转项目内文件按住Alt,拖拽鼠标即可竖向选择
双击⌘然后按住,可以弹出侧边和底部的工具栏
⌘+R同时打开搜索栏和替换栏,同理也可用在路径搜索(Shift+⌘+R)✔️
Alt+⌘+T 为选中的代码选择包裹块(if之类的)
若当前未选中任何代码,按下Ctrl+C会复制光标所在行的代码
重构当前代码(变量名),可以按下Ctrl+T
⌘+O 查找类 Shift+⌘+O 查找文件 Alt+⌘+delete 查找变量,方法 Shift+⌘+A 查找动作(记不清快捷键的时候可以用)
Shift+⌘+O 搜索文件,在文件名后添加
:
+行号可以直接跳转到选定文件行Alt+F12 打开终端
Webstorm在代码右上侧提供了浏览器预览按钮 直接点击和按住Shift预览的区别:
F1 查询相关文档
F12 聚焦于工具栏 Esc 聚焦于编辑区 Shift+Esc聚焦于编辑区且关闭当前工具栏
Shift+⌘+delete 返回到上次修改的位置(内容不作变动) Shift+Alt+C 列出最近所做的修改
Shift+⌘+V 选择历史复制记录进行粘贴
Ctrl+Alt+R 弹出
run/debug
菜单右键点击HTML标签,在弹出菜单中选择「show applied styles for tag」,即可查看标签所有样式
VS Code
快速打开文件 Cmd+P 输入文件名并选中即可前往所要查看的文件
设定当前显示语言项
⌘ + K + M
命令行中用 VS Code 打开
lorem
lorem + number 输出几个无意义字符
状态栏 Shift+Cmd+M 打开错误提示状态栏 Cmd+K+M 打开文件语言类型选择栏
文字编辑 Shift+Alt+Up / Down 复制当前行到上或下行 Alt+Up/Down 向上或向下移动当前行 Cmd+Up /Down 快速到达代码第一行或最后一行
快速跳转到与当前括号相匹配的另一个括号处 Ctrl + Cmd + \
关闭左侧编辑器导航
explore.editor.visible:0
BrowserSync
指定入口文件: