《JavaScript 高級程序設計(第3版)》 源碼分析

js是單線程語言,不能同時幹兩件事

單線程是爲了避免dom渲染的衝突,同一時間只能做一件事,通過事件輪循(event-loop)實現,會將進程分爲同步進程和異步進程兩個隊列,同步執行完畢,在執行異步隊列

同步、異步的理解 是否阻塞程序的執行,如果是就是同步,否則就是異步

典型的 alert() 就是同步執行,如果用戶不點擊確定按鈕 就會一直等待

異步有:ajax請求、定時器、圖片加載、點擊事件

promise

new一個promise對象傳入兩個函數分別是resolve、reject,最後return出去的還是一個promise對象,最早在jquery1.5中的deferred用到了promise

統一捕獲異常、多個接口請求支持鏈式執行「promise.all所有的請求都完成再往下執行、promise.race只要有一個完成就往下執行」

指定執行循序,通過.then;第一個執行完return 第二個promise對象

function loadImg(src) {
    var promise = new Promise(function (resolve, reject) {
        var img = document.createElement('img')
        img.onload = function () {
            resolve(img)
        }
        img.onerror = function () {
            reject('圖片加載失敗')
        }
        img.src = src
    })
    return promise
}
        
var src1 = 'https://www.imooc.com/static/img/index/logo_new.png'
var result1 = loadImg(src1)
var src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'
var result2 = loadImg(src2)
result1.then(function (img1) {
    console.log('第一個圖片加載完成', img1.width)
    return result2  // 重要!!!
}).then(function (img2) {
    console.log('第二個圖片加載完成', img2.width)
}).catch(function (ex) {
    console.log(ex)
})

async、await

是一個同步的寫法,使用的時候注意 async加在函數的前面、await後面是一個promise的實例

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, "finish");
  });
}
async function asyncTimeSys(){
    await timeout(1000);
    console.log("第一層異步結束!")
    await timeout(1000);
    console.log("第二層異步結束!")
    await timeout(1000);
    console.log("第三層異步結束!")
    await timeout(1000);
    console.log("第四層異步結束!")
    await timeout(1000);
    console.log("第五層異步結束!")
    return "all finish";
}
asyncTimeSys().then((value)=>{
    console.log(value);
});

爲什麼結果爲13524?,由於單線程的機制,執行到setTimeout會被暫存起來不會立即執行

console.log(1)

setTimeout(function(){
  console.log(2)
},0)
console.log(3)
setTimeout(function(){
  console.log(4)
},1000)
console.log(5)

關於var、let、const

用var在函數內部聲明,這個變量就屬於當前的函數作用域,如果不用var關鍵字則聲明的是一個全局變量,var 聲明的變量存在提升。

var a = 1; //此處聲明的變量a爲全局變量
function foo(){
   a = 2;//此處的變量a也是全局變量
   console.log(a);//2
}
foo();
console.log(a);//2

let 聲明的變量不存在變量提升,換一種說法,就是 let 聲明存在暫時性死區(TDZ)。

經典面試題

for(var i = 0; i<10; i++){
      console.log(i)
    }
    alert(i) 此時i已經變爲10for(let i = 0; i<10; i++){
      console.log(i)
    }
    alert(i) 此時i會輸出0-9

let 聲明的變量具有塊作用域的特徵。
在同一個塊級作用域,不能重複聲明變量。

const 聲明創建一個值的只讀引用。

保存在堆中的數據不能修改、保存在棧中的數據支持修改

const a = 1;
console.log(a);//1
a = 2;
console.log(a);//Uncaught TypeError: Assignment to constant variable.

const obj = {a:1,b:2};
console.log(obj.a);//1
obj.a = 3;
console.log(obj.a);//3

一、數據類型

初始化未經聲明的變量,總是會創建一個全局變量。

數據類型分爲:基本數據類型(undefined、null、string、number、boolean),引用數據類型(object、array、函數)基本類型就是保存在棧內存中的簡單數據段,而引用類型指的是那些保存在堆內存中的對象

關於賦值

基本數據類型賦值 互不影響 而引用類型賦值後指向的是同一個引用地址 所以修改一個其他的都會變

var message;

alert(message) // undefined
alert(age) // 產生錯誤

js內置函數

object、array、bloolean、function、string、number

關於 NAN

即非數值,不是一個數字。

NAN與任何值都不相等,包括它自己,進行關係比較結果都是false

alert(isNAN(NAN))  //是否 不是一個數字  true
alert(isNAN(10)) // false
alert(isNAN('ni')) // true
alert(isNAN(true)) // false 因爲true 可以被轉換爲1
1.undefined  聲明變量沒有定義
2. null 空對象指針 
3. bloolean 
4. number  數值轉換 Number()函數在轉換字符串時比較複雜而且不夠合理,推薦使用,parseInt()parseFloat()
5. string 字符串類型  toString() 有個缺點 對null 和 undefined 不起作用
6. string() 方法更全面
7. Boolean() 檢測數據類型是否返回true、false ,對於0, '', null, undefined, NaN都返回false

一、布爾,數字,字符串

截取給定位置的那個字符 charAt 只接受一個參數

var str = 'nihao';
alert(str.charAt(1)) //i

字符串的拼接 concat 或者 + 加號操作符

var str = 'nihao';
var newstr = str.concat('world', '!');

alert(newstr) // nihao world !

字符串的截取 slice()、substr()、substring()。接收最多兩個參數

str.substring(indexStart, [indexEnd]) subsrting()方法返回一個索引和另一個索引之間的字符串

substring()從提取的字符indexStart可達但不包括 indexEnd
如果indexStart 等於indexEnd,substring()返回一個空字符串。
如果indexEnd省略,則將substring()字符提取到字符串的末尾。
如果任一參數小於0或是NaN,它被視爲爲0。
如果任何一個參數都大於stringName.length,則被視爲是stringName.length。
如果indexStart大於indexEnd,那麼效果substring()就好像這兩個論點被交換了一樣; 例如,str.substring(1, 0) == str.substring(0, 1)


str.substr(start, [length]) substr()方法返回從指定位置開始的字符串中指定字符數的字符

substr()會從start獲取長度爲length字符(如果截取到字符串的末尾,則會停止截取)。
如果start是正的並且大於或等於字符串的長度,則substr()返回一個空字符串。
若start爲負數,則將該值加上字符串長度後再進行計算(如果加上字符串的長度後還是負數,則從0開始截取)。
如果length爲0或爲負數,substr()返回一個空字符串。如果length省略,則將substr()字符提取到字符串的末尾。


str.slice(beginIndex[, endIndex]) slice()返回一個索引和另一個索引之間的字符串

若beginIndex爲負數,則將該值加上字符串長度後再進行計算(如果加上字符串的長度後還是負數,則從0開始截取)。
如果beginIndex大於或等於字符串的長度,則slice()返回一個空字符串。
如果endIndex省略,則將slice()字符提取到字符串的末尾。如果爲負,它被視爲strLength + endIndex其中strLength是字符串的長度。

typeof 用來檢測給定變量的數據類型是一個操作符,不是函數

關於類型檢測,對於基本數據類型來說typeof就很好了,但對於複雜數據類型(引用類型)來說instanceof更合適。


javascript與java/C# 的區別

一切(引用類型)都是對象,對象是屬性的集合。最需要了解的就是對象的概念,和java/C#完全不一樣。所以,切記切記!


