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已經變爲10了
for(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對象)
性能優化
- css放在head中否則瀏覽器會重新再設置一遍
- js不要放在body中因爲它會阻塞dom的渲染
- DomContentLoaded只是dom渲染完成
- window.onload頁面中所有的元素都渲染完
- 靜態資源合併壓縮減少服務器的請求
- 使用cdn從最近的服務器獲取資源
- 圖片做懶加載(原理:預覽圖放在src,真正的圖片放在自定義data屬性下用js控制給src屬性賦值)、下拉加載更多、合併多個dom操作
- 緩存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存儲在服務器端。
- 用戶提交包含用戶名和密碼的表單,發送HTTP請求。
- 服務器驗證用戶發來的用戶名密碼。
- 如果正確則把當前用戶名(通常是用戶對象)存儲到redis中,並生成它在redis中的ID。
- 這個ID稱爲Session ID,通過Session ID可以從Redis中取出對應的用戶對象, 敏感數據(比如authed=true)都存儲在這個用戶對象中。
- 設置Cookie爲sessionId=xxxxxx|checksum併發送HTTP響應, 仍然爲每一項Cookie都設置簽名
- 用戶收到HTTP響應後,便看不到任何敏感數據了。在此後的請求中發送該Cookie給服務器。
- 服務器收到此後的HTTP請求後,發現Cookie中有SessionID,進行放篡改驗證。
- 如果通過了驗證,根據該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協議與各種應用層協議之間,爲數據通訊提供安全支持。
- http是超文本傳輸協議,信息是明文傳輸,https則是具有安全性的ssl加密傳輸協議。
- http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,後者是443。
- http的連接很簡單,是無狀態的。Https協議是由SSL+Http協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全。(無狀態的意思是其數據包的發送、傳輸和接收都是相互獨立的。無連接的意思是指通信雙方都不長久的維持對方的任何信息。)