1.1 :嚴格界定範圍和使用順序,做用域消失變量消失,必須先聲明再使用
console.log(test)//wrong let test='1';1.2 :重複定義
let test='1' let test='1'//wrong
var test='1' for(let test=1;test++;test<10)
{ alert(test)//wrong }
1.3 :let應用:閉包問題的優化
- 問題1:請思考代碼執行結果 function A(){ for( a=0;a<10;a++) { ClassB=function B(){ alert(a)
} } } A() ClassB()- 結果1:彈出10次 從0到9 ? 代碼執行函數定義,執行了10次定義過程,
前1次覆蓋後1次的定義過程,每次循環均覆蓋
內存方法區裏被寫入函數定義,發現同名函數會替換函數方法體
在函數調用的時候 只會執行1次alert,結果1確定是錯的
- 結果2:彈出1次 9
結果1是錯的,那麼結果2中只彈出1次是對的
到底彈出幾呢?循環9次最後9應該是9
難道是彈出9?- 結果3:undefined
得出這個答案的朋友基礎知識很是棒,對函數調用和內存的問題掌握的很好:
在函數A中聲明瞭另外的內部函數B 內部B使用了外部函數A中的變量a 咱們先思考下函數調用的問題吧 變量a位於函數A中, A函數發生調用之後 A函數出棧
A中定義的全部變量會被清空 一樣變量a消失 但是當咱們調用函數B的時候須要用到a 那麼a消失了因此是undefined- 正確答案:alert(10) 根據結果3得知:函數內定義了另外函數,另外函數使用了外部函數的變量
熟悉java的同窗:是否是和java的內部類和外部類很像? 在發生函數定義的時候 javaScript引擎發現內部函數在使用這個變量
該變量須要共享 ,不能存到棧中,由於棧中會被清空數據 對應於JAVA程序中 該變量會被存儲到內存的方法區
和全部static變量位於同1個區域,該區域內的變量要加final關鍵字
變量共享之後內部函數和外部函數均操做該變量的引用
外部函數在代碼執行完畢時候注意最後還有個a++ 此刻a=10;
此刻內部函數也在使用這個a(共享的a對象引用) 那麼內部函數在使用a(共享的a)的時候,a(共享的a)已經被修改爲10了 因此內部函數再調用,訪問a的時候 彈出alert(10)- 閉包解決方案1:使用函數封裝閉包代碼 形式參數接受共享變量 //使用函數封裝閉包代碼 //那麼在函數內使用的是形式參數i接受共享變量a //在發生閉包調用的時候 直接調用這個函數把共享變量傳遞進來i=a; //方法中的形式參數但是每次都出棧的,咱們操做的都是形式參數 //第一次循環操做的是形式參數i 此刻i=a,a是0那麼i=0,方法寫入方法區 //第2次循環i=a,a是1 彈出alert(i)彈出1 方法寫入方法區替換 //第10次循環i=a,a是9 彈出alert(i)是9 方法寫入方法區替換 //A執行完畢修改了a,a++此刻a=10 //可是方法區裏的方法操做的是alert(i) 再也不是a了,寫入方法區那一時刻i=9 //因此結果是alert(9) function A() { var _loop = function _loop(i) { ClassB = function B() { alert(i); }; }; for (var a = 0; a < 10; a++) { _loop(a); } } A(); ClassB();
- 閉包解決方案優化: //每次都寫個函數定義形式參數好麻煩,使用let可完成上述操做 function A(){ for(let a=0;a<10;a++) { ClassB=function B(){ alert(a)
} } } A() ClassB()
1.4 普通賦值析構
[對象屬性.賦值的變量]=對象 [a.A,b.B,c]=對象 獲取對象的屬性a 賦值給變量A b同a賦值 獲取對象的屬性c 沒有發現要賦值的變量 自動生成同名變量c 則c=對象c屬性對應的數值
1.5 參數傳遞析構
var func= function({a,b,c}){ console.log(a)//獲取實際參數對象中的屬性a賦值 沒有賦值變量 生成同名變量a console.log(b) console.log(c) } func({a:1,b:2,c:3})
1.6函數調統階段使用。。。展開
//參數展開
function sum (x, y, z) {return x+y+z } args=[0,1,2] sum(..args)//自動獲取數組args中的每1個對象而且賦值
1.7 函數定義階段使用。。。動態傳參 做爲數組接受全部參數
function sum(..x)
{ console.log(x) } sum(1,2,3)
1.8 賦值階段使用:拷貝數據
var arr = [1,2,3];
var arr2 = [...arr];//展開[1,2,3]而且拷貝返回新的對象1,2,3
1.9 在箭頭函數中實現代碼使用{},大括號表示代碼執行
(a)={alert(a)} ==== function(a){alert(a)}
1.10 箭頭函數返回對象(),小括號表示對象返回
(a)={return a} ==== (a)=>(a) ===function(a) {return a}
1.11 操做this
var func=(a)={alert(this)} this是什麼?區別於普通函數this再也不是函數調用這
p=new Person() p.func();//func中的this再也不是Person對象 箭頭函數中的this是箭頭函數聲明所在類,或者所在function的對象 箭頭函數在哪裏聲明的this就是誰 優勢:節省了綁定this操做 以往在jsx中操做點擊事件 要處理當前組件對象數據 須要手動bind(this)這樣才能夠訪問到當前組件 如今只須要用箭頭函數 自動綁定
1.12 鏈式調用
var func=(a)=>(b)=>{alert(b)}
b是什麼? b是執行一次調用:func() 之後再次執行調用傳遞的參數 func()('this is b') 即b是用來接收第2次調用的形式參數 在發生調用的時候 第2次調用的參數賦值給b
- 優勢: 使用高階函數實現柯里化操做 再調用函數時 業務更加清晰
未柯里化: func(a,b,c) { 分別使用a,b,c處理業務 }
func(1,2,3) 柯里化 : func(a) { a處理業務 return func(b) { b處理業務 return func(c) { c處理業務 } } } func(1)(2)(3) 即把之前須要傳遞多個參數的函數操做
定義成只須要傳遞1個參數
每次操做結束後再次調用 接受下1個參數
完成之前多個參數的需求- 缺點: 自動生成了大量閉包,變量過多被共享到內存方法區裏注意內存泄漏
1.13 鏈式調用的應用
- 實現異步操做:(也叫作thunk封裝)
需求:獲取數據之後再更新HTML頁面
使用指定api:UpdateHtml(getData())
getData爲異步ajax獲取數據 UpdateHtml爲更新HTML
ajax響應成功之後纔會更新 如何在調用UpdateHtml此函數的時候 不更新頁面
讓getData再ajax獲取數據成功之後在更新頁面呢? //定義異步操做
Update=function(){ document.getElementById('root').innerHTML="數據更新成功" } //該UpdateHtml函數此刻已經成爲異步函數 //調用它自己不會去更新dom //咱們在使用getData的時候 經過鏈式函數能夠獲取到真正的更新DOM的Update方法 //此類封裝叫作thunk var UpdateHtml=(getdata)=>{ getdata()(Update)}//調用傳遞過來的getdata函數而且執行高階調用,把更新dom的函數做爲高階函數的參數傳遞過去 //函數聲明完畢開始函數調用 //如何在getData函數裏獲取到Update方法呢? var getData=()=>assignByUpdate=>{ //getData函數發送ajax請求更新dom //經過鏈式調用 聲明assignByUpdate //該變量assignByUpdate被UpdateHtml第2次調用的時候傳遞的參數賦值 //var UpdateHtml=(getData)(Update) //assignByUpdate==Update //true ajax( success:function(){ assignByUpdate } ) } UpdateHtml(getData)- Redux裏使用異步action會大量使用上述的thunk操做
1.14 定義:
給函數A標記* 函數被*標記之後表示該函數會被while(true)無限循環監控 在標記有yield的代碼的地方會暫停下來 由於有while監控 因此當使用特殊指令的時候 代碼纔會繼續執行 該函數會返回1個對象用於控制代碼執行流程 使用返回對象.netx()代碼纔會繼續執行 //發現星號標記 開啓無限循環while(1)監控代碼執行步驟 var work=function* A() { yield console.log(1) //發現yield標記代碼會在此處中止 console.log(2) console.log(3) } work.next();//從yield出繼續執行下面的代碼
1.15 對比上面的thunk封裝,yield更加簡潔
//首先在使用yiled上的函數上加*號 //異步ajax獲取數據 //標記yield的代碼行被javaScript引擎解析 //代碼在此處暫停,等待接受指令 var result=*function UpdateHTML(){ yiled getData(); document.getElementById('root').innerHTML="數據更新成功" } function getData(){ if('數據查詢成功') { result.next();//發出指令 容許返回result標記*號的方法繼續從yield處執行 //即UpdateHTML()從yield標記開始繼續執行了 } }
1.16 yield應用:redux saga
如下代碼爲redux saga示例 經過*和yield來控制代碼執行流程 缺點:while無限循環影響性能 import { call, put } from 'redux-saga/effects' export function* fetchData(action) { try { const data = yield call(Api.fetchUser, action.payload.url); yield put({type: "FETCH_SUCCEEDED", data}); } catch (error) { yield put({type: "FETCH_FAILED", error}); } }
1.17 export:變量導出 不一樣位置js文件變量交互
在1.js中定義了2個變量 var name='李雷' var age="15" 在2.js中如何使用這2個定義好的變量呢? 咱們只須要在1.js中 var name='李雷' var age="12" //添加以下代碼 export {name,age} 在2.js中使用就可使用了
1.18 import:使用導出的變量
import {name,age} from "./1.js" console.log(name) //'李雷' console.log(age)//12
1.19 變量導出導入的方案:
普通導出export {name,age} -- 使用的時候必須指定name和age:即import {name,age} from "./1.js" -- 若是在2.js中使用的時候不想使用name和age,想用your1_name或your_age2來接受 1.js中的2個變量怎麼辦? -- 使用默認導出 默認導出 export default 只支持一個變量導出 export default name -- 使用的時候 import any from "./1.js" -- any能夠任意起名 -- console.log(any)//李雷 批量導出1.js -- export const name = '李雷' -- export const age = '12' 批量導入2.js -- import * as types from '1.js' 使用types對象封裝1.js中全部export的變量 export中變量的定義名字是types對象的屬性 export中改變量的對應的數值是types對象的改屬性的數值 console.log(type)//{name:'李雷',age:'12' }
1.20 變量導出導入的方案:
在數據狀態變化的時候,去執行預先定義好的方法 //定義好了異步對象 分別封裝了2種狀態 //異步函數須要傳遞個回調函數做爲狀態切換方法 //該回調函數中2個參數resolve, reject //resolve是resolve(data)用於切換resolve狀態 //reject是reject(data)用於切換reject狀態 //data爲兩種狀態切換時傳遞的參數 var notify=new Promise(function (resolve, reject) { if (ready) { resolve("請求成功"); } else { reject("網絡出錯"); } //若是對象狀態發生切換,即手動調用了resolve(data),reject(data) //會觸發promis對象的then中定義的對應的回調方法,即參數1函數和參數2函數 nofity.then(func1(data){alert(data)//會顯示"請求成功"},func2(data)//會顯示"請求失敗")
1.21 代理模式概述
代理對象調用本身的一切方法時候均會被攔截從而實現業務操做 靜態代理:被代理的原始類方法發生變化須要手動修改地代理裏方法 動態代理:根據被代理的原始類自動生成代理類對象, 原始類方法變化 代理對象按照原始類生成也隨着變化 不用手動修改,java中spring aop中用的基本都是asm動態代理
1.22 代理模式應用
代碼中有10個service類 ,之前的業務是不涉及到事務操做 如今需求是在service中查詢數據庫的時候開啓事務和關閉事務 那麼若是沒有代理模式 須要操做10個類文件一步一步改 爲了不在每一個service對象中均開啓事務 關閉事務 咱們能夠把原始的service對象修改爲代理對象, 那麼在代理對象中查詢數據庫 等相關操做均會被攔截 從而開啓事務 關閉事務 咱們僅僅在1個類中修改 就能夠完成之前在全部service對象中修改的效果
1.23 代理模式ES6應用
const target = {
name: '李雷', age: 15 }; let handler = { //獲取被代理對象的屬性會調用 get(target, key, proxy) { //容許方法繼續執行下去 return Reflect.get(target, key, proxy); } } //構建代理對象 const proxy = new Proxy(target, handler);
1.24 arrayObject.map(function(i){})
//迭代處理數組中的每一個元素i是迭代器,即每次迭代的對象1.25 arrayObject.reduce(回調函數(p,c){},初始數值)
//若是指定了初始數值 則第一次迭代的p返回數值就是這個初始數值 //若是沒有指定初始數值,則第一次的帶返回的p默認是0 //c是當前指針位置元素 //[1,2,3].reduce(function(p,c){return p+c},0) // 第一次迭代函數返回0,因此p=0,指針指向0號位置數值是1因此c=1 // 第二次迭代p=上次的返回數值0+1 c是2 //依次類推0+1+2+3是結果 arrayObject.reduce(回調函數(p,c){},初始數值) //find,filter api相似map
#類和屬性和方法定義java
1.26 使用Class定義類
class Person { }
1.27 定義屬性:經過構造器定義屬性,區別於java的成員變量
class Person { constructor(name) { this.name = name;//定義屬性name this.age=12 ;//定義屬性age 這點和java有所區分 不須要成員變量 } }
1.28 定義類方法:方法函數不要加function
class Person { constructor(name) { this.name = name;//定義屬性name this.age=12 ;//定義屬性age 這點和java有所區分 不須要成員變量 } say(){ console.log("hello");//類中方法千萬不要加function } }
switch (i) { case 0: let a=0; break; case 1: let a=1; // TypeError for redeclaration. break; } 由於switch做用域內禁止出現同名let變量 即便break,在代碼編譯時會失效
版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證)ajax