判斷一個變量是不是對象非常簡單。值類型的類型判斷用typeof,引用類型的類型判斷用instanceof。

var fn = function () { };
console.log(fn instanceof Object);  // true

轉換數字的時候會莫名其妙的變爲NAN

如果第一個字符不是數字或者負號使用parseInt就會返回NAN

console.log(Number("")); //0
console.log(parseInt(""))//NAN

字符串的查找 indexOf match

var str = 'lele nihao,zheli shi di yi jia zhu';
var arr = [];
var op = str.indexOf('e');

while(op>-1){
	arr.push(op);
	op = str.indexOf('e', op+1);
}
//進入循環後每次給indexOf傳遞上一次的位置加1
alert(arr)

trim() 會創建一個字符串的副本,刪除前置及後置的所有空格

match()方法只接受一個參數 
var text = 'bat cat';
var parrent = /.at/gi;

var new = text.match(parrent)

統計一個字符串出現最多的字母 「考察數組去重、數據整合」

var data = "aaaaacccccssdsddddddddda";
    var length = data.length;
    var datas = [];
    var num = [];
    for (var i = 0; i < length; i++) {
        if (datas.indexOf(data[i]) < 0) {
            datas[i] = data[i];
            num[data[i]] = 1;
        }
        else {
            num[data[i]]++;
        }
    }
    console.log(datas); //["a", empty × 4, "c", empty × 4, "s", empty, "d"]
    console.log(num); //[a: 6, c: 5, s: 3, d: 10] 到這一步就已經統計出來了

    var max = num[datas[0]];  //6
    var datamax = datas[0]; //a

    //下面的方法是爲了取值
    for (var i = 1; i < datas.length; i++) {
        if (max < num[datas[i]]) {
            console.log(num[datas[i]])
            max = num[datas[i]];
            datamax = datas[i];
        }
    }
    console.log("出現最多的字母:" + datamax + "  出現次數:" + max);

字符串的替換 replace()

var text = 'bat cat';
var result = text.replace(/at/gi, 'oo')

字符串轉數組的方法 split()

var text = 'arr,op,kj,hg,';
var result = text.split(',')
接受第二個參數,固定數組的length

二、URL編碼方法

encodeURL() 是對整個URL進行編碼,
encodeURLComponent()對附加在現有URL後面的使用

Math對象獲取數組最大值最小值

三、 Math 對象

獲取數組中最大值和最小值 避免過多的使用循環和在if語句中確定數值。

var val = [1,2,3,4,5,6,7,8,9];

var max = Math.max.apply(Math, val);

這個技巧的關鍵,把MAth作爲apply的第一個參數,從而正確的設置了this、

Math.ceil() 向上取整 Math.floor() 向下取整 Math.round() 四捨五入取整

random()方法

值 = Math.floor ( Math.round() * 可能值的總數 + 第一個可能的值 )

 var color = ['red', 'blue', 'green', 'yellow'];
 function randomNum(minNum, maxNum) {
        switch (arguments.length) {
            case 1:
                return parseInt(Math.random() * minNum + 1, 10);
                break;
            case 2:
                return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
                break;
            default:
                return 0;
                break;
        }
    }

    var str = randomNum(0, color.length-1)

    console.log(color[str])

運算

1、求模(取餘數)
var result = 26 % 5
2、由於運算是從左往右的打印message第一個加法操作將一個字符串和一個數值拼接,
結果是一個字符串,這是加法運算的特殊之處。
 var num1 = 5;
 var num2 = 10;
 var message = "this is a number = "+ num1 +num2;
 var message2 = "this is a number = "+ (num1 +num2);
 console.log(message) //this is a number = 510
 console.log(message2) ////this is a number = 15
3、減法運算,來說就沒那麼特殊。
console.log('8'-1)
4、關於==!=,這兩個操作符。
console.log('55'==55); //true
console.log('55'===55) //false
1、對於string,number等基礎類型,=====是有區別的
1)不同類型間比較,==之比較“轉化成同一類型後的值”看“值”是否相等,===如果類型不同,其結果就是不等
2)同類型比較,直接進行“值”比較,兩者結果一樣
2、對於Array,Object等高級類型,=====是沒有區別的

5、一元運算
var num1 = 2;
var num2 = 20;
var num33 = num1++ +num2;
console.log(num33) 22

var num1 = 2;
var num2 = 20;
var num33 = ++num1 +num2;
console.log(num33) 23

二、語句

do-whlie語句 是一種後測試循環語句,換句話說代碼至少執行一次

var i = 0;
    do {
      i += 2;
    }
    while(i > 10)
    alert(i) //2

whlie語句屬於前測試循環語句,相對for語句也是

var i = 0
whlie(i<10){
	i+=2
}

關於break和continue語句

var num = 0;
for(var i = 0;i++;i<num.length){
	if(i%5==0){
		break;
	}
	num++;
}

alert(num) //4  break 是立即退出循環強制執行循環後面的語句


var num = 0;
for(var i = 0;i++;i<num.length){
	if(i%5==0){
		continue;
	}
	num++;
}

alert(num) //8  continue 退出循環後從循環頂部繼續執行

arguments

函數體內可以通過arguments 對象來訪問這個參數數組 類數組
通過arguments[0]訪問第一個參數,arguments[1]訪問第二個參數
函數沒有重載,定義兩個相同名字的函數,後一個會覆蓋前一個 通過檢查傳入函數中參數的類型和數量做出不同的反應,來模擬方法重載重新加載多次使用,如下所示。

技巧點:函數的參數只是提供了使用的便利性,不是必需的,因爲通過類數組arguments照樣可以訪問

function add(){
	if(arguments.length ==1){
		alert(arguments[0]+10)
	}else if(arguments.length ==2){
		alert(arguments[0]+arguments[1])
	}
}

add(10) //20
add(10,20) //30

對象都是通過函數創建的

在編程語言中,下面叫做“語法糖”。

var obj = { a: 10, b: 20 };
var arr = [5, 'x', true];

其實以上代碼的本質是:

//var obj = { a: 10, b: 20 };
//var arr = [5, 'x', true];

var obj = new Object();
obj.a = 10;
obj.b = 20;

var arr = new Array();
arr[0] = 5;
arr[1] = 'x';
arr[2] = true;

prototype

每個函數都有一個屬性叫做prototype。這個prototype的屬性值是一個對象(屬性的集合,再次強調!),默認的只有一個叫做constructor的屬性,指向這個函數本身。

可以自定義的增加許多屬性,如下圖:

在這裏插入圖片描述

隱式原型

每個對象都有一個__proto__ javascript不希望開發者用到這個屬性值

函數也不是從石頭縫裏蹦出來的,函數也是被創建出來的。誰創建了函數呢?——Function——注意這個大寫的“F”。

Instanceof運算符

Instanceof的判斷隊則是:沿着A的__proto__這條線來找,同時沿着B的prototype這條線來找,如果兩條線能找到同一個引用,即同一個對象,那麼就返回true。如果找到終點還未重合,則返回false。

通過上以規則,你可以解釋很多比較怪異的現象,例如:
console.log(Object instanceof Function) // true
console.log(Function instanceof Function) // true
console.log(Function instanceof Object) // true

在這裏插入圖片描述

構造函數,及其執行原理

