面试整理 - JS篇(一)

12 篇文章 0 订阅
4 篇文章 0 订阅

JS

值类型 vs 引用类型

队列 FIFO,堆栈 FILO

内存地址分布如下:
栈在上,堆在下,一般不会重合。

let a = 100;
let b = a;
a = 200;
console.log(b); // 100

在这里插入图片描述

let a = { age: 20 };
let b = a;
b.age = 21;
console.log(a.age); // 21

常见值类型:

let a;  //undefined,若定义const a;会报错
const s = 'abc';
const n = 100;
const b = ture;
const s = Symbol('s');  //Symbol独一无二的类型

常见引用类型:

const obj = { x: 100 };
const arr = [1, 2, 3];
const n = null; //特殊引用类型,指针所指地址为空地址

//特殊引用类型,但不用于存储数据,所以没有“拷贝、复制函数”这一说
function fn() {}

typeof可以判断哪些类型

  • 能识别所有的值类型
  • 识别函数
  • 判断是否是引用类型(不可再细分)
let a = 'abc';
typeof a; // 'string'

深拷贝

//深拷贝
const obj = {
	age: 20;
	name: 'xxx',
	address: {
		city: 'beijing';
	},
	arr: ['a', 'b', 'c']
}

function deepclone(obj = {}){
	if(typeof obj !== 'object' || obj == null){
		return obj;
	}
	let result;
	if(obj instanceof Array){
		result = [];
	} else {
		result = {}
	}
	for(let key in obj){
		//保证不是原型的key
		if(obj.hasOwnProperty(key)){
			//递归调用
			result[key] = deepclone(obj[key]);
		}
	}
	//返回结果
	return result;
} 

强制类型转换

字符串拼接

const c = true = '10'; // 'true10'

== 运算符

0 == ''; // true
0 == false; // true
false == ''; // true
null == undefined; // true

综上,除了 == null之外,其他地方一律用 ===

const obj = { x: 100 }
if(obj.a == null) {}
//相当于:
//if(obj.a === null || obj.a === undefined) {}

trully变量!!a === true
falsely变量 !!a === false

!!0 === false
!!NaN === false
!!'' === false
!!null === false
!!undefined === false
!!false === false

原型和继承

继承:

  • extends
  • super
  • 扩展或重写方法
// 父类
class People {
	constructor(name){
		this.name = name;
	}
	eat(){
		console.log(`${this.name} eat something`);
	}
}
// 子类
class Student extends Prople{
	constructor(name, number){
		super(name); //super会把name交给父类来处理(赋值)
		this.number = number;
	}
	sayHi(){
		console.log(`姓名${this.name},学号${this.number}`);
	}
}

const v= new Student('小明', 100);
console.log(xiaoming);
xiaoming.eat();
xiaoming.sayHi(); // 姓名小明,学号100

//注意:
//如果调用方式为:
xiaoming.__proto__.sayHi(); // 姓名undefined,学号undefined
//是因为隐式原型中没有name、number
xiaoming.__proto__.name; // undefined
xiaoming.__proto__.number; // undefined
//可以这么写:
xiaoming.__proto__.sayHi.call(xiaoming);

instanceof判断属于哪个构造函数/class

xiaoming instanceof Student; // true
xiaoming instanceof Prople; // true
xiaoming instanceof Object; // true Object是所有的父类

[] instanceof Array; // true
[] instanceof Object; // true

{} instanceof Object; // true

原型:

  • 每个class都有显式原型prototype
  • 每个实例都有隐式原型__proto__
  • 实例的__proto__ 指向对应的class的prototype
    在这里插入图片描述
    原型链:
    访问当前属性,若没有则通过隐式原型__proto__向上查找,
console.log(People.prototype === Student.prototype.__proto__);

在这里插入图片描述
hasOwnproperty:
在这里插入图片描述
instanceof:
顺着实例的原型链网上找显示原型,如果在原型链上,返回true。
例如上图,

xialuo instanceof Array // false 因为不在原型链上

手写简易的jQuery考虑插件和扩展性

jQuery做DOM查询的

class jQuery {
  constructor(selector) {
    const result = document.querySelectorAll(selector);
    const length = result.length;
    for (let i = 0; i < length; i++) {
      this[i] = result[i];
    }
    this.length = length;
    this.selector = selector;
  }

