数据缓存
SessionStorage
LocalStorage
和Cookie一样, SessionStorage和LocalStorage也是用于存储网页中的数据的。
Cookie、 SessionStorage、LocalStorage区别:
生命周期(同一浏览器下):
Cookie生命周期: 默认是关闭浏览器后失效, 但是也可以设置过期时间
SessionStorage生命周期: 仅在当前会话(窗口)下有效,关闭窗口或浏览器后被清除, 不能设置过期时间
LocalStorage生命周期: 除非被清除,否则永久保存
容量:
Cookie容量: 有大小(4KB左右)和个数(20~50)限制
SessionStorage容量: 有大小限制(5M左右) http://dev-test.nemikor.com/web-storage/support-test/
LocalStorage容量: 有大小限制(5M左右) http://dev-test.nemikor.com/web-storage/support-test/
网络请求:
Cookie网络请求: 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题
SessionStorage网络请求: 仅在浏览器中保存,不参与和服务器的通信
LocalStorage网络请求: 仅在浏览器中保存,不参与和服务器的通信
Cookie、 SessionStorage、LocalStorage应用场景:
Cookie: 判断用户是否登录
LocalStorage: 购物车
sessionStorage: 表单数据
同源策略
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能。所谓同源是指: 协议,域名,端口都相同,就是同源,否则就是跨域。
同源策略带来的影响:
在同源策略下, 浏览器只允许Ajax请求同源的数据, 不允许请求不同源的数据; 但在企业开发中, 一般情况下为了提升网页的性能, 网页和数据都是单独存储在不同服务器上的, 这时如果再通过Ajax请求数据就会拿不到跨域数据.
跨域解决方案
jsonp
document.domain+iframe // iframe潜入不同页面,document.domain修改页面的域名为同一个
location.hash + iframe
window.name + iframe // window.name在不同页面不同域名中加载一直存在,可以保存的数据大小2M
window.postMessage
flash等第三方插件
JSONP
JSONP让网页从别的地址(跨域的地址)那获取资料,即跨域读取数据
JSONP实现跨域访问的原理
1、在同一界面中可以定义多个script标签
2、同一个界面中多个script标签中的数据可以相互访问
3、可以通过script的src属性导入其它资源
4、通过src属性导入其它资源的本质就是将资源拷贝到script标签中
5、script的src属性不仅能导入本地资源, 还能导入远程资源
6、由于script的src属性没有同源限制, 所以可以通过script的src属性来请求跨域数据
<script src="http://127.0.0.1:80/jsonp.php"></script>
通过JS动态创建script标签默认就是异步的,不用等到前面的标签加载完就可以执行后面的script标签。
// jQuery中jsonp使用
$.ajax({
url: "http://127.0.0.1:80/jsonp.php",
data:{
"teacher": "meihao",
"age": 34
},
dataType: "jsonp", // 告诉jQuery需要请求跨域的数据
jsonp: "cb", // 告诉jQuery服务器在获取回调函数名称的时候需要用什么key来获取
jsonpCallback: "meihao", // 告诉jQuery服务器在获取回调函数名称的时候回调函数的名称是什么
success: function (msg) {
console.log(msg);
}
});
window.name
Windows对象是浏览器的窗体,很多时候它不受同源策略的限制,利用这个对象可以实现跨域跨页面传递数据。其中,Windows的name属性是在一个窗体的生命周期内所有页面所共享的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
<script type="text/javascript">
window.name = "hello";
setTimeout(function () {
window.location.href = "http://127.0.0.1:8080/test.html"
}, 1000)
</script>
// test.html
<script type="text/javascript">
alert(window.name)
</script>
存在漏洞攻击Eval(window.name);
postMessage
postMessage允许每一个Window(包括当前窗口、弹出窗口、iframe等)对象往其他的窗口发送文本消息,从而实现跨窗口消息传递,且这个功能不受同源策略影响。postMessage语法如下:
otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow:其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象。
message:将要发送到其他 window的数据。
targetOrigin:通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。
transfer :是一串和message 同时传递的 Transferable 对象
Flash跨域
flash在跨域时唯一的限制策略就是crossdomain.xml
文件,该文件限制了flash是否可以跨域读写数据以及允许从什么地方跨域读写数据。位于http://www.a.com域中的SWF文件要访问http://www.b.com的文件时,SWF首先会检查http://www.b.com服务器目录下是否有crossdomain.xml文件,如果没有,则访问不成功;若crossdomain.xml文件存在,且里边设置了允许http://www.a.com域访问,那么通信正常。所以要使Flash可以跨域传输数据,其关键就是crossdomain.xml。
promise
promise是ES6中新增的异步编程解决方案, 在代码中的表现是一个对象。通过Promise就可以实现用同步的流程来表示异步的操作,通过Promise就可以避免回调函数层层嵌套(回调地狱)问题。
new Promise(function(resolve, reject){});
// promise对象不是异步的, 只要创建promise对象就会立即执行存放的代码
// promise对象是通过状态的改变来实现通过同步的流程来表示异步的操作的, 只要状态发生改变就会自动触发对应的函数
状态
pending: 默认状态,只要没有告诉promise任务是成功还是失败就是pending状态
fulfilled(resolved): 只要调用resolve函数, 状态就会变为fulfilled, 表示操作成功
rejected: 只要调用rejected函数, 状态就会变为rejected, 表示操作失败
注意点: 状态一旦改变既不可逆, 既从pending变为fulfilled, 那么永远都是fulfilled; 既从pending变为rejected, 那么永远都是rejected
监听Promise状态改变,resolved --> then();rejected --> catch()
then方法
1. 在修改promise状态时, 可以传递参数给then方法中的回调函数.
2. 同一个promise对象可以多次调用then方法,当该promise对象的状态发生改变时所有then方法都会被执行.
3. then方法每次执行完毕后会返回一个新的promise对象.
4. 可以通过上一个promise对象的then方法给下一个promise对象的then方法传递参数.无论是在上一个promise对象成功的回调还是失败的回调传递的参数,都会传递给下一个promise对象成功的回调.
5. 如果then方法返回的是一个Promise对象, 那么会将返回的Promise对象的执行结果中的值传递给下一个then方法
let promise = new Promise(function (resolve, reject) {
// resolve("111"); // 将状态修改为成功
reject("aaa"); // 将状态修改为失败
});
let ppp = new Promise(function (resolve, reject) {
resolve("222"); // 将状态修改为成功
// reject("bbb"); // 将状态修改为失败
});
let p2 = promise.then(function (data) {
console.log("成功1", data);
return ppp;
}, function (data) {
console.log("失败1", data);
return "bbb";
});
p2.then(function (data) {
console.log("成功2", data);
}, function (data) {
console.log("失败2", data);
});
// 失败1 aaa
// 成功2 bbb
catch方法
catch 其实是 then(undefined, () => {})
的语法糖
1. 如果需要分开监听, 也就是通过then监听成功通过catch监听失败, 那么必须使用链式编程, 否则会报错.
不使用链式编程的原因是, 如果promise的状态是失败, 但是没有对应失败的监听就会报错;then方法会返回一个新的promise, 新的promise会继承原有promise的状态,如果新的promise状态是失败, 但是没有对应失败的监听也会报错.
all静态方法
1. all方法接收一个数组.
2. 如果数组中有多个Promise对象, 只有都成功才会执行then方法, 并且会按照添加的顺序, 将所有成功的结果重新打包到一个数组中返回给我们.
3. 如果数组中不是Promise对象, 那么会直接执行then方法.
let p1 = new MyPromise(function (resolve, reject) {
// resolve("111");
reject("aaa");
});
let p2 = new MyPromise(function (resolve, reject) {
setTimeout(function () {
resolve("222");
}, 5000);
});
let p3 = new MyPromise(function (resolve, reject) {
setTimeout(function () {
resolve("333");
}, 3000);
});
let pp = MyPromise.all([p1, p2, p3]);
/* MyPromise 实现在下面
p1.then 执行then方法会直接走到if(this.status === REJECTED)方法里面,执行失败回调函数,结果:
then REJECTED ƒ (error) {console.log('失败回调函数', error)}
失败回调函数 aaa
执行.catch 方法,再次执行catch 方法里传入的失败回调,结果:
then REJECTED ƒ (e) {
console.log('try 捕获异常')
reject(e);
}
try 捕获异常
*/
race方法
1. all方法接收一个数组,
2. 如果数组中有多个Promise对象, 谁先返回状态就听谁的, 后返回的会被抛弃
3. 如果数组中不是Promise对象, 那么会直接执行then方法
MyPromise.race([p1, p2, p3]).then(function (value) {
console.log("成功", value);
}).catch(function (e) {
console.log("失败", e);
});
手写Promise
1.Promise特点
1.1. 创建时必须传入一个函数, 否则会报错
1.2. 传入的函数形参需要两个回调函数
1.3. 刚创建的Promise对象状态是pending
1.4. 状态一旦发生改变就不可再次改变
1.5. 可以通过then来监听状态的改变
1.5.1. 如果添加监听时状态已经改变, 立即执行监听的回调
1.5.2. 如果添加监听时状态还未改变, 那么状态改变时候再执行监听回调
1.5.3. 同一个Promise对象可以添加多个then监听, 状态改变时所有的监听按照添加顺序执行
2.
2.1. then方法每次执行完毕都会返回一个新的Promise对象
2.2. 上一个Promise对象的then可以给下一个Promise的then传递数据
2.2.1. 无论上一个是在成功的回调还是失败的回调传递的参数都会传递给下一个成功的回调
2.2.2. 如果上一个传递的是Promise对象, 那么传给下一个的成功还是失败由传递的Promise状态决定
3.
3.1. then方法返回的Promise对象的状态和前一个Promise的状态默认相同
3.2. 后一个Promise对象的then可以捕获前一个Promise then的异常
3.3. catch方法就是then方法的语法糖 then(undefined, function(){});
// 1.
let promise = new Promise(function (resolve, reject) {
resolve();
// reject();
});
promise.then(function (data) {
console.log("成功1", data);
}, function (data) {
console.log("失败1", data);
});
// 2.
let promise = new MyPromise(function (resolve, reject) {
resolve("111");
// reject("aaa");
});
let ppp = new MyPromise(function (resolve, reject) {
// resolve("222");
reject("bbb");
});
let p2 = promise.then(function (data) {
console.log("成功1", data);
return "222"; // p2.then返回 成功2 222
// return ppp; // p2.then返回 失败2 bbb
}, function (data) {
console.log("失败1", data);
// return "bbb"; // promise返回失败, 执行这里 p2.then返回 成功2 bbb
return ppp; // 执行这里 p2.then返回 失败2 bbb
});
p2.then(function (data) {
console.log("成功2", data);
}, function (data) {
console.log("失败2", data);
});
// 3.
let promise = new MyPromise(function (resolve, reject) {
// resolve();
reject(); // 失败
});
let p2 = promise.then(function () {
console.log("成功");
});
p2.catch(function () {
console.log("失败");
});
// 定义常量保存对象的状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise{
constructor(handle){
// 0.初始化默认的状态
this.status = PENDING;
// 定义变量保存传入的参数
this.value = undefined;
this.reason = undefined;
// 定义变量保存监听的函数
// this.onResolvedCallback = null;
// this.onRejectedCallback = null;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
// 1.判断是否传入了一个函数, 如果没有传入就抛出一个异常
if(!this._isFunction(handle)){
throw new Error("请传入一个函数");
}
// 2.给传入的函数传递形参(传递两个函数)
handle(this._resolve.bind(this), this._reject.bind(this));
}
then(onResolved, onRejected){
return new MyPromise((nextResolve, nextReject) => {
// 1.判断有没有传入成功的回调
if(this._isFunction(onResolved)){
// 2.判断当前的状态是否是成功状态
if(this.status === FULFILLED){
try {
// 拿到上一个promise成功回调执行的结果
let result = onResolved(this.value);
// console.log("result", result);
// 判断执行的结果是否是一个promise对象
if(result instanceof MyPromise){
result.then(nextResolve, nextReject);
}else{
// 将上一个promise成功回调执行的结果传递给下一个promise成功的回调
nextResolve(result);
}
}catch (e) {
nextReject(e);
}
}
}
// 1.判断有没有传入失败的回调
// if(this._isFunction(onRejected)){ // 实现了catch方法,这里可以不需要,异常会
try {
// 2.判断当前的状态是否是失败状态
if(this.status === REJECTED){
console.log('then REJECTED', onRejected)
let result = onRejected(this.reason);
if(result instanceof MyPromise){
result.then(nextResolve, nextReject);
}else if(result !== undefined){
nextResolve(result);
}else{
nextReject();
}
}
}catch (e) {
nextReject(e);
}
// }
// 2.判断当前的状态是否是默认状态
if(this.status === PENDING){
if(this._isFunction(onResolved)){
// this.onResolvedCallback = onResolved;
this.onResolvedCallbacks.push(() => {
try {
let result = onResolved(this.value);
if(result instanceof MyPromise){
result.then(nextResolve, nextReject);
}else{
nextResolve(result);
}
}catch (e) {
nextReject(e);
}
});
}
// if(this._isFunction(onRejected)){
// this.onRejectedCallback = onRejected;
this.onRejectedCallbacks.push(() => {
try {
let result = onRejected(this.reason);
if(result instanceof MyPromise){
result.then(nextResolve, nextReject);
}else if(result !== undefined){
nextResolve(result);
}else{
nextReject();
}
}catch (e) {
nextReject(e);
}
});
// }
}
});
}
catch(onRejected){
return this.then(undefined, onRejected);
}
_resolve(value){
// 这里是为了防止重复修改
if(this.status === PENDING){
this.status = FULFILLED;
this.value = value;
// this.onResolvedCallback(this.value);
this.onResolvedCallbacks.forEach(fn => fn(this.value));
}
}
_reject(reason){
if(this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// this.onRejectedCallback(this.reason);
this.onRejectedCallbacks.forEach(fn => fn(this.reason));
}
}
_isFunction(fn){
return typeof fn === "function";
}
static all(list){
return new MyPromise(function (resolve, reject) {
let arr = [];
let count = 0;
for(let i = 0; i < list.length; i++){
let p = list[i];
p.then(function (value) {
arr.push(value);
count++;
if(list.length === count){
resolve(arr);
}
}, function (error) {console.log('失败回调函数', error)}) // 失败回调可以不加
.catch(function (e) {
console.log('try 捕获异常')
reject(e);
});
}
});
}
static race(list){
return new MyPromise(function (resolve, reject) {
for(let p of list){
p.then(function (value) {
resolve(value);
}).catch(function (e) {
reject(e);
});
}
})
}
}
fetch
和Ajax一样都是用于请求网络数据的,fetch是ES6中新增的,基于Promise的网络请求方法。
用法
fetch(url, {options})
.then()
.catch();
fetch("http://127.0.0.1/index/html.php", {
method: "post",
body: JSON.stringify({name:"zs", age:666})
}).then(function (res) {
// console.log(res.text());
return res.json();
}).then(function (data) {
console.log(data);
console.log(typeof data);
}).catch(function (e) {
console.log(e);
});
axios
axios 是一个基于 promise 的 HTTP 库网络请求插件,可以用在浏览器和 node.js 中,支持 Promise API,自动转换 JSON 数据,客户端支持防御 CSRF
Symbol
Symbol是ES6中新增的一种数据类型, 被划分到了基本数据类型中;用来表示一个独一无二的值,可以用这个值设置一个标记,仅仅用于区分,没有其他含义。
基本数据类型: 字符串、数值、布尔、undefined、null、Symbol
引用数据类型: Object
let name = Symbol("name");
let say = Symbol("say");
let obj = {
// 注意点: 如果想使用变量作为对象属性的名称, 那么必须加上[]
[name]: "mh",
[say]: function () {
console.log("say");
}
}
obj.name = "zs"; // 会给obj添加新的属性
obj[Symbol("name")] = "ls"; // 会给obj添加新的属性
obj[name] = 'www' // 修改
console.log(obj);
/*
{name: "zs", Symbol(name): "www", Symbol(say): ƒ, Symbol(name): "ls"}
name: "zs"
Symbol(name): "www"
Symbol(name): "ls"
Symbol(say): ƒ ()
__proto__: Object
*/
Symbol.for()
与Symbol()
这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()
不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key
是否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for("cat")
30 次,每次都会返回同一个 Symbol 值,但是调用Symbol("cat")
30 次,会返回 30 个不同的 Symbol 值。
Symbol.for("bar") === Symbol.for("bar")
// true
Symbol("bar") === Symbol("bar")
// false
Symbol.keyFor()
方法返回一个已登记的 Symbol 类型值的key
。
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined
Iterator
Iterator
又叫做迭代器,是一种接口、一种标准一种规范;它规定了不同数据类型统一访问的机制,这里的访问机制主要指数据的遍历。在ES6中Iterator
接口主要供for...of
消费。
默认情况下以下数据类型都实现的Iterator
接口,Array/Map/Set/String/TypedArray/ 函数的 arguments 对象/NodeList 对象
1. 只要一个数据已经实现了Iterator接口, 那么这个数据就有一个叫做[Symbol.iterator]的属性
2. [Symbol.iterator]的属性会返回一个函数
3. [Symbol.iterator]返回的函数执行之后会返回一个对象
4. [Symbol.iterator]函数返回的对象中有一个名称叫做next的方法
5. next方法每次执行都会返回一个对象{value: 1, done: false}
6. 这个对象中存储了当前取出的数据和是否取完了的标记
let arr = [1, 3, 5];
console.log(arr[Symbol.iterator]); // ƒ values() { [native code] }
let it = arr[Symbol.iterator]();
console.log(it) // Array Iterator
class MyArray{
constructor(){
for(let i = 0; i < arguments.length; i++){
// this[0] = 1;
// this[1] = 3;
// this[2] = 5;
this[i] = arguments[i];
}
this.length = arguments.length;
}
[Symbol.iterator](){
let index = 0;
let that = this;
return {
next(){
if(index < that.length){
return {value: that[index++], done: false}
}else{
return {value: that[index], done: true}
}
}
}
}
}
let arr = new MyArray(1, 3, 5);
console.log(arr[Symbol.iterator]); // 函数
let it = arr[Symbol.iterator]() // 获取 [Symbol.iterator] 返回的含有 next 的对象
console.log(it.next()) // {value: 1, done: false}
console.log(it.next()) // {value: 3, done: false}
console.log(it.next()) // {value: 5, done: false}
console.log(it.next()) // {value: undefined, done: true}
for(let value of arr){
console.log(value); // 1 3 5
}
let arr1 = new MyArray(1, 3);
let arr2 = new MyArray(2, 4);
let arr3 = [...arr1, ...arr2];
Generator
Generator 函数是 ES6 提供的一种异步编程解决方案,Generator 函数内部可以封装多个状态,因此又可以理解为是一个状态机。
定义
只需要在普通函数的function后面加上 *
即可
Generator函数和普通函数区别:
1. 调用Generator函数后, 无论函数有没有返回值, 都会返回一个迭代器对象。
2. 调用Generator函数后, 函数中封装的代码不会立即被执行。
function* gen() {
yield "aaa";
yield 1 + 1;
yield true;
}
yield
yield
关键字只能在Generator
函数中使用,不能在普通函数中使用。
1. 在Generator函数内部使用yield关键字定义状态
2. 并且yield关键字可以让 Generator 内部的逻辑能够切割成多个部分。
3. 通过调用迭代器对象的next方法执行一个部分代码,执行哪个部分就会返回哪个部分定义的状态
5. 在调用next方法的时候可以传递一个参数, 这个参数会传递给下一个yield
function* gen() {
console.log("123");
let res = yield "aaa"; // 这里拿不到返回值
console.log(res);
console.log("567");
yield 1 + 1;
console.log("789");
yield true;
}
let it = gen(); // 相当于 函数[Symbol.iterator]()执行之后返回的对象
console.log(it);
console.log(it.next()); // 123 {value: "aaa", done: false}
// console.log(it.next("meihao")); // meihao 567 {value: 2, done: false}
console.log(it.next()); // undefined 567 {value: 2, done: false}
console.log(it.next()); // 789 {value: true, done: false}
console.log(it.next()); // {value: undefined, done: true}
function* calculate(a, b) {
yield a + b;
yield a - b;
}
let it = calculate(10, 5);
console.log(it.next().value); // 15
console.log(it.next().value); // 5
console.log(it.next()); // {value: undefined, done: true}
利用 Generator 函数,在任意对象上快速部署 Iterator 接口
let obj = {
name: "meihao",
age: 18,
gender: "man",
[Symbol.iterator](){
let keys = Object.keys(this); // ["name", "age", "gender"]
let index = 0;
let that = this;
return {
next(){
if(index < keys.length){
return {value: that[keys[index++]], done: false};
}else{
return {value: undefined, done: true};
}
}
}
}
}
// console.log(obj[Symbol.iterator]);
// let it = obj[Symbol.iterator]();
// console.log(it);
// console.log(it.next()); // 拿到对象中每个值 {value: "meihao", done: false}
// console.log(it.next());
// console.log(it.next());
// console.log(it.next());
for(let value of obj){
console.log(value); // meihao 18 man
}
用Generator
实现
let obj = {
name: "meihao",
age: 18,
gender: "man"
}
function* gen(){
let keys = Object.keys(obj);
for(let i = 0; i < keys.length; i++){
yield obj[keys[i]];
}
}
obj[Symbol.iterator] = gen; // 重点
let it = obj[Symbol.iterator]();
console.log(it);
console.log(it.next()); // {value: "meihao", done: false}
console.log(it.next()); // {value: 18, done: false}
console.log(it.next()); // {value: "man", done: false}
console.log(it.next()); // {value: undefined, done: true}
实现异步操作
function request() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("拿到的数据");
}, 1000);
});
}
request().then(function (data) {
console.log(data, 1);
return request(); // 返回一个新的promise,
}).then(function (data) {
console.log(data, 2);
return request();
}).then(function (data) {
console.log(data, 3);
});
// 第二种方式
function request() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("拿到的数据");
}, 1000);
});
}
function* gen() {
yield request();
yield request();
yield request();
}
let it = gen();
it.next().value.then(function (data) {
console.log(data, 1);
return it.next().value;
}).then(function (data) {
console.log(data, 2);
return it.next().value;
}).then(function (data) {
console.log(data, 3);
});
// 第二种方式用 ES8 语法改造
async function gen() {
let res1 = await request();
console.log(res1, 1);
let res2 = await request();
console.log(res2, 2);
let res3 = await request();
console.log(res3, 3);
}
gen();
async
async
函数是ES8中新增的一个函数,用于定义一个异步函数,async
函数函数中的代码会自动从上至下的执行代码。
await
await
操作符只能在异步函数 async function
中使用,await
表达式会暂停当前 async function
的执行,等待 Promise
处理完成。若 Promise
正常处理(fulfilled
),其回调的resolve
函数参数作为 await
表达式的值,然后继续执行 async function
。