定義構造函數的時候 方法首字母要大寫,和普通函數做區分,他執行的時候裏面的this會變成一個空對象,最後會return this 他是構造函數裏面默認有的一行,不用另外書寫,每一個構造函數都會包含一個顯示屬性(prototype)和隱式屬性(__proto__)其實隱式屬性就指向它的顯示屬性

在這裏插入圖片描述

new出來的對象f、通過隱式屬性指向構造函數的顯示屬性、構造函數的隱式原型指向object的顯示原型、object的隱式屬性最後指向null,構成了一個完整的原型鏈;所有實例(new出來的)的隱式原型等於函數的顯示原型

function Test(name){
  this.name = name
}

Test.prototype.alertName = function(){
  console.log(this.name)
}

var f = new Test("lishijie")
f.say = function(){
  console.log(this.name ,"lll")
}

f.say() //lishijie lll

f.alertName() //lishijie

for(var item in f){
  //高級瀏覽器已經屏蔽了來自原型的屬性,下面的代碼不寫也可以,爲了程序健壯還是加上保險
  if(f.hasOwnProperty(item)){
    console.log(item)
    //name
    //say
  }
}

原型的繼承

實際應用中如何區分一個屬性到底是基本的還是從原型中找到的呢?大家可能都知道答案了——hasOwnProperty
在這裏插入圖片描述

在這裏插入圖片描述

Function.prototype繼承自Object.prototype。Function.prototype.__proto__指向Object.prototype。

子級構造函數的原型賦值爲父級構造函數的實例

// 動物
function Animal() {
    this.eat = function () {
        alert('Animal eat')
    }
}

// 狗
function Dog() {
    this.bark = function () {
        alert('Dog bark')
    }
}

// 綁定原型,實現繼承
Dog.prototype = new Animal()

var hashiqi = new Dog()
hashiqi.bark()
hashiqi.eat()

es6 class類

子級構造函數通過extends和父級構造函數建立關係,通過super繼承父級的屬性,是js構造函數的語法糖使用的還是原型,符合構造函數的原理

es6常用的API

1、反引號定義多行字符串,通過${}存放變量。2、解構賦值 const {a,b} obj const [a,b] arr 塊級作用域、函數默認值、箭頭函數

執行上下文環境 瞭解程序的執行環境

函數在定義的時候(不是調用的時候),就已經確定了函數體內部自由變量的作用域
在這裏插入圖片描述

給執行上下文環境下一個通俗的定義——在執行代碼之前,把將要用到的所有的變量都事先拿出來,有的直接賦值了,有的先用undefined佔個空。

在這裏插入圖片描述
在執行代碼之前,首先將創建全局上下文環境。

在這裏插入圖片描述
代碼執行到第12行之前,上下文環境中的變量都在執行過程中被賦值。

在這裏插入圖片描述
執行到第13行,調用bar函數。
跳轉到bar函數內部,執行函數體語句之前,會創建一個新的執行上下文環境。

在這裏插入圖片描述

在這裏插入圖片描述


關於對象的面試題:

第一題就是簡單指向考察, 第二題指向發生變化,因爲取值是從左往右的,賦值是從右往左,a.x在a上面創建了x,在往右a的指向變了, 第三題考察對象合併。

var obj1 = {
      a: 1,
      b: 2,
      c: 3
    };
    var obj2 = obj1;
    obj2.b = 10;
    console.log(obj2, obj1);
    //Object { a: 1, b: 10, c: 3 },Object { a: 1, b: 10, c: 3 }
var a = {
      n: 1
    };
    var b = a;
    a.x = a = {
      n: 2
    };
    //console.log(a.x); //undefined
    //console.log(b.x); //{n:1}
$(function () {
      var object1 = {
        apple: 0,
        banana: {
          weight: 52,
          price: 100
        },
        cherry: 97
      };
      var object2 = {
        banana: {
          price: 200
        },
        durian: 100
      };
      /* object2 合併到 object1 中 */
      $.extend(object1, object2);
    })
var obj = {
       a:1,
       b:2,
       c:3
     }
    for(key in obj){
      console.log(key) // a,b,c
    }

this的指向
首先帶好兩個錦囊:
1.函數被調用時(即運行時)纔會確定該函數內this的指向。因爲在函數中this與arguments是兩個特殊的變量,在函數被調用時纔會取得它們,而且搜索這兩個變量時只會在活動對象範圍裏面去搜。
2.要確定函數中this的指向,必須先找到該函數被調用的位置。

認準第一種“test()”形式

 var a = 1
    function test() {
        console.log(this.a)
    }
    var obj = {
        a: 2,
        test
    }
    var testCopy = obj.test
    testCopy()  //1

    var a = 1
    function test() {
        console.log(this.a)
    }
    var obj = {
        a: 2,
        test
    }
    setTimeout(obj.test)  //1

在這裏插入圖片描述

認準第二種“xxx.test()”形式

即使是第二種串串燒的形式,結果也是一樣的,test()中的this只對直屬上司(直接調用者obj)負責。

var a = 1
    function test() {
        console.log(this.a)
    }
    var obj = {
        a: 2,
        test
    }
    obj.test()//2

var a = 1
    function test() {
        console.log(this.a)
    }
    var obj = {
        a: 2,
        test
    }
    var obj0 = {
        a: 3,
        obj
    }
    obj0.obj.test()//2 
   

在這裏插入圖片描述

認準第三種“test.call(xxx) / test.apply(xxx) / test.bind()”形式

test.call(xxx) / test.apply(xxx) / test.bind()的區別

如何用xw的say方法來顯示xh的數據?

var xw = {
   name : "小王",
   gender : "男",
   age : 24,
   say : function() {
     alert(this.name + " , " + this.gender + " ,今年" + this.age);  
                                   
   }
}
var xh = {
   name : "小紅",
   gender : "女",
   age : 18
}
xw.say();

對於call可以這樣:
xw.say.call(xh);

對於apply可以這樣:
xw.say.apply(xh);

而對於bind來說需要這樣:
xw.say.bind(xh)();

call和apply都是對函數的直接調用,而bind方法返回的仍然是一個函數,因此後面還需要()來進行調用纔可以

這個時候,say方法多了兩個參數

var xw = {
  name : "小王",
  gender : "男",
  age : 24,
  say : function(school,grade) {
     alert(this.name + " , " + this.gender + " ,今年" + this.age + " ,在" + school + "上" + grade)                          
  }
}
var xh = {
  name : "小紅",
  gender : "女",
  age : 18
}

對於call來說是這樣的
xw.say.call(xh,“實驗小學”,“六年級”);

而對於apply來說是這樣的
xw.say.apply(xh,[“實驗小學”,“六年級鄭州牛皮癬醫院”]);

call後面的參數與say方法中是一一對應的,而apply的第二個參數是一個數組,數組中的元素是和say方法中一一對應的,這就是兩者最大的區別

xw.say.bind(xh,“實驗小學”,“六年級”)();


 var a = 1
    function test() {
        console.log(this.a)
    }
    var obj = {
        a: 2,
        test
    }
    var testCopy = obj.test
    testCopy.call(obj)  //2

擴展

function fn() {
    console.log('real', this)  // real {a: 100}

    var arr = [1, 2, 3]
    arr.map(function (item) {
        console.log(this)  // window
    })
}
fn.call({a: 100})

第四種“new test()”形式

構造函數裏的this指的就是new出來的新對象

var a = 1
    function test(a) {
        this.a = a
    }
    var b = new test(2)
    console.log(b.a) //2

箭頭函數