  get(index) {
    return this[index];
  }
  each(fn) {
    for (let i = 0; i < this.length; i++) {
      const elem = this[i];
      fn(elem);
    }
  }
  on(type, fn) {
    return this.each((elem) => {
      elem.addEventListener(type, fn, false);
    });
  }
}

//考虑插件:往jQuery原型中添加
jQuery.prototype.dialog = function (info) {
	alert(info);
}

//复写机制: 造轮子
class myJQuery extends jQuery {
  constructor(selector) {
    super(selector);
  }
  //扩展自己的方法
  addClass(className) {
    //...
  }
  style(data) {
    //...
  }
}

插件:用的还是jQuery本身,是在jQuery上扩展的
轮子:是继承了jQuery,上面添加包裹一些更好的方法,然后提供的是封好的新类

使用例子:

let $q = new jQuery('p');
$q.get(1); // <p></p>
$q.each((i) => { console.log(i.nodeName) }); // p 三次
$q.on('click', ()=>{ alert('click') });
$q.dialog(3);

作用域和闭包

作用域举例:

// 场景一
let i, a;
for ( i = 0; i < 10; i++) {
  a = document.createElement("a");
  a.innerHTML = i + "</br>";
  document.body.appendChild(a);
  a.addEventListener("click", function () {
    alert(i); //点击所有的全部是10
  });
}
// 场景二
let i, a;
for ( let i = 0; i < 10; i++) {  //这里定义局部变量
  a = document.createElement("a");
  a.innerHTML = i + "</br>";
  document.body.appendChild(a);
  a.addEventListener("click", function () {
    alert(i); //点击所有的一次是 0 ~ 9
  });
}

闭包两种场景:

  • 函数作为返回值
  • 函数作为参数
    闭包:自由变量的查找,是在函数定义的地方,向上级作用域查找,而不是在执行的地方!!!
    在这里插入图片描述

this几种场景:

  • 作为普通函数
  • 使用call apply bind
  • 作为对象方法被调用
  • 在class方法中被调用
  • 箭头函数
    ** this的取值是在函数执行的时候确定的,不加粗样式是函数定义的时候确定的!!**
// 作为普通函数
function fn1() {
  console.log(this);
}
fn1(); // window

// 使用call apply bind 
fn1.call({ x: 100 }); // { x: 100 }

const fn2 = fn1.bind({ x: 200 });
fn2(); // { x: 200 }

// call 直接执行函数
// bind 返回一个函数
// 作为对象方法被调用,setTimeout为普通函数
const zhangsan = {
  name: "张三",
  sayHi() {
    // this 为当前对象
    console.log(this);
  },
  wait() {
    setTimeout(funciton(){
      // this === window, 因为是setTimeout触发的执行,而不是wait触发的执行
      console.log(this);
    });
  },
};

// 作为对象方法被调用,setTimeout为箭头函数
const zhangsan = {
  name: "张三",
  sayHi() {
    // this 为当前对象
    console.log(this);
  },
  waitAgain() {
    setTimeout(() => {
      // this 为当前对象,箭头函数的this永远取它上级作用域的this
      console.log(this);
    });
  },
};
// 在class方法中被调用
class People {
  constructor(name) {
    this.name = name;
    this.age = 30; // 这里的this指向创建的实例
  }
  sayHi() {
    console.log(this); // 这里的this指向创建的实例
  }
}

const zhangsan = new People("张三");
zhangsan.sayHi(); //zhangsan对象

手写bind

//模拟 bind
Function.prototype.bind1 = function () {
  // 将参数拆解为数组
  const args = Array.prototype.slice.call(arguments);

  // 获取 this(数组第一项),并改变原数组
  const t = args.shift();
  // Array.prototype.slice.call()方法能够将一个具有length属性的对象转换为数组

  // fn1执行,所以this指向 fn1.bind(...) 中的 fn1
  const self = this;

  return function () {
    return self.apply(t, args);
  };
};

function fn1(a, b, c) {
  console.log(this); // {x: 100}
  console.log(a, b, c); // 10 20 30
  return "this is fn1";
}
const fn2 = fn1.bind1({ x: 100 }, 10, 20, 30);
const res = fn2();
console.log(res); // this is fn1

