while (true) {
宏任务队列.shift();
微任务队列全部任务();
}
while (true) {
loop.forEach((阶段) => {
阶段全部任务();
nextTick全部任务();
microTask全部任务();
});
loop = loop.next;
}
使用HTML Meta 标签
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
使用缓存有关的HTTP消息报头
<script type="text/javascript">
var thisPage = location.href;
var referringPage = (document.referrer) ? document.referrer : "none";
var beacon = new Image();
beacon.src = "http://www.example.com/logger/beacon.gif?page=" + encodeURI(thisPage)
+ "&ref=" + encodeURI(referringPage);
</script>
造成边框变粗的原因:因为css中的1px并不等于移动设备的1px,这些由于不同的手机有不同的像素密度。在window对象中有一个devicePixelRatio属性,他可以反应css中的像素与设备的像素比。
使用0.5px边框实现:
在2014年的 WWDC,“设计响应的Web体验” 一讲中,Ted O’Connor 讲到关于“retinahairlines”(retina 极细的线):在retina屏上仅仅显示1物理像素的边框,开发者应该如何处理呢。他们曾介绍到 iOS 8 和 OS X Yosemite 即将支持 0.5px 的边框。但问题是 retina 屏的浏览器可能不认识0.5px的边框,将会把它解释成0px,没有边框。包括 iOS 7 和之前版本,OS X Mavericks 及以前版本,还有 Android 设备。
解决方案:通过 JavaScript 检测浏览器能否处理0.5px的边框,如果可以,给html标签元素添加个class。
if (window.devicePixelRatio && devicePixelRatio >= 2) {
var testElem = document.createElement('div');
testElem.style.border = '.5px solid transparent';
document.body.appendChild(testElem);
}
if (testElem.offsetHeight == 1) {
document.querySelector('html').classList.add('hairlines');
}
document.body.removeChild(testElem);
}
// 脚本应该放在内,如果在里面运行,需要包装 $(document).ready(function() {})
div {
border: 1px solid #bbb;
}
.hairlines div {
border-width: 0.5px;
}
优点:简单,不需要过多代码。
缺点:无法兼容安卓设备、 iOS 8 以下设备。
使用border-image实现:
.border-bottom-1px {
border-width: 0 0 1px 0;
-webkit-border-image: url(linenew.png) 0 0 2 0 stretch;
border-image: url(linenew.png) 0 0 2 0 stretch;
}
.border-image-1px {
border-width: 1px 0;
-webkit-border-image: url(linenew.png) 2 0 stretch;
border-image: url(linenew.png) 2 0 stretch;
}
.border-image-1px {
border-bottom: 1px solid #666;
}
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
.border-image-1px {
border-bottom: none;
border-width: 0 0 1px 0;
-webkit-border-image: url(../img/linenew.png) 0 0 2 0 stretch;
border-image: url(../img/linenew.png) 0 0 2 0 stretch;
}
}
使用background-image实现:
.background-image-1px {
background: url(../img/line.png) repeat-x left bottom;
-webkit-background-size: 100% 1px;
background-size: 100% 1px;
}
多背景渐变实现:
.background-gradient-1px {
background:
linear-gradient(#000, #000 100%, transparent 100%) left / 1px 100% no-repeat,
linear-gradient(#000, #000 100%, transparent 100%) right / 1px 100% no-repeat,
linear-gradient(#000,#000 100%, transparent 100%) top / 100% 1px no-repeat,
linear-gradient(#000,#000 100%, transparent 100%) bottom / 100% 1px no-repeat
}
/* 或者 */
.background-gradient-1px{
background:
-webkit-gradient(linear, left top, right bottom, color-stop(0, transparent), color-stop(0, #000), to(#000)) left / 1px 100% no-repeat,
-webkit-gradient(linear, left top, right bottom, color-stop(0, transparent), color-stop(0, #000), to(#000)) right / 1px 100% no-repeat,
-webkit-gradient(linear, left top, right bottom, color-stop(0, transparent), color-stop(0, #000), to(#000)) top / 100% 1px no-repeat,
-webkit-gradient(linear, left top, right bottom, color-stop(0, transparent), color-stop(0, #000), to(#000)) bottom / 100% 1px no-repeat
}
使用box-shadow模拟边框:
.box-shadow-1px {
box-shadow: inset 0px -1px 1px -1px #c8c7cc;
}
viewport + rem 实现:
<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
<meta name="viewport" content="initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no">
伪类 + transform 实现:
.scale-1px{
position: relative;
border:none;
}
.scale-1px:after{
content: '';
position: absolute;
bottom: 0;
background: #000;
width: 100%;
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}
.scale-1px{
position: relative;
margin-bottom: 20px;
border:none;
}
.scale-1px:after{
content: '';
position: absolute;
top: 0;
left: 0;
border: 1px solid #000;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 200%;
height: 200%;
-webkit-transform: scale(0.5);
transform: scale(0.5);
-webkit-transform-origin: left top;
transform-origin: left top;
}
if(window.devicePixelRatio && devicePixelRatio >= 2){
document.querySelector('ul').className = 'scale-1px';
}
DOM0事件模型(原始事件模型)
<button onclick="click()">点我</button>
const btn = document.getElementById('btn')
btn.onclick = function(){
//do something
}
//解除事件
btn.onclick = null
DOM2事件模型
btn.addEventListener('click',function(){
console.log('btn')
},true)
box.addEventListener('click',function(){
console.log('box')
},false)
IE事件模型
// 绑定事件
el.attachEvent(eventType, handler)
// 移除事件
el.detachEvent(eventType, handler)
<ul id="list">
<li>item 1li>
<li>item 2li>
<li>item 3li>
......
<li>item nli>
ul>
var $a = document.getElementsByTagName("a")[0];
$a.onclick = function(e){
alert("跳转动作被我阻止了")
e.preventDefault();
//return false;//也可以但仅仅是在HTML事件属性 和 DOM0级事件处理方法中,才能通过返回 return false 的形式组织事件宿主的默认行为。
}
function stopBubble(e){
if(e&&e.stopPropagation){//非IE
e.stopPropagation();
}else{//IE
window.event.cancelBubble=true;
}
}
概述:浏览器的缓存机制也就是我们说的HTTP缓存机制,其机制是根据HTTP报文的缓存标识进行的,所以在分析浏览器缓存机制之前,我们先使用图文简单介绍一下HTTP报文,HTTP报文分为两种:
HTTP请求(Request)报文,报文格式为:请求行 – HTTP头(通用信息头,请求头,实体头) – 请求报文主体(只有POST才有报文主体),如下图:
HTTP响应(Response)报文,报文格式为:状态行 – HTTP头(通用信息头,响应头,实体头) – 响应报文主体,如下图:
注:通用信息头指的是请求和响应报文都支持的头域,分别为Cache-Control、Connection、Date、Pragma、Transfer-Encoding、Upgrade、Via;实体头则是实体信息的实体头域,分别为Allow、Content-Base、Content-Encoding、Content-Language、Content-Length、Content-Location、Content-MD5、Content-Range、Content-Type、Etag、Expires、Last-Modified、extension-header。这里只是为了方便理解,将通用信息头,响应头/请求头,实体头都归为了HTTP头。
缓存过程分析:浏览器与服务器通信的方式为应答模式,即是:浏览器发起HTTP请求 – 服务器响应该请求。那么浏览器第一次向服务器发起该请求后拿到请求结果,会根据响应报文中HTTP头的缓存标识,决定是否缓存结果,是则将请求结果和缓存标识存入浏览器缓存中,简单的过程如下图:
为了方便理解,这里我们根据是否需要向服务器重新发起HTTP请求将缓存过程分为两个部分,分别是强制缓存和协商缓存:
强制缓存:强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程,强制缓存的情况主要有三种(暂不分析协商缓存过程),如下:
强制缓存的缓存规则:当浏览器向服务器发送请求的时候,服务器会将缓存规则放入HTTP响应的报文的HTTP头中和请求结果一起返回给浏览器,控制强制缓存的字段分别是Expires和Cache-Control,其中Cache-Conctrol的优先级比Expires高。
Expires是HTTP/1.0控制网页缓存的字段,其值为服务器返回该请求的结果缓存的到期时间,即再次发送请求时,如果客户端的时间小于Expires的值时,直接使用缓存结果。 到了HTTP/1.1,Expires已经被Cache-Control替代,原因在于Expires控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,如果客户端与服务端的时间由于某些原因(时区不同;客户端和服务端有一方的时间不准确)发生误差,那么强制缓存直接失效,那么强制缓存存在的意义就毫无意义。
在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存,主要取值为:
由于Cache-Control的优先级比expires,那么直接根据Cache-Control的值进行缓存,意思就是说在600秒内再次发起该请求,则会直接使用缓存结果,强制缓存生效。
注:在无法确定客户端的时间是否与服务端的时间同步的情况下,Cache-Control相比于expires是更好的选择,所以同时存在时,只有Cache-Control生效。
浏览器的缓存存放在哪里,如何在浏览器中判断强制缓存是否生效?
那么from memory cache 和 from disk cache又分别代表的是什么呢?什么时候会使用from disk cache,什么时候会使用from memory cache呢?
协商缓存:协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:
总结:
跨域的解决方案:
JSONP 实现:JSONP(JSON with Padding(填充))是 JSON 的一种“使用模式”,本质不是 Ajax 请求,是 script 标签请求。JSONP 请求本质上是利用了 “Ajax 请求会受到同源策略限制,而 script 标签请求不会” 这一点来绕过同源策略。
class Jsonp {
constructor(req) {
this.url = req.url;
this.callbackName = req.callbackName;
}
create() {
const script = document.createElement("script");
const url = `${this.url}?callback=${this.callbackName}`;
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
}
new Jsonp({
url: "http://127.0.0.1:8000/",
callbackName: "getMsg"
}).create();
function getMsg(data) {
data = JSON.parse(data);
console.log(`My name is ${data.name}, and ${data.age} years old.`);
}
const http = require("http");
const querystring = require("querystring");
const server = http.createServer((req, res) => {
const url = req.url;
const query = querystring.parse(url.split("?")[1]);
const { callback } = query;
const data = {
name: "Yang Min",
age: "8"
};
res.end(`${callback}('${JSON.stringify(data)}')`);
});
server.listen(8000);
My name is Yang Min, and 8 years old.
http://127.0.0.1:8000/?callback=getMsg
getMsg("{name: 'Yang Min', age: '8'}");
getMsg("{name: 'Yang Min', age: '8'}")
const jsonp = ({ url, params, callbackName }) => {
const generateURL = () => {
let dataStr = "";
for (let key in params) {
dataStr += `${key}=${params[key]}&`;
}
dataStr += `callback=${callbackName}`;
return `${url}?${dataStr}`;
};
return new Promise((resolve, reject) => {
// 初始化回调函数名称
callbackName =
callbackName ||
"cb" +
Math.random()
.toString()
.replace(".", "");
let scriptEle = document.createElement("script");
scriptEle.src = generateURL();
document.body.appendChild(scriptEle);
// 绑定到 window 上,为了后面调用
window[callbackName] = data => {
resolve(data);
// script 执行完了,成为无用元素,需要清除
document.body.removeChild(scriptEle);
};
});
};
jsonp({
url: "http://127.0.0.1:8000/",
params: {
name: "Yang Min",
age: "8"
},
callbackName: "getData"
})
.then(data => JSON.parse(data))
.then(data => {
console.log(data); // {name: "Yang Min", age: "8"}
});
const http = require("http");
const querystring = require("querystring");
const server = http.createServer((req, res) => {
const url = req.url;
const query = querystring.parse(url.split("?")[1]);
const { name, age, callback } = query;
const data = {
name,
age
}
res.end(`${callback}('${JSON.stringify(data)}')`);
});
server.listen(8000);
function getAjaxData() {
$.ajax({
type: "get",
async: false,
url: "http://127.0.0.1:8000/",
dataType: "jsonp", //由 JSON 改为 JSONP
jsonp: "callback", //传递给请求处理程序或页面的,标识jsonp回调函数名(一般为:callback)
jsonpCallback: "getData", //callback的function名称,成功就会直接走 success 方法
success: function(data) {
data = JSON.parse(data);
console.log(`My name is ${data.name}, and ${data.age} years old.`);
},
error: function() {
console.log("Error");
}
});
}
getAjaxData();
function getAjaxData() {
const def = $.ajax({
type: "get",
async: false,
url: "http://127.0.0.1:8000/",
dataType: "jsonp",
jsonp: "callback",
jsonpCallback: "getData"
});
def
.done(data => {
data = JSON.parse(data);
console.log(`My name is ${data.name}, and ${data.age} years old.`);
})
.fail(err => {
console.log(err);
});
}
跨域资源共享 CORS :跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的 Web 应用被准许访问来自不同源服务器上的指定的资源。 — MDN
let xhr = new XMLHttpRequest();
xhr.withCredentials = true;
Access-Control-Expose-Headers: aaa
1.意外的全局变量
function f1(){
//初始化这个变量没有声明,成为全局变量,不会自动被回收
bar = "this is a global variable"
//window.bar = "this is a global variable"
}
function f1(){
this.bar = "this is a global variable"
}
f1()
2.计时器或回调函数
var someResource = getData()
//这个列子说明只要存在节点才需要定时器和回调函数,此时定时器并没有回收(只有停止时才回收)
setInterval(function(){
var node = document.getElementById('Node');
if(node){
node.innerHTML = JSON.stringify(someResource)
}
},1000)
3.dom清空或删除时,事件未清除导致的内存泄漏
<div id="container"></div>
$('#container').bind('click', function(){
console.log('click');
}).remove();
// 事件没清除
// 解决
<div id="container"></div>
$('#container').bind('click', function(){
console.log('click');
}).off('click').remove();
//把事件清除了,即可从内存中移除
4.闭包
// 匿名函数可以访问父级作用域中的变量
function assignHandler(){
var element = document.getElementById('someElement') //该变量被闭包引用,引用次数至少为1,不会被回收
element.onclick=function(){
alert(element.id)
}
}
function assignHandler(){
var element = document.getElementById('someElement')
var id = element.id
element.onclick=function(){
alert(id)
}
element=null
}
if (typeof Array.prototype.forEach != 'function') {
//不支持,此时我们需要自己定义一个类似forEach功能的函数。
}
function(callback){
for (var i = 0; i < this.length; i++){
callback.apply(this, [this[i], i, this]);
}
}
if (typeof Array.prototype.forEach != 'function') {
Array.prototype.forEach = function(callback){
for (var i = 0; i < this.length; i++){
callback.apply(this, [this[i], i, this]);
}
};
}
数字证书、摘要、数字签名
经常有人开发网页爬虫爬取大家辛辛苦苦一点一点发布的数据成果,有些会影响你的竞争力,有些会降低你的知名度,甚至有些出于恶意爬取你的公开数据后进行全量公开……比如有些食谱网站被爬掉所有食谱,站点被克隆;有些求职网站被爬掉所有职位,被拿去卖信息;甚至有些小说漫画网站赖以生存的内容也很容易被爬取。
Plan:将文本内容进行展示层加密,利用字体的引用特点,把拿给爬虫的数据变成“乱码”。举个栗子:正常来讲,当我们拥有一串数字“12345”并将其放在网站页面上的时候,其实网站页面上显示的并不是简单的数字,而是数字对应的字体的“12345”。这时我们打乱一下字体中图形和字码的对应关系,比如我们搞成这样:
图形:1 2 3 4 5
字码:2 3 1 5 4
这时,如果你想让用户看到“12345”,你在页面中渲染的数字就应该是“23154”。这种手段也可以算作一种加密。