箭頭函數中的this在函數定義的時候就已經確定,它this指向的是它的外層作用域this的指向。

 var a = 1
    var test = () => {
        console.log(this.a)
    }
    var obj = {
        a: 2,
        test
    }
    obj.test() //1

array類型

數組檢測 有兩個問題:instanceof、array.isArray(),如果有多個框架會有多個全局執行環境會存在不同版本的構造函數,後者目的是確定這個值是不是數組,不論是在那個環境創建的

最常使用join()重現了toString的方法,傳遞逗號將以逗號分割,傳遞雙豎線將以雙豎線分割

ECMAscript專門爲數組提供了push和pop方法,模擬棧、隊列方法,後進先出。
unshift和shift先進先出。

重排序方法:sort() 注意改變原數組

function com (val1,val2){
	if(val1<val2){
		return -1;
	}else if(val2>val2){
		return 1;
	}else{
		return 0
	}
}
var arr = [12,2,3,34,567];
arr.sort(com)
alert (arr);

操作方法: concat() 不改變原數組,複製原數組返回副本,有參數返回拼接以後的新數組,就是簡單粗暴的拼接。

var arr1 = [1,23,3];
var arr2 = arr1.concat("one",[8,"two"])
arr2 => 1,23,3,one,8,two

slice()用於截取數組,不改變原數組創建一個新數組,不包含結束位置

arr.slice(1) 從下標1開始到結束
arr.slice(1,4)同理
如果是負數,用數組的長度做運算,再如果結束位置小於起始位置返回空數組
 var arr = [1,3,8,7,9,0]
 console.log(arr.slice(1,4)) //[ 3, 8, 7 ]

splice() 強大的方法用於刪除、插入、替換數組
從當前位置往後數包含當前位置

arr.splice(0,1) 從第零個開始刪除1位
arr.splice(1,0,"one") 從第一個位置開始不刪除 插入一項
arr.splice(1,1,"two") 從第一個位置刪除一位 插入一項

reverse() 給數組做倒序,注意會改變原數組


數組6大API 工作中會經常用到

ES5 中數組常用的遍歷方法有 for 和 for in

1. foreach 遍歷所有元素
2. every 用來判斷數組中所有元素是否都滿足一個條件 返回布爾值,every ( ) 判斷每一個是否都滿足條件,如果有一個返回的結果是false,直接返回false
3. some 用來判斷數組中某個元素是否都滿足一個條件 返回布爾值,some ( ) 遍歷數組,查找是否有滿足條件(返回的結果如果是true,)就直接跳出遍歷,返回true
4. sort 排序
5. map 將數組元素重組 例如返回 <a>+item+</a>
6. filler 通過一個條件過濾數組 返回數組

實際應用注意:ES6 中可以使用 forEach() 和 map() 來對數組進行遍歷,forEach無法阻止它在循環中斷循環跳出,也有方法實現,但是使用for循環來的更快,,,map可以將回調函數中 return 的結果返回到新數組中,,,在項目中不要亂用讓他們各司其職!!!


filter() 的用法,假如後臺返回一些數據,需要將符合要求的數據篩選出來,實際開發中對於單數組數據格式很有用!!!

//篩選出價格大於60的數據
var list1 = data.filter(function(item,index,arr){
	return item.price>60;
})
//篩選出id爲1006的數據
var list2=data.filter(function(item){
    return item.id==1006;
})[0];

歸併方法 reduce()和reduceRight()

使用歸併方法求數組之和以及,對數組和字符串進行反轉也就是倒序

var val = [1,2,3,4,5]
var sum = val.reduce(function(pre,cur,index,array){
	return pre+cur
})
alert(sum) 15

pre代表前一項 cur代表當前項  
reduce 和 reduceRight 結果相同但取決於從哪頭開始遍歷數組。
var lop = [1,2,3].reduceRight(function (pre, cur) {
      return pre + '-' + cur;
    })
var lop3 = Array.prototype.reduceRight.call('1234', function (pre, cur) {
      return pre + '-' + cur;
    })
 //console.log(lop3)4-3-2-1

面試題

數組去重,但是有個問題indexOf只能在ie9以上有效

Array.prototype.unique = function () {
      var result = [];
      this.forEach(function (item) {
        if (result.indexOf(item) < 0) {
          result.push(item)
        }
      })
      return result;
    }
    var arr2 = ['1', '2', '1', 3, 3];
    //console.log(arr2.unique())

獲取數組最大最小值,有兩個方法,第二種方法用了一個小技巧

Array.prototype.max = function () {
      var max = this[0];
      for (var i = 1; i < this.length; i++) {
        if (this[i] > max) {
          max = this[i]
        }
      }
      return max
    }
    //console.log([2,4,45,4].max())
    方法二 技巧點在於把math對象作爲apply的第一個參數從而正確的設置了this值
    var min = Math.max.apply(Math, [1, 0, 23, 3])
    //console.log(min)

三、date類型

常用的時間戳

var start = +new Date() dosomething(); var end = +new Date() result = end-start

四、regExp類型

匹配第一個 bat 或者 cat 不區分大小寫

var pattern = /[bc]at/i;

匹配第一個[bc]at 不區分大小寫

var pattren = /\[bc]\at/i;

匹配所有以 at結尾的字符不區分大小寫

var partten = /.at/gi;

匹配所有以 .at 結尾的字符不區分大小寫

var partten = /\.at\/gi;

test()方法

var text = "000-00-0000";
var partten = /\d{3}-\d{2}-\d{4}/;
if(partten.test(text)){
	alert('yes')
}

function類型

函數的參數 其實是其函數的局部變量

函數聲明與函數表達式 聲明提前

執行上下文、也就是說代碼執行的時候會把聲明提前,給他一個默認值(undefined)最後再賦值,函數聲明也是一樣,會把函數提到最前面再通過上下文環境去執行、對於函數表達式,函數執行的時候變量sum中不會保存對函數的引用所以會報錯!!!

var a = undefined

a = 100

var a = 100

爲了理解函數表達式,在結尾的時候加一個分號,就像聲明其他變量時一樣
alert(sum(10,10));
var sum = function(){
	return num+num2
}

作用域鏈

js沒有塊級作用域,每個函數的自由變量(閉包中受保護的變量)會在當前作用域查找、如果沒有向父級作用域查找、如果還沒有就到全局環境去找,這樣就形成了作用域鏈,作用域鏈 引發了閉包的概念,全局變量只能訪問全局的環境 而局部環境不僅可以訪問自己 還可以訪問全局

在這裏插入圖片描述

在這裏插入圖片描述

以上代碼中:第13行,fn()返回的是bar函數,賦值給x。執行x(),即執行bar函數代碼。取b的值時,直接在fn作用域取出。取a的值時,試圖在fn作用域取,但是取不到,只能轉向創建fn的那個作用域中去查找,結果找到了。


> 創建10個a標籤點擊返回每個標籤的下標--------考察作用域--------正確的寫法如下

var i, a;

for (i = 0; i < 10; i++) {
  (function(i) {
    a = document.createElement("a");
    a.innerHTML = i + "<br>";
    a.addEventListener("click", function(e) {
      e.preventDefault();
      alert(i);   //自由變量
    });

    document.body.appendChild(a);
  })(i);
}

要去創建這個函數的作用域取值,而不是“父作用域

在這裏插入圖片描述

在這裏插入圖片描述