apply、bind、call 区分

// eg.
obj.myFun.call(db,'成都','上海');     // 德玛 年龄 99  来自 成都去往上海
obj.myFun.apply(db,['成都','上海']);      // 德玛 年龄 99  来自 成都去往上海  
obj.myFun.bind(db,'成都','上海')();       // 德玛 年龄 99  来自 成都去往上海
obj.myFun.bind(db,['成都','上海'])();   // 德玛 年龄 99  来自 成都, 上海去往 undefined

闭包的应用

  • 隐藏数据
  • 做一个简单的cache工具
// 闭包隐藏数据,只提供API
function createCache() {
  const data = {}; //闭包中的数据,被隐藏,不被外界访问
  return {
    set: function (key, val) {
      data[key] = val;
    },
    get: function (key) {
      return data[key];
    },
  }; 
}
const c = createCache();
c.set("a", 100);
console.log(c.get("a")); // 100

异步和单线程

同步和异步的区别

  • JS是基于单线程语言
  • 异步不会阻塞代码执行
  • 同步会阻塞代码执行

手写promise加载图片

const url = "https://img.mukewang.com/5ab869bb000180cd05000500-100-100.jpg";
const url2 = "https://img1.sycdn.imooc.com/szimg/5c7c82630820acf806000338-358-201.jpg";

function loadImg(src) {
  p = new Promise((resolve, reject) => {
    const img = document.createElement("img");
    img.onload = () => {
      resolve(img);
    };
    img.onerror = () => {
      reject(new Error("加载失败"));
    };
    img.src = src;
  });
  return p;
}

loadImg(url)
  .then((img) => {
    console.log(img.width); // 100
    return img;
    // 这里返回值由两种形式:
    // 1. 如果是普通对象,下面的then的参数接收的参数为该对象
    // 2. 如果是promise实例,下面的then的参数接收的是promise返回的promise实例,即resolve或reject
  })
  .then((img) => {
    console.log(img.height); // 100
    return loadImg(url2);
  })
  .then((img2) => {
    console.log(img2.width); // 358
  })
  .catch((ex) => console.log(ex));
  

异步进阶

event loop(事件循环/事件轮询)

在这里插入图片描述

promise进阶

Promise三种状态:

  • pending resolved rejected
  • pending -> resolved / pending -> rejected
  • 变化不可逆
    在这里插入图片描述

状态表现:

  • pending状态,不会触发then和catch
  • resolved状态,触发then回调
  • rejected状态,触发catch回调
// 另一种写法
const p = Promise.resolve(100);
p.then(res = > {
	console.log(res); // 100
}).catch(err => {
	console.log(res); // 不会被执行
})

then和catch状态改变

  • then正常返回resolved状态的promise,里面有报错则返回rejected状态的promise

  • catch正常返回resolved状态的promise,里面有报错则返回rejected状态的promise

  • resolved状态的promise会触发后面的then

  • rejected状态的promise会触发后面的catch

// resolved 状态示例
const p1 = Promise.resolve().then((res) => {
  return 100;
});
console.log("p1", p1); //resolved触发后续then回调
p1.then(() => {
  console.log("123");
});

const p2 = Promise.resolve().then((res) => {
  throw new Error("then error");
});
console.log("p2", p2); //rejected触发后续catch回调
p2.then(() => {
  console.log("456"); // 不执行
}).catch((err) => {
  console.log("err456", err);
});

// rejected 状态示例
const p3 = Promise.reject("my error").catch((err) => {
  console.log(err);
});
console.log("p3", p3); // resolved状态

const p4 = Promise.reject("my error").catch((err) => {
  throw new Error("catch err");
});
console.log("p4", p4); // rejected状态

例题:

// 第一题
Promise.resolve()
  .then(() => {
    console.log(1); // 1
  })
  .catch(() => {
    console.log(2);
  })
  .then(() => {
    console.log(3); //3
  }); // 最后返回resolved状态的promise
// 第二题
Promise.resolve()
  .then(() => {
    console.log(1); // 1
    throw new Error("error1");
  })
  .catch(() => {
    console.log(2); // 2
  })
  .then(() => {
    console.log(3); // 3
  }); // 最后返回resolved状态的promise
