代码输入后果
console.log(1);
setTimeout(() => {console.log(2);
Promise.resolve().then(() => {console.log(3)
});
});
new Promise((resolve, reject) => {console.log(4)
resolve(5)
}).then((data) => {console.log(data);
})
setTimeout(() => {console.log(6);
})
console.log(7);
代码输入后果如下:
1
4
7
5
2
3
6
代码执行过程如下:
- 首先执行 scrip 代码,打印出 1;
- 遇到第一个定时器 setTimeout,将其退出到宏工作队列;
- 遇到 Promise,执行外面的同步代码,打印出 4,遇到 resolve,将其退出到微工作队列;
- 遇到第二个定时器 setTimeout,将其退出到红工作队列;
- 执行 script 代码,打印出 7,至此第一轮执行实现;
- 指定微工作队列中的代码,打印出 resolve 的后果:5;
- 执行宏工作中的第一个定时器 setTimeout,首先打印出 2,而后遇到 Promise.resolve().then(),将其退出到微工作队列;
- 执行完这个宏工作,就开始执行微工作队列,打印出 3;
- 继续执行宏工作队列中的第二个定时器,打印出 6。
响应式设计的概念及基本原理
响应式网站设计(Responsive Web design
)是一个网站可能兼容多个终端,而不是为每一个终端做一个特定的版本。
对于原理:基本原理是通过媒体查问 (@media)
查问检测不同的设施屏幕尺寸做解决。
对于兼容:页面头部必须有 mate 申明的viewport
。
<meta name="’viewport’" content="”width=device-width," initial-scale="1." maximum-scale="1,user-scalable=no”"/>
具体阐明 Event loop
家喻户晓 JS 是门非阻塞单线程语言,因为在最后 JS 就是为了和浏览器交互而诞生的。如果 JS 是门多线程的语言话,咱们在多个线程中解决 DOM 就可能会产生问题(一个线程中新加节点,另一个线程中删除节点),当然能够引入读写锁解决这个问题。
JS 在执行的过程中会产生执行环境,这些执行环境会被程序的退出到执行栈中。如果遇到异步的代码,会被挂起并退出到 Task(有多种 task)队列中。一旦执行栈为空,Event Loop 就会从 Task 队列中拿出须要执行的代码并放入执行栈中执行,所以实质上来说 JS 中的异步还是同步行为。
console.log('script start');
setTimeout(function() {console.log('setTimeout');
}, 0);
console.log('script end');
以上代码尽管 setTimeout
延时为 0,其实还是异步。这是因为 HTML5 标准规定这个函数第二个参数不得小于 4 毫秒,有余会主动减少。所以 setTimeout
还是会在 script end
之后打印。
不同的工作源会被调配到不同的 Task 队列中,工作源能够分为 微工作(microtask)和 宏工作(macrotask)。在 ES6 标准中,microtask 称为 jobs
,macrotask 称为 task
。
console.log('script start');
setTimeout(function() {console.log('setTimeout');
}, 0);
new Promise((resolve) => {console.log('Promise')
resolve()}).then(function() {console.log('promise1');
}).then(function() {console.log('promise2');
});
console.log('script end');
// script start => Promise => script end => promise1 => promise2 => setTimeout
以上代码尽管 setTimeout
写在 Promise
之前,然而因为 Promise
属于微工作而 setTimeout
属于宏工作,所以会有以上的打印。
微工作包含 process.nextTick
,promise
,Object.observe
,MutationObserver
宏工作包含 script
,setTimeout
,setInterval
,setImmediate
,I/O
,UI rendering
很多人有个误区,认为微工作快于宏工作,其实是谬误的。因为宏工作中包含了 script
,浏览器会先执行一个宏工作,接下来有异步代码的话就先执行微工作。
所以正确的一次 Event loop 程序是这样的
- 执行同步代码,这属于宏工作
- 执行栈为空,查问是否有微工作须要执行
- 执行所有微工作
- 必要的话渲染 UI
- 而后开始下一轮 Event loop,执行宏工作中的异步代码
通过上述的 Event loop 程序可知,如果宏工作中的异步代码有大量的计算并且须要操作 DOM 的话,为了更快的 界面响应,咱们能够把操作 DOM 放入微工作中。
Node 中的 Event loop
Node 中的 Event loop 和浏览器中的不雷同。
Node 的 Event loop 分为 6 个阶段,它们会依照程序重复运行
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<──connections─── │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
timer
timers 阶段会执行 setTimeout
和 setInterval
一个 timer
指定的工夫并不是精确工夫,而是在达到这个工夫后尽快执行回调,可能会因为零碎正在执行别的事务而提早。
上限的工夫有一个范畴:[1, 2147483647]
,如果设定的工夫不在这个范畴,将被设置为 1。
I/O
I/O 阶段会执行除了 close 事件,定时器和 setImmediate
的回调
idle, prepare
idle, prepare 阶段外部实现
poll
poll 阶段很重要,这一阶段中,零碎会做两件事件
- 执行到点的定时器
- 执行 poll 队列中的事件
并且当 poll 中没有定时器的状况下,会发现以下两件事件
- 如果 poll 队列不为空,会遍历回调队列并同步执行,直到队列为空或者零碎限度
-
如果 poll 队列为空,会有两件事产生
- 如果有
setImmediate
须要执行,poll 阶段会进行并且进入到 check 阶段执行setImmediate
- 如果没有
setImmediate
须要执行,会期待回调被退出到队列中并立刻执行回调
- 如果有
如果有别的定时器须要被执行,会回到 timer 阶段执行回调。
check
check 阶段执行 setImmediate
close callbacks
close callbacks 阶段执行 close 事件
并且在 Node 中,有些状况下的定时器执行程序是随机的
setTimeout(() => {console.log('setTimeout');
}, 0);
setImmediate(() => {console.log('setImmediate');
})
// 这里可能会输入 setTimeout,setImmediate
// 可能也会相同的输入,这取决于性能
// 因为可能进入 event loop 用了不到 1 毫秒,这时候会执行 setImmediate
// 否则会执行 setTimeout
当然在这种状况下,执行程序是雷同的
var fs = require('fs')
fs.readFile(__filename, () => {setTimeout(() => {console.log('timeout');
}, 0);
setImmediate(() => {console.log('immediate');
});
});
// 因为 readFile 的回调在 poll 中执行
// 发现有 setImmediate,所以会立刻跳到 check 阶段执行回调
// 再去 timer 阶段执行 setTimeout
// 所以以上输入肯定是 setImmediate,setTimeout
下面介绍的都是 macrotask 的执行状况,microtask 会在以上每个阶段实现后立刻执行。
setTimeout(()=>{console.log('timer1')
Promise.resolve().then(function() {console.log('promise1')
})
}, 0)
setTimeout(()=>{console.log('timer2')
Promise.resolve().then(function() {console.log('promise2')
})
}, 0)
// 以上代码在浏览器和 node 中打印状况是不同的
// 浏览器中打印 timer1, promise1, timer2, promise2
// node 中打印 timer1, timer2, promise1, promise2
Node 中的 process.nextTick
会先于其余 microtask 执行。
setTimeout(() => {console.log("timer1");
Promise.resolve().then(function() {console.log("promise1");
});
}, 0);
process.nextTick(() => {console.log("nextTick");
});
// nextTick, timer1, promise1
什么是 CSRF 攻打?
(1)概念
CSRF 攻打指的是 跨站申请伪造攻打,攻击者诱导用户进入一个第三方网站,而后该网站向被攻打网站发送跨站申请。如果用户在被攻打网站中保留了登录状态,那么攻击者就能够利用这个登录状态,绕过后盾的用户验证,假冒用户向服务器执行一些操作。
CSRF 攻打的 实质是利用 cookie 会在同源申请中携带发送给服务器的特点,以此来实现用户的假冒。
(2)攻打类型
常见的 CSRF 攻打有三种:
- GET 类型的 CSRF 攻打,比方在网站中的一个 img 标签里构建一个申请,当用户关上这个网站的时候就会主动发动提交。
- POST 类型的 CSRF 攻打,比方构建一个表单,而后暗藏它,当用户进入页面时,主动提交这个表单。
- 链接类型的 CSRF 攻打,比方在 a 标签的 href 属性里构建一个申请,而后诱导用户去点击。
代码输入问题
function A(){}
function B(a){this.a = a;}
function C(a){if(a){this.a = a;}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log(new A().a);
console.log(new B().a);
console.log(new C(2).a);
输入后果:1 undefined 2
解析:
- console.log(new A().a),new A()为构造函数创立的对象,自身没有 a 属性,所以向它的原型去找,发现原型的 a 属性的属性值为 1,故该输入值为 1;
- console.log(new B().a),ew B()为构造函数创立的对象,该构造函数有参数 a,但该对象没有传参,故该输入值为 undefined;
- console.log(new C(2).a),new C()为构造函数创立的对象,该构造函数有参数 a,且传的实参为 2,执行函数外部,发现 if 为真,执行 this.a = 2, 故属性 a 的值为 2。
代码输入后果
function a() {console.log(this);
}
a.call(null);
打印后果:window 对象
依据 ECMAScript262 标准规定:如果第一个参数传入的对象调用者是 null 或者 undefined,call 办法将把全局对象(浏览器上是 window 对象)作为 this 的值。所以,不论传入 null 还是 undefined,其 this 都是全局对象 window。所以,在浏览器上答案是输入 window 对象。
要留神的是,在严格模式中,null 就是 null,undefined 就是 undefined:
'use strict';
function a() {console.log(this);
}
a.call(null); // null
a.call(undefined); // undefined
参考 前端进阶面试题具体解答
深拷贝浅拷贝
浅拷贝:浅拷贝通过 ES6 新个性 Object.assign()或者通过扩大运算法... 来达到浅拷贝的目标,浅拷贝批改
正本,不会影响原数据,但毛病是浅拷贝只能拷贝第一层的数据,且都是值类型数据,如果有援用型数据,批改
正本会影响原数据。深拷贝:通过利用 JSON.parse(JSON.stringify())来实现深拷贝的目标,但利用 JSON 拷贝也是有毛病的,当要拷贝的数据中含有 undefined/function/symbol 类型是无奈进行拷贝的,当然咱们想我的项目开发中须要
深拷贝的数据个别不会含有以上三种类型,如有须要能够本人在封装一个函数来实现。
compose
题目形容: 实现一个 compose 函数
// 用法如下:
function fn1(x) {return x + 1;}
function fn2(x) {return x + 2;}
function fn3(x) {return x + 3;}
function fn4(x) {return x + 4;}
const a = compose(fn1, fn2, fn3, fn4);
console.log(a(1)); // 1+4+3+2+1=11
实现代码如下:
function compose(...fn) {if (!fn.length) return (v) => v;
if (fn.length === 1) return fn[0];
return fn.reduce((pre, cur) =>
(...args) =>
pre(cur(...args))
);
}
数组去重
应用 indexOf/includes 实现
function unique(arr) {var res = [];
for(var i = 0; i < arr.length; i++) {if(res.indexOf(arr[i]) === -1) res.push(arr[i]);
// if(!res.includes(arr[i])) res.push(arr[i]);
}
return res;
}
应用 filter(forEach) + indexOf/includes 实现
// filter
function unique(arr) {var res = arr.filter((value, index) => {
// 只存第一个呈现的元素
return arr.indexOf(value) === index;
});
return res;
}
// forEach
function unique(arr) {var res = [];
arr.forEach((value) => {if(!res.includes(value)) res.push(value);
});
return res;
}
非 API 版本(原生)实现
function unique(arr) {var res = [];
for(var i = 0; i < arr.length; i++) {
var flag = false;
for(var j = 0; j < res.length; j++) {if(arr[i] === res[j]) {
flag = true;
break;
}
}
if(flag === false) res.push(arr[i]);
}
return res;
}
ES6 应用 Set + 扩大运算符(…)/Array.from() 实现
function unique(arr) {// return [...new Set(arr)];
return Array.from(new Set(arr));
}
深拷贝
实现一:不思考 Symbol
function deepClone(obj) {if(!isObject(obj)) return obj;
let newObj = Array.isArray(obj) ? [] : {};
// for...in 只会遍历对象本身的和继承的可枚举的属性(不含 Symbol 属性)for(let key in obj) {// obj.hasOwnProperty() 办法只思考对象本身的属性
if(obj.hasOwnProperty(key)) {newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key];
}
}
return newObj;
}
实现二:思考 Symbol
// hash 作为一个查看器,防止对象深拷贝中呈现环援用,导致爆栈
function deepClone(obj, hash = new WeakMap()) {if(!isObject(obj)) return obj;
// 查看是有存在雷同的对象在之前拷贝过,有则返回之前拷贝后存于 hash 中的对象
if(hash.has(obj)) return hash.get(obj);
let newObj = Array.isArray(obj) ? [] : {};
// 备份存在 hash 中,newObj 目前是空对象、数组。前面会对属性进行追加,这里存的值是对象的栈
hash.set(obj, newObj);
// Reflect.ownKeys 返回一个数组,蕴含对象本身的(不含继承的)所有键名,不论键名是 Symbol 或字符串,也不论是否可枚举。Reflect.ownKeys(obj).forEach(key => {
// 属性值如果是对象,则进行递归深拷贝,否则间接拷贝
newObj[key] = isObject(obj[key]) ? deepClone(obj[key], hash) : obj[key];
});
return newObj;
}
Node 中的 Event Loop 和浏览器中的有什么区别?process.nextTick 执行程序?
Node 中的 Event Loop 和浏览器中的是齐全不雷同的货色。
Node 的 Event Loop 分为 6 个阶段,它们会依照 程序 重复运行。每当进入某一个阶段的时候,都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量达到零碎设定的阈值,就会进入下一阶段。
(1)Timers(计时器阶段):首次进入事件循环,会从计时器阶段开始。此阶段会判断是否存在过期的计时器回调(蕴含 setTimeout 和 setInterval),如果存在则会执行所有过期的计时器回调,执行结束后,如果回调中触发了相应的微工作,会接着执行所有微工作,执行完微工作后再进入 Pending callbacks 阶段。
(2)Pending callbacks:执行推延到下一个循环迭代的 I / O 回调(零碎调用相干的回调)。
(3)Idle/Prepare:仅供外部应用。
(4)Poll(轮询阶段):
- 当回调队列不为空时:会执行回调,若回调中触发了相应的微工作,这里的微工作执行机会和其余中央有所不同,不会等到所有回调执行结束后才执行,而是针对每一个回调执行结束后,就执行相应微工作。执行完所有的回调后,变为上面的状况。
- 当回调队列为空时(没有回调或所有回调执行结束):但如果存在有计时器(setTimeout、setInterval 和 setImmediate)没有执行,会完结轮询阶段,进入 Check 阶段。否则会阻塞并期待任何正在执行的 I / O 操作实现,并马上执行相应的回调,直到所有回调执行结束。
(5)Check(查问阶段):会查看是否存在 setImmediate 相干的回调,如果存在则执行所有回调,执行结束后,如果回调中触发了相应的微工作,会接着执行所有微工作,执行完微工作后再进入 Close callbacks 阶段。
(6)Close callbacks:执行一些敞开回调,比方 socket.on(‘close’, …)等。
上面来看一个例子,首先在有些状况下,定时器的执行程序其实是 随机 的
setTimeout(() => { console.log('setTimeout')}, 0)setImmediate(() => { console.log('setImmediate')})
对于以上代码来说,setTimeout
可能执行在前,也可能执行在后
- 首先
setTimeout(fn, 0) === setTimeout(fn, 1)
,这是由源码决定的 - 进入事件循环也是须要老本的,如果在筹备时候破费了大于 1ms 的工夫,那么在 timer 阶段就会间接执行
setTimeout
回调 - 那么如果筹备工夫破费小于 1ms,那么就是
setImmediate
回调先执行了
当然在某些状况下,他们的执行程序肯定是固定的,比方以下代码:
const fs = require('fs')
fs.readFile(__filename, () => {setTimeout(() => {console.log('timeout');
}, 0)
setImmediate(() => {console.log('immediate')
})
})
在上述代码中,setImmediate
永远 先执行。因为两个代码写在 IO 回调中,IO 回调是在 poll 阶段执行,当回调执行结束后队列为空,发现存在 setImmediate
回调,所以就间接跳转到 check 阶段去执行回调了。
下面都是 macrotask 的执行状况,对于 microtask 来说,它会在以上每个阶段实现前 清空 microtask 队列,
setTimeout(() => {console.log('timer21')
}, 0)
Promise.resolve().then(function() {console.log('promise1')
})
对于以上代码来说,其实和浏览器中的输入是一样的,microtask 永远执行在 macrotask 后面。
最初来看 Node 中的 process.nextTick
,这个函数其实是独立于 Event Loop 之外的,它有一个本人的队列,当每个阶段实现后,如果存在 nextTick 队列,就会 清空队列中的所有回调函数,并且优先于其余 microtask 执行。
setTimeout(() => {console.log('timer1')
Promise.resolve().then(function() {console.log('promise1')
})
}, 0)
process.nextTick(() => {console.log('nextTick')
process.nextTick(() => {console.log('nextTick')
process.nextTick(() => {console.log('nextTick')
process.nextTick(() => {console.log('nextTick')
})
})
})
})
对于以上代码,永远都是先把 nextTick 全副打印进去。
Object.assign()
形容 :Object.assign()
办法用于将所有 可枚举 (Object.propertyIsEnumerable()
返回 true
)和 自有(Object.hasOwnProperty()
返回 true
)属性的值从一个或多个源对象复制到指标对象。它将返回批改后的指标对象(请留神这个操作是浅拷贝)。
实现:
Object.assign = function(target, ...source) {if(target == null) {throw new TypeError('Cannot convert undefined or null to object');
}
let res = Object(target);
source.forEach(function(obj) {if(obj != null) {
// for...in 只会遍历对象本身的和继承的可枚举的属性(不含 Symbol 属性)// hasOwnProperty 办法只思考对象本身的属性
for(let key in obj) {if(obj.hasOwnProperty(key)) {res[key] = obj[key];
}
}
}
});
return res;
}
代码输入后果
async function async1 () {console.log('async1 start');
await new Promise(resolve => {console.log('promise1')
})
console.log('async1 success');
return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')
输入后果如下:
script start
async1 start
promise1
script end
这里须要留神的是在 async1
中await
前面的 Promise 是没有返回值的,也就是它的状态始终是 pending
状态,所以在 await
之后的内容是不会执行的,包含 async1
前面的 .then
。
冒泡排序 – 工夫复杂度 n^2
题目形容: 实现一个冒泡排序
实现代码如下:
function bubbleSort(arr) {
// 缓存数组长度
const len = arr.length;
// 外层循环用于管制从头到尾的比拟 + 替换到底有多少轮
for (let i = 0; i < len; i++) {
// 内层循环用于实现每一轮遍历过程中的反复比拟 + 替换
for (let j = 0; j < len - 1; j++) {
// 若相邻元素后面的数比前面的大
if (arr[j] > arr[j + 1]) {
// 替换两者
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
// 返回数组
return arr;
}
// console.log(bubbleSort([3, 6, 2, 4, 1]));
代码输入后果
(function(){var x = y = 1;})();
var z;
console.log(y); // 1
console.log(z); // undefined
console.log(x); // Uncaught ReferenceError: x is not defined
这段代码的关键在于:var x = y = 1; 实际上这里是从右往左执行的,首先执行 y = 1, 因为 y 没有应用 var 申明,所以它是一个全局变量,而后第二步是将 y 赋值给 x,讲一个全局变量赋值给了一个局部变量,最终,x 是一个局部变量,y 是一个全局变量,所以打印 x 是报错。
动静布局求解硬币找零问题
题目形容: 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算能够凑成总金额所需的起码的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1
示例 1:输出: coins = [1, 2, 5], amount = 11
输入: 3
解释: 11 = 5 + 5 + 1
示例 2:输出: coins = [2], amount = 3
输入: -1
实现代码如下:
const coinChange = function (coins, amount) {
// 用于保留每个指标总额对应的最小硬币个数
const f = [];
// 提前定义已知状况
f[0] = 0;
// 遍历 [1, amount] 这个区间的硬币总额
for (let i = 1; i <= amount; i++) {
// 求的是最小值,因而咱们预设为无穷大,确保它肯定会被更小的数更新
f[i] = Infinity;
// 循环遍历每个可用硬币的面额
for (let j = 0; j < coins.length; j++) {
// 若硬币面额小于指标总额,则问题成立
if (i - coins[j] >= 0) {
// 状态转移方程
f[i] = Math.min(f[i], f[i - coins[j]] + 1);
}
}
}
// 若指标总额对应的解为无穷大,则意味着没有一个符合条件的硬币总数来更新它,本题无解,返回 -1
if (f[amount] === Infinity) {return -1;}
// 若有解,间接返回解的内容
return f[amount];
};
数字证书是什么?
当初的办法也不肯定是平安的,因为没有方法确定失去的公钥就肯定是平安的公钥。可能存在一个中间人,截取了对方发给咱们的公钥,而后将他本人的公钥发送给咱们,当咱们应用他的公钥加密后发送的信息,就能够被他用本人的私钥解密。而后他伪装成咱们以同样的办法向对方发送信息,这样咱们的信息就被窃取了,然而本人还不晓得。为了解决这样的问题,能够应用数字证书。
首先应用一种 Hash 算法来对公钥和其余信息进行加密,生成一个信息摘要,而后让有公信力的认证核心(简称 CA)用它的私钥对音讯摘要加密,造成签名。最初将原始的信息和签名合在一起,称为数字证书。当接管方收到数字证书的时候,先依据原始信息应用同样的 Hash 算法生成一个摘要,而后应用公证处的公钥来对数字证书中的摘要进行解密,最初将解密的摘要和生成的摘要进行比照,就能发现失去的信息是否被更改了。
这个办法最要的是认证核心的可靠性,个别浏览器里会内置一些顶层的认证核心的证书,相当于咱们主动信赖了他们,只有这样能力保证数据的平安。
代码输入后果
var obj = {
name : 'cuggz',
fun : function(){console.log(this.name);
}
}
obj.fun() // cuggz
new obj.fun() // undefined
应用 new 构造函数时,其 this 指向的是全局环境 window。
函数防抖
触发高频事件 N 秒后只会执行一次,如果 N 秒内事件再次触发,则会从新计时。
简略版:函数外部反对应用 this 和 event 对象;
function debounce(func, wait) {
var timeout;
return function () {
var context = this;
var args = arguments;
clearTimeout(timeout)
timeout = setTimeout(function(){func.apply(context, args)
}, wait);
}
}
应用:
var node = document.getElementById('layout')
function getUserAction(e) {console.log(this, e) // 别离打印:node 这个节点 和 MouseEvent
node.innerHTML = count++;
};
node.onmousemove = debounce(getUserAction, 1000)
最终版:除了反对 this 和 event 外,还反对以下性能:
- 反对立刻执行;
- 函数可能有返回值;
- 反对勾销性能;
function debounce(func, wait, immediate) {
var timeout, result;
var debounced = function () {
var context = this;
var args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
// 如果曾经执行过,不再执行
var callNow = !timeout;
timeout = setTimeout(function(){timeout = null;}, wait)
if (callNow) result = func.apply(context, args)
} else {timeout = setTimeout(function(){func.apply(context, args)
}, wait);
}
return result;
};
debounced.cancel = function() {clearTimeout(timeout);
timeout = null;
};
return debounced;
}
应用:
var setUseAction = debounce(getUserAction, 10000, true);
// 应用防抖
node.onmousemove = setUseAction
// 勾销防抖
setUseAction.cancel()
常见的 HTTP 申请头和响应头
HTTP Request Header 常见的申请头:
- Accept: 浏览器可能解决的内容类型
- Accept-Charset: 浏览器可能显示的字符集
- Accept-Encoding:浏览器可能解决的压缩编码
- Accept-Language:浏览器以后设置的语言
- Connection:浏览器与服务器之间连贯的类型
- Cookie:以后页面设置的任何 Cookie
- Host:发出请求的页面所在的域
- Referer:发出请求的页面的 URL
- User-Agent:浏览器的用户代理字符串
HTTP Responses Header 常见的响应头:
- Date:示意音讯发送的工夫,工夫的形容格局由 rfc822 定义
- server: 服务器名称
- Connection:浏览器与服务器之间连贯的类型
- Cache-Control:管制 HTTP 缓存
- content-type: 示意前面的文档属于什么 MIME 类型
常见的 Content-Type 属性值有以下四种:
(1)application/x-www-form-urlencoded:浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 形式提交数据。该种形式提交的数据放在 body 外面,数据依照 key1=val1&key2=val2 的形式进行编码,key 和 val 都进行了 URL 转码。
(2)multipart/form-data:该种形式也是一个常见的 POST 提交形式,通常表单上传文件时应用该种形式。
(3)application/json:服务器音讯主体是序列化后的 JSON 字符串。
(4)text/xml:该种形式次要用来提交 XML 格局的数据。