如上代碼中,fn函數作爲一個參數被傳遞進入另一個函數,賦值給f參數。執行f(15)時,max變量的取值是10,而不是100。


閉包的形式

在這裏插入圖片描述

這裏的重點就在於,創建bar函數是在執行fn()時創建的。fn()早就執行結束了,但是fn()執行上下文環境還存在與棧中,因此bar(15)時,max可以查找到。

爲什麼過度使用閉包會導致內存佔用過多
閉包不光引用自己的活動對象還會引用外層函數的活動對象。所以函數執行完畢後其活動對象不會銷燬,因爲匿名函數的作用域任然還在引用這個活動對象

函數作爲返回值、函數作爲參數傳遞

function only (){
  var _list = [];

  return function(val){
    if(_list.indexOf(val)>=0){
      return false
    }
    else{
      _list.push(val)
      return true
    }
  }
}

裏面的_list就被封裝起來了,在函數外面根本修改不了

var common = only()

console.log(common(1))  //true
console.log(common(1))  //false
console.log(common(2))  //true

作爲值的函數

function someFuction(sf,arg){
      return sf(arg)
    }
    function adr(i){
      return 10+i;
    }
    var as = someFuction(adr,20)
    console.log(as)

閉包的實例

function newsort(pro){
	return function(obj1,obj2){
		var val1 = obj1[pro];
		var val2 = obj2[pro];
		if(val1<val2){
			return -1;
		}else if(val1>val2){
			return 1
		}else{
			return 0
		}
	}
}

var data = [{name:'lj', age:23},{name:'db', age:45}];
data.sort(newsort(name)) //按姓名排
data.sort(newsort(age)) //按年齡排

關於閉包中使用this

var name = 'window'
var obj = {
	name:'lsj',
	say:function(){
		return function(){
			return this.name
		}
	}
}

alert(obj.say()()) //window

爲什麼會返回window?因爲調用的時候會立刻返回函數,每個函數調用的時候也會自動,取得兩個特殊變量:this、arguments,當內部函數搜索的時候只會搜索到活動對象爲止。就如下面改變this的指向就可以完美的實現想要的效果。

var name = 'window'
var obj = {
	name:'lsj',
	say:function(){
		var that = this;
		return function(){
			return that.name
		}
	}
}

alert(obj.say()()) //lsj

爲什麼返回的是10?

function nc(){
	var result = new Array();
	for(var i = 0;i< 10;i++){
		result[i] = function(){
			return i
		}
	}
	return result
}
、、返回的10

是因爲閉包取得的是函數變量最後一個值,再往詳細了說就是:它引用的是整個活動對象而不是某個特殊的變量。

可以通過一個匿名函數強制讓閉包的行爲符合預期

通過傳入變量i建立引用關係

function nc(){
	var result = new Array();
	for(var i = 0;i< 10;i++){
		result[i] = function(num){
			return function(){
				return num
			}
		}(i)
	}
	return result
}

閉包可以實現類的繼承和封裝、以及對象數組屬性的排序

函數節流、函數防抖

函數節流是指一定時間內js方法只跑一次。比如人的眨眼睛,就是一定時間內眨一次。這是函數節流最形象的解釋。

函數節流應用的實際場景,多數在監聽頁面元素滾動事件的時候會用到。因爲滾動事件,是一個高頻觸發的事件 函數節流的要點是,聲明一個變量當標誌位,記錄當前代碼是否在執行。


函數防抖是指頻繁觸發的情況下,代碼只執行一次。比如生活中的坐公交,就是一定時間內,如果有人陸續刷卡上車,司機就不會開車。只有別人沒刷卡了,司機纔開車。

函數防抖的應用場景,最常見的就是用戶註冊時候的手機號碼驗證和郵箱驗證了 函數防抖的要點,也是需要一個setTimeout來輔助實現。延遲執行需要跑的代碼。如果方法多次觸發,則把上次記錄的延遲執行代碼用clearTimeout清掉,重新開始。


二、原型鏈實現繼承,通過將一個類型的實例賦值給另一個構造函數的原型實現


淺拷貝與深拷貝

基本數據類型的特點:直接存儲在棧(stack)中的數據
引用數據類型的特點:真實的數據存放在堆內存裏

深拷貝和淺拷貝是隻針對Object和Array這樣的引用數據類型的。

賦值和淺拷貝的區別

賦值的其實是該對象的在棧中的地址,而不是堆中的數據。也就是兩個對象指向的是同一個存儲空間,無論哪個對象發生改變,其實都是改變的存儲空間的內容,因此,兩個對象是聯動的。
淺拷貝是按位拷貝對象,它會創建一個新對象,這個對象有着原始對象屬性值的一份精確拷貝。如果屬性是基本類型,拷貝的就是基本類型的值;如果屬性是內存地址(引用類型),拷貝的就是內存地址 ,因此如果其中一個對象改變了這個地址,就會影響到另一個對象。

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

淺拷貝:

Object.assign() 方法可以把任意多個的源對象自身的可枚舉屬性拷貝給目標對象,然後返回目標對象。但是 Object.assign()進行的是淺拷貝,拷貝的是對象的屬性的引用,而不是對象本身。注意:當object只有一層的時候,是深拷貝

在這裏插入圖片描述

在這裏插入圖片描述
​關於Array的slice和concat方法的補充說明:Array的slice和concat方法不修改原數組,只會返回一個淺複製了原數組中的元素的一個新數組。

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述
深拷貝的實現方式
JSON.parse(JSON.stringify())
遞歸方法實現深度克隆原理:遍歷對象、數組直到裏邊都是基本數據類型,然後再去複製,就是深度拷貝


什麼是內存泄露?

佔用的內存永遠不會被回收

function dom(){
      var ele = document.getElementById('test');
      ele.onclick=function(){
        console.log(ele.id)
      }
    }
    dom()

上面的例子,閉包會引用包含函數的整個活動對象也包含ele,佔用的內存將永遠不會回收。下面改寫後會解決這個問題。

function dom(){
      var ele = document.getElementById('test');
      var id = ele.id;
      ele.onclick=function(){
        console.log(id)
      }
      ele = null;
    }
    dom()

爲什麼使用私有作用域?

通過用私有作用域的匿名函數可以模仿塊級作用域

function dom(count){
      for(var i= 0; i<count; i++){
        console.log(i) //0,,1
      }
      alert(i) //2
    }
    dom(2)
function dom(count) {
      $(function () {
        for (var i = 0; i < count; i++) {
          console.log(i)//0,,1
        }
      })
      alert(i)//報錯
    }
    dom(2)
    

先輸出8,再輸出9

var a = 9;
     (function(){
       var a1 = 8
       alert(a1)
     })()
     alert(a);
     

對象API

for(var key in obj){}

BOM瀏覽器對象模型

location對象是BOM最有用的對象之一,例如:location.search 查詢字符串參數,location.host返回服務器名稱和端口號,location.href返回整個URL地址。

function get(){
	var qs = (location.search.length>0? 
	location.search.substring(1):''),
	args = {},
	items = qs.length? qs.split('&'): [],
	item = =null,
	name = null,
	value = null;
	for(var i = 0;i++;i<items.length){
		item = item[i].split('=')
		name = decodeURLComponent(item[0])
		value= decodeURLComponent(item[1])
		if(name.length){
			args[name] = value
		}
	}
	return args
}

假設查詢字符串爲: ?q=javascript&num=0

var result = get()
alert(result['q']) //javascript