// 第三题
Promise.resolve()
  .then(() => {
    console.log(1); // 1
    throw new Error("error1");
  })
  .catch(() => {
    console.log(2); // 2
  })
  .catch(() => {
    console.log(3);
  }); // 最后返回resolved状态的promise

async/await

  • Promise then catch 链式调用,但也基于回调函数
  • saync/await是同步语法,彻底消灭了回调函数

与promise的关系

  • 执行async函数,返回的是promise对象
  • await相当于promise的then
  • try…catch可捕获异常,代替了promise的catch
// 执行async函数,返回的是promise对象
async function fn1() {
  return 100;
  // return Promise.resolve(100); //这两句等价,如果返回的不是promise对象,会封装成一个promise返回,如果返回的是一个promise对象,直接返回
}

const res1 = fn1(); //执行saync函数,返回的是一个promise对象
console.log(res1); //promise对象
res1.then((data) => {
  console.log(data); // 100
});
// await相当于promise的then,(await后的函数为promise函数或普通数)
!(async function () {
  const p1 = Promise.resolve(100);
  const data = await p1;
  const data1 = await 100; // 如果不是promise,会封装成一个promise返回
  console.log(data); // 100
  console.log(data1); // 100
})();
// await相当于promise的then,(await后的函数为async函数)
async function fn1() {
  return Promise.resolve(100);
}
!(async function () {
  const p1 = await fn1();
  console.log(p1); // 100
})();
// try...catch可捕获异常,代替了promise的catch
!(async function () {
  const p1 = Promise.reject("err");
  try {
    const res = await p1;
    console.log(res);
  } catch (ex) {
    console.log(ex); // err
  }
})();

微任务/宏任务(macroTask/microTask)

  • 宏任务: setTimeout、setInterval、Ajax、DOM事件
  • 微任务:Promise、async/await
  • 微任务执行时机比宏任务要早

evnt loop 和DOM渲染

  • js是单线程的,而且和DOM渲染共用一个线程
  • js执行的时候,得留一些时机供DOM渲染 (alert可以终端js继续执行)

在这里插入图片描述
说明:

  • 每次callstack清空(即每次轮询结束),即同步任务执行完
  • 都是DOM重新渲染的机会,DOM结构如有改变则重新渲染
  • 然后再去触发下一次Eventloop
微任务和宏任务的区别:
  • 宏任务:DOM渲染后触发,如setTimeout。宏任务是ES6语法规定的,存放在Web APIs里面
  • 微任务:DOM渲染前触发,如Promise,微任务是浏览器规定的,存放在micro task queue里面
渲染时机:
  1. 执行代码,将微任务和宏任务分别放在对应的队列中
  2. 执行微任务
  3. 重新渲染DOM
  4. 执行宏任务
  5. 触发event loop

  6. 在这里插入图片描述
    例子:
!(async function () {
  console.log("start"); //start
  const a = await 100;
  console.log("a", a); // a 100
  const b = await Promise.resolve(200);
  console.log("b", b); // b 200
  const c = await Promise.reject(300); // reject得用try/catch来,所以后面的都不执行
  console.log("c", c);
  console.log("end");
})();

在这里插入图片描述

例子:

!(async function () {
  console.log("start"); //start
  const a = await 100;
  console.log("a", a); // a 100
  const b = await Promise.resolve(200);
  console.log("b", b); // b 200
  const c =  Promise.reject(300); // reject得用try/catch来接,await接不住,所以后面的都不执行
  try {
    const x = await c;
    console.log('x',x);
  } catch (error) {
    console.log('error',error);
  }
  console.log("c", c);
  console.log("end");
})();

在这里插入图片描述
例子:

async function async1() {
  console.log("async1 start"); // 2
  await async2();
  console.log("async1 end"); // 6
}
async function async2() {
  console.log("async2"); // 3
}
console.log("script start"); // 1

setTimeout(function () {
  console.log("setTimeout"); // 8
}, 0);

async1();

//初始化primose时,传入的函数会被立即执行
new Promise(function (resolve) {
  console.log("promise1");  // 4
  resolve();  // 写resolve(),then才会被执行
}).then(function () {
  console.log("promise2"); // 7
});
console.log("script end"); // 5

JS Web API

DOM的本质:是从html中解析出来的一棵树

attribute和property

