es基礎

CaoJianbang發表於2024-10-31
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); 

相關文章