navigator對象,已成爲識別客戶端瀏覽器的標準,screen客戶端顯示器相關信息,history瀏覽器的歷史記錄,可以通過js的navigator.userAgent屬性來確定用戶使用的瀏覽器,分爲以下幾步:1、識別瀏覽器引擎;2、識別瀏覽器;3、識別平臺;4、識別操作系統;5、通過navigator.platform檢測平臺系統是不是IOS或者安卓,以及字符串裏是否有mobile字段

關於window路徑跳轉的幾種方法:

//效果相同
location.assign('http://nihao.com')
location.href = 'http://nihao.com'
window.location = 'http://nihao.com'

DOM文檔對象模型

dom是一個樹形結構的數據類型、它將html字符串結構化成一個樹模型

domAPI

getElementById、getElementByTagName(返回數組)、querySelectorAll(返回數組)、createElement、parentElement(獲取父元素)、childNodes、nodeName、attribute是對html標籤屬性的修改

查找元素

document.forms包含了文檔所有的form元素,與document.getElementsByTagName('form')得到的結果相同

var div = document.getElementById('test');
var tag = document.getElementsByTagName('div');
通過name屬性獲取元素,返回一個nodelist
console.log(document.getElementsByName('op').length)

操作節點

var id = document.getElementById('test');
var p = document.createElement('p');
var a = document.createElement('a');
a.innerText='baidu'
id.appendChild(p);
id.insertBefore(a,p) //插入到p元素的前面

標籤屬性的修改

標籤上的每個屬性都可以採用下面的方式去修改,但是對於自定義屬性就不適用了,需要用到getAttribute

 <div id="test" title="op"></div>
  <script type="text/javascript" src="./jquery-1.11.0.min.js"></script>
  <script>
     var div = document.getElementById('test');
     div.title = '你好'
  </script>

getAttribute,獲取標籤屬性;setAttribute創建該屬性並設置相應的值,也可以修改相應的值;removeAttribute刪除標籤屬性

var div = document.getElementById('test');
alert(div.my_p)
alert(div.getAttribute("my_p"))
div.setAttribute("my_p", "3")
div.removeAttribute('my_p')

創建標籤
var div = document.getElementById('test');
var template = document.createElement('p')
template.setAttribute('class', 'child');
div.appendChild(template)

document.querySelector返回匹配的第一個元素,document.querySelectorAll返回匹配元素的一個nodelist

var div = document.querySelector('#test');
console.log(div.id) 
var allUl = document.querySelectorAll('ul li');
console.log(allUl) //NodeList(6) [ li, li, li, li, li, li ]

html5新添加的getElementsByClassName()方法返回一個NodeList

 var cla = document.getElementsByClassName('le');
 console.log(cla) //HTMLCollection { 0: div#test.le, length: 1, … }

實踐題
刪除元素裏的一個class

var classArr =document.getElementById('test')
.getAttribute('class').split(/\s+/);
var pos = -1;
for(var i=0; i<classArr.length; i++){
  if(classArr[i]== 'le'){
    pos = i;
    break;
  }
}
classArr.splice(pos, 1);
var newV = classArr.join(' ')
document.getElementById('test').setAttribute('class', newV)

html5自定義數據屬性的取值,dataset

html
<div id="test" class="le user les" data-id="23" title="op"></div>
js
document.getElementById('test').dataset.id;

offsetWidth元素在水平方向上佔用的空間,包括元素的寬度,可見的垂直滾動條的寬度,左右邊框的寬度,offsetHeight同理。clientWidth是元素內容寬度加上左右的內邊距寬度,clientHeight是元素的內容高度加上上下內邊距高度,確定瀏覽器視口大小的時候常用到這個屬性。

事件

跨瀏覽器的事件對象兼容

var EventUtil = {
	getEvent:function(event){
		return event? event: window.evnet
	},
	getTarget:function(event){
		return event.target || event.srcElement
	},
	perventDefault:function(event){ //阻止默認事件
		if(event.perventDefault){
			event.perventDefault()
		}
		else{
			event.returnValue = false
		}
	},
	stopPropagation:function(event){ //阻止冒泡
	       if(event.stopPropagation){
	         event.stopPropagation();
	       }
	       else{
	         event.cancelBubble = true;
	       }
	}
}

var a = document.getElementById('link');

a.onclick = function(event){
	event = EventUtil.getEvent(event)
	EventUtil.perventDefault(event)
}


以上代碼可以確保所有瀏覽器中點擊該鏈接都會阻止默認跳轉

事件類型

UI事件

load當頁面加載完後(包括圖像、js,css文件、外部資源),resize當瀏覽器被調整到一個新高度或寬度觸發這個事件,scroll事件

鼠標事件

blur在元素上失去焦點,focus獲取焦點以上兩個事件不會冒泡,click,dbclick,mouseenter,mouseleave,mouseover

觸摸事件

touchstart當手指觸摸屏幕時觸發,touchmove手指在屏幕上滑動連續觸發,調用preventDefault()可以阻止滾動,touchend手指從屏幕上離開,clientX,clientY觸摸目標在視口中的x,y座標,pageX,pageY觸摸目標在頁面中的x,y座標,screenX,screenY,觸摸目標在屏幕中的x,y座標,target觸摸的dom節點目標。,

事件委託

建立在事件冒泡機制上的事件委託技術
只需在dom樹中最高層次添加一個事件處理程序,這種技術佔用內存少,dom引用少能夠提升整體的性能。

<ul id = 'list'>
	<li id="one">one</li>
	<li id="two">two</li>
	<li id="three">three</li>
</ul>

var list = document.getElementById('list');

list.onlick = function(){
	event = EventUtil.getEvent(event)
	var target = EventUtil.getTarget(event)
	switch(target.id){
		case "one":
		alert(1);
		break;
		case "two":
		alert(2);
		break;
	}
}

表單腳本

如何避免用戶的重複提交 1、表單提交後禁用提交按鈕 2、利用onsubmit事件取消後續的提交操作,session中存放一個特殊標誌發現表單提交裏沒有有效的標誌串,這說明表單已經被提交過了,忽略這次提交

  //獲取表單元素
     var form1 = document.forms['form1'];
     //不推薦下面的方法,容易出錯,未來的瀏覽器可能不會支持
     var form2 = document.form2;
 //表單提交的時候如果驗證不通過可以通過阻止默認行爲的方法,阻止表單提交
     //注意是submit而不能用click,因爲click在不同的瀏覽器之間存在時差
     //event.preventDefault();
     
     //h5爲表單新增了autofocus屬性,自動獲取焦點,
     //是一個布爾屬性支持這個屬性的瀏覽器中顯示TRUE,否則是FALSE
     //每個表單都有elements屬性,返回一個有序列表
     var textBox = document.forms['form1'].elements['text'];
     console.log(textBox.value)
     //input,textarea,支持select()方法,
     //選中文本框中的文本,如何取得選中的文本可以結合,selectionStart,selectionEnd
     //h5其他輸入類型type:email,url,number,range,新屬性:required,pattern

表單序列化

返回值:表單內容的字符串格式

var serializeUrl = $("#test_form").serialize();
alert("序列化爲url格式爲:"+serializeUrl);

返回的是JSON對象而非JSON字符串

var serializeJson = $("#test_form").serializeArray(); 
alert("序列化爲json格式爲:"+JSON.stringify(serializeJson)); 
//JSON.stringify(json對象)