这两个都是属性,(都可能引起DOM重新渲染,一般尽可能使用property) :

  • property 修改对象属性,不会体现到html结构中
const pList = document.querySelectorAll('p');
const p1 = pList[0];

p1.style.width = '50px';
p1.className = 'red';
  • attribute 标签上的属性,会改变html结构
p1.setAttribute('data-name', 'imooc');
console.log(p2.getAttribute('data-name'));

//也能操作标签上的style
p1.setAttribute('style', 'font-size: 50px');

DOM结构操作

const div1 = document.getElementById("div1");
const div2 = document.getElementById("div2");

// 新增节点
const newP = document.createElement("p");
newP.innerHTML = "this is newP.";

div1.appendChild(newP);

// 移动节点(对于现有节点appendChild会将其移动)
const p1 = document.getElementById("p1");
div2.appendChild(p1);

// 获取父元素
console.log(p1.parentNode);

// 获取子元素列表
const div1ChildNode = div1.childNodes;
console.log(div1.childNode);
const div1ChildNodeP = Array.prototype.slice.call(div.childNodes).filter((child) => {
  // 过滤出需要的节点类型
  if (child.nodeType == 1) {
    return true;
  }
  return false;
});

// 删除
div1.removeChild(div1ChildNodeP[0]);

DOM性能

  • DOM操作非常‘昂贵’,避免频繁的DOM操作
  • 对DOM查询做缓存
  • 将频繁的操作改为一次性操作
    在这里插入图片描述
    正确写法:
const listNode = document.getElementById("list");

// 创建一个文档片段,此时还没有插入到DOM中
const frag = document.createDocumentFragment();

// 插入到文档片段中
for (let x = 0; x < 10; x++) {
  const li = document.createElement("li");
  li.innerHTML = "list item" + x;
  frag.appendChild(li);
}

// 完成之后再插入到DOM中
listNode.appendChild(frag);

BOM

// navigator
const ua = navigator.userAgent;
const isChrome = ua.indexOf("Chrome");
console.log(isChrome);

// screen
console.log(screen.width);
console.log(screen.height);

// location
console.log(location.href); //整个网址
console.log(location.protocol); // 协议:http  https
console.log(location.host); // 端口
console.log(location.pathname); // 路径 /classchapter/115.html
console.log(location.search); //查询参数: ?a=100&b=2
console.log(location.hash); //#Anchor

// history
history.back();
history.forward();

JS Web API 事件

事件绑定

写一个通用的事件绑定函数:

// 通用的绑定函数
function bindEvent(ele, type, fn) {
  ele.addEventListener(type, fn);
}

const a = document.getElementById("link1");
bindEvent(a, "click", (e) => {
  e.preventDefault();  // 阻止默认行为
  alert("clicked");
});

事件冒泡

基于DOM树形结构,事件会顺着触发元素向上冒泡。
阻止事件冒泡:
e.stopProgagation()

事件代理

通过冒泡的形式,在父节点拿到子节点的信息并加以处理。优点:简洁、减少浏览器内存占用、不要滥用。
场景如:瀑布流形式滑动加载图片,点击后进入新的页面。

<div id="div3">
  <a href="#">a1</a>
  <a href="#">a2</a>
  <a href="#">a3</a>
  <a href="#">a4</a>
  <a href="#">a5</a>
  <button>加载更多...</button>
</div>
const div3 = document.getElementById("div3");
bindEvent(div3, "click", (e) => {
  // a标签有默认行为,会跳转链接
  e.preventDefault();
  const target = e.target;
  if ((target.nodeName = "A")) {
    alert(target.innerHTML);
  }
});

优化通用的事件绑定函数:

// 通用的绑定函数
function bindEvent(ele, type, selector, fn) {
  // 两种绑定方式传入参数数量不同
  if (fn == null) {
    fn = selector;
    selector = null;
  }
  ele.addEventListener(type, (e) => {
    const target = e.target;
    if (selector) {
      //代理绑定
      if (target.matches(selector)) {
        fn.call(target, e);
      }
    } else {
      // 普通绑定
      fn.call(target, e);
    }
  });
}

const div1 = document.getElementById("div1");
bindEvent(div1, "click", function (e) {
  e.preventDefault();
  alert(this.innerHTML);
});

