{
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"extraFileExtensions": [".vue"]
}
}
没有足够的数据
穗乃果 回答了问题 · 2020-03-04
{
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"extraFileExtensions": [".vue"]
}
}
{代码...}
关注 3 回答 2
穗乃果 提出了问题 · 2020-02-19
想做到的是以下映射关系:
api.example.com -> example.com/api
api.example.com/xxx -> example.com/api/xxx
以下写法似乎不对,请求帮助。
server api.example.com;
location ~ ^/(.*)$ {
proxy_pass http://example.com/api/$1;
}
想做到的是以下映射关系:api.example.com -> example.com/apiapi.example.com/xxx -> example.com/api/xxx
关注 2 回答 1
穗乃果 赞了回答 · 2020-01-09
一般是webSocket长链接,收到拿消息的数据后再去get请求拿数据;轮询一般是考虑了低版本浏览器
一般是webSocket长链接,收到拿消息的数据后再去get请求拿数据;轮询一般是考虑了低版本浏览器
关注 7 回答 5
穗乃果 发布了文章 · 2019-11-25
转载请注明出处: [https://github.com/qiu-deqing...](https://github.com/qiu-deqing...
个人收集的前端知识点、面试题和答案,参考答案仅代表个人观点,方便复习,目录如下,通过文档内搜索目录可快速定位章节

常见 dotype:
参考资料:[MDN: html global attribute](https://developer.mozilla.org..._attributes)或者[W3C HTML global-attributes](http://www.w3.org/TR/html-mar...
web 语义化是指通过 HTML 标记表示页面包含的信息,包含了 HTML 标签的语义化和 css 命名的语义化。
HTML 标签的语义化是指:通过使用包含语义的标签(如 h1-h6)恰当地表示文档结构
css 命名的语义化是指:为 html 标签添加有意义的 class,id 补充未表达的语义,如[Microformat](http://en.wikipedia.org/wiki/... class 描述信息
为什么需要语义化:
浏览器查看**缓存**,如果请求资源在缓存中并且新鲜,跳转到转码步骤
检验新鲜通常有两个 HTTP 头进行控制`Expires`和`Cache-Control`:
浏览器**获取主机 ip 地址**,过程如下:
**打开一个 socket 与目标 IP 地址,端口建立 TCP 链接**,三次握手如下:
浏览器接收 HTTP 响应,然后根据情况选择**关闭 TCP 连接或者保留重用,关闭 TCP 连接的四次握手如下**:
**构建 DOM 树**:
构建**CSSOM 树**:
**[根据 DOM 树和 CSSOM 树构建渲染树](https://developers.google.com...**:
**js 解析如下**:

[rfc2616](http://www.w3.org/Protocols/r...:
```
GET /Protocols/rfc2616/rfc2616-sec5.html HTTP/1.1
Host: www.w3.org
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36
Referer: https://www.google.com.hk/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: authorstyle=yes
If-None-Match: "2cc8-3e3073913b100"
If-Modified-Since: Wed, 01 Sep 2004 13:24:52 GMT
name=qiu&age=25
```
[rfc2616](http://www.w3.org/Protocols/r...:
```
HTTP/1.1 200 OK
Date: Tue, 08 Jul 2014 05:28:43 GMT
Server: Apache/2
Last-Modified: Wed, 01 Sep 2004 13:24:52 GMT
ETag: "40d7-3e3073913b100"
Accept-Ranges: bytes
Content-Length: 16599
Cache-Control: max-age=21600
Expires: Tue, 08 Jul 2014 11:28:43 GMT
P3P: policyref="http://www.w3.org/2001/05/P3P/p3p.xml"
Content-Type: text/html; charset=iso-8859-1
{"name": "qiu", "age": 25}
```
[雅虎 Best Practices for Speeding Up Your Web Site](https://developer.yahoo.com/p...:
content 方面
Server 方面
Cookie 方面
css 方面
Javascript 方面
图片方面
移动方面
渐进增强是指在 web 设计时强调可访问性、语义化 HTML 标签、外部样式表和脚本。保证所有人都能访问页面的基本内容和功能同时为高级浏览器和高带宽用户提供更好的用户体验。核心原则如下:
参考[RFC 2616](http://www.w3.org/Protocols/r...
1XX:信息状态码
2XX:成功状态码
3XX:重定向
4XX:客户端错误
5XX: 服务器错误
概念:将多个小图片拼接到一个图片中。通过 background-position 和元素尺寸调节需要显示的背景图案。
优点:
缺点:
联系:它们都能让元素不可见
区别:
原理:利用**不同浏览器对 CSS 的支持和解析结果不一样**编写针对特定浏览器样式。常见的 hack 有 1)属性 hack。2)选择器 hack。3)IE 条件注释
```js
<!--[if IE 6]>
Special instructions for IE 6 here
<![endif]-->
```
```css
/***** Selector Hacks ******/
/* IE6 and below */
* html #uno {
color: red;
}
/* IE7 */
*:first-child + html #dos {
color: red;
}
/* IE7, FF, Saf, Opera */
html > body #tres {
color: red;
}
/* IE8, FF, Saf, Opera (Everything but IE 6,7) */
html>/**/body #cuatro {
color: red;
}
/* Opera 9.27 and below, safari 2 */
html:first-child #cinco {
color: red;
}
/* Safari 2-3 */
html[xmlns*=''] body:last-child #seis {
color: red;
}
/* safari 3+, chrome 1+, opera9+, ff 3.5+ */
body:nth-of-type(1) #siete {
color: red;
}
/* safari 3+, chrome 1+, opera9+, ff 3.5+ */
body:first-of-type #ocho {
color: red;
}
/* saf3+, chrome1+ */
@media screen and (-webkit-min-device-pixel-ratio: 0) {
#diez {
color: red;
}
}
/* iPhone / mobile webkit */
@media screen and (max-device-width: 480px) {
#veintiseis {
color: red;
}
}
/* Safari 2 - 3.1 */
html[xmlns*='']:root #trece {
color: red;
}
/* Safari 2 - 3.1, Opera 9.25 */
*|html[xmlns*=''] #catorce {
color: red;
}
/* Everything but IE6-8 */
:root * > #quince {
color: red;
}
/* IE7 */
* + html #dieciocho {
color: red;
}
/* Firefox only. 1+ */
x:-moz-any-link {
color: red;
}
/* Firefox 3.0+ */
x:-moz-any-link,
x:default {
color: red;
}
```
```
/* IE6 */
/* IE6, IE7 */
/* Everything but IE6 */
/* IE6, IE7, IE8 */
/* IE7, IE8 */
/* IE6, IE7 -- acts as an !important */
```
specified value: 计算方法如下:
used value:属性计算后的最终值,对于大多数属性可以通过 window.getComputedStyle 获得,尺寸值单位为像素。以下属性依赖于布局,
`block`元素特点:
1.处于常规流中时,如果`width`没有设置,会自动填充满父容器 2.可以应用`margin/padding` 3.在没有设置高度的情况下会扩展高度以包含常规流中的子元素 4.处于常规流中时布局时在前后元素位置之间(独占一个水平空间) 5.忽略`vertical-align`
`inline`元素特点
1.水平方向上根据`direction`依次布局 2.不会在元素前后进行换行 3.受`white-space`控制 4.`margin/padding`在竖直方向上无效,水平方向上有效 5.`width/height`属性对非替换行内元素无效,宽度由元素内容决定 6.非替换行内元素的行框高由`line-height`确定,替换行内元素的行框高由`height`,`margin`,`padding`,`border`决定 6.浮动或绝对定位时会转换为`block` 7.`vertical-align`属性生效
参考资料: [选择正确的图片格式](http://www.yuiblog.com/blog/2...
**GIF**:
**JPEG**:
**PNG**:
关于文字排版的属性如:
```
.target {
min-height: 100px;
height: auto !important;
height: 100px; // IE6下内容高度超过会自动扩展高度
}
```
```
<style type="text/css">
.outer {
width: 215px;
height: 100px;
border: 1px solid red;
overflow: auto;
position: relative; /\* 修复bug \*/
}
.inner {
width: 100px;
height: 200px;
background-color: purple;
position: relative;
}
</style>
<div class="outer">
<div class="inner"></div>
</div>
```
```
<style type="text/css">
.p:hover,
.hover {
background: purple;
}
</style>
<p class="p" id="target">aaaa bbbbb<span>DDDDDDDDDDDd</span> aaaa lkjlkjdf j</p>
<script type="text/javascript">
function addClass(elem, cls) {
if (elem.className) {
elem.className += ' ' + cls;
} else {
elem.className = cls;
}
}
function removeClass(elem, cls) {
var className = ' ' + elem.className + ' ';
var reg = new RegExp(' +' + cls + ' +', 'g');
elem.className = className.replace(reg, ' ').replace(/^ +| +$/, '');
}
var target = document.getElementById('target');
if (target.attachEvent) {
target.attachEvent('onmouseenter', function () {
addClass(target, 'hover');
});
target.attachEvent('onmouseleave', function () {
removeClass(target, 'hover');
})
}
</script>
```
```
.opacity {
opacity: 0.4
filter: alpha(opacity=60); /\* for IE5-7 \*/
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; /\* for IE 8\*/
}
```
```
display: inline-block;
\*display: inline;
\*zoom: 1;
```
```
/**
* 在标准浏览器下使用
* 1 content内容为空格用于修复opera下文档中出现
* contenteditable属性时在清理浮动元素上下的空白
* 2 使用display使用table而不是block:可以防止容器和
* 子元素top-margin折叠,这样能使清理效果与BFC,IE6/7
* zoom: 1;一致
**/
.clearfix:before,
.clearfix:after {
content: " "; /\* 1 \*/
display: table; /\* 2 \*/
}
.clearfix:after {
clear: both;
}
/**
* IE 6/7下使用
* 通过触发hasLayout实现包含浮动
**/
.clearfix {
\*zoom: 1;
}
```
Flash Of Unstyled Content:用户定义样式表加载之前浏览器使用默认样式显示文档,用户样式加载渲染之后再从新显示文档,造成页面闪烁。**解决方法**:把样式表放到文档的`head`
创建规则:
作用:
毗邻的两个或多个`margin`会合并成一个 margin,叫做外边距折叠。规则如下:
如果元素`position`为`absolute`,它的包含块由祖先元素中最近一个`position`为`relative`,`absolute`或者`fixed`的元素产生,规则如下:
如果找不到定位的祖先元素,包含块为**初始包含块**
z 轴上的默认层叠顺序如下(从下到上):
如何创建 stacking context:
```
<body>
<div class="content">
aaaaaa aaaaaa a a a a a a a a
</div>
</body>
<style>
body {
background: #DDD;
text-align: center; /\* 3 \*/
}
.content {
width: 500px; /\* 1 \*/
text-align: left; /\* 3 \*/
margin: 0 auto; /\* 2 \*/
background: purple;
}
</style>
```
```
<body>
<div class="content">
aaaaaa aaaaaa a a a a a a a a
</div>
</body>
<style>
body {
background: #DDD;
}
.content {
width: 500px; /\* 1 \*/
float: left;
position: relative; /\* 2 \*/
left: 50%; /\* 3 \*/
margin-left: -250px; /\* 4 \*/
background-color: purple;
}
</style>
```
```
<body>
<div class="content">
aaaaaa aaaaaa a a a a a a a a
</div>
</body>
<style>
body {
background: #DDD;
position: relative;
}
.content {
width: 800px;
position: absolute;
left: 50%;
margin-left: -400px;
background-color: purple;
}
</style>
```
```
<body>
<div class="content">
aaaaaa aaaaaa a a a a a a a a
</div>
</body>
<style>
body {
background: #DDD;
position: relative;
}
.content {
width: 800px;
position: absolute;
margin: 0 auto;
left: 0;
right: 0;
background-color: purple;
}
</style>
```
参考资料:[6 Methods For Vertical Centering With CSS](http://www.vanseodesign.com/c...。 [盘点 8 种 CSS 实现垂直居中](http://blog.csdn.net/freshlov...
```
<p class="text">center text</p>
<style>
.text {
line-height: 200px;
}
</style>
```
[Measuring Element Dimension and Location with CSSOM in Windows Internet Explorer 9](http://msdn.microsoft.com/en-...)

可获得焦点的元素:
例子:鼠标从 div#target 元素移出时进行处理,判断逻辑如下:
<div id="target"><span>test</span></div>
<script type="text/javascript">
var target = document.getElementById('target');
if (target.addEventListener) {
target.addEventListener('mouseout', mouseoutHandler, false);
} else if (target.attachEvent) {
target.attachEvent('onmouseout', mouseoutHandler);
}
function mouseoutHandler(e) {
e = e || window.event;
var target = e.target || e.srcElement;
// 判断移出鼠标的元素是否为目标元素
if (target.id !== 'target') {
return;
}
// 判断鼠标是移出元素还是移到子元素
var relatedTarget = event.relatedTarget || e.toElement;
while (relatedTarget !== target
&& relatedTarget.nodeName.toUpperCase() !== 'BODY') {
relatedTarget = relatedTarget.parentNode;
}
// 如果相等,说明鼠标在元素内部移动
if (relatedTarget === target) {
return;
}
// 执行需要操作
//alert('鼠标移出');
}
</script>
同源:两个文档同源需满足
跨域通信:js 进行 DOM 操作、通信时如果目标与当前窗口不满足同源条件,浏览器为了安全会阻止跨域操作。跨域通信通常有以下方法
六种基本数据类型
一种引用类型
**闭包是在某个作用域内定义的函数,它可以访问这个作用域内的所有变量**。闭包作用域链通常包括三个部分:
闭包常见用途:
重要参考资料:[MDN:Functions_and_function_scope](https://developer.mozilla.org..._and_function_scope)
HTML5 新增应用程序缓存,允许 web 应用将应用程序自身保存到用户浏览器中,用户离线状态也能访问。 1.为 html 元素设置 manifest 属性:`<html manifest="myapp.appcache">`,其中后缀名只是一个约定,真正识别方式是通过`text/cache-manifest`作为 MIME 类型。所以需要配置服务器保证设置正确
2.manifest 文件首行为`CACHE MANIFEST`,其余就是要缓存的 URL 列表,每个一行,相对路径都相对于 manifest 文件的 url。注释以#开头
3.url 分为三种类型:`CACHE`:为默认类型。`NETWORK`:表示资源从不缓存。 `FALLBACK`:每行包含两个 url,第二个 URL 是指需要加载和存储在缓存中的资源, 第一个 URL 是一个前缀。任何匹配该前缀的 URL 都不会缓存,如果从网络中载入这样的 URL 失败的话,就会用第二个 URL 指定的缓存资源来替代。以下是一个文件例子:
```
CACHE MANIFEST
CACHE:
myapp.html
myapp.css
myapp.js
FALLBACK:
videos/ offline_help.html
NETWORK:
cgi/
```
```
localStorage.setItem('x', 1); // storge x->1
localStorage.getItem('x); // return value of x
// 枚举所有存储的键值对
for (var i = 0, len = localStorage.length; i < len; ++i ) {
var name = localStorage.key(i);
var value = localStorage.getItem(name);
}
localStorage.removeItem('x'); // remove x
localStorage.clear(); // remove all data
```
```
document.cookie = 'name=qiu; max-age=9999; path=/; domain=domain; secure';
document.cookie = 'name=aaa; path=/; domain=domain; secure';
// 要改变cookie的值,需要使用相同的名字、路径和域,新的值
// 来设置cookie,同样的方法可以用来改变有效期
// 设置max-age为0可以删除指定cookie
//读取cookie,访问document.cookie返回键值对组成的字符串,
//不同键值对之间用'; '分隔。通过解析获得需要的值
```
[cookieUtil.js](https://github.com/qiu-deqing...:自己写的 cookie 操作工具
1. 如果对象有valueOf()方法并且返回元素值,javascript将返回值转换为数字作为结果
2. 否则,如果对象有toString()并且返回原始值,javascript将返回结果转换为数字作为结果
3. 否则,throws a TypeError
所有比较运算符都支持任意类型,但是**比较只支持数字和字符串**,所以需要执行必要的转换然后进行比较,转换规则如下:
var args = Array.prototype.slice.call(arguments, 0);
```
/**
* 跨浏览器事件处理工具。只支持冒泡。不支持捕获
* @author (qiu_deqing@126.com)
*/
var EventUtil = {
getEvent: function (event) {
return event || window.event;
},
getTarget: function (event) {
return event.target || event.srcElement;
},
// 返回注册成功的监听器,IE中需要使用返回值来移除监听器
on: function (elem, type, handler) {
if (elem.addEventListener) {
elem.addEventListener(type, handler, false);
return handler;
} else if (elem.attachEvent) {
var wrapper = function () {
var event = window.event;
event.target = event.srcElement;
handler.call(elem, event);
};
elem.attachEvent('on' + type, wrapper);
return wrapper;
}
},
off: function (elem, type, handler) {
if (elem.removeEventListener) {
elem.removeEventListener(type, handler, false);
} else if (elem.detachEvent) {
elem.detachEvent('on' + type, handler);
}
},
preventDefault: function (event) {
if (event.preventDefault) {
event.preventDefault();
} else if ('returnValue' in event) {
event.returnValue = false;
}
},
stopPropagation: function (event) {
if (event.stopPropagation) {
event.stopPropagation();
} else if ('cancelBubble' in event) {
event.cancelBubble = true;
}
},
/\*\*
\* keypress事件跨浏览器获取输入字符
\* 某些浏览器在一些特殊键上也触发keypress,此时返回null
\*\*/
getChar: function (event) {
if (event.which == null) {
return String.fromCharCode(event.keyCode); // IE
}
else if (event.which != 0 && event.charCode != 0) {
return String.fromCharCode(event.which); // the rest
}
else {
return null; // special key
}
}
};
```
```
function Shape() {}
function Rect() {}
// 方法1
Rect.prototype = new Shape();
// 方法2
Rect.prototype = Shape.prototype;
// 方法3
Rect.prototype = Object.create(Shape.prototype);
Rect.prototype.area = function () {
// do something
};
```
方法 1:
方法 2:
方法 3:
改进:
```
function Rect() {
Shape.call(this);
}
```
```
function create(obj) {
if (Object.create) {
return Object.create(obj);
}
function f() {};
f.prototype = obj;
return new f();
}
```

```
<style>
width: 200px;
height: 300px;
margin: 40px;
background-color: tomato;
}
</style>
<div id="target"></div>
<script>
function addMask(elem, opacity) {
opacity = opacity || 0.2;
var rect = elem.getBoundingClientRect();
var style = getComputedStyle(elem, null);
var mask = document.createElement('div');
mask.style.position = 'absolute';
var marginLeft = parseFloat(style.marginLeft);
mask.style.left = (elem.offsetLeft - marginLeft) + 'px';
var marginTop = parseFloat(style.marginTop);
mask.style.top = (elem.offsetTop - marginTop) + 'px';
mask.style.zIndex = 9999;
mask.style.opacity = '' + opacity;
mask.style.backgroundColor = '#000';
mask.style.width = (parseFloat(style.marginLeft) +
parseFloat(style.marginRight) + rect.width) + 'px';
mask.style.height = (parseFloat(style.marginTop) +
parseFloat(style.marginBottom) + rect.height) + 'px';
elem.parentNode.appendChild(mask);
}
var target = document.getElementById('target');
addMask(target);
target.addEventListener('click', function () {
console.log('click');
}, false);
</script>
```
```
var days = ['日','一','二','三','四','五','六'];
var date = new Date();
console.log('今天是星期' + days[date.getDay()]);
```
```
for (var i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i + ' ');
}, 100);
}
```
不能输出正确结果,因为循环中 setTimeout 接受的参数函数通过闭包访问变量 i。javascript 运行环境为单线程,setTimeout 注册的函数需要等待线程空闲才能执行,此时 for 循环已经结束,i 值为 5.五个定时输出都是 5
修改方法:将 setTimeout 放在函数立即调用表达式中,将 i 值作为参数传递给包裹函数,创建新闭包
```
for (var i = 0; i < 5; ++i) {
(function (i) {
setTimeout(function () {
console.log(i + ' ');
}, 100);
}(i));
}
```
```
function Page() {}
Page.prototype = {
constructor: Page,
postA: function (a) {
console.log('a:' + a);
},
postB: function (b) {
console.log('b:' + b);
},
postC: function (c) {
console.log('c:' + c);
},
check: function () {
return Math.random() > 0.5;
}
}
function checkfy(obj) {
for (var key in obj) {
if (key.indexOf('post') === 0 && typeof obj\[key\] === 'function') {
(function (key) {
var fn = obj\[key\];
obj\[key\] = function () {
if (obj.check()) {
fn.apply(obj, arguments);
}
};
}(key));
}
}
} // end checkfy()
checkfy(Page.prototype);
var obj = new Page();
obj.postA('checkfy');
obj.postB('checkfy');
obj.postC('checkfy');
```

function deepClone(obj) {
var \_toString = Object.prototype.toString;
// null, undefined, non-object, function
if (!obj || typeof obj !== 'object') {
return obj;
}
// DOM Node
if (obj.nodeType && 'cloneNode' in obj) {
return obj.cloneNode(true);
}
// Date
if (\_toString.call(obj) === '\[object Date\]') {
return new Date(obj.getTime());
}
// RegExp
if (\_toString.call(obj) === '\[object RegExp\]') {
var flags = \[\];
if (obj.global) { flags.push('g'); }
if (obj.multiline) { flags.push('m'); }
if (obj.ignoreCase) { flags.push('i'); }
return new RegExp(obj.source, flags.join(''));
}
var result = Array.isArray(obj) ? \[\] :
obj.constructor ? new obj.constructor() : {};
for (var key in obj ) {
result\[key\] = deepClone(obj\[key\]);
}
return result;
}
function A() {
this.a = a;
}
var a = {
name: 'qiu',
birth: new Date(),
pattern: /qiu/gim,
container: document.body,
hobbys: \['book', new Date(), /aaa/gim, 111\]
};
var c = new A();
var b = deepClone(c);
console.log(c.a === b.a);
console.log(c, b);
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TEst</title>
</head>
<body>
<div>
<input type="button" id ="button1" value="1" />
<input type="button" id ="button2" value="2" />
</div>
<script type="text/javascript">
var btn1 = document.getElementById('button1');
var btn2 = document.getElementById('button2');
addListener(btn1, 'click', function (event) {
btn1.parentNode.insertBefore(btn2, btn1);
});
function addListener(elem, type, handler) {
if (elem.addEventListener) {
elem.addEventListener(type, handler, false);
return handler;
} else if (elem.attachEvent) {
function wrapper() {
var event = window.event;
event.target = event.srcElement;
handler.call(elem, event);
}
elem.attachEvent('on' + type, wrapper);
return wrapper;
}
}
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>TEst</title>
</head>
<body>
<span id="target"></span>
<script type="text/javascript">
// 为了简化。每月默认30天
function getTimeString() {
var start = new Date();
var end = new Date(start.getFullYear() + 1, 0, 1);
var elapse = Math.floor((end - start) / 1000);
var seconds = elapse % 60 ;
var minutes = Math.floor(elapse / 60) % 60;
var hours = Math.floor(elapse / (60 \* 60)) % 24;
var days = Math.floor(elapse / (60 \* 60 \* 24)) % 30;
var months = Math.floor(elapse / (60 \* 60 \* 24 \* 30)) % 12;
var years = Math.floor(elapse / (60 \* 60 \* 24 \* 30 \* 12));
return start.getFullYear() + '年还剩' + years + '年' + months + '月' + days + '日'
+ hours + '小时' + minutes + '分' + seconds + '秒';
}
function domText(elem, text) {
if (text == undefined) {
if (elem.textContent) {
return elem.textContent;
} else if (elem.innerText) {
return elem.innerText;
}
} else {
if (elem.textContent) {
elem.textContent = text;
} else if (elem.innerText) {
elem.innerText = text;
} else {
elem.innerHTML = text;
}
}
}
var target = document.getElementById('target');
setInterval(function () {
domText(target, getTimeString());
}, 1000)
</script>
</body>
</html>
如:[1, [2, [ [3, 4], 5], 6]] => [1, 2, 3, 4, 5, 6]
```
var data = \[1, \[2, \[ \[3, 4\], 5\], 6\]\];
function flat(data, result) {
var i, d, len;
for (i = 0, len = data.length; i < len; ++i) {
d = data\[i\];
if (typeof d === 'number') {
result.push(d);
} else {
flat(d, result);
}
}
}
var result = \[\];
flat(data, result);
console.log(result);
```
如果浏览器支持 Array.isArray()可以直接判断否则需进行必要判断
```
/**
* 判断一个对象是否是数组,参数不是对象或者不是数组,返回false
*
* @param {Object} arg 需要测试是否为数组的对象
* @return {Boolean} 传入参数是数组返回true,否则返回false
*/
function isArray(arg) {
if (typeof arg === 'object') {
return Object.prototype.toString.call(arg) === '\[object Array\]';
}
return false;
}
```
```
if (window.addEventListener) {
var addListener = function (el, type, listener, useCapture) {
el.addEventListener(type, listener, useCapture);
};
}
else if (document.all) {
addListener = function (el, type, listener) {
el.attachEvent('on' + type, function () {
listener.apply(el);
});
};
}
```
作用:浏览器功能检测实现跨浏览器 DOM 事件绑定
优点:
缺点:
改进:
```
var addListener;
if (window.addEventListener) {
addListener = function (el, type, listener, useCapture) {
el.addEventListener(type, listener, useCapture);
return listener;
};
}
else if (window.attachEvent) {
addListener = function (el, type, listener) {
// 标准化this,event,target
var wrapper = function () {
var event = window.event;
event.target = event.srcElement;
listener.call(el, event);
};
el.attachEvent('on' + type, wrapper);
return wrapper;
// 返回wrapper。调用者可以保存,以后remove
};
}
```
```
/**
* 判断对象是否为函数,如果当前运行环境对可调用对象(如正则表达式)
* 的typeof返回'function',采用通用方法,否则采用优化方法
*
* @param {Any} arg 需要检测是否为函数的对象
* @return {boolean} 如果参数是函数,返回true,否则false
*/
function isFunction(arg) {
if (arg) {
if (typeof (/./) !== 'function') {
return typeof arg === 'function';
} else {
return Object.prototype.toString.call(arg) === '\[object Function\]';
}
} // end if
return false;
}
```
```
/**
* 解析query string转换为对象,一个key有多个值时生成数组
*
* @param {String} query 需要解析的query字符串,开头可以是?,
* 按照application/x-www-form-urlencoded编码
* @return {Object} 参数解析后的对象
*/
function parseQuery(query) {
var result = {};
// 如果不是字符串返回空对象
if (typeof query !== 'string') {
return result;
}
// 去掉字符串开头可能带的?
if (query.charAt(0) === '?') {
query = query.substring(1);
}
var pairs = query.split('&');
var pair;
var key, value;
var i, len;
for (i = 0, len = pairs.length; i < len; ++i) {
pair = pairs\[i\].split('=');
// application/x-www-form-urlencoded编码会将' '转换为+
key = decodeURIComponent(pair\[0\]).replace(/\\+/g, ' ');
value = decodeURIComponent(pair\[1\]).replace(/\\+/g, ' ');
// 如果是新key,直接添加
if (!(key in result)) {
result\[key\] = value;
}
// 如果key已经出现一次以上,直接向数组添加value
else if (isArray(result\[key\])) {
result\[key\].push(value);
}
// key第二次出现,将结果改为数组
else {
var arr = \[result\[key\]\];
arr.push(value);
result\[key\] = arr;
} // end if-else
} // end for
return result;
}
function isArray(arg) {
if (arg && typeof arg === 'object') {
return Object.prototype.toString.call(arg) === '\[object Array\]';
}
return false;
}
/**
console.log(parseQuery('sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8'));
*/
```
```
/**
* 解析一个url并生成window.location对象中包含的域
* location:
* {
* href: '包含完整的url',
* origin: '包含协议到pathname之前的内容',
* protocol: 'url使用的协议,包含末尾的:',
* username: '用户名', // 暂时不支持
* password: '密码', // 暂时不支持
* host: '完整主机名,包含:和端口',
* hostname: '主机名,不包含端口'
* port: '端口号',
* pathname: '服务器上访问资源的路径/开头',
* search: 'query string,?开头',
* hash: '#开头的fragment identifier'
* }
*
* @param {string} url 需要解析的url
* @return {Object} 包含url信息的对象
*/
function parseUrl(url) {
var result = {};
var keys = \['href', 'origin', 'protocol', 'host',
'hostname', 'port', 'pathname', 'search', 'hash'\];
var i, len;
var regexp = /((\[^:\]+:)\\/\\/((\[^:\\/\\?#\]+)(:\\d+)?))(\\/\[^?#\]\*)?(\\?\[^#\]\*)?(#.\*)?/;
var match = regexp.exec(url);
if (match) {
for (i = keys.length - 1; i >= 0; --i) {
result\[keys\[i\]\] = match\[i\] ? match\[i\] : '';
}
}
return result;
}
```
```
/**
* 查询指定窗口的视口尺寸,如果不指定窗口,查询当前窗口尺寸
**/
function getViewportSize(w) {
w = w || window;
// IE9及标准浏览器中可使用此标准方法
if ('innerHeight' in w) {
return {
width: w.innerWidth,
height: w.innerHeight
};
}
var d = w.document;
// IE 8及以下浏览器在标准模式下
if (document.compatMode === 'CSS1Compat') {
return {
width: d.documentElement.clientWidth,
height: d.documentElement.clientHeight
};
}
// IE8及以下浏览器在怪癖模式下
return {
width: d.body.clientWidth,
height: d.body.clientHeight
};
}
```
/\*\*
\* 获取指定window中滚动条的偏移量,如未指定则获取当前window
\* 滚动条偏移量
\*
\* @param {window} w 需要获取滚动条偏移量的窗口
\* @return {Object} obj.x为水平滚动条偏移量,obj.y为竖直滚动条偏移量
\*/
function getScrollOffset(w) {
w = w || window;
// 如果是标准浏览器
if (w.pageXOffset != null) {
return {
x: w.pageXOffset,
y: w.pageYOffset
};
}
// 老版本IE,根据兼容性不同访问不同元素
var d = w.document;
if (d.compatMode === 'CSS1Compat') {
return {
x: d.documentElement.scrollLeft,
y: d.documentElement.scrollTop
}
}
return {
x: d.body.scrollLeft,
y: d.body.scrollTop
};
}
function richText(text) {
var div = document.createElement('div');
div.innerHTML = text;
var p = div.getElementsByTagName('p');
var i, len;
for (i = 0, len = p.length; i < len; ++i) {
if (p\[i\].getElementsByTagName('img').length === 1) {
p\[i\].classList.add('pic');
}
}
return div.innerHTML;
}
function Event() {
if (!(this instanceof Event)) {
return new Event();
}
this.\_callbacks = {};
}
Event.prototype.on = function (type, handler) {
this\_callbacks = this.\_callbacks || {};
this.\_callbacks\[type\] = this.callbacks\[type\] || \[\];
this.\_callbacks\[type\].push(handler);
return this;
};
Event.prototype.off = function (type, handler) {
var list = this.\_callbacks\[type\];
if (list) {
for (var i = list.length; i >= 0; --i) {
if (list\[i\] === handler) {
list.splice(i, 1);
}
}
}
return this;
};
Event.prototype.trigger = function (type, data) {
var list = this.\_callbacks\[type\];
if (list) {
for (var i = 0, len = list.length; i < len; ++i) {
list\[i\].call(this, data);
}
}
};
Event.prototype.once = function (type, handler) {
var self = this;
function wrapper() {
handler.apply(self, arguments);
self.off(type, wrapper);
}
this.on(type, wrapper);
return this;
};
```
<ul id="target">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
var target = document.getElementById('target');
var i;
var frag = document.createDocumentFragment();
for (i = target.children.length - 1; i >= 0; --i) {
frag.appendChild(target.children\[i\]);
}
target.appendChild(frag);
</script>
```
```
// define
(function (window) {
function fn(str) {
this.str = str;
}
fn.prototype.format = function () {
var arg = \_\_1\_\_;
return this.str.replace(\_\_2\_\_, function (a, b) {
return arg\[b\] || '';
});
};
window.fn = fn;
})(window);
// use
(function () {
var t = new fn('<p><a href="{0}">{1}</a><span>{2}</span></p>');
console.log(t.format('http://www.alibaba.com', 'Alibaba', 'Welcome'));
})();
```
define 部分定义一个简单的模板类,使用{}作为转义标记,中间的数字表示替换目标,format 实参用来替换模板内标记
横线处填:
<form id="target">
<select name="age">
<option value="aaa">aaa</option>
<option value="bbb" selected>bbb</option>
</select>
<select name="friends" multiple>
<option value="qiu" selected>qiu</option>
<option value="de">de</option>
<option value="qing" selected>qing</option>
</select>
<input name="name" value="qiudeqing">
<input type="password" name="password" value="11111">
<input type="hidden" name="salery" value="3333">
<textarea name="description">description</textarea>
<input type="checkbox" name="hobby" checked value="football">Football
<input type="checkbox" name="hobby" value="basketball">Basketball
<input type="radio" name="sex" checked value="Female">Female
<input type="radio" name="sex" value="Male">Male
</form>
<script>
/\*\*
\* 将一个表单元素序列化为可提交的字符串
\*
\* @param {FormElement} form 需要序列化的表单元素
\* @return {string} 表单序列化后的字符串
\*/
function serializeForm(form) {
if (!form || form.nodeName.toUpperCase() !== 'FORM') {
return;
}
var result = \[\];
var i, len;
var field, fieldName, fieldType;
for (i = 0, len = form.length; i < len; ++i) {
field = form.elements\[i\];
fieldName = field.name;
fieldType = field.type;
if (field.disabled || !fieldName) {
continue;
} // enf if
switch (fieldType) {
case 'text':
case 'password':
case 'hidden':
case 'textarea':
result.push(encodeURIComponent(fieldName) + '=' +
encodeURIComponent(field.value));
break;
case 'radio':
case 'checkbox':
if (field.checked) {
result.push(encodeURIComponent(fieldName) + '=' +
encodeURIComponent(field.value));
}
break;
case 'select-one':
case 'select-multiple':
for (var j = 0, jLen = field.options.length; j < jLen; ++j) {
if (field.options\[j\].selected) {
result.push(encodeURIComponent(fieldName) + '=' +
encodeURIComponent(field.options\[j\].value || field.options\[j\].text));
}
} // end for
break;
case 'file':
case 'submit':
break; // 是否处理?
default:
break;
} // end switch
} // end for
return result.join('&');
}
var form = document.getElementById('target');
console.log(serializeForm(form));
</script>
```
<ul id="nav">
<li><a href="http://11111">111</a></li>
<li><a href="http://2222">222</a></li>
<li><a href="http://333">333</a></li>
<li><a href="http://444">444</a></li>
</ul>
Object:
{
"index": 1,
"name": "111",
"link": "http://1111"
}
```
script:
```
var EventUtil = {
getEvent: function (event) {
return event || window.event;
},
getTarget: function (event) {
return event.target || event.srcElement;
},
// 返回注册成功的监听器,IE中需要使用返回值来移除监听器
on: function (elem, type, handler) {
if (elem.addEventListener) {
elem.addEventListener(type, handler, false);
return handler;
} else if (elem.attachEvent) {
function wrapper(event) {
return handler.call(elem, event);
};
elem.attachEvent('on' + type, wrapper);
return wrapper;
}
},
off: function (elem, type, handler) {
if (elem.removeEventListener) {
elem.removeEventListener(type, handler, false);
} else if (elem.detachEvent) {
elem.detachEvent('on' + type, handler);
}
},
preventDefault: function (event) {
if (event.preventDefault) {
event.preventDefault();
} else if ('returnValue' in event) {
event.returnValue = false;
}
},
stopPropagation: function (event) {
if (event.stopPropagation) {
event.stopPropagation();
} else if ('cancelBubble' in event) {
event.cancelBubble = true;
}
}
};
var DOMUtil = {
text: function (elem) {
if ('textContent' in elem) {
return elem.textContent;
} else if ('innerText' in elem) {
return elem.innerText;
}
},
prop: function (elem, propName) {
return elem.getAttribute(propName);
}
};
var nav = document.getElementById('nav');
EventUtil.on(nav, 'click', function (event) {
var event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
var children = this.children;
var i, len;
var anchor;
var obj = {};
for (i = 0, len = children.length; i < len; ++i) {
if (children\[i\] === target) {
obj.index = i + 1;
anchor = target.getElementsByTagName('a')\[0\];
obj.name = DOMUtil.text(anchor);
obj.link = DOMUtil.prop(anchor, 'href');
}
}
alert('index: ' + obj.index + ' name: ' + obj.name +
' link: ' + obj.link);
});
```
```
/\*\*
\* 数组去重
\*\*/
function normalize(arr) {
if (arr && Array.isArray(arr)) {
var i, len, map = {};
for (i = arr.length; i >= 0; --i) {
if (arr\[i\] in map) {
arr.splice(i, 1);
} else {
map\[arr\[i\]\] = true;
}
}
}
return arr;
}
/\*\*
\* 用100个随机整数对应的字符串填充数组。
\*\*/
function fillArray(arr, start, end) {
start = start == undefined ? 1 : start;
end = end == undefined ? 100 : end;
if (end <= start) {
end = start + 100;
}
var width = end - start;
var i;
for (i = 100; i >= 1; --i) {
arr.push('' + (Math.floor(Math.random() \* width) + start));
}
return arr;
}
var input = \[\];
fillArray(input, 1, 100);
input.sort(function (a, b) {
return a - b;
});
console.log(input);
normalize(input);
console.log(input);
```
查看原文合理的 title、description、keywords:搜索对着三项的权重逐个减小,title 值强调重点即可,重要关键词出现不要超过 2 次,而且要靠前,不同页面 title 要有所不同;description 把页面内容高度概括,长度合适,不可过分堆砌关键词,不同页面 description 有所不同...
赞 8 收藏 8 评论 1
穗乃果 赞了文章 · 2019-09-20
在我们平时的工作开发中,大多数都是大人协同开发的公共项目;在我们平时开发中代码codeing的时候我们考虑代码的可读性
、复用性
和扩展性
。
干净的代码,既在质量上较为可靠,也为后期维护、升级奠定了良好基础。
我们从以下几个方面进行探讨:
一般我们在定义变量是要使用有意义的词汇命令,要做到见面知义
//bad code
const yyyymmdstr = moment().format('YYYY/MM/DD');
//better code
const currentDate = moment().format('YYYY/MM/DD');
通过一个变量生成了一个新变量,也需要为这个新变量命名,也就是说每个变量当你看到他第一眼你就知道他是干什么的。
//bad code
const ADDRESS = 'One Infinite Loop, Cupertino 95014';
const CITY_ZIP_CODE_REGEX = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(ADDRESS.match(CITY_ZIP_CODE_REGEX)[1], ADDRESS.match(CITY_ZIP_CODE_REGEX)[2]);
//better code
const ADDRESS = 'One Infinite Loop, Cupertino 95014';
const CITY_ZIP_CODE_REGEX = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [, city, zipCode] = ADDRESS.match(CITY_ZIP_CODE_REGEX) || [];
saveCityZipCode(city, zipCode);
在for、forEach、map的循环中我们在命名时要直接
//bad code
const locations = ['Austin', 'New York', 'San Francisco'];
locations.map((l) => {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
// 需要看其他代码才能确定 'l' 是干什么的。
dispatch(l);
});
//better code
const locations = ['Austin', 'New York', 'San Francisco'];
locations.forEach((location) => {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
dispatch(location);
});
例如我们只创建一个对象是,没有必要再把每个对象的属性上再加上对象名
//bad code
const car = {
carMake: 'Honda',
carModel: 'Accord',
carColor: 'Blue'
};
function paintCar(car) {
car.carColor = 'Red';
}
//better code
const car = {
make: 'Honda',
model: 'Accord',
color: 'Blue'
};
function paintCar(car) {
car.color = 'Red';
}
//bad code
function createMicrobrewery(name) {
const breweryName = name || 'Hipster Brew Co.';
// ...
}
//better code
function createMicrobrewery(name = 'Hipster Brew Co.') {
// ...
}
一般参数多的话要使用ES6的解构传参的方式
//bad code
function createMenu(title, body, buttonText, cancellable) {
// ...
}
//better code
function createMenu({ title, body, buttonText, cancellable }) {
// ...
}
//better code
createMenu({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
});
一个方法里面最好只做一件事,不要过多的处理,这样代码的可读性非常高
//bad code
function emailClients(clients) {
clients.forEach((client) => {
const clientRecord = database.lookup(client);
if (clientRecord.isActive()) {
email(client);
}
});
}
//better code
function emailActiveClients(clients) {
clients
.filter(isActiveClient)
.forEach(email);
}
function isActiveClient(client) {
const clientRecord = database.lookup(client);
return clientRecord.isActive();
}
//bad code
const menuConfig = {
title: null,
body: 'Bar',
buttonText: null,
cancellable: true
};
function createMenu(config) {
config.title = config.title || 'Foo';
config.body = config.body || 'Bar';
config.buttonText = config.buttonText || 'Baz';
config.cancellable = config.cancellable !== undefined ? config.cancellable : true;
}
createMenu(menuConfig);
//better code
const menuConfig = {
title: 'Order',
// 'body' key 缺失
buttonText: 'Send',
cancellable: true
};
function createMenu(config) {
config = Object.assign({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
}, config);
// config 就变成了: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
// ...
}
createMenu(menuConfig);
函数接收一个值返回一个新值,除此之外的行为我们都称之为副作用,比如修改全局变量、对文件进行 IO 操作等。
当函数确实需要副作用时,比如对文件进行 IO 操作时,请不要用多个函数/类进行文件操作,有且仅用一个函数/类来处理。也就是说副作用需要在唯一的地方处理。
副作用的三大天坑:随意修改可变数据类型、随意分享没有数据结构的状态、没有在统一地方处理副作用。
//bad code
// 全局变量被一个函数引用
// 现在这个变量从字符串变成了数组,如果有其他的函数引用,会发生无法预见的错误。
var name = 'Ryan McDermott';
function splitIntoFirstAndLastName() {
name = name.split(' ');
}
splitIntoFirstAndLastName();
console.log(name); // ['Ryan', 'McDermott'];
//better code
var name = 'Ryan McDermott';
var newName = splitIntoFirstAndLastName(name)
function splitIntoFirstAndLastName(name) {
return name.split(' ');
}
console.log(name); // 'Ryan McDermott';
console.log(newName); // ['Ryan', 'McDermott'];
在 JavaScript 中,基本类型通过赋值传递,对象和数组通过引用传递。以引用传递为例:
假如我们写一个购物车,通过 addItemToCart()
方法添加商品到购物车,修改 购物车数组
。此时调用 purchase()
方法购买,由于引用传递,获取的 购物车数组
正好是最新的数据。
看起来没问题对不对?
如果当用户点击购买时,网络出现故障, purchase()
方法一直在重复调用,与此同时用户又添加了新的商品,这时网络又恢复了。那么 purchase()
方法获取到 购物车数组
就是错误的。
为了避免这种问题,我们需要在每次新增商品时,克隆 购物车数组
并返回新的数组。
//bad code
const addItemToCart = (cart, item) => {
cart.push({ item, date: Date.now() });
};
//better code
const addItemToCart = (cart, item) => {
return [...cart, {item, date: Date.now()}]
};
在 JavaScript 中,永远不要污染全局,会在生产环境中产生难以预料的 bug。举个例子,比如你在 Array.prototype
上新增一个 diff
方法来判断两个数组的不同。而你同事也打算做类似的事情,不过他的 diff
方法是用来判断两个数组首位元素的不同。很明显你们方法会产生冲突,遇到这类问题我们可以用 ES2015/ES6 的语法来对 Array
进行扩展。
//bad code
Array.prototype.diff = function diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
};
//better code
class SuperArray extends Array {
diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
}
}
JavaScript 是无类型的,意味着你可以传任意类型参数,这种自由度很容易让人困扰,不自觉的就会去检查类型。仔细想想是你真的需要检查类型还是你的 API 设计有问题?
//bad code
function travelToTexas(vehicle) {
if (vehicle instanceof Bicycle) {
vehicle.pedal(this.currentLocation, new Location('texas'));
} else if (vehicle instanceof Car) {
vehicle.drive(this.currentLocation, new Location('texas'));
}
}
//better code
function travelToTexas(vehicle) {
vehicle.move(this.currentLocation, new Location('texas'));
}
如果你需要做静态类型检查,比如字符串、整数等,推荐使用 TypeScript,不然你的代码会变得又臭又长。
//bad code
function combine(val1, val2) {
if (typeof val1 === 'number' && typeof val2 === 'number' ||
typeof val1 === 'string' && typeof val2 === 'string') {
return val1 + val2;
}
throw new Error('Must be of type String or Number');
}
//better code
function combine(val1, val2) {
return val1 + val2;
}
我们编写js代码时经常遇到复杂逻辑判断的情况,通常大家可以用if/else或者switch来实现多个条件判断,但这样会有个问题,随着逻辑复杂度的增加,代码中的if/else/switch会变得越来越臃肿,越来越看不懂,那么如何更优雅的写判断逻辑
点击列表按钮事件
/**
* 按钮点击事件
* @param {number} status 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消
*/
const onButtonClick = (status)=>{
if(status == 1){
sendLog('processing')
jumpTo('IndexPage')
}else if(status == 2){
sendLog('fail')
jumpTo('FailPage')
}else if(status == 3){
sendLog('fail')
jumpTo('FailPage')
}else if(status == 4){
sendLog('success')
jumpTo('SuccessPage')
}else if(status == 5){
sendLog('cancel')
jumpTo('CancelPage')
}else {
sendLog('other')
jumpTo('Index')
}
}
从上面我们可以看到的是通过不同的状态来做不同的事情,代码看起来非常不好看,大家可以很轻易的提出这段代码的改写方案,switch出场:
/**
* 按钮点击事件
* @param {number} status 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消
*/
const onButtonClick = (status)=>{
switch (status){
case 1:
sendLog('processing')
jumpTo('IndexPage')
break
case 2:
case 3:
sendLog('fail')
jumpTo('FailPage')
break
case 4:
sendLog('success')
jumpTo('SuccessPage')
break
case 5:
sendLog('cancel')
jumpTo('CancelPage')
break
default:
sendLog('other')
jumpTo('Index')
break
}
}
这样看起来比if/else清晰多了,细心的同学也发现了小技巧,case 2和case 3逻辑一样的时候,可以省去执行语句和break,则case 2的情况自动执行case 3的逻辑。
将判断条件作为对象的属性名,将处理逻辑作为对象的属性值,在按钮点击的时候,通过对象属性查找的方式来进行逻辑判断,这种写法特别适合一元条件判断的情况。
const actions = {
'1': ['processing','IndexPage'],
'2': ['fail','FailPage'],
'3': ['fail','FailPage'],
'4': ['success','SuccessPage'],
'5': ['cancel','CancelPage'],
'default': ['other','Index'],
}
/**
* 按钮点击事件
* @param {number} status 活动状态:1开团进行中 2开团失败 3 商品售罄 4 开团成功 5 系统取消
*/
const onButtonClick = (status)=>{
let action = actions[status] || actions['default'],
logName = action[0],
pageName = action[1]
sendLog(logName)
jumpTo(pageName)
}
const actions = new Map([
[1, ['processing','IndexPage']],
[2, ['fail','FailPage']],
[3, ['fail','FailPage']],
[4, ['success','SuccessPage']],
[5, ['cancel','CancelPage']],
['default', ['other','Index']]
])
/**
* 按钮点击事件
* @param {number} status 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消
*/
const onButtonClick = (status)=>{
let action = actions.get(status) || actions.get('default')
sendLog(action[0])
jumpTo(action[1])
}
这样写用到了es6里的Map对象,是不是更爽了?Map对象和Object对象有什么区别呢?
//bad code
const DAYS_IN_WEEK = 7;
const daysInMonth = 30;
const songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude'];
const Artists = ['ACDC', 'Led Zeppelin', 'The Beatles'];
function eraseDatabase() {}
function restore_database() {}
class animal {}
class Alpaca {}
//better code
const DAYS_IN_WEEK = 7;
const DAYS_IN_MONTH = 30;
const SONGS = ['Back In Black', 'Stairway to Heaven', 'Hey Jude'];
const ARTISTS = ['ACDC', 'Led Zeppelin', 'The Beatles'];
function eraseDatabase() {}
function restoreDatabase() {}
class Animal {}
class Alpaca {}
//bad code
class PerformanceReview {
constructor(employee) {
this.employee = employee;
}
lookupPeers() {
return db.lookup(this.employee, 'peers');
}
lookupManager() {
return db.lookup(this.employee, 'manager');
}
getPeerReviews() {
const peers = this.lookupPeers();
// ...
}
perfReview() {
this.getPeerReviews();
this.getManagerReview();
this.getSelfReview();
}
getManagerReview() {
const manager = this.lookupManager();
}
getSelfReview() {
// ...
}
}
const review = new PerformanceReview(employee);
review.perfReview();
//better code
class PerformanceReview {
constructor(employee) {
this.employee = employee;
}
perfReview() {
this.getPeerReviews();
this.getManagerReview();
this.getSelfReview();
}
getPeerReviews() {
const peers = this.lookupPeers();
// ...
}
lookupPeers() {
return db.lookup(this.employee, 'peers');
}
getManagerReview() {
const manager = this.lookupManager();
}
lookupManager() {
return db.lookup(this.employee, 'manager');
}
getSelfReview() {
// ...
}
}
const review = new PerformanceReview(employee);
review.perfReview();
查看原文前言 在我们平时的工作开发中,大多数都是大人协同开发的公共项目;在我们平时开发中代码codeing的时候我们考虑代码的可读性、复用性和扩展性。 干净的代码,既在质量上较为可靠,也为后期维护、升级奠定了良好基础。 我们从以下几个方面进行探讨: 变量 1、变量命名...
赞 123 收藏 83 评论 9
穗乃果 回答了问题 · 2019-02-02
不想踩坑就用moment
不想踩坑就用moment
关注 4 回答 4
穗乃果 评论了文章 · 2019-01-17
最近想要学习一下swoole,虽然机子上装的是php7.0,但是考虑到一些有关swoole的轮子要依赖更高版本(例如swooletw),所以就在机子上升级了php7.2,下面是在网上搜索或者自己折腾出来的一些笔记。
sudo add-apt-repository ppa:ondrej/php
sudo apt-get update
sudo apt-get upgrade php
# 拓展升级
sudo apt-get install php7.2-mbstring
sudo apt-get install php7.2-gd
sudo apt-get install php7.2-dom
sudo apt-get install php7.2-mysql
sudo apt-get install php7.2-curl
......
有需要可以安装swoole: https://wiki.swoole.com/wiki/...
这里建议直接用pecl install swoole安装,一步到位,如果手动编译,貌似会出现各种因为版本对应不上的问题。
# 禁用7.0版本
sudo a2dismod php7.0
# 启用7.2版本
sudo a2enmod php7.2
# 重启nginx(或apache)
sudo service nginx restart
如果出现ERROR: Module php7.0 does not exist! ,执行sudo apt-get install libapache2-mod-php7.0再执行上面操作
这时候在浏览器查看phpinfo()应该就可以看到7.2版本了,但是命令行cli那里还是7.0,需要执行下面操作:
# 切换cli
sudo update-alternatives --set php /usr/bin/php7.2
这样在命令行执行php -v也能看到7.2版本了
如果有两个版本,其实可以直接用sudo service php7.0-fpm stop/restart来进行切换,当然这只能切换php-fpm,也就是web端,命令行cli端还是不能切换的
升级版本后,我还折腾了php7.1,然后第二天回来启动电脑就发现所以laravel项目都报这样的错,网上搜索后知道是版本问题,但是看了下php -v没问题就是7.2,这时候打算重启php报错,以下的报错就是问题所在了:
ERROR: unable to bind listening socket for address '127.0.0.1:9001': Address already in use (98)
大致就是端口被占用,然后php fastCGI程序启动失败
解决方法:
ps aux|grep php查看所有php-fpm进程,发现有个php7.1-fpm进程,拿到进程id,再kill掉就能够重启了。
网上查询的解决方法是killall php-pfm这个命令,执行后只有显示 php-pfm:没有发现操作,这种方法没用,最后使用上面这种查询进程,然后根据进程id杀死进程的方法查看原文
最近想要学习一下swoole,虽然机子上装的是php7.0,但是考虑到一些有关swoole的轮子要依赖更高版本(例如swooletw),所以就在机子上升级了php7.2,下面是在网上搜索或者自己折腾出来的一些笔记。
穗乃果 回答了问题 · 2019-01-04
token是非标准头,需要后端在header中输出Access-Control-Allow-Origin
头,另外在postman中请求不会存在跨域问题,只有浏览器中才有
token是非标准头,需要后端在header中输出Access-Control-Allow-Origin头,另外在postman中请求不会存在跨域问题,只有浏览器中才有
关注 3 回答 2
穗乃果 评论了文章 · 2018-12-24
日常工作中再牛逼的大佬都不敢说自己的代码是完全没有问题的,既然有问题,那就也就有调试,说到调试工具,大家可能对于fiddler
、Charles
、chrome devtools
、Firebug
、还有Safari远程调试等比较熟悉,甚至有些是我可能也没有用过的;
这里喷一句吧,谁都别给我提IE啊,IE那个不叫调试工具,那叫坑爹神器,话说最近不是又甩锅了,把自己的革命老根据地都甩了。
俗话说预先善其事必先利其器,今天想给大家分享的是一个可能被人们忽略的小工具,为什么说被人们忽略呢?因为发现github上它才4.6k Star、457 Fork、Watch 173次,也就是说千万开发者中知道它的人可能不超过5w,于是决定分享一波,此文重在引导,希望能帮大家开发中带来一点点便利、效率的提升:
这里是IT平头哥联盟,我是首席填坑官
—苏南,用心分享 做有温度的攻城狮。
公众号:honeyBadger8
,群:912594095。
Eruda是什么?Eruda 是一个专为前端移动端、移动端设计的调试面板,类似Chrome DevTools
的迷你版(没有chrome强大 这个是可以肯定的),其主要功能包括:捕获 console
日志、检查元素状态、显示性能指标、捕获XHR请求、显示本地存储
和 Cookie
信息、浏览器特性检测等等。
虽说日常的移动端开发时,一般都是在用Chrome DevTools浏览器的移动端模式模拟各种手机型号来进行开发和调试,确保功能/页面展示等都没有问题了,才会提交测试;
但是前面都讲了,只是模拟、模拟,当下手机品牌可算是千千万,手机中各种类浏览器,甚至APP都有自己不一样的特色 腰间盘突出,有的还特别突出,有病我们得给它治啊,不然测试、产品、需求、领导都不会放过我们,比如下图场景。
方式一,默认引入:
<script data-original="//cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init();</script>
方式二,动态加载:
__DEBUG__ && loadJS('http://cdn.jsdelivr.net/eruda/1.0.5/eruda.min.js', ()=>{
eruda.init();
});//苏南的专栏 交流:912594095、公众号:honeyBadger8
方式三 ,指定场景加载:
//比如线上 给自己留一个后门,
//我们一般的做法是喜欢给某个不起眼的元素,添加一个点击事件,要点它十次、八次以后才开启 debug 模式;
;(function () {
var src = 'http://cdn.jsdelivr.net/eruda/1.0.5/eruda.min.js';
if (!/eruda=true/.test(window.location) && localStorage.getItem('active-eruda') != 'true') return;
document.write('<scr' + 'ipt data-original="' + src + '"></scr' + 'ipt>');
document.write('<scr' + 'ipt>eruda.init();</scr' + 'ipt>');
})();
方式四 ,npm:
npm install eruda --save
…… 加载的方式很多
约100kb gzip
);console
诞生之前,我们的调试功能都是alert过多,包括现在的移动端,在手机上我们想看到参数值、数据、节点等都以alert打印为多数,但过于粗暴、而且一不小心有可能带到线上去了;chrome
,直接在控制台执行js代码;Plugins
插件,做到跟PC端一样,形成 dom tree;Application
+ Source,两者的结合体;1.0.5
,好像是没有插件这一项的;Dom tree
,插件这部分并没有都实际应用过,所以也就不打肿脸充胖子
了,有兴趣的同学可以自己看看。以上就是今天给大家带来的分享,工作中用了蛮久,挺方便的,对于定位移动端的疑难杂症问题、甚至留下后门定位线上问题都有很大帮助,如文中有理解不到位之处,欢迎留言指出。
线上问题我们一般的做法是喜欢给某个不起眼的元素,添加一个点击事件,要点它十次、八次以后才开启 debug 模式;
上面二维码确实是真实的官方Demo,不用担心有套路,也有链接:https://eruda.liriliri.io/
Github 地址:https://github.com/liriliri/eruda
作者:苏南 - 首席填坑官查看原文
链接:http://susouth.com/
交流:912594095、公众号:honeyBadger8
本文原创,著作权归作者所有。商业转载请联系@IT·平头哥联盟
获得授权,非商业转载请注明原链接及出处。
日常工作中再牛逼的大佬都不敢说自己的代码是完全没有问题的,既然有问题,那就也就有调试,说到调试工具,大家可能对于fiddler、Charles、chrome devtools、Firebug、还有Safari远程调试等比较熟悉,甚至有些是我可能也没有用过的;
穗乃果 回答了问题 · 2018-12-17
this.$http
改成Vue.prototype.$http
,别忘了import Vue
this.$http改成Vue.prototype.$http,别忘了import Vue
关注 7 回答 6
查看全部 个人动态 →
(゚∀゚ )
暂时没有
A pinterest-style layout site, shows illusts on pixiv.net order by popularity. Written with react.
注册于 2015-10-24
个人主页被 8.7k 人浏览
推荐关注