是時候擼一波 JS 基礎啦,擼熟了,銀十速拿 offer;
本文不從傳統的問答方式梳理,而是從知識維度梳理,以便造成知識網絡;
包括函數,數組,對象,數據結構,算法,設計模式和 http.html
//ES5
function getSum(){}
function (){}//匿名函數
//ES6
()=>{}//若是{}內容只有一行{}和return關鍵字可省,
複製代碼
//ES5
var sum=function(){}
//ES6
let sum=()=>{}//若是{}內容只有一行{}和return關鍵字可省,
複製代碼
const sum = new Function('a', 'b' , 'return a + b')
複製代碼
1.函數聲明有預解析,並且函數聲明的優先級高於變量; 2.使用Function構造函數定義函數的方式是一個函數表達式,這種方式會致使解析兩次代碼,影響性能。第一次解析常規的JavaScript代碼,第二次解析傳入構造函數的字符串vue
在ES5中函數內容的this指向和調用方法有關node
包括函數名()和匿名函數調用,this指向windowes6
function getSum() {
console.log(this) //這個屬於函數名調用,this指向window
}
getSum()
(function() {
console.log(this) //匿名函數調用,this指向window
})()
var getSum=function() {
console.log(this) //實際上也是函數名調用,window
}
getSum()
複製代碼
對象.方法名(),this指向對象算法
var objList = {
name: 'methods',
getSum: function() {
console.log(this) //objList對象
}
}
objList.getSum()
複製代碼
new 構造函數名(),this指向實例化的對象json
function Person() {
console.log(this); //是構造函數調用,指向實例化的對象personOne
}
var personOne = new Person();
複製代碼
利用call和apply來實現,this就是call和apply對應的第一個參數,若是不傳值或者第一個值爲null,undefined時this指向window設計模式
function foo() {
console.log(this);
}
foo.apply('我是apply改變的this值');//我是apply改變的this值
foo.call('我是call改變的this值');//我是call改變的this值
複製代碼
箭頭函數不能夠看成構造函數使用,也就是不能用new命令實例化一個對象,不然會拋出一個錯誤 箭頭函數的this是和定義時有關和調用無關 調用就是函數調用模式api
(() => {
console.log(this)//window
})()
let arrowFun = () => {
console.log(this)//window
}
arrowFun()
let arrowObj = {
arrFun: function() {
(() => {
console.log(this)//this指向的是arrowObj對象
})()
}
}
arrowObj.arrFun();
複製代碼
1.IE5以前不支持call和apply,bind是ES5出來的; 2.call和apply能夠調用函數,改變this,實現繼承和借用別的對象的方法;數組
調用方法,用一個對象替換掉另外一個對象(this) 對象.call(新this對象,實參1,實參2,實參3.....) 對象.apply(新this對象,[實參1,實參2,實參3.....])瀏覽器
1.間接調用函數,改變做用域的this值 2.劫持其餘對象的方法
var foo = {
name:"張三",
logName:function(){
console.log(this.name);
}
}
var bar={
name:"李四"
};
foo.logName.call(bar);//李四
實質是call改變了foo的this指向爲bar,並調用該函數
複製代碼
3.兩個函數實現繼承
function Animal(name){
this.name = name;
this.showName = function(){
console.log(this.name);
}
}
function Cat(name){
Animal.call(this, name);
}
var cat = new Cat("Black Cat");
cat.showName(); //Black Cat
複製代碼
4.爲類數組(arguments和nodeList)添加數組方法push,pop
(function(){
Array.prototype.push.call(arguments,'王五');
console.log(arguments);//['張三','李四','王五']
})('張三','李四')
複製代碼
5.合併數組
let arr1=[1,2,3];
let arr2=[4,5,6];
Array.prototype.push.apply(arr1,arr2); //將arr2合併到了arr1中
複製代碼
6.求數組最大值
Math.max.apply(null,arr)
複製代碼
7.判斷字符類型
Object.prototype.toString.call({})
複製代碼
bind是function的一個函數擴展方法,bind之後代碼從新綁定了func內部的this指向,不會調用方法,不兼容IE8
var name = '李四'
var foo = {
name: "張三",
logName: function(age) {
console.log(this.name, age);
}
}
var fooNew = foo.logName;
var fooNewBind = foo.logName.bind(foo);
fooNew(10)//李四,10
fooNewBind(11)//張三,11 由於bind改變了fooNewBind裏面的this指向
複製代碼
call實現:
Function.prototype.newCall = function(context, ...parameter) {
if (typeof context === 'object' || typeof context === 'function') {
context = context || window
} else {
context = Object.create(null)
}
context[fn] = this
const res =context[fn](...parameter)
delete context.fn;
return res
}
let person = {
name: 'Abiel'
}
function sayHi(age,sex) {
console.log(this.name, age, sex);
}
sayHi.newCall (person, 25, '男'); // Abiel 25 男
複製代碼
apply實現:
Function.prototype.newApply = function(context, parameter) {
if (typeof context === 'object' || typeof context === 'function') {
context = context || window
} else {
context = Object.create(null)
}
let fn = Symbol()
context[fn] = this
return res=context[fn](..parameter);
delete context[fn]
return res
}
sayHi.newApply (person,[ 25, '男']) //Abiel 25 男
複製代碼
bind實現:
Function.prototype.bind = function (context,...innerArgs) {
var me = this
return function (...finnalyArgs) {
return me.call(context,...innerArgs,...finnalyArgs)
}
}
let person = {
name: 'Abiel'
}
function sayHi(age,sex) {
console.log(this.name, age, sex);
}
let personSayHi = sayHi.bind(person, 25)
personSayHi('男')
複製代碼
同:都是改變this指向,均可接收參數 異:bind和call是接收單個參數,apply是接收數組
類型 | 概念 | 應用 |
---|---|---|
節流 | 事件觸發後每隔一段時間觸發一次,可觸發屢次 | scroll,resize事件一段時間觸發屢次 |
防抖 | 事件觸發動做完成後一段時間觸發一次 | scroll,resize事件觸發完後一段時間觸發 |
節流:
let throttle = function(func, delay) {
let timer = null;
return ()=> {
if (!timer) {
timer = setTimeout(function() {
func.apply(this, arguments);
timer = null;
}, delay);
}
};
};
function handle() {
console.log(Math.random());
}
window.addEventListener("scroll", throttle(handle, 1000)); //事件處理函數
複製代碼
function debounce(fn, wait) {
var timeout = null;
return function() {
if (timeout !== null) clearTimeout(timeout);//若是屢次觸發將上次記錄延遲清除掉
timeout = setTimeout(()=> {
fn.apply(this, arguments);
timeout = null;
}, wait);
};
}
// 處理函數
function handle() {
console.log(Math.random());
}
// 滾動事件
window.addEventListener("scroll", debounce(handle, 1000));
複製代碼
對象繼承屬性的一個鏈條
var Person = function (name) { this.name = name; }//person是構造函數
var o3personTwo = new Person('personTwo')//personTwo是實例
複製代碼
原型對象都有一個默認的constructor屬性指向構造函數
1.字面量
let obj={'name':'張三'}
複製代碼
2.Object構造函數建立
let Obj=new Object()
Obj.name='張三'
複製代碼
3.使用工廠模式建立對象
function createPerson(name){
var o = new Object();
o.name = name;
return o;
}
var person1 = createPerson('張三');
複製代碼
4.使用構造函數建立對象
function Person(name){
this.name = name;
}
var person1 = new Person('張三');
複製代碼
1.創了一個新對象;
2.this指向構造函數;
3.構造函數有返回,會替換new出來的對象,若是沒有就是new出來的對象
4.手動封裝一個new運算符
var new2 = function (func) {
var o = Object.create(func.prototype);//建立對象
var k = func.call(o);//改變this指向,把結果付給k
if (k && typeof k === 'object') {//判斷k的類型是否是對象
return k; //是,返回k
} else {
return o;//不是返回返回構造函數的執行結果
}
}
複製代碼
JS是一門弱類型動態語言,封裝和繼承是他的兩大特性
將父類的實例做爲子類的原型 1.代碼實現 定義父類:
// 定義一個動物類
function Animal (name) {
// 屬性
this.name = name || 'Animal';
// 實例方法
this.sleep = function(){
console.log(this.name + '正在睡覺!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
複製代碼
子類:
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
// Test Code
var cat = new Cat();
console.log(cat.name);//cat
console.log(cat.eat('fish'));//cat正在吃:fish undefined
console.log(cat.sleep());//cat正在睡覺! undefined
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
複製代碼
2.優缺點 簡單易於實現,可是要想爲子類新增屬性和方法,必需要在new Animal()這樣的語句以後執行,沒法實現多繼承
實質是利用call來改變Cat中的this指向 1.代碼實現 子類:
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
複製代碼
2.優缺點 能夠實現多繼承,不能繼承原型屬性/方法
爲父類實例添加新特性,做爲子類實例返回 1.代碼實現 子類
function Cat(name){
var instance = new Animal();
instance.name = name || 'Tom';
return instance;
}
複製代碼
2.優缺點 不限制調用方式,但不能實現多繼承
將父類的屬性和方法拷貝一份到子類中 1.子類:
function Cat(name){
var animal = new Animal();
for(var p in animal){
Cat.prototype[p] = animal[p];
}
Cat.prototype.name = name || 'Tom';
}
複製代碼
2.優缺點 支持多繼承,可是效率低佔用內存
經過調用父類構造,繼承父類的屬性並保留傳參的優勢,而後經過將父類實例做爲子類原型,實現函數複用 1.子類:
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
複製代碼
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
(function(){
// 建立一個沒有實例方法的類
var Super = function(){};
Super.prototype = Animal.prototype;
//將實例做爲子類的原型
Cat.prototype = new Super();
})();
複製代碼
ES6 的繼承機制是先創造父類的實例對象this(因此必須先調用super方法),而後再用子類的構造函數修改this
//父類
class Person {
//constructor是構造方法
constructor(skin, language) {
this.skin = skin;
this.language = language;
}
say() {
console.log('我是父類')
}
}
//子類
class Chinese extends Person {
constructor(skin, language, positon) {
//console.log(this);//報錯
super(skin, language);
//super();至關於父類的構造函數
//console.log(this);調用super後獲得了this,不報錯,this指向子類,至關於調用了父類.prototype.constructor.call(this)
this.positon = positon;
}
aboutMe() {
console.log(`${this.skin} ${this.language} ${this.positon}`);
}
}
//調用只能經過new的方法獲得實例,再調用裏面的方法
let obj = new Chinese('紅色', '中文', '香港');
obj.aboutMe();
obj.say();
複製代碼
函數的參數是函數或返回函數
map,reduce,filter,sort
1.定義:只傳遞給函數一部分參數來調用它,讓它返回一個函數去處理剩下的參數
fn(a,b,c,d)=>fn(a)(b)(c)(d)
複製代碼
2.代碼實現:
const currying = fn => {
const len = fn.length
return function curr (...args1) {
if (args1.length >= len) {
return fn(...args1)
}
return (...args2) => curr(...args1, ...args2)
}
}
複製代碼
1.定義:
obj.func(arg1, arg2)=>func(obj, arg1, arg2)
複製代碼
2.代碼實現:
Function.prototype.uncurrying = function() {
var that = this;
return function() {
return Function.prototype.call.apply(that, arguments);
}
};
function sayHi () {
return "Hello " + this.value +" "+[].slice.call(arguments);
}
let sayHiuncurrying=sayHi.uncurrying();
console.log(sayHiuncurrying({value:'world'},"hahaha"));
複製代碼
1.定義:指定部分參數來返回一個新的定製函數的形式 2.例子:
function foo(a, b, c) {
return a + b + c;
}
function func(a, b) {
return foo(a,b,8);
}
複製代碼
var test2 = {x:123,y:345};
console.log(test2);//{x:123,y:345};
console.log(test2.x);//123
console.log(test2.__proto__.x);//undefined
console.log(test2.__proto__.x === test2.x);//false
複製代碼
var test1 = new Object({x:123,y:345});
console.log(test1);//{x:123,y:345}
console.log(test1.x);//123
console.log(test1.__proto__.x);//undefined
console.log(test1.__proto__.x === test1.x);//false
複製代碼
new的做用: 1.創了一個新對象; 2.this指向構造函數; 3.構造函數有返回,會替換new出來的對象,若是沒有就是new出來的對象
Obejct.create(obj,descriptor),obj是對象,describe描述符屬性(可選)
let test = Object.create({x:123,y:345});
console.log(test);//{}
console.log(test.x);//123
console.log(test.__proto__.x);//3
console.log(test.__proto__.x === test.x);//true
複製代碼
1.功能:都能實現對象的聲明,並可以賦值和取值
2.繼承性:內置方法建立的對象繼承到__proto__屬性上
3.隱藏屬性:三種聲明方法會默認爲內部的每一個成員(屬性或方法)生成一些隱藏屬性,這些隱藏屬性是能夠讀取和可配置的,屬性分類見下面
4.屬性讀取:Object.getOwnPropertyDescriptor()或getOwnPropertyDescriptor()
5.屬性設置:Object.definePropertype或Object.defineProperties
1.數據屬性4個特性: configurable(可配置),enumerable(可枚舉),writable(可修改),value(屬性值)
2.訪問器屬性2個特性: get(獲取),set(設置)
3.內部屬性 由JavaScript引擎內部使用的屬性; 不能直接訪問,可是能夠經過對象內置方法間接訪問,如:[[Prototype]]能夠經過 Object.getPrototypeOf()訪問; 內部屬性用[[]]包圍表示,是一個抽象操做,沒有對應字符串類型的屬性名,如[[Prototype]].
1.定義:將一個屬性的全部特性編碼成一個對象返回 2.描述符的屬性有:數據屬性和訪問器屬性 3.使用範圍: 做爲方法Object.defineProperty, Object.getOwnPropertyDescriptor, Object.create的第二個參數,
1.訪問對象存在的屬性
特性名 | 默認值 |
---|---|
value | 對應屬性值 |
get | 對應屬性值 |
set | undefined |
writable | true |
enumerable | true |
configurable | true |
因此經過上面三種聲明方法已存在的屬性都是有這些默認描述符 2.訪問對象不存在的屬性
特性名 | 默認值 |
---|---|
value | undefined |
get | undefined |
set | undefined |
writable | false |
enumerable | false |
configurable | false |
get,set與wriable,value是互斥的,若是有交集設置會報錯
1.定義屬性的函數有兩個:Object.defineProperty和Object.defineProperties.例如: Object.defineProperty(obj, propName, desc)
2.在引擎內部,會轉換成這樣的方法調用: obj.[[DefineOwnProperty]](propName, desc, true)
1.賦值運算符(=)就是在調用[[Put]].好比: obj.prop = v;
2.在引擎內部,會轉換成這樣的方法調用: obj.[[Put]]("prop", v, isStrictModeOn)
名稱 | 含義 | 用法 |
---|---|---|
in | 若是指定的屬性在指定的對象或其原型鏈中,則in 運算符返回true | 'name' in test //true |
hasOwnProperty() | 只判斷自身屬性 | test.hasOwnProperty('name') //true |
.或[] | 對象或原型鏈上不存在該屬性,則會返回undefined | test.name //"lei" test["name"] //"lei" |
是一種數據類型; 不能new,由於Symbol是一個原始類型的值,不是對象。
Symbol(),能夠傳參 var s1 = Symbol(); var s2 = Symbol(); s1 === s2 // false
// 有參數的狀況
var s1 = Symbol("foo");
var s2 = Symbol("foo");
s1 === s2 // false
複製代碼
1.不能與其餘類型的值進行運算; 2.做爲屬性名
let mySymbol = Symbol();
// 第一種寫法
var a = {};
a[mySymbol] = 'Hello!';
// 第二種寫法
var a = {
[mySymbol]: 'Hello!'
};
// 第三種寫法
var a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上寫法都獲得一樣結果
a[mySymbol] // "Hello!"
複製代碼
3.做爲對象屬性名時,不能用點運算符,能夠用[]
let a = {};
let name = Symbol();
a.name = 'lili';
a[name] = 'lucy';
console.log(a.name,a[name]);
複製代碼
4.遍歷不會被for...in、for...of和Object.keys()、Object.getOwnPropertyNames()取到該屬性
1.定義:在全局中搜索有沒有以該參數做爲名稱的Symbol值,若是有,就返回這個Symbol值,不然就新建並返回一個以該字符串爲名稱的Symbol值 2.舉例:
var s1 = Symbol.for('foo');
var s2 = Symbol.for('foo');
s1 === s2 // true
複製代碼
1.定義:返回一個已登記的Symbol類型值的key 2.舉例:
var s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
var s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined
複製代碼
方法 | 特性 |
---|---|
for ... in | 遍歷對象自身的和繼承的可枚舉屬性(不含Symbol屬性) |
Object.keys(obj) | 返回一個數組,包括對象自身的(不含繼承的)全部可枚舉屬性(不含Symbol屬性) |
Object.getOwnPropertyNames(obj) | 返回一個數組,包括對象自身的全部可枚舉和不可枚舉屬性(不含Symbol屬性) |
Object.getOwnPropertySymbols(obj) | 返回一個數組,包含對象自身的全部Symbol屬性 |
Reflect.ownKeys(obj) | 返回一個數組,包含對象自身的全部(不枚舉、可枚舉和Symbol)屬性 |
Reflect.enumerate(obj) | 返回一個Iterator對象,遍歷對象自身的和繼承的全部可枚舉屬性(不含Symbol屬性) |
總結:1.只有Object.getOwnPropertySymbols(obj)和Reflect.ownKeys(obj)能夠拿到Symbol屬性 2.只有Reflect.ownKeys(obj)能夠拿到不可枚舉屬性
數據模型:
var treeNodes = [
{
id: 1,
name: '1',
children: [
{
id: 11,
name: '11',
children: [
{
id: 111,
name: '111',
children:[]
},
{
id: 112,
name: '112'
}
]
},
{
id: 12,
name: '12',
children: []
}
],
users: []
},
];
複製代碼
遞歸:
var parseTreeJson = function(treeNodes){
if (!treeNodes || !treeNodes.length) return;
for (var i = 0, len = treeNodes.length; i < len; i++) {
var childs = treeNodes[i].children;
console.log(treeNodes[i].id);
if(childs && childs.length > 0){
parseTreeJson(childs);
}
}
};
console.log('------------- 遞歸實現 ------------------');
parseTreeJson(treeNodes);
複製代碼
1.定義:將源對象(source)的全部可枚舉屬性,複製到目標對象(target) 2.用法:
合併多個對象
var target = { a: 1, b: 1 };
var source1 = { b: 2, c: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
複製代碼
3.注意: 這個是僞深度拷貝,只能拷貝第一層
1.原理:是將對象轉化爲字符串,而字符串是簡單數據類型
function deepClone(source){
const targetObj = source.constructor === Array ? [] : {}; // 判斷複製的目標是數組仍是對象
for(let keys in source){ // 遍歷目標
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof source[keys] === 'object'){ // 若是值是對象,就遞歸一下
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{ // 若是不是,就直接賦值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
複製代碼
定義:利用對象內置方法,設置屬性,進而改變對象的屬性值
1.ES5出來的方法; 2.三個參數:對象(必填),屬性值(必填),描述符(可選); 3.defineProterty的描述符屬性
數據屬性:value,writable,configurable,enumerable
訪問器屬性:get,set
注:不能同時設置value和writable,這兩對屬性是互斥的
複製代碼
4.攔截對象的兩種狀況:
let obj = {name:'',age:'',sex:'' },
defaultName = ["這是姓名默認值1","這是年齡默認值1","這是性別默認值1"];
Object.keys(obj).forEach(key => {
Object.defineProperty(obj, key, {
get() {
return defaultName;
},
set(value) {
defaultName = value;
}
});
});
console.log(obj.name);
console.log(obj.age);
console.log(obj.sex);
obj.name = "這是改變值1";
console.log(obj.name);
console.log(obj.age);
console.log(obj.sex);
let objOne={},defaultNameOne="這是默認值2";
Object.defineProperty(obj, 'name', {
get() {
return defaultNameOne;
},
set(value) {
defaultNameOne = value;
}
});
console.log(objOne.name);
objOne.name = "這是改變值2";
console.log(objOne.name);
複製代碼
5.攔截數組變化的狀況
let a={};
bValue=1;
Object.defineProperty(a,"b",{
set:function(value){
bValue=value;
console.log("setted");
},
get:function(){
return bValue;
}
});
a.b;//1
a.b=[];//setted
a.b=[1,2,3];//setted
a.b[1]=10;//無輸出
a.b.push(4);//無輸出
a.b.length=5;//無輸出
a.b;//[1,10,3,4,undefined];
複製代碼
結論:defineProperty沒法檢測數組索引賦值,改變數組長度的變化; 可是經過數組方法來操做能夠檢測到
多級嵌套對象監聽
let info = {};
function observe(obj) {
if (!obj || typeof obj !== "object") {
return;
}
for (var i in obj) {
definePro(obj, i, obj[i]);
}
}
function definePro(obj, key, value) {
observe(value);
Object.defineProperty(obj, key, {
get: function() {
return value;
},
set: function(newval) {
console.log("檢測變化", newval);
value = newval;
}
});
}
definePro(info, "friends", { name: "張三" });
info.friends.name = "李四";
複製代碼
6.存在的問題
不能監聽數組索引賦值和改變長度的變化
必須深層遍歷嵌套的對象,由於defineProterty只能劫持對象的屬性,所以咱們須要對每一個對象的每一個屬性進行遍歷,若是屬性值也是對象那麼須要深度遍歷,顯然能劫持一個完整的對象是更好的選擇
複製代碼
1.ES6出來的方法,實質是對對象作了一個攔截,並提供了13個處理方法
2.兩個參數:對象和行爲函數
let handler = {
get(target, key, receiver) {
console.log("get", key);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log("set", key, value);
return Reflect.set(target, key, value, receiver);
}
};
let proxy = new Proxy(obj, handler);
proxy.name = "李四";
proxy.age = 24;
複製代碼
涉及到多級對象或者多級數組
//傳遞兩個參數,一個是object, 一個是proxy的handler
//若是是否是嵌套的object,直接加上proxy返回,若是是嵌套的object,那麼進入addSubProxy進行遞歸。
function toDeepProxy(object, handler) {
if (!isPureObject(object)) addSubProxy(object, handler);
return new Proxy(object, handler);
//這是一個遞歸函數,目的是遍歷object的全部屬性,若是不是pure object,那麼就繼續遍歷object的屬性的屬性,若是是pure object那麼就加上proxy
function addSubProxy(object, handler) {
for (let prop in object) {
if ( typeof object[prop] == 'object') {
if (!isPureObject(object[prop])) addSubProxy(object[prop], handler);
object[prop] = new Proxy(object[prop], handler);
}
}
object = new Proxy(object, handler)
}
//是否是一個pure object,意思就是object裏面沒有再嵌套object了
function isPureObject(object) {
if (typeof object!== 'object') {
return false;
} else {
for (let prop in object) {
if (typeof object[prop] == 'object') {
return false;
}
}
}
return true;
}
}
let object = {
name: {
first: {
four: 5,
second: {
third: 'ssss'
}
}
},
class: 5,
arr: [1, 2, {arr1:10}],
age: {
age1: 10
}
}
//這是一個嵌套了對象和數組的數組
let objectArr = [{name:{first:'ss'}, arr1:[1,2]}, 2, 3, 4, 5, 6]
//這是proxy的handler
let handler = {
get(target, property) {
console.log('get:' + property)
return Reflect.get(target, property);
},
set(target, property, value) {
console.log('set:' + property + '=' + value);
return Reflect.set(target, property, value);
}
}
//變成監聽對象
object = toDeepProxy(object, handler);
objectArr = toDeepProxy(objectArr, handler);
//進行一系列操做
console.time('pro')
objectArr.length
objectArr[3];
objectArr[2]=10
objectArr[0].name.first = 'ss'
objectArr[0].arr1[0]
object.name.first.second.third = 'yyyyy'
object.class = 6;
object.name.first.four
object.arr[2].arr1
object.age.age1 = 20;
console.timeEnd('pro')
複製代碼
3.問題和優勢 reflect對象沒有構造函數 能夠監聽數組索引賦值,改變數組長度的變化, 是直接監聽對象的變化,不用深層遍歷
1.defineProterty是es5的標準,proxy是es6的標準;
2.proxy能夠監聽到數組索引賦值,改變數組長度的變化;
3.proxy是監聽對象,不用深層遍歷,defineProterty是監聽屬性;
3.利用defineProterty實現雙向數據綁定(vue2.x採用的核心) 4.利用proxy實現雙向數據綁定(vue3.x會採用)
數組基本上考察數組方法多一點,因此這裏就單純介紹常見的場景數組的方法,還有不少場景後續補充;
本文主要從應用來說數組api的一些騷操做;
如一行代碼扁平化n維數組、數組去重、求數組最大值、數組求和、排序、對象和數組的轉化等;
上面這些應用場景你能夠用一行代碼實現?
1.終極篇
[1,[2,3]].flat(2) //[1,2,3]
[1,[2,3,[4,5]].flat(3) //[1,2,3,4,5]
[1,[2,3,[4,5]]].toString() //'1,2,3,4,5'
[1[2,3,[4,5[...]].flat(Infinity) //[1,2,3,4...n]
複製代碼
Array.flat(n)是ES10扁平數組的api,n表示維度,n值爲Infinity時維度爲無限大
2.開始篇
function flatten(arr) {
while(arr.some(item=>Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
flatten([1,[2,3]]) //[1,2,3]
flatten([1,[2,3,[4,5]]) //[1,2,3,4,5]
複製代碼
實質是利用遞歸和數組合並方法concat實現扁平
1.終極篇
Array.from(new Set([1,2,3,3,4,4])) //[1,2,3,4]
[...new Set([1,2,3,3,4,4])] //[1,2,3,4]
複製代碼
set是ES6新出來的一種一種定義不重複數組的數據類型 Array.from是將類數組轉化爲數組 ...是擴展運算符,將set裏面的值轉化爲字符串 2.開始篇
Array.prototype.distinct = function() {
const map = {}
const result = []
for (const n of this) {
if (!(n in map)) {
map[n] = 1
result.push(n)
}
}
return result
}
[1,2,3,3,4,4].distinct(); //[1,2,3,4]
複製代碼
取新數組存值,循環兩個數組值相比較
1.終極篇
[1,2,3,4].sort((a, b) => a - b); // [1, 2,3,4],默認是升序
[1,2,3,4].sort((a, b) => b - a); // [4,3,2,1] 降序
複製代碼
sort是js內置的排序方法,參數爲一個函數 2.開始篇 冒泡排序:
Array.prototype.bubleSort=function () {
let arr=this,
len = arr.length;
for (let outer = len; outer >= 2; outer--) {
for (let inner = 0; inner <= outer - 1; inner++) {
if (arr[inner] > arr[inner + 1]) {
//升序
[arr[inner], arr[inner + 1]] = [arr[inner + 1], arr[inner]];
console.log([arr[inner], arr[inner + 1]]);
}
}
}
return arr;
}
[1,2,3,4].bubleSort() //[1,2,3,4]
複製代碼
選擇排序
Array.prototype.selectSort=function () {
let arr=this,
len = arr.length;
for (let i = 0, len = arr.length; i < len; i++) {
for (let j = i, len = arr.length; j < len; j++) {
if (arr[i] > arr[j]) {
[arr[i], arr[j]] = [arr[j], arr[i]];
}
}
}
return arr;
}
[1,2,3,4].selectSort() //[1,2,3,4]
複製代碼
1.終極篇
Math.max(...[1,2,3,4]) //4
Math.max.apply(this,[1,2,3,4]) //4
[1,2,3,4].reduce( (prev, cur,curIndex,arr)=> {
return Math.max(prev,cur);
},0) //4
複製代碼
Math.max()是Math對象內置的方法,參數是字符串; reduce是ES5的數組api,參數有函數和默認初始值; 函數有四個參數,pre(上一次的返回值),cur(當前值),curIndex(當前值索引),arr(當前數組)
2.開始篇 先排序再取值
1.終極篇
[1,2,3,4].reduce(function (prev, cur) {
return prev + cur;
},0) //10
複製代碼
2.開始篇
function sum(arr) {
var len = arr.length;
if(len == 0){
return 0;
} else if (len == 1){
return arr[0];
} else {
return arr[0] + sum(arr.slice(1));
}
}
sum([1,2,3,4]) //10
複製代碼
利用slice截取改變數組,再利用遞歸求和
1.終極篇
[1,2,3,4].concat([5,6]) //[1,2,3,4,5,6]
[...[1,2,3,4],...[4,5]] //[1,2,3,4,5,6]
let arrA = [1, 2], arrB = [3, 4]
Array.prototype.push.apply(arrA, arrB))//arrA值爲[1,2,3,4]
複製代碼
2.開始篇
let arr=[1,2,3,4];
[5,6].map(item=>{
arr.push(item)
})
//arr值爲[1,2,3,4,5,6],注意不能直接return出來,return後只會返回[5,6]
複製代碼
1.終極篇
[1,2,3].includes(4) //false
[1,2,3].indexOf(4) //-1 若是存在換回索引
[1, 2, 3].find((item)=>item===3)) //3 若是數組中無值返回undefined
[1, 2, 3].findIndex((item)=>item===3)) //2 若是數組中無值返回-1
複製代碼
includes(),find(),findIndex()是ES6的api
2.開始篇
[1,2,3].some(item=>{
return item===3
}) //true 若是不包含返回false
複製代碼
1.終極篇
Array.prototype.slice.call(arguments) //arguments是類數組(僞數組)
Array.prototype.slice.apply(arguments)
Array.from(arguments)
[...arguments]
複製代碼
類數組:表示有length屬性,可是不具有數組的方法
call,apply:是改變slice裏面的this指向arguments,因此arguments也可調用數組的方法
Array.from是將相似數組或可迭代對象建立爲數組
...是將類數組擴展爲字符串,再定義爲數組
2.開始篇
Array.prototype.slice = function(start,end){
var result = new Array();
start = start || 0;
end = end || this.length; //this指向調用的對象,當用了call後,可以改變this的指向,也就是指向傳進來的對象,這是關鍵
for(var i = start; i < end; i++){
result.push(this[i]);
}
return result;
}
複製代碼
1.終極篇
[1,2,3].fill(false) //[false,false,false]
複製代碼
fill是ES6的方法 2.開始篇
[1,2,3].map(() => 0)
複製代碼
[1,2,3].every(item=>{return item>2}) //false
複製代碼
every是ES5的api,每一項知足返回 true
[1,2,3].some(item=>{return item>2}) //true
複製代碼
some是ES5的api,有一項知足返回 true
[1,2,3].filter(item=>{return item>2}) //[3]
複製代碼
filter是ES5的api,返回知足添加的項的數組
Object.keys({name:'張三',age:14}) //['name','age']
Object.values({name:'張三',age:14}) //['張三',14]
Object.entries({name:'張三',age:14}) //[[name,'張三'],[age,14]]
Object.fromEntries([name,'張三'],[age,14]) //ES10的api,Chrome不支持 , firebox輸出{name:'張三',age:14}
複製代碼
[{count:1},{count:2},{count:3}].reduce((p, e)=>p+(e.count), 0)
複製代碼
數據結構是計算機存儲、組織數據的方式,算法是系統描述解決問題的策略。瞭解基本的數據結構和算法能夠提升代碼的性能和質量。
也是程序猿進階的一個重要技能。
手擼代碼實現棧,隊列,鏈表,字典,二叉樹,動態規劃和貪心算法
棧的特色:先進後出
class Stack {
constructor() {
this.items = [];
}
// 入棧
push(element) {
this.items.push(element);
}
// 出棧
pop() {
return this.items.pop();
}
// 末位
get peek() {
return this.items[this.items.length - 1];
}
// 是否爲空棧
get isEmpty() {
return !this.items.length;
}
// 長度
get size() {
return this.items.length;
}
// 清空棧
clear() {
this.items = [];
}
}
// 實例化一個棧
const stack = new Stack();
console.log(stack.isEmpty); // true
// 添加元素
stack.push(5);
stack.push(8);
// 讀取屬性再添加
console.log(stack.peek); // 8
stack.push(11);
console.log(stack.size); // 3
console.log(stack.isEmpty); // false
複製代碼
隊列:先進先出 class Queue { constructor(items) { this.items = items || []; }
enqueue(element) {
this.items.push(element);
}
dequeue() {
return this.items.shift();
}
front() {
return this.items[0];
}
clear() {
this.items = [];
}
get size() {
return this.items.length;
}
get isEmpty() {
return !this.items.length;
}
print() {
console.log(this.items.toString());
}
}
const queue = new Queue();
console.log(queue.isEmpty); // true
queue.enqueue("John");
queue.enqueue("Jack");
queue.enqueue("Camila");
console.log(queue.size); // 3
console.log(queue.isEmpty); // false
queue.dequeue();
queue.dequeue();
複製代碼
鏈表:
存貯有序元素的集合;
可是不一樣於數組,每一個元素是一個存貯元素自己的節點和指向下一個元素引用組成
要想訪問鏈表中間的元素,須要從起點開始遍歷找到所需元素
class Node {
constructor(element) {
this.element = element;
this.next = null;
}
}
// 鏈表
class LinkedList {
constructor() {
this.head = null;
this.length = 0;
}
// 追加元素
append(element) {
const node = new Node(element);
let current = null;
if (this.head === null) {
this.head = node;
} else {
current = this.head;
while (current.next) {
current = current.next;
}
current.next = node;
}
this.length++;
}
// 任意位置插入元素
insert(position, element) {
if (position >= 0 && position <= this.length) {
const node = new Node(element);
let current = this.head;
let previous = null;
let index = 0;
if (position === 0) {
this.head = node;
node.next = current;
} else {
while (index++ < position) {
previous = current;
current = current.next;
}
node.next = current;
previous.next = node;
}
this.length++;
return true;
}
return false;
}
// 移除指定位置元素
removeAt(position) {
// 檢查越界值
if (position > -1 && position < length) {
let current = this.head;
let previous = null;
let index = 0;
if (position === 0) {
this.head = current.next;
} else {
while (index++ < position) {
previous = current;
current = current.next;
}
previous.next = current.next;
}
this.length--;
return current.element;
}
return null;
}
// 尋找元素下標
findIndex(element) {
let current = this.head;
let index = -1;
while (current) {
if (element === current.element) {
return index + 1;
}
index++;
current = current.next;
}
return -1;
}
// 刪除指定文檔
remove(element) {
const index = this.findIndex(element);
return this.removeAt(index);
}
isEmpty() {
return !this.length;
}
size() {
return this.length;
}
// 轉爲字符串
toString() {
let current = this.head;
let string = "";
while (current) {
string += ` ${current.element}`;
current = current.next;
}
return string;
}
}
const linkedList = new LinkedList();
console.log(linkedList);
linkedList.append(2);
linkedList.append(6);
linkedList.append(24);
linkedList.append(152);
linkedList.insert(3, 18);
console.log(linkedList);
console.log(linkedList.findIndex(24));
複製代碼
字典:相似對象,以key,value存貯值 class Dictionary { constructor() { this.items = {}; }
set(key, value) {
this.items[key] = value;
}
get(key) {
return this.items[key];
}
remove(key) {
delete this.items[key];
}
get keys() {
return Object.keys(this.items);
}
get values() {
/*
也可使用ES7中的values方法
return Object.values(this.items)
*/
// 在這裏咱們經過循環生成一個數組並輸出
return Object.keys(this.items).reduce((r, c, i) => {
r.push(this.items[c]);
return r;
}, []);
}
}
const dictionary = new Dictionary();
dictionary.set("Gandalf", "gandalf@email.com");
dictionary.set("John", "johnsnow@email.com");
dictionary.set("Tyrion", "tyrion@email.com");
console.log(dictionary);
console.log(dictionary.keys);
console.log(dictionary.values);
console.log(dictionary.items);
複製代碼
特色:每一個節點最多有兩個子樹的樹結構 class NodeTree { constructor(key) { this.key = key; this.left = null; this.right = null; } }
class BinarySearchTree {
constructor() {
this.root = null;
}
insert(key) {
const newNode = new NodeTree(key);
const insertNode = (node, newNode) => {
if (newNode.key < node.key) {
if (node.left === null) {
node.left = newNode;
} else {
insertNode(node.left, newNode);
}
} else {
if (node.right === null) {
node.right = newNode;
} else {
insertNode(node.right, newNode);
}
}
};
if (!this.root) {
this.root = newNode;
} else {
insertNode(this.root, newNode);
}
}
//訪問樹節點的三種方式:中序,先序,後序
inOrderTraverse(callback) {
const inOrderTraverseNode = (node, callback) => {
if (node !== null) {
inOrderTraverseNode(node.left, callback);
callback(node.key);
inOrderTraverseNode(node.right, callback);
}
};
inOrderTraverseNode(this.root, callback);
}
min(node) {
const minNode = node => {
return node ? (node.left ? minNode(node.left) : node) : null;
};
return minNode(node || this.root);
}
max(node) {
const maxNode = node => {
return node ? (node.right ? maxNode(node.right) : node) : null;
};
return maxNode(node || this.root);
}
}
const tree = new BinarySearchTree();
tree.insert(11);
tree.insert(7);
tree.insert(5);
tree.insert(3);
tree.insert(9);
tree.insert(8);
tree.insert(10);
tree.insert(13);
tree.insert(12);
tree.insert(14);
tree.inOrderTraverse(value => {
console.log(value);
});
console.log(tree.min());
console.log(tree.max());
複製代碼
冒泡排序,選擇排序,插入排序,此處不作贅述.
特色:第三項等於前面兩項之和
function fibonacci(num) {
if (num === 1 || num === 2) {
return 1
}
return fibonacci(num - 1) + fibonacci(num - 2)
}
複製代碼
特色:經過全局規劃,將大問題分割成小問題來取最優解
案例:最少硬幣找零
美國有如下面額(硬幣):d1=1, d2=5, d3=10, d4=25
若是要找36美分的零錢,咱們能夠用1個25美分、1個10美分和1個便士( 1美分)
class MinCoinChange {
constructor(coins) {
this.coins = coins
this.cache = {}
}
makeChange(amount) {
if (!amount) return []
if (this.cache[amount]) return this.cache[amount]
let min = [], newMin, newAmount
this.coins.forEach(coin => {
newAmount = amount - coin
if (newAmount >= 0) {
newMin = this.makeChange(newAmount)
}
if (newAmount >= 0 &&
(newMin.length < min.length - 1 || !min.length) &&
(newMin.length || !newAmount)) {
min = [coin].concat(newMin)
}
})
return (this.cache[amount] = min)
}
}
const rninCoinChange = new MinCoinChange([1, 5, 10, 25])
console.log(rninCoinChange.makeChange(36))
// [1, 10, 25]
const minCoinChange2 = new MinCoinChange([1, 3, 4])
console.log(minCoinChange2.makeChange(6))
// [3, 3]
複製代碼
特色:經過最優解來解決問題 用貪心算法來解決2.3中的案例
class MinCoinChange2 {
constructor(coins) {
this.coins = coins
}
makeChange(amount) {
const change = []
let total = 0
this.coins.sort((a, b) => a < b).forEach(coin => {
if ((total + coin) <= amount) {
change.push(coin)
total += coin
}
})
return change
}
}
const rninCoinChange2 = new MinCoinChange2 ( [ 1, 5, 10, 25])
console.log (rninCoinChange2. makeChange (36))
複製代碼
設計模式若是應用到項目中,能夠實現代碼的複用和解耦,提升代碼質量。 本文主要介紹14種設計模式 寫UI組件,封裝框架必備
1.定義:又叫靜態工廠方法,就是建立對象,並賦予屬性和方法
2.應用:抽取類相同的屬性和方法封裝到對象上
3.代碼:
let UserFactory = function (role) {
function User(opt) {
this.name = opt.name;
this.viewPage = opt.viewPage;
}
switch (role) {
case 'superAdmin':
return new User(superAdmin);
break;
case 'admin':
return new User(admin);
break;
case 'user':
return new User(user);
break;
default:
throw new Error('參數錯誤, 可選參數:superAdmin、admin、user')
}
}
//調用
let superAdmin = UserFactory('superAdmin');
let admin = UserFactory('admin')
let normalUser = UserFactory('user')
//最後獲得角色,能夠調用
複製代碼
1.定義:對產品類的抽象使其建立業務主要負責用於建立多類產品的實例
2.應用:建立實例
3.代碼:
var Factory=function(type,content){
if(this instanceof Factory){
var s=new this[type](content);
return s;
}else{
return new Factory(type,content);
}
}
//工廠原型中設置建立類型數據對象的屬性
Factory.prototype={
Java:function(content){
console.log('Java值爲',content);
},
PHP:function(content){
console.log('PHP值爲',content);
},
Python:function(content){
console.log('Python值爲',content);
},
}
//測試用例
Factory('Python','我是Python');
複製代碼
1.定義:設置函數的原型屬性 2.應用:實現繼承 3.代碼:
function Animal (name) {
// 屬性
this.name = name || 'Animal';
// 實例方法
this.sleep = function(){
console.log(this.name + '正在睡覺!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
// Test Code
var cat = new Cat();
console.log(cat.name);//cat
console.log(cat.eat('fish'));//cat正在吃:fish undefined
console.log(cat.sleep());//cat正在睡覺! undefined
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
複製代碼
1.定義:只容許被實例化依次的類 2.應用:提供一個命名空間 3.代碼:
let singleCase = function(name){
this.name = name;
};
singleCase.prototype.getName = function(){
return this.name;
}
// 獲取實例對象
let getInstance = (function() {
var instance = null;
return function(name) {
if(!instance) {//至關於一個一次性閥門,只能實例化一次
instance = new singleCase(name);
}
return instance;
}
})();
// 測試單體模式的實例,因此one===two
let one = getInstance("one");
let two = getInstance("two");
複製代碼
1.定義:爲子系統中的一組接口提供一個一致的界面 2.應用:簡化複雜接口 3.代碼: 外觀模式
1.定義:將一個接口轉換成客戶端須要的接口而不須要去修改客戶端代碼,使得不兼容的代碼能夠一塊兒工做 2.應用:適配函數參數 3.代碼: 適配器模式
1.定義:不改變原對象的基礎上,給對象添加屬性或方法 2.代碼
let decorator=function(input,fn){
//獲取事件源
let input=document.getElementById(input);
//若事件源已經綁定事件
if(typeof input.onclick=='function'){
//緩存事件源原有的回調函數
let oldClickFn=input.onclick;
//爲事件源定義新事件
input.onclick=function(){
//事件源原有回調函數
oldClickFn();
//執行事件源新增回調函數
fn();
}
}else{
//未綁定綁定
input.onclick=fn;
}
}
//測試用例
decorator('textInp',function(){
console.log('文本框執行啦');
})
decorator('btn',function(){
console.log('按鈕執行啦');
})
複製代碼
1.定義:將抽象部分與它的實現部分分離,使它們均可以獨立地變化 2.代碼 橋接模式
1.定義:定義一個模板,供之後傳不一樣參數調用 2.代碼: 模塊方法模式
1.做用:解決類與對象,對象與對象之間的耦合 2.代碼:
let Observer=
(function(){
let _message={};
return {
//註冊接口,
//1.做用:將訂閱者註冊的消息推入到消息隊列
//2.參數:因此要傳兩個參數,消息類型和處理動做,
//3.消息不存在從新建立,存在將消息推入到執行方法
regist:function(type,fn){
//若是消息不存在,建立
if(typeof _message[type]==='undefined'){
_message[type]=[fn];
}else{
//將消息推入到消息的執行動做
_message[type].push(fn);
}
},
//發佈信息接口
//1.做用:觀察這發佈消息將全部訂閱的消息一次執行
//2.參數:消息類型和動做執行傳遞參數
//3.消息類型參數必須校驗
fire:function(type,args){
//若是消息沒有註冊,則返回
if(!_message[type]) return;
//定義消息信息
var events={
type:type, //消息類型
args:args||{} //消息攜帶數據
},
i=0,
len=_message[type].length;
//遍歷消息
for(;i<len;i++){
//依次執行註冊消息
_message[type][i].call(this,events);
}
},
//移除信息接口
//1.做用:將訂閱者註銷消息從消息隊列清除
//2.參數:消息類型和執行的動做
//3.消息參數校驗
remove:function(type,fn){
//若是消息動做隊列存在
if(_message[type] instanceof Array){
//從最後一個消息動做序遍歷
var i=_message[type].length-1;
for(;i>=0;i--){
//若是存在該動做在消息隊列中移除
_message[type][i]===fn&&_message[type].splice(i,1);
}
}
}
}
})()
//測試用例
//1.訂閱消息
Observer.regist('test',function(e){
console.log(e.type,e.args.msg);
})
//2.發佈消息
Observer.fire('test',{msg:'傳遞參數1'});
Observer.fire('test',{msg:'傳遞參數2'});
Observer.fire('test',{msg:'傳遞參數3'});
複製代碼
1.定義:一個對象狀態改變會致使行爲變化 2.做用:解決複雜的if判斷 3.代碼 狀態模式
1.定義:定義了一系列家族算法,並對每一種算法單獨封裝起來,讓算法之間能夠相互替換,獨立於使用算法的客戶 2.代碼 策略模式
1.定義:經過繼承封裝一些該數據類型不具有的屬性, 2.做用:讓對象具有數組的操做方法 3.代碼: 訪問者模式
1.定義:設置一箇中間層,處理對象之間的交互 2.代碼: 中介者模式
HTTP 是一個鏈接客戶端,網關和服務器的一個協議。
支持客戶/服務器模式:能夠鏈接客戶端和服務端;
簡單快速:請求只需傳送請求方法,路徑和請求主體;
靈活:傳輸數據類型靈活;
無鏈接:請求結束當即斷開;
無狀態:沒法記住上一次請求。
無狀態:HTTP 協議自己沒法解決這個狀態,只有經過 cookie 和 session 將狀態作貯存,常見的場景是登陸狀態保持;
無鏈接:能夠經過自身屬性 Keep-Alive。
HTTP(S) 請求地址 → DNS 解析 → 三次握手 → 發送請求 → 四次揮手
三次握手過程圖片來源 CSDN)
在這裏插入圖片描述只容許客戶端發送 GET 這一種請求;
且不支持請求頭,協議只支持純文本;
無狀態性,每一個訪問獨立處理,完成斷開;
無狀態碼。
有身份認證,三次握手; 請求與響應支持頭域; 請求頭內容;
屬性名 | 含義 |
---|---|
Accept | 可接受的 MIME 類型 |
Accept-Encoding | 數據可解碼的格式 |
Accept-Language | 可接受語言 |
Connection | 值 keep-alive 是長鏈接 |
Host | 主機和端口 |
Pragma | 是否緩存,指定 no-cache 返回刷新 |
Referer | 頁面路由 |
If-Modified-Since | 值爲時間 |
響應頭內容;
屬性名 | 含義 |
---|---|
Connection | 值 keep-alive 是長鏈接 |
Content-Type | 返回文檔類型,常見的值有 text/plain,text/html,text/json |
Date | 消息發送的時間 |
Server | 服務器名字 |
Last-Modified | 值爲時間,s 返回的最後修改時間 |
Expires | 緩存過時時間,b 和 s 時間作對比 |
注意
expires 是響應頭內容,返回一個固定的時間,缺陷是時間到了服務器要從新設置;
請求頭中若是有 If-Modified-Since,服務器會將時間與 last-modified 對比,相同返回 304;
響應對象以一個響應狀態行開始;
響應對象不僅限於超文本;
支持 GET、HEAD、POST 方法;
有狀態碼;
支持長鏈接(但默認仍是使用短鏈接)、緩存機制以及身份認證。
請求頭增長 Cache-Control
屬性名 | 含義 |
---|---|
Cache-Control | 在1.1 引入的方法,指定請求和響應遵循的緩存機制,值有:public(b 和 s 都緩存),private(b 緩存),no-cache(不緩存),no-store(不緩存),max-age(緩存時間,s 爲單位),min-fresh(最小更新時間),max-age=3600 |
If-None-Match | 上次請求響應頭返回的 etag 值響應頭增長 Cache-Control,表示全部的緩存機制是否能夠緩存及哪一種類型 etag 返回的哈希值,第二次請求頭攜帶去和服務器值對比 |
注意
Cache-Control 的 max-age 返回是緩存的相對時間 Cache-Control 優先級比 expires 高 缺點:不能第一時間拿到最新修改文件
採用二進制格式傳輸;
多路複用,其實就是將請求數據分紅幀亂序發送到 TCP 中。TCP 只能有一個 steam,因此仍是會阻塞;
報頭壓縮;
服務器推送主動向 B 端發送靜態資源,避免往返延遲。
1.是基於 QUIC 協議,基於 UDP
2.特色:
自定義鏈接機制:TCP 以 IP/端口標識,變化從新鏈接握手,UDP 是一 64 位 ID 標識,是無鏈接;
自定義重傳機制:TCP 使用序號和應答傳輸,QUIC 是使用遞增序號傳輸; 無阻塞的多路複用:同一條 QUIC 能夠建立多個 steam。
1.https 是在 http 協議的基礎上加了個 SSL;
2.主要包括:握手(憑證交換和驗證)和記錄協議(數據進行加密)。
1.按協議分:協議層緩存和非 http 協議緩存:
1.1協議層緩存:利用 http 協議頭屬性值設置;
1.2非協議層緩存:利用 meta 標籤的 http-equiv 屬性值 Expires,set-cookie。
2.按緩存分:強緩存和協商緩存:
2.1強緩存:利用 cache-control 和 expires 設置,直接返回一個過時時間,因此在緩存期間不請求,If-modify-since;
2.2協商緩存:響應頭返回 etag 或 last-modified 的哈希值,第二次請求頭 If-none-match 或 IF-modify-since 攜帶上次哈希值,一致則返回 304。
3.協商緩存對比: etag 優先級高於 last-modified;
4.etag 精度高,last-modified 精度是 s,1s 內 etag 修改多少次都會被記錄; last-modified 性能好,etag 要獲得 hash 值。
5.瀏覽器讀取緩存流程: 會先判斷強緩存;再判斷協商緩存 etag(last-modified)是否存在;
存在利用屬性 If-None-match(If-Modified-since)攜帶值;
請求服務器,服務器對比 etag(last-modified),生效返回 304。
F5 刷新會忽略強緩存不會忽略協商緩存,ctrl+f5 都失效
序列 | 詳情 |
---|---|
1XX(通知) | |
2XX(成功) | 200(成功)、201(服務器建立)、202(服務器接收未處理)、203(非受權信息)、204(未返回內容)、205(重置內容)、206(部份內容) |
3XX(重定向) | 301(永久移動)、302(臨時移動)、303(查看其餘位置)、304(未修改)、305(使用代理)、307(臨時重定向) |
4XX(客戶端錯誤) | 400(錯誤請求)、401(未受權)、403(禁止)、404(未找到)、405(方法禁用)、406(不接受)、407(須要代理受權) |
5XX(服務器錯誤) | 500(服務器異常)、501(還沒有實施)、502(錯誤網關)、503(服務不可用)、504(網關超時)、505(HTTP 版本不受支持) |
協議
版本 | 內容 |
---|---|
http0.9 | 只容許客戶端發送 GET 這一種請求;且不支持請求頭,協議只支持純文本;無狀態性,每一個訪問獨立處理,完成斷開;無狀態碼 |
http1.0 解決 0.9 的缺點,增長 If-modify-since(last-modify)和 expires 緩存屬性 | |
http1.x | 增長 cache-control 和 If-none-match(etag)緩存屬性 |
http2.0 | 採用二進制格式傳輸;多路複用;報頭壓縮;服務器推送 |
http3.0 | 採用 QUIC 協議,自定義鏈接機制;自定義重傳機制;無阻塞的多路複用 |
緩存
類型 | 特性 |
---|---|
強緩存 | 經過 If-modify-since(last-modify)、expires 和 cache-control 設置,屬性值是時間,因此在時間內不用請求 |
協商緩存 | 經過 If-none-match(etag)設置,etag 屬性是哈希值,因此要請求和服務器值對比 |
這只是 JS 原生從初級到高級的梳理; 原創碼字不易,歡迎 star!