const div3 = document.getElementById("div3");
bindEvent(div3, "click", "a", function (e) {
  e.preventDefault();
  alert(this.innerHTML);
});

Ajax核心API

手写ajax

最原始的写法:

const xhr = new XMLHttpRequest();

// get请求
xhr.open("GET", "/data.json", true);  // true表示异步
// 状态改变时
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      alert(xhr.responseText);
    } else {
      alert("other cases");
    }
  }
};
xhr.send(null);

// post请求
xhr.open("POST", "/login", false);
// 状态改变时
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      alert(xhr.responseText);
    } else {
      alert("other cases");
    }
  }
};
const postData = {
  username: "a",
};
// 必须发送字符串
xhr.send(JSON.stringify(postData));

封装成ajax:

function ajax(url) {
  const p = new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        if (xhr.status == 200) {
          resolve(JSON.parse(xht.responseText));
        } else if (xhr.status === 404) {
          PromiseRejectionEvent(new Error("404 not found"));
        }
      }
    };
    xhr.send(null);
  });
  return p;
}
// jQuery中的ajax使用
$.ajax({
  url: "http://localhost:8882/x-origin.json",
  type: 'POST',
  contentType: "application/json;charset=UTF-8",
  data: JSON.stringify(list),
  success: function (data) {
    console.log(data);
  },
  error: function (data) {
	console.log(e.status);
	console.log(e.responseText);
  }
});

xhr.readyState

  • 0 (未初始化)还有没有调用send()方法
  • 1 (载入)已调用send方法,正在发送请求
  • 2 (载入完成)send方法执行完成,已经接收到全部相应
  • 3 (交互)正在解析响应内容
  • 4(完成)相应内容解析完成,可以在客户端使用

xhr.status

  • 2xx 表示成功处理请求
  • 3xx 需要重定向,浏览器直接跳转。如301(永久重定向) 、302(临时重定向) 304(资源未改变)
  • 4xx 请求错误。403(没有权限)
  • 5xx 服务端报错

跨域

同源策略
  • ajax请求时,浏览器要求当前网页必须和server同源(安全)
  • 同源:协议、域名、端口,三者必须一致

加载图片 css js可无视同源策略

  • <img src="跨域的图片地址" /> 可用于统计打点,可使用第三方统计服务
  • <link src="跨域的css地址" /> 可使用CDN,CDN一般是外域
  • <script src="跨域的js地址" ></script> 可使用CDN,CDN一般是外域。可实现JSONP
JSONP
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      window.abc = function (data) {
        console.log(data);
      };
    </script>
    <script src="http://localhost:8002/jsonp.js?username=xxx&callback=abc"></script>
  </body>
</html>
// 利用jQuery
$.ajax({
  url: "http://localhost:8882/x-origin.json",
  dataType: "jsonp",
  jsonpCallback: "callback",
  success: function (data) {
    console.log(data);
  },
});
CORS - 服务器设置 http header

服务器设置,允许跨域:

// 第二个参数填写允许跨域的域名称,不建议直接写*
response.setHeader("Access-Control-Allow-Origin", "http://localhost:8011");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With");
response.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");

// 接收跨域的cookie
response.setHeader("Access-Control-Allow-Credentials", "true");
Fetch axios

Fetch_API传送门

请注意,fetch 规范与 jQuery.ajax() 主要有三种方式的不同:
当接收到一个代表错误的 HTTP 状态码时,从 fetch() 返回的 Promise 不会被标记为 reject, 即使响应的 HTTP 状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。

axios传送门
支持浏览器和noide.js
支持promise API

JS-Web-API-存储

cookie

是一个追加的过程,不是重新赋值。如果key存在则更新值:

document.cookie = 'a=100'; // 'a=100'
document.cookie = 'b=200'; // 'a=100;b=200'
document.cookie = 'a=300'; // 'a=300;b=200'

刷新页面不会清除cookie,除非手动删除。因为cookie也可以被借用来做本地存储。
缺点:

  • 最大存4kb
  • 每次请求都会带上,影响请求时长

localStorage 和sessionStorage

  • HTML5专门为存储而设计,针对每个域名最大可存5M
  • API简单 setItem getItem
  • 不会被http发送出去
  • localStorage数据会永久存储,除非代码或手动删除
  • sessionStorage只存在于当前会话,浏览器关闭则清空
  • localStorage用的多一些,(例如下次用户来的时候继续使用)