性能優化

  1. css放在head中否則瀏覽器會重新再設置一遍
  2. js不要放在body中因爲它會阻塞dom的渲染
  3. DomContentLoaded只是dom渲染完成
  4. window.onload頁面中所有的元素都渲染完
  5. 靜態資源合併壓縮減少服務器的請求
  6. 使用cdn從最近的服務器獲取資源
  7. 圖片做懶加載(原理:預覽圖放在src,真正的圖片放在自定義data屬性下用js控制給src屬性賦值)、下拉加載更多、合併多個dom操作
  8. 緩存dom查詢存到變量中

同源策略

協議/主機/端口相同則是同源

沒有同源策略限制的接口請求
如果你請求了接口進行登錄,服務端驗證通過後會在響應頭加入Set-Cookie字段,然後下次再發請求的時候,瀏覽器會自動將cookie附加在HTTP請求的頭字段Cookie中,服務端就能知道這個用戶已經登錄過了。知道這個之後,我們來看場景:
1.你準備去清空你的購物車,於是打開了買買買網站www.maimaimai.com,然後登錄成功,一看,購物車東西這麼少,不行,還得買多點。
2.你在看有什麼東西買的過程中,你的好基友發給你一個鏈接www.nidongde.com,一臉yin笑地跟你說:“你懂的”,你毫不猶豫打開了。
3.你饒有興致地瀏覽着www.nidongde.com,誰知這個網站暗地裏做了些不可描述的事情!由於沒有同源策略的限制,它向www.maimaimai.com發起了請求!聰明的你一定想到上面的話“服務端驗證通過後會在響應頭加入Set-Cookie字段,然後下次再發請求的時候,瀏覽器會自動將cookie附加在HTTP請求的頭字段Cookie中”,這樣一來,這個不法網站就相當於登錄了你的賬號,可以爲所欲爲了!如果這不是一個買買買賬號,而是你的銀行賬號,那…


沒有同源策略限制的Dom查詢

// HTML
<iframe name="yinhang" src="www.yinhang.com"></iframe>
// JS
// 由於沒有同源策略的限制,釣魚網站可以直接拿到別的網站的Dom
const iframe = window.frames['yinhang']
const node = iframe.document.getElementById('你輸入賬號密碼的Input')
console.log(`拿到了這個${node},我還拿不到你剛剛輸入的賬號密碼嗎`)

同源策略限制下接口請求的正確打開方式

JSONP、CORS是一個W3C標準,全稱是"跨域資源共享"、代理幫我們把這個請求轉發到真正的後端域名上、細心的朋友可能發現,JSONP只能發GET請求,因爲本質上script加載資源就是GET,那麼如果要發POST請求怎麼辦呢?空iframe加form

CSRF攻擊

已經登錄的網站,以你的名義發送惡意請求

你不能保證你關閉瀏覽器了後,你本地的Cookie立刻過期,你上次的會話已經結束。(事實上,關閉瀏覽器不能結束一個會話,但大多數人都會錯誤的認爲關閉瀏覽器就等於退出登錄/結束會話了…)

CSRF的防禦可以從服務端和客戶端兩方面着手,防禦效果是從服務端着手效果比較好

在表單裏增加Hash值,以認證這確實是用戶發送的請求。然後在服務器端進行Hash值驗證可以杜絕99%的CSRF攻擊


每次的用戶提交都需要用戶在表單中填寫一個圖片上的隨機字符串,厄…這個方案可以完全解決CSRF,但個人覺得在易用性方面似乎不是太好,還有聽聞是驗證碼圖片的使用涉及了一個被稱爲MHTML的Bug,可能在某些版本的微軟IE中受影響。


Cookie

存儲cookie是瀏覽器提供的功能,網頁要發http請求時,瀏覽器會先檢查是否有相應的cookie,有則自動添加在request header中的cookie字段中。這些是瀏覽器自動幫我們做的,而且每一次http請求瀏覽器都會自動幫我們做。每個域名下的cookie 的大小最大爲4KB,每個域名下的cookie數量最多爲20個。

存在瀏覽器的一段字符串

跨域不共享

每次發送http請求會將請求域的cookie發送給server端

server端可以修改cookie並返回瀏覽器

瀏覽器可以通過js修改cookie但是有限制

客戶端可以設置cookie 的下列選項:expires、domain、path、secure(有條件:只有在https協議的網頁中,(前端)客戶端設置secure類型的 cookie 才能成功),但無法設置HttpOnly選項。

Cookie/Session的機制與安全

本地儲存官方說法是 5M 的大小;cookie 不適合大量數據的存儲,因爲它們由每個對服務器的請求來傳遞,cookie 默認如果不設置有效期,那麼他默認是隨着窗口關閉而清除;存放的數據大約爲4K左右;用於服務器通信,每次都會攜帶在HTTP請求頭中


現在的服務器之所以知道我們是否已經登錄,是因爲服務器在登錄時設置了瀏覽器的Cookie!Session則是藉由Cookie而實現的更高層的服務器與瀏覽器之間的會話。

瀏覽器向某個URL發起HTTP請求
對應的服務器收到該HTTP請求,
HTTP響應包括請求頭和請求體兩部分
在響應頭加入Set-Cookie字段,它的值是要設置的Cookie。

瀏覽器收到來自服務器的HTTP響應。
瀏覽器在響應頭中發現Set-Cookie字段,就會將該字段的值保存在內存或者硬盤中。

Set-Cookie字段的值可以是很多項Cookie,每一項都可以指定過期時間Expires。 默認的過期時間是用戶關閉瀏覽器時。

瀏覽器下次給該服務器發送HTTP請求時, 會將服務器設置的Cookie附加在HTTP請求的頭字段Cookie中。
只發送當前請求的域名曾經指定的Cookie

服務器收到這個HTTP請求,發現請求頭中有Cookie字段, 便知道之前就和這個用戶打過交道了。

過期的Cookie會被瀏覽器刪除。

Session 的實現機制

Cookie 防篡改機制,因爲Cookie是明文傳輸的, 只要服務器設置過一次authed=true|xxxx我不就知道true的簽名是xxxx了麼, 以後就可以用這個簽名來欺騙服務器了。因此Cookie中最好不要放敏感數據。 一般來講Cookie中只會放一個Session Id,而Session存儲在服務器端。

  1. 用戶提交包含用戶名和密碼的表單,發送HTTP請求。
  2. 服務器驗證用戶發來的用戶名密碼。
  3. 如果正確則把當前用戶名(通常是用戶對象)存儲到redis中,並生成它在redis中的ID。
  4. 這個ID稱爲Session ID,通過Session ID可以從Redis中取出對應的用戶對象, 敏感數據(比如authed=true)都存儲在這個用戶對象中。
  5. 設置Cookie爲sessionId=xxxxxx|checksum併發送HTTP響應, 仍然爲每一項Cookie都設置簽名
  6. 用戶收到HTTP響應後,便看不到任何敏感數據了。在此後的請求中發送該Cookie給服務器。
  7. 服務器收到此後的HTTP請求後,發現Cookie中有SessionID,進行放篡改驗證。
  8. 如果通過了驗證,根據該ID從Redis中取出對應的用戶對象, 查看該對象的狀態並繼續執行業務邏輯。

XSS 漏洞修復

跨站腳本攻擊,比如獲取用戶的Cookie發送到自己的服務器,導航到惡意網站,攜帶木馬等

