1.變數 1.1var,let,const var 全域性作用域和函式作用域,及只要申明的程式碼執行了,下面的程式碼也能使用該變數如例一 掛載帶wondow上,可以先列印後宣告(列印undefined,不會報錯,這就是變數提升),可重複申明,後申明的會覆蓋前面申明的 let 塊級作用域,宣告的變數只能在當前塊{}的申明後面與子塊{{}}中使用,for()裡申明的變數只能作用於for{}中 不會掛載帶wondow上,不能先列印後宣告(會報錯,列印與申明之間會形成暫時性死區,let變數也會提升,但是暫時性死區的緣故導致報錯) 子塊可呼叫父塊變數,如果子塊也申明跟父塊一樣的變數名,則子塊的變數同意遵循在當前塊{}的申明後面與子塊{{}}中使用 //例一 if (1) { var a = 11; let b= 22; } console.log(a); //正常,列印11,如果if(false)就不正常 console.log(b); //不正常 //例二 var a = []; for (let i = 0; i < 5; i++) { a[i] = function () { console.log(i); }; } //for (let i = 0; i < 5; i++) 列印3,每迴圈一次,i的作用域僅限該次迴圈{}中 //for (var i = 0; i < 5; i++) 列印5,迴圈體已結束,此時呼叫列印最終結果5 a[3](); //例三 var a = 11; console.log(window.a); //列印11 let a = 11; console.log(window.a); //列印undefined //例四 console.log(a); //列印undefined var a = 11; console.log(b); //報錯 var b = 11; //例五 let a = 11; { console.log(a); //正常,使用父級a } let b = 11; { console.log(b); //不正常,下面有b的申明,所以b是當前塊的b而不是父級b let b = 11; } let c = 11; { let c = 22; console.log(c); //正常 列印22 } const 常量,不能重新賦值(變數重新等於其他的值就報錯),物件和陣列可以修改裡面的值,其他規則與let一樣 const a = XXX; a = YYY; //重新賦值報錯 a.bb = ZZZ; //正常 a.push = XXx; //正常 1.2解構賦值 基本用法:陣列的解構用[],物件的解構用{},等號左邊是解構出來的變數,右邊是資料來源,可以全部解構,也可解構部分,可賦預設值,也可用剩餘擴充套件符...rest 例一 let [a,b] = [1,2,3]; console.log(a); //1 console.log(b); //2 例二 let {age,name,score} = {name:11,age:22} //物件必須用大括號,[age,name]報錯,裡面的鍵跟後面的鍵保持一致,不一致的取得是空 console.log(age); //22 console.log(name); //11 console.log(score); //undefined 陣列的...運算子,陣列解構可在陣列最後面使用...運算子,表示資料來源剩餘資料,列印的是個陣列 例一 let [a,b,...c] = [1,2,3,4,5]; console.log(a); //1 console.log(b); //2 console.log(c); //[3,4,5] 物件的k:v解構模式,對物件的鍵取別名解構,此時原鍵名被新的別名取代,使用必須用別名 例一 let {yingyu:english} = {yingyu:88}; console.log(english); //列印88 console.log(yingyu); //報錯 解構賦值也可用作函式引數 例一 function fun([a,b]){ console.log(a+b); } fun([1,2]); //列印3 例二 function func({math,english}){ console.log(math+english); } func({math:60,english:70}); //列印130 1.3其他 2,陣列 2.1form 陣列物件轉陣列,靜態方法,物件的屬性名必須與陣列的下標對應,必須有length屬性,length是幾,生成的陣列就有幾項,生成陣列的下標對應物件的屬性名,對不上就是undefined 例如 let obj={ 0:11, 1:22, length:2 } let arr = Array.from(obj); //[11,22] console.log(arr); 2.2of,靜態方法,將一組值轉成陣列 let arr = Array.of(1,2,3,4); 2.3fill 物件方法,使用給定值,填充一個陣列 let arr7=new Array(5).fill(3); console.log(arr7);//[ 3, 3, 3, 3, 3 ] 2.4find,物件函式,遍歷陣列,返回第一個返回true的項,找不到返回undefined,引數是回撥函式,回撥函式有三個引數,代表陣列的值,下標,原陣列 let arr = [1,2,3,4,3]; let theItem = arr.find(function(val,index,a){ if (val%3 === 0) { return true; } }); console.log(theItem); //3 2.5findIndex,物件函式,遍歷陣列,返回第一個返回true的下標,找不到返回-1,用法同find let arr = [1,2,3,4,3]; let theItem = arr.findIndex遍歷陣列(function(val,index,a){ if (val%3 === 0) { return true; } }); console.log(theItem); //2,3的下標正式2 2.6includes(str)是否包含某些,返回布林型 2.7for of遍歷 let arr = [11,22,33]; for(let v of arr){} for(let(k,v) of arr.entries){} 3,物件 3.1屬性簡潔表示,如果kv相同可省略v, let name = 'Li'; let obj = {name};//相當與{name:name} 3.2屬性名錶達式,屬性名用[]包裹,及表示式變數名,即[變數名] 變數值為屬性名 let key = 'name'; let obj = {[key]:'Li'}; console.log(obj.name); 3.3新增方法 is assign keys values entries freeze 4,類 4.1es5寫法 function Person(name,age){ this.name = name; this.age = age; this.sayAge = function(){ return this.age; } } Person.prototype.sayName = function(){ return this.name; } function Ming(){} Ming.prototype = Person.prototype; //繼承Person類的原型方法 let p = new Person("Li",12); console.log(p.sayName()); 4.2 es6寫法 class Anim{ constructor(name,age){ this.name = name; this.age = age; } function sayName(){ console.log(this.name); } function sayAge(){ console.log(this.age); } } //繼承 class Dog extend Anim{ constructor(name,age,color){ super(name,age); this.color = color; } function sayColor(){ console.log(this.color); } //重寫父級方法 function sayName(){ console.log('dog name'+ this.name); } } let dog = new Dog('Li',12,'red'); dog.sayName(); 5,函式 5.1預設值 function(a=1,b=2){} //普通寫法 function([a=1,b=2]=[]){} //陣列解構寫法 function({a=1,b=2}={}){} //物件解構寫法 5.2rest引數(形式為...變數名),用於獲取函式的多餘引數,必須放在函式形參最後面 function demo4(a,b,...abc){console.log(abc);} demo4(1,2,3,4,5);//[ 3, 4, 5 ] 5.3嚴格模式,只要函式引數使用了預設值、解構賦值、或者擴充套件運算子,那麼函式內部就不能顯式設定為嚴格模式,否則會報錯。一般把嚴格模式加在全域性 5.4name屬性,函式的name屬性,返回該函式的函式名。 function demo(){} demo.name //demo 5.5箭頭函式 ()=>{} (1)this作用域 (2)不可做建構函式 (3)沒有arguments物件,使用rest引數替代 5.6call,apply,bind,都是為了改變this指向,第一個引數是this執行,不用改變執行,就傳null call(this, 1, 2,...); //第一個引數是函式里this的指向,後面的引數與aa函式的引數對應(不對應有幾個取幾個),會直接執行函式 apply(this,[1,2,...]) //第一個引數是函式里this的指向,第二個引數是個陣列,跟函式的引數對應,會直接執行函式 bind(this,1,2,...) //第一個引數是函式里this的指向,後面的引數與aa函式的引數對應(不對應有幾個取幾個),不執行函式,要執行,後面加() 例如取最大值 Math.max(1,2,3) let arr = [1,2,3]; console.log(Math.max(...arr )); console.log(Math.max.apply(null,arr)) console.log(Math.max.call(null,...arr)) console.log(Math.max.bind(null,...arr)()) //不會執行,要執行,後面加() 6,symbol型別 6.1原始資料型別(其他六種資料型別string,number,boolean,null,undefined,array,object),表示獨一無二的值 6.2使用方式如下,直接呼叫Symbol方法,可帶一個字串引數 let s = Symbol(); console.log(typeof s); //symbol console.log(s); //Symbol() let ss = Symbol('haha'); console.log(typeof ss); //symbol console.log(ss); //Symbol(haha) 6.3在物件中作為物件的屬性使用,操作屬性必須用[變數名] let s = Symbol(); let obj = { [s] : 100 } console.log(obj[s]); //100 6.4物件中Symbol屬性,無法透過for in,Object.keys() 遍歷出來 要遍歷出來,可以用Object.getOwnPropertySymbol(obj) 或者 Reflect.ownKeys(obj)遍歷出來 7,模組module 7.1 import匯入 export匯出基本使用 import script標籤必須新增type='module'屬性,import from '檔案路徑',模組檔案不用透過src引入 import * as f from xxx 匯入所有專案,要獲取export default,可用f.default export可匯出變數,常量,函式,物件,類,可單獨匯出,也可透過export {name,sayName,Person}進行多個匯出,注意一點,函式的作為多個匯出必須放大括號內 export普通匯出的項,import 必須放到大括號內匯入 export default預設匯出項,模組檔案內最多隻有一個,import不用放大括號內匯入 ./js/index.js程式碼如下 export const name = 'Li' export function sayName(){ } export const man={ } export default class Person{ function constructor(){ } } <script type='module'> import Person,{name,sayName,man} from './js/index.js' </script> 8,代理 8.1proxy //透過代理,操作源資料,源資料可以是物件,也可以是陣列 //target 源資料,key 要操作的資料key,value 要對key賦的值,receiver代理物件 let obj={ name:'lilei', age:21, sex:'男' } obj=new Proxy(obj,{ get(target,key,receiver){ //return target[key] //為什麼不用這種做法,這種做法也能達到目的,但是target[key] 本身也是獲取資料,導致也走了某個代理,所以最好用下面這種寫法 return Reflect.get(target,key,receiver); }, set(target,propKey,value,receiver){ //return target[key] = value return Reflect.set(target,propKey,value,receiver); } }); console.log(obj.name);//1. false---- 2.結果:lilei obj.name='小明'; console.log(obj.name);//結果:小明 8.2defineProperty let obj={ name:'Li', age:21, sex:'男' } let internalName = obj.name; //必須需要這個中間變數 Object.defineProperty(obj,'name',{ get:function(){ //千萬不能寫出obj.name 這麼寫會導致回撥陷阱,即獲取操作又走到get,又執行obj.name,獲取操作又走到get,最終導致記憶體不足的錯誤Maximum call stack size exceeded return '老名字是:'+internalName }, set:function(newValue){ internalName = '新名字是:'+newValue } }); console.log(obj.name); obj.name = 11 console.log(obj.name); 9,擴充套件運算子 10,迭代器與生產器 10.1迭代器Iterator,一種新的遍歷機制,透過next()獲取遍歷得到的值,值的形式為{value:xxx,done:false};當值的done為true時,遍歷不出東西了 舉例:將陣列使用迭代器遍歷 let arr = [11,22]; let i = arr[Symbol.iterator](); console.log(i.next());//{value: 11, done: false} console.log(i.next());//{value: 22, done: false} console.log(i.next());//{value: undefined, done: true} 10.2生成器Generator,一個函式,透過yield表示式,中斷執行,應用為使非同步程式碼同步執行 10.2.1生成器基本宣告方式 function* func(){ yield 1; yield 2; return 3; } let g = func(); //返回一個生成器物件,但是函式里面的程式碼不會執行 console.log(g.next()); //{value: 1, done: false} 呼叫next方法才會執行函式里面的程式碼,遇到yield就終止,next方法的返回值為yield的值 console.log(g.next()); //{value: 2, done: false} console.log(g.next()); //{value: 3, done: true} value為return的值,如果生成器函式沒有return,value的值就為undefined,done為true表示遍歷完成 10.2.2next帶引數的形式 yield會中斷程式碼的執行,所以let x = yield 1;不會執行,需要在下次next執行,而x就等於下次next的引數 function* add(){ let x = yield 1; let y = yield 2; return x+y; } let g = add(); console.log(g.next()); //yield 1; 列印{value:1,done:false} console.log(g.next(11)); //let x = 11; yield 2; 列印{value:2,done:false} console.log(g.next(22)); //let y = 22; return x+y; 列印{value:33,done:true} 10.2.3應用例項 例一解決回撥地獄 let request = (url)=>{ $.get({ url:url, success(res){ g.next(res); //取得資料,把資料付給main函式里的res變數,g是下面生成器變數,已經變數提升了,let也會提升,這種非同步先使用在宣告不會產生錯誤 } }); } function* main(){ let res = yield request('https://v1.yiketianqi.com/api?unescape=1&version=v63&appid=&appsecret='); console.log(res); console.log('請求完成,繼續操作'); } let g = main(); g.next(); //先讓main跑起來,跑到yield裡面傳送請求 最終列印結果 /* {errcode: 100, errmsg: '缺少註冊引數appid或appsecret 請仔細看文件,賬號註冊地址 http://tianqiapi.com'} 請求完成,繼續操作 */ 例二 按照loading 請求資料 loadClose的順序完成 function loading(){ console.log('loading'); } function requestData(){ setTimeout(()=>{ let data = '這是模擬的資料' console.log('獲取了初始資料'); g.next(data); },1000); } function loadClose(){ console.log('loading close'); } function* load(){ loading(); let res = yield requestData(); console.dir('獲取非同步資料:'+res); loadClose(); console.log('loading完成,繼續操作'); } let g = load(); g.next(); 列印結果 /* loading 獲取了初始資料 獲取非同步資料:這是模擬的資料 loading close loading完成,繼續操作 */ 11,promise async await 12,set與hashmap 13,字串 13.1模板字串,``符號包裹的則是模板字串,裡面${變數名}是變數 例如 let name = 'Li'; let str = `${name}愛洗澡` console.log(str); //Li愛洗澡 13.2標籤模板 13.3新增函式 13.3.1 includes 是否包含字串 13.3.2 startsWith 是否包含字串 13.3.3 endsWith 是否包含字串 13.3.4 repeat 將字串重複幾次,不會改變源字串資料 例子 let str = '我愛北京' console.log(str.includes('愛'));//true console.log(str.startsWith('我'));//true console.log(str.endsWith('我'));//false console.log(str.endsWith('北京'));//true console.log(str.repeat(2)); //我愛北京我愛北京 console.log(str); //我愛北京 14,原型和原型鏈 14.1原型是儲存公共內容(公共變數,公共函式==)的區域。申明一個類,就會自動建立一個原型prototype 14.2類例項化後的物件,會為物件新增一個__proto__屬性,該__proto__屬性指向類的prototype,所以物件.__proto__ === 類的prototype function Anim(){ } let a = new Anim(); console.log(Anim.prototype === a.__proto__); //true //a.__proto__指向類的原型,列印的是cunstructor Anim() f [[prototype]] //a.__proto__.__proto__指向Object的原型,列印的是cunstructor Object() f [[prototype]] 還有大量方法hasOwnProperty,isPrototypeOf,propertyIsEnumerable== //object的__proto__是顯示的,在控制檯能看到,上面的控制檯看不到,但能列印出來 //a.__proto__.__proto__.__proto__ //列印null,最終的原型是null console.log(a.__proto__.__proto__.__proto__);//列印null 14.3類的方法申明在原型上的作用 (1)節約記憶體 function Anim(){ } Anim.prototype.sayName = function(){} let a1 = new Anim(); //不會為物件開闢sayName方法的記憶體,從原型上取 let a2 = new Anim(); //不會為物件開闢sayName方法的記憶體,從原型上取,這樣sayName只要在原型上申明一次即可,無論建立多少物件,都可以從原型獲取這個方法 (2)透過原型繼承 function Anim(){} Anim.prototype.name = 'aa' function Dog(){} Dog.prototype = Anim.prototype let d = new Dog(); console.log(d.name); 14.4陣列的物件原型是Array 14.5物件的原型是Object let arr = []; //列印的是cunstructor Array() f [[prototype]] 還有大量跟陣列有關的方法find,findindex,includes,indexof== console.log(arr.__proto__); let obj = {}; console.log(obj.__proto__); 14.6類的原型prototype,物件和陣列的物件原型是__proto__.類例項化物件的__proto__指向類的prototype,__proto__本身也是個物件,它的__proto__指向下級(可能是object),最終指向null 15,深複製與淺複製 16,防抖和節流 16.1防抖debounce:是指單位時間內,頻繁觸發事件,只執行最後一次(每次觸發事件,取消上次的執行,重新計算觸發時間,導致最後一次執行的時間延後) 16.1.1 場景 (1)搜尋框搜尋輸入,只需使用者最後一個字元輸入完,在發起請求 (2)手機號,郵箱驗證檢測 (3)div滑動事件,滑鼠不動了,會在1s後獲取座標 例如 let box = document.querySelector('.box'); function mousemove(e){ box.innerHTML = e.clientX+" "+e.clientY; } function debounce(fn,t){ let timer; return function(e){ if (timer) clearTimeout(timer); timer = setTimeout(fn.bind(null,e),t); } } box.addEventListener('mousemove',debounce(mousemove,500)); 16.2節流throttle:是指一定時間內執行的操作只執行一次,即每段時間內只執行一次,resize,scroll 例如 let box = document.querySelector('.box'); function mousemove(e){ box.innerHTML = e.clientX+" "+e.clientY; } function throttle(fn,t){ let timer = null; return function(e){ if (!timer){ timer = setTimeout(function(){ fn(e); timer = null; //為什麼不用clearTimeout,clearTimeout放在setTimeout裡面無法清除定時器 },t); } } } box.addEventListener('mousemove',throttle(mousemove,500)); 16.3 效果總結, 防抖,滑鼠一致動,座標一致不變,直到滑鼠停止500毫秒後,座標才變成滑鼠最後的位置座標 節流,滑鼠一致動,座標每隔500毫秒變一次 17,addEventListener的幾點研究 ///防抖和節流的知識儲備 let box = document.querySelector('.box'); function mousemove(e,a=1,b=2){ //e必須放最前面,因為是直接呼叫,不帶其他引數 box.innerHTML = e.clientX+" "+a; } function mousemoveRest(a,b,e){ //為函式繫結其他資料,e必須放最後面 box.innerHTML = e.clientX+" "+a; } function mousemoveGo(){ return function(e){ box.innerHTML = e.clientX; } } //addEventListener第二個引數是個函式 //繫結事件,回撥函式不會執行 box.addEventListener('click',mousemove); box.addEventListener('click',mousemoveRest.bind(null,1,2)); //繫結其他引數 //繫結事件,回撥函式會執行,下面這種情況導致觸發click,也不會執行mousemove,mousemove裡也沒有e //因為addEventListener第二個引數是個函式,而執行過後的mousemove不是一個函式,導致出問題 box.addEventListener('click',mousemove()); //函式名稱後只要帶上(),就會立即執行 //繫結事件,回撥函式會執行 //相當於繫結裡mouseMoveGo裡面的那個匿名函式 //所以觸發click,只會執行匿名函式里面的程式碼,匿名函式外的程式碼不會再次執行,e也再匿名函式內 box.addEventListener('click',mouseMoveGo()); //相當於下面這種寫法 let go = mousemoveGo(); //go是mousemoveGo返回的那個匿名函式 box.addEventListener('click',go);