HTTP

状态码

  • 1xx 服务器收到请求
  • 2xx 表示成功处理请求
  • 3xx 需要重定向,浏览器直接跳转。如301(永久重定向) 、302(临时重定向) 304(资源未改变)
  • 4xx 请求错误。403(没有权限)
  • 5xx 服务端报错。500(服务器错误)、504(网关超时)

百度搜索引擎、qq邮箱、外链、短链等的链接都是先访问自己的域名下的链接,之后再跳转的。将链接粘出来之后重新打开一个浏览器跳转,会发现请求302了。如果之前通过重定向打开过这个网页,再次访问就有可能是200

http methods

  • get 获取数据
  • post 新建数据
  • patch/put 更新数据
  • delete 删除数据

Restful API

  • 一种新的API设计方法
  • 传统API设计:把每个url当作一个功能。例如:/api/list?pageIndex=2
  • Restful API设计:把每个url当作一个唯一的资源的标识。例如:/api/list/2
    例如:
  • post请求,/api/blog
  • patch请求,/api/blog/100
  • get请求,/api/blog/100

http headers

常见的Request Headers

  • Accept 浏览器可接收的数据格式
  • Accept-Encoding 浏览器可接收的解压算法,如gzip
  • Accept-Language 浏览器可接收的语言,如zh-CN
  • Connection:keep-alive 一次TCP连接重复使用
  • cookie
  • Host
  • User-Agent (简称UA)浏览器信息
  • Content-type 发送数据格式,如application/json,一般post或者patch需要

常见的Respones Headers

  • Content-type 返回数据格式,如application/json
  • Content-length 返回数据大小,多少字节
  • Content-Encoding 返回数据的压缩算法,如gzip
  • Set-Cookie 服务端通过这个属性改cookie

缓存相关的headers

  • Cache-Control
  • Expires
  • Last-Modified
  • If-Modified-Since
  • Etag
  • If-None-Match

可以自定义header,如axios自定义header

header:{'aaa':'aaa'}

http缓存 - 强制缓存

  • Cache-Control 控制强制缓存的逻辑,服务端来控制
    catch-control:max-age=5184000[常用]
    catch-control:no-catch[常用] 不用强制缓存,交给服务器处理
    catch-control:no-store 不用缓存,服务器处理重新给一份
    catch-control:private 只允许最终用户做缓存,例如手机、电脑
    catch-control:private 允许中间的代理做一些缓存
  • Expires 在response header中,控制缓存过期,现已被catch-control代替。两者都有时以catch-control为准

http缓存 - 协商缓存

  • 服务器端缓存策略
  • 服务器判断客户端资源,是否和服务器端资源一样
  • 一致则返回304,否则返回200和最新资源
    在这里插入图片描述
    资源标识:
  • 在ResponseHeaders中,有两种
  • Last-Modified资源的最后修改时间
  • Etag资源的唯一标识(一个字符串 ,类似人类的指纹)
    在这里插入图片描述
    在这里插入图片描述

Last-Modified和Etag

  • 会优先使用Etag
  • Last-Modified只能精确到秒级
  • 如果资源被重复生成,而内容不变,则Etag更精准
    在这里插入图片描述

三种刷新操作:

  • 地址栏输入url,跳转链接,前进后退(强制缓存有效,协商缓存有效)
  • 手动刷新:F5,点击刷新按钮,右击菜单刷新(强制缓存失效,协商缓存有效)
  • 强制刷新:shift+commid+r 或 ctrl+F5(强制缓存失效,协商缓存失效)

开发环境

抓包

  • 移动端h5页,查看网络请求,需要抓包工具

  • Windows一般用fiddler

  • Mac OS一般用charles

  • 手机和电脑连同一个局域网

  • 将手机代理到电脑上

  • 手机浏览网页,即可抓包

  • 查看网络请求

  • 网址代理

  • https

webpack 和 babel

  • ES6模块化,浏览器暂不支持
  • ES6语法,浏览器并不完全支持
  • 压缩代码,整合代码,让网页加载更快
  //package.json
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config webpack.config.js",//指定webpack打包的文件是webpack.config.js,这个文件是webpack的默认名字,可以不写
    "build": "webpack"
  },t