將重要的cookie標記爲http only, 這樣的話Javascript 中的document.cookie語句就不能獲取到cookie了.
只允許用戶輸入我們期望的數據。 例如: 年齡的textbox中,只允許用戶輸入數字。 而數字之外的字符都過濾掉。

手寫ajax

var xhr=new XMLHttpRequest();
var url="http://127.0.0.1:8080/xxx.do?username=testuser&userno=123";
//url=decodeURI(url);
xhr.open("GET",url);
xhr.onreadystatechange=function(){
   if(xhr.readyState==4 && xhr.status==200){
          console.log(xhr.responseText);
    }
 }
xhr.send();

在這裏插入圖片描述

HTTP協議簡析

在這裏插入圖片描述

在這裏插入圖片描述

目前互聯網採用的網絡協議是tcp/ip協議族,HTTP協議處於應用層,TCP/UDP處於傳輸層,IP網絡處於網絡層,通信電纜等處於物理鏈路層。


請求方法是告知服務器意圖的HTTP方法。主要包括POST、GET、PUT、HEAD、DELETE等方法。目前主要在用的是POST和GET,主要區分是POST不會講請求實體內容添加到URL鏈接上,而GET會將請求實體添加到URL鏈接上。

1、說一下什麼是Http協議?

對器客戶端和 服務器端之間數據傳輸的格式規範,格式簡稱爲“超文本傳輸協議”。

2、什麼是Http協議無狀態協議?怎麼解決Http協議無狀態協議?(曾經去某創業公司問到)

無狀態協議對於事務處理沒有記憶能力。缺少狀態意味着如果後續處理需要前面的信息
無狀態協議解決辦法: 通過1、Cookie 2、通過Session會話保存。
3、說一下Http協議中302狀態(阿里經常問)

http協議中,返回狀態碼302表示重定向。
這種情況下,服務器返回的頭部信息中會包含一個 Location 字段,內容是重定向到的url。
4、Http協議有什麼組成?

請求報文包含三部分:

請求行:包含請求方法、URI、HTTP版本信息
請求首部字段
請求內容實體
響應報文包含三部分:

狀態行:包含HTTP版本、狀態碼、狀態碼的原因短語
響應首部字段
響應內容實體
說一下網絡傳輸的過程

5、Http協議中有那些請求方式?

GET: 用於請求訪問已經被URI(統一資源標識符)識別的資源,可以通過URL傳參給服務器
POST:用於傳輸信息給服務器,主要功能與GET方法類似,但一般推薦使用POST方式。
PUT: 傳輸文件,報文主體中包含文件內容,保存到對應URI位置。
HEAD: 獲得報文首部,與GET方法類似,只是不返回報文主體,一般用於驗證URI是否有效。
DELETE:刪除文件,與PUT方法相反,刪除對應URI位置的文件。
OPTIONS:查詢相應URI支持的HTTP方法。
6、Http協議中Http1.0與1.1區別?

在http1.0中,當建立連接後,客戶端發送一個請求,服務器端返回一個信息後就關閉連接,當瀏覽器下次請求的時候又要建立連接,顯然這種不斷建立連接的方式,會造成很多問題。
在http1.1中,引入了持續連接的概念,通過這種連接,瀏覽器可以建立一個連接之後,發送請求並得到返回信息,然後繼續發送請求再次等到返回信息,也就是說客戶端可以連續發送多個請求,而不用等待每一個響應的到來。
7、get與post請求區別?(初級程序員必備問題)

區別一:

get重點在從服務器上獲取資源。
post重點在向服務器發送數據。
區別二:

get傳輸數據是通過URL請求,以field(字段)= value的形式,置於URL後,並用"?“連接,多個請求數據間用”&"連接,如http://127.0.0.1/Test/login.action?name=admin&password=admin,這個過程用戶是可見的。
post傳輸數據通過Http的post機制,將字段與對應值封存在請求實體中發送給服務器,這個過程對用戶是不可見的。
區別三:

Get傳輸的數據量小,因爲受URL長度限制,但效率較高。
Post可以傳輸大量數據,所以上傳文件時只能用Post方式。
區別四:

get是不安全的,因爲URL是可見的,可能會泄露私密信息,如密碼等。
post較get安全性較高。
區別五:

get方式只能支持ASCII字符,向服務器傳的中文字符可能會亂碼。
瀏覽器直接發送的請求默認是get請求。
post支持標準字符集,可以正確傳遞中文字符。

9、常見Http協議狀態?

200:請求被正常處理

204:請求被受理但沒有資源可以返回

206:客戶端只是請求資源的一部分,服務器只對請求的部分資源執行GET方法,相應報文中通過Content-Range指定範圍的資源。

301:永久性重定向

302:臨時重定向

303:與302狀態碼有相似功能,只是它希望客戶端在請求一個URI的時候,能通過GET方法重定向到另一個URI上

304:發送附帶條件的請求時,條件不滿足時返回,與重定向無關

307:臨時重定向,與302類似,只是強制要求使用POST方法

400:請求報文語法有誤,服務器無法識別

401:請求需要認證

403:請求的對應資源禁止被訪問

404:服務器無法找到對應資源

500:服務器內部錯誤

503:服務器正忙
10、Http協議首部字段?

a、通用首部字段(請求報文與響應報文都會使用的首部字段)

Date:創建報文時間
Connection:連接的管理
Cache-Control:緩存的控制
Transfer-Encoding:報文主體的傳輸編碼方式
b、請求首部字段(請求報文會使用的首部字段)

Host:請求資源所在服務器
Accept:可處理的媒體類型
Accept-Charset:可接收的字符集
Accept-Encoding:可接受的內容編碼
Accept-Language:可接受的自然語言
c、響應首部字段(響應報文會使用的首部字段)

Accept-Ranges:可接受的字節範圍
Location:令客戶端重新定向到的URI
Server:HTTP服務器的安裝信息
d、實體首部字段(請求報文與響應報文的的實體部分使用的首部字段)

Allow:資源可支持的HTTP方法
Content-Type:實體主類的類型
Content-Encoding:實體主體適用的編碼方式
Content-Language:實體主體的自然語言
Content-Length:實體主體的的字節數
Content-Range:實體主體的位置範圍,一般用於發出部分請求時使用
11、Http與Https優缺點?

通信使用明文不加密,內容可能被竊聽,也就是被抓包分析。
不驗證通信方身份,可能遭到僞裝
無法驗證報文完整性,可能被篡改
HTTPS就是HTTP加上加密處理(一般是SSL安全通信線路)+認證+完整性保護
12、Http優化

利用負載均衡優化和加速HTTP應用
利用HTTP Cache來優化網站
13、Http協議有那些特徵?

1、支持客戶/服務器模式;2、簡單快速;3、靈活;4、無連接;5、無狀態。

https與http

Https:是以安全爲目標的Http通道,是Http的安全版。Https的安全基礎是SSL。SSL協議位於TCP/IP協議與各種應用層協議之間,爲數據通訊提供安全支持。

  1. http是超文本傳輸協議,信息是明文傳輸,https則是具有安全性的ssl加密傳輸協議。
  2. http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,後者是443。
  3. http的連接很簡單,是無狀態的。Https協議是由SSL+Http協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全。(無狀態的意思是其數據包的發送、傳輸和接收都是相互獨立的。無連接的意思是指通信雙方都不長久的維持對方的任何信息。)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章