npm install html-webpack-plugin解析html的插件
npm install webpack-dev-server启动服务的插件

// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  mode: "development", //production模式会压缩代码
  entry: path.join(__dirname, "src", "index.js"), //__dirname是当前目录
  output: {
    filename: "bundle.js",
    path: path.join(__dirname, "dist"),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "src", "index.html"), //根据这个文件模板
      filename: "index.html", //产出文件名
    }),
  ],
  devServer: {
    port: 3000,
    contentBase: path.join(__dirname, "dist"), // 当前目录
  },
};

运行环境

加载过程

  • DNS解析: 域名 -> IP地址
  • 浏览器根据IP地址向服务器发起http请求
  • 服务器处理http请求,并返回给浏览器

渲染过程:

  • 根绝html生成DOM Tree
  • 根据CSS代码生成CSSOM(一个css可结构化的对象)
  • 将DOM Tree和CSSOM整合成Render Tree
  • 根据Render Tree渲染页面
  • 遇到

为何建议把css放在header中?
答:先渲染ODM Tree后发现还有css 还要再根据css重新整合成render tree。如果将css放在头部,在dom树生成完成之前就加载完,当dom Trere加载完成之后,跟css一起生成DOM tree

为何建议把js放在body最后?
答:可能会出现渲染过程中页面卡住了,停一会儿之后再加载。

img标签在网页加载比较慢的时候,不会阻塞dom渲染。

window.addEventListener('load', function () {
	// 页面的全部资源加载完成才会执行,包括图片、视频
})
window.addEventListener('DOMContentLoaded', function () {
	// DOM渲染完即可执行,此时图片、视频可能还没有加载完
	// 这种更好
})

性能优化

优化原则

  • 多使用内存、缓存或其他方法
  • 减少CPU计算量,减少网络加载耗时
    -( 适用于所有编程的性能优化 - 空间换时间)

从何入手:

  • 让加载更快
  • 让渲染更快

让加载更快:

  • 减少资源体系,压缩代码
  • 减少访问次数:合并代码,SSR服务器端渲染,缓存
  • 使用更快的网络:CDN

让渲染更快:

  • css放在head,js放在body最下面
  • 尽早开始执行js,用DOMContentLoaded触发
  • 懒加载(图片懒加载,上拉加载更多)
  • 对DOM查询进行缓存
  • 频繁DOM操作,合并到一起插入DOM结构
  • 节流throttle防抖debounce

缓存:

  • 静态资源加hash后缀,根据问价内容计算hash
  • 文件内容不变,则hash不变,则url不变
  • url和文件不变,则会自动触发http缓存机制,返回304

SSR:

  • 服务器端渲染:将网页和数据一起加载,一起渲染
  • 非SSR(前后端分离):先加载网页,再加载数据,再渲染数据
  • 早先的JSP ASP PHP,现在的vue react SSR

懒加载:
在这里插入图片描述

DOM在这里插入图片描述
在这里插入图片描述

防抖

const input1 = document.getElementById("input");
function debounce(fn, delay = 500) {
  // timer是闭包中的
  let timer = null;

  return function () {
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
      fn.apply(this, arguments);
      timer = null;
    }, delay);
  };
}
input1.addEventListener(
  "keyup",
  debounce(function (e) {
    console.log(input1.value);
  }, 2000)
);

节流

  • 拖拽一个元素的时候要拿到该元素的位置
  • 直接用drag事件会被频繁触发,容易卡顿
  • 节流:无论拖拽速度多快,都每隔固定时间触发一次(100ms)
const div1 = document.getElementById("div1");

function throttle(fn, delay = 100) {
  let timer = null;
  return function () {
    if (timer) {
      return;
    }
    timer = setTimeout(() => {
      fn.apply(this, arguments);
      timer = null;
    }, delay);
  };
}
div1.addEventListener(
  "drag",
  throttle(function (e) {
    console.log(e.offsetX, e.offsetY);
  }, 100)
);

安全

常见的web安全攻击方式有哪些?

  • xss跨站请求攻击:
    在这里插入图片描述
    预防:
    在这里插入图片描述
  • XSRF 攻击
    在这里插入图片描述
    在这里插入图片描述
    预防:
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值