體驗ES6中類的祕密

class聲明類的語法:css

    <script>
        // class cc { }
        // console.log(typeof cc);


        // let cc = class { };
        // console.log(typeof cc);


        // class User {
        //     show() {
        //         console.log('show');
        //     }
        // };
        // let u = new User();
        // u.show();


        // function Lesson(title) {
        //     this.title = title;
        // }
        // let l = new Lesson('html');
        // console.log(l.title);


        class Lesson {
            constructor(title) {
                this.title = title;
            }
            getTitle() {
                console.log(this.title);
            }
        }
        let l1 = new Lesson('l1');
        console.log(l1.title);
        l1.getTitle();

    </script>

 

類的內部工做機制就是原型操做:html

 

    <script>

        class User {
            // 屬性初始值
            constructor(name) {
                this.name = name;
            }
            show() {
                console.log('show');
            }
        }
        console.dir(User);
        console.log(User.prototype.constructor == User);
        let u = new User('user');
        // Object.getOwnPropertyNames()方法返回一個由指定對象的全部自身屬性的屬性名(包括不可枚舉屬性但不包括Symbol值做爲名稱的屬性)組成的數組。
        console.log(Object.getOwnPropertyNames(u));
        console.log(Object.getOwnPropertyNames(User.prototype));


        function Func(name) { this.name = name; }
        Func.prototype.show = function () {
            console.log('show');
        }
        console.dir(Func);
        console.log(Func.prototype.constructor == Func);
        let f = new Func('Func');
        console.log(Object.getOwnPropertyNames(f));
        console.log(Object.getOwnPropertyNames(Func.prototype));
    </script>

 

對象屬性的聲明:html5

    <script>

        class User {
            site = 'www.baidu.com';
            constructor(name) {
                this.name = name;
            }
        }
        let u = new User('user');
        console.log(u.site);
    </script>

 

class聲明的方法爲何不能遍歷:css3

    <script>

        // function Func(name) { this.name = name; }
        // Func.prototype.show = function () { }
        // console.log(JSON.stringify(Object.getOwnPropertyDescriptor(Func.prototype, 'show'), null, 2));
        // // 可遍歷
        // let f = new Func('cyy');
        // for (const key in f) { // 會去找原型
        //     console.log(key);
        // }
        // for (const key in f) {
        //     if (f.hasOwnProperty(key)) {
        //         console.log(key);
        //         console.log(f[key]);
        //     }
        // }


        class User {
            constructor(name) {
                this.name = name;
            }
            show() {
                console.log('show');
            }
        }
        let u = new User('cyy');
        console.log(JSON.stringify(Object.getOwnPropertyDescriptor(User.prototype, 'show'), null, 2));
        // 方法不可遍歷
        for (const key in u) {
            console.log(key);
        }

    </script>

 

嚴格模式下運行:數組

    <script>
        // 'use strict';


        // function func() {
        //     console.log(this);
        // }
        // func();


        // function User() {

        // }
        // User.prototype.show = function () {
        //     function func() {
        //         console.log(this);//非嚴格模式下指向window
        //     }
        //     func();
        // }
        // let u = new User();
        // u.show();


        // 無論是在嚴格模式仍是非嚴格模式下,都不會指向window
        class User {
            show() {
                function func() {
                    console.log(this);//非嚴格模式下指向window
                }
                func();
            }
        }
        let u = new User();
        u.show();
    </script>

 

靜態屬性使用:app

    <script>
        // 分配給構造函數的屬性,稱爲靜態屬性
        // function User(site) {
        //     this.site = site;
        // }
        // User.site = 'cyy';

        // let u = new User('user');
        // console.log(u.site);// 非靜態屬性
        // console.log(User.site);//靜態屬性
        // 不能使用name,name會指向構造函數的名字


        //靜態屬性在類上,而不是在實例化的對象上
        class Request {
            host = 'www.baidu.com';
            static host2 = 'www.google.com';//靜態屬性
            show(url) {
                console.log(Request.host2 + url);
            }
        }
        let r1 = new Request();
        console.log(r1);
        let r2 = new Request();
        console.log(r2);
        console.dir(Request);
        r2.show('/taobao');
    </script>

 

靜態方法的實現原理:less

   <script>
        // function User() {
        // }
        // User.prototype.show = function () { }

        // let u = new User();
        // console.dir(u); //User的原型上有show,非靜態方法

        // User.show2 = function () { console.log(User == this); console.log(User.prototype.constructor == this); }//prototype.constructor指回構造函數
        // console.dir(User);//靜態方法:直接定義到函數上(對象原型上)
        // // 函數也是對象,因此也能夠用下面的寫法
        // User.__proto__.show3 = function () { }
        // console.dir(User);
        // User.show2();


        // class Request {
        //     show() {
        //         console.log('show');
        //     }
        //     static show() {
        //         console.log('static-show')
        //     }
        // }
        // Request.__proto__.show2 = function () { } // 也是靜態方法
        // let r1 = new Request();
        // r1.show();
        // Request.show();//靜態方法
        // Request.show2();
        // console.dir(r1);
        // console.dir(Request);


        class Member {
            constructor(name, age) {
                this.name = name;
                this.age = age;
            }
            static create(...args) {
                return new this(...args);
            }
        }
        let m = Member.create('cyy', 18);
        console.dir(m);
    </script>

 

靜態屬性練習之課程管理類:ide

 

    <script>
        const lessons = [
            { title: 'title1', click: 99 },
            { title: 'title2', click: 13 },
            { title: 'title3', click: 44 },
        ];

        class Lesson {
            constructor(item) {
                this.model = item;
            }
            // 訪問器
            get gclick() {
                return this.model.click;
            }
            get gtitle() {
                return this.model.title;
            }
            click() {
                return this.model.click;
            }
            title() {
                return this.model.title;
            }
            static createBatch(data) {
                return data.map(item => new Lesson(item));
            }
            static maxClick(data) {
                return data.sort((a, b) => b.click - a.click)[0]; // 訪問器
                // return data.sort((a, b) => b.click() - a.click())[0];
            }
            static totalClick(data) {
                return data.reduce((t, c) => {
                    return t + c.gclick;
                }, 0);
            }
        }

        let res = Lesson.createBatch(lessons);
        // console.log(res);
        let res2 = Lesson.maxClick(res);
        // console.log(res2);
        // console.log(res2.title());
        // console.log(res2.gtitle);//訪問器
        console.log(Lesson.totalClick(res));
    </script>

 

在類中使用訪問器:函數

    <script>
        class Request {
            constructor(url) {
                this._url = url;
            }
            // setUrl(url) {
            //     if (!/^https?:\/\//i.test(url)) {
            //         throw new Error('地址錯誤~');
            //     }
            //     this.url = url;
            // }
            set url(url) {
                if (!/^https?:\/\//i.test(url)) {
                    throw new Error('地址錯誤~');
                }
                this._url = url;//這裏不能使用url,不然會死循環;
            }
            get url() {
                return this._url;
            }
        }
        let r = new Request('baidu.com');
        // r.url = 'taobao.com';
        // console.log(r);
        // r.setUrl('google.com');
        console.log(r.url);
    </script>

 

使用命名規則保護屬性:flex

<script>
        class User {
            site = "baidu.com";
            // 使用_表明被保護的屬性
            _age = 18;
            constructor(name) {
                this.name = name;
            }
            set age(age) {
                if (age < 0 || age > 100) {
                    throw new Error('年齡不合法~')
                    ;
                }
                this._age = age;
            }
        }
        let u = new User('user');
        //開放屬性能夠在外部被更改
        u.name = 'cyy';
        u.site = 'taobao.com';
        console.log(u);
        
        u.age = 101;
        console.log(u);
    </script>

 

使用Symbol定義protected屬性:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
</head>

<body>

    <script>
        // const AGE = Symbol();
        // class User {
        //     site = "baidu.com";
        //     // 使用_表明被保護的屬性
        //     // 實際上仍是能夠被修改的
        //     // _age = 18;
        //     [AGE] = 18;
        //     constructor(name) {
        //         this.name = name;
        //     }
        //     set age(age) {
        //         if (age < 0 || age > 100) {
        //             throw new Error('年齡不合法~')
        //             ;
        //         }
        //         // this._age = age;
        //         this[AGE] = age;
        //     }
        //     get age() {
        //         return this[AGE];
        //     }
        // }
        // let u = new User('user');
        // //開放屬性能夠在外部被更改
        // u.name = 'cyy';
        // u.site = 'taobao.com';
        // console.log(u);

        // // u.age = 101;
        // // console.log(u);
        // u.age = 66;
        // console.log(u);


        //關於繼承
        // const AGE = Symbol();
        // class Common {
        //     [AGE] = 18;
        //     set age(age) {
        //         if (age < 0 || age > 100) {
        //             throw new Error('年齡不合法~')
        //             ;
        //         }
        //         this[AGE] = age;
        //     }
        //     get age() {
        //         return this[AGE];
        //     }
        // }
        // class User extends Common {
        //     constructor(name) {
        //         super();//必須在子類中使用super()調用父類的構造函數
        //         this.name = name;
        //     }

        // }
        // let u = new User('user');
        // u.age = 66;
        // console.log(u);


        //多個受保護的屬性
        const protectes = Symbol();
        class Common {
            constructor() {
                this[protectes] = {};
                this[protectes].age = 18;
            }

            set age(age) {
                if (age < 0 || age > 100) {
                    throw new Error('年齡不合法~')
                    ;
                }
                this[protectes].age = age;
            }
            get age() {
                return this[protectes].age;
            }
        }
        class User extends Common {
            constructor(name) {
                super();//必須在子類中使用super()調用父類的構造函數
                this.name = name;
            }

        }
        let u = new User('user');
        u.age = 66;
        console.log(u);
    </script>
</body>

</html>

 

使用WeakMap保護屬性:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
</head>

<body>

    <script>
        // 繼承的狀況
        // const host = new WeakMap();
        // class Common {
        //     constructor() {
        //         host.set(this, 'baidu.com');
        //     }
        //     set host(url) {
        //         if (!/^https?/i.test(url)) {
        //             throw new Error('網址不合法~');
        //         }
        //         host.set(this, url);
        //     }
        //     get host() {
        //         return host.get(this);
        //     }
        // }
        // class User extends Common {
        //     constructor() {
        //         super();//調用父類的構造函數
        //     }
        // }
        // let u = new User();
        // console.log(u);
        // console.log(u.host);


        // 不繼承的狀況
        // const host = new WeakMap();
        // class User {
        //     constructor() {
        //         host.set(this, 'baidu.com');
        //     }
        //     set host(url) {
        //         if (!/^https?/i.test(url)) {
        //             throw new Error('網址不合法~');
        //         }
        //         host.set(this, url);
        //     }
        //     get host() {
        //         return host.get(this);
        //     }
        // }
        // let u = new User();
        // console.log(u);
        // console.log(u.host);


        // 多個屬性值的狀況
        const protects = new WeakMap();
        class User {
            constructor() {
                protects.set(this, {
                    host: 'baidu.com',
                    name: 'cyy'
                });
            }
            set host(url) {
                if (!/^https?/i.test(url)) {
                    throw new Error('網址不合法~');
                }
                protects.set(this, { ...protects.get(this)['host'], url });
            }
            get host() {
                return protects.get(this)['host'];
            }
            set name(name) {
                protects.set(this, { ...protects.get(this)['name'], name });
            }
            get name() {
                return protects.get(this)['name'];
            }
        }
        let u = new User();
        console.log(u);
        console.log(u.host);
        console.log(u.name);
    </script>
</body>

</html>

 

private私有屬性使用:

<script>
        //私有屬性不可被繼承,只能在當前類使用,子類不能使用
        class Common {
            #check() {
                if (this.name.length < 5) {
                    throw new Error("名字長度不夠呀~");
                }
                console.log(true);
            }
        }
        class User extends Common {
            constructor(name) {
                this.name = name;
                super();
                this.#check();
            }
        }
        let u = new User('cyy111');
        console.log(u);
    </script>

 

class屬性繼承原理:

<script>
        //構造函數中屬性繼承的原理
        // function User(name) {
        //     this.name = name;
        // }
        // function Admin(name) {
        //     User.call(this, name);//繼承屬性
        // }
        // Admin.prototype = Object.create(User.prototype);//會自動繼承方法
        // Admin.prototype.show = function () {

        // }
        // console.dir(Admin);


        //class使用super
        class Common {
            constructor(name) {
                this.name = name;
            }
        }
        class Admin extends Common {
            constructor(name) {
                super(name);
            }
        }
        let a = new Admin('cyy');
        console.log(a);

    </script>

 

類的方法繼承原理:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
</head>

<body>

    <script>
        // function User() { }
        // User.prototype.show = function () { console.log('user-show'); }

        // function Admin() { }
        // Admin.prototype = Object.create(User.prototype);//使用原型繼承

        // console.dir(Admin);
        // let a = new Admin();
        // a.show();


        class Common {
            show() { console.log('user-show'); }
        }
        class Admin extends Common { }
        let a = new Admin('cyy');
        a.show();

    </script>
</body>

</html>

 

super原理分析:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
</head>

<body>

    <script>
        //super訪問父類

        class Common {
            show() {
                console.log(this.name);
            }
        }
        class Admin extends Common {
            constructor(name) {
                super();
                this.name = name;
            }

            show() {
                super.show();//只調用父類的方法,this仍然指向當前類
            }
        }
        let a = new Admin('cyy');
        a.show();


        // let user = {
        //     name: 'user',
        //     show() {
        //         // console.log('user-show');
        //         console.log(this.name);
        //     }
        // }
        // let admin = {
        //     name: 'admin',
        //     __proto__: user,
        //     show() {
        //         //使用父類的方法
        //         //this.__proto__.show();//this指向父類
        //         this.__proto__.show.call(this);//this指向子類
        //         console.log('admin-show');
        //     }
        // }
        // console.log(admin);
        // admin.show();

    </script>
</body>

</html>

 

多重繼承中super的魅力:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
</head>

<body>

    <script>
        //注意:super只能在簡寫的方法中使用,不能在function中使用!!!

        // let common = {
        //     show() {
        //         console.log('common-show');
        //     }
        // }
        // let user = {
        //     name: 'user',
        //     __proto__: common,
        //     show() {
        //         this.__proto__.show.call(this);//this指向admin,所以沒法訪問common中的方法
        //         console.log(this.name);
        //     }
        // }
        // let admin = {
        //     name: 'admin',
        //     __proto__: user,
        //     show() {
        //         this.__proto__.show.call(this);//this指向admin
        //         console.log('admin-show');
        //     }
        // }
        // console.log(admin);
        // admin.show();


        //使用super解決多重繼承的問題
        let common = {
            show() {
                console.log('common-show');
            }
        }
        let user = {
            name: 'user',
            __proto__: common,
            show() {
                super.show(this);
                console.log(this.name);
            }
        }
        let admin = {
            name: 'admin',
            __proto__: user,
            show() {
                super.show(this);//調用父類的方法,this指向當前類
            }
        }
        console.log(admin);
        admin.show();

    </script>
</body>

</html>

 

爲何子類constructor中執行super:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
</head>

<body>

    <script>
        // function User(name) {
        //     this.name = name;
        // }
        // function Admin(...args) {
        //     User.call(this, ...args);
        //     // User.apply(this,args);
        // }
        // let a = new Admin('admin');
        // console.log(a);


        //子類沒有constructor時,系統默認會使用super調用父類的constructor
        // class User {
        //     constructor(name) {
        //         this.name = name;
        //     }
        // }
        // class Admin extends User {

        // }
        // let a = new Admin('admin');
        // console.log(a);


        //子類中有constructor時,必須使用super用父類的constructor
        class User {
            constructor(name) {
                this.name = name;
            }
        }
        class Admin extends User {
            constructor() {
                super();
            }
        }
        let a = new Admin('admin');
        console.log(a);

    </script>
</body>

</html>

 

使用super訪問父類方法:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
</head>

<body>

    <script>
        // class Controller {
        //     total(data) {
        //         return data.reduce((t, c) => t + c.price, 0);
        //     }
        // }
        // class Lesson extends Controller {
        //     constructor(data) {
        //         super();
        //         this.data = data;
        //     }
        //     result() {
        //         return {
        //             totalPrice: super.total(this.data)
        //         };
        //     }
        // }
        // let lessons = [
        //     { name: 'name1', price: 48 },
        //     { name: 'name2', price: 56 },
        //     { name: 'name3', price: 33 },
        // ];
        // let l = new Lesson(lessons);
        // console.log(l.result());


        //多層繼承
        class Common {
            total(data) {
                return data.reduce((t, c) => t + c.price, 0);
            }
        }
        class Controller extends Common {

        }
        class Lesson extends Controller {
            constructor(data) {
                super();
                this.data = data;
            }
            result() {
                return {
                    totalPrice: super.total(this.data)
                };
            }
        }
        let lessons = [
            { name: 'name1', price: 48 },
            { name: 'name2', price: 56 },
            { name: 'name3', price: 33 },
        ];
        let l = new Lesson(lessons);
        console.log(l.result());

    </script>
</body>

</html>

 

方法的重寫:

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
</head>

<body>

    <script>
        class Common {
            total(data) {
                return data.reduce((t, c) => t + c.price, 0);
            }
            getByKey(key) {
                return this.data.filter(item => item.name.includes(key));
            }
        }
        class Controller extends Common {

        }
        class Lesson extends Controller {
            constructor(data) {
                super();
                this.data = data;
            }
            result() {
                return {
                    totalPrice: super.total(this.data)
                };
            }
            //方法重寫
            getByKey(key) {
                return super.getByKey(key).map(item => item.name);
            }
        }
        let lessons = [
            { name: 'html', price: 48 },
            { name: 'html5', price: 56 },
            { name: 'css3', price: 33 },
        ];
        let l = new Lesson(lessons);
        console.log(l.result());
        console.log(l.getByKey('html'));

    </script>
</body>

</html>

 

靜態繼承原理:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
</head>

<body>

    <script>
        // function User() { }
        // User.name = 'user';
        // User.show = function () {
        //     console.log('show');
        // }
        // function Admin() { }
        // Admin.__proto__ = User;
        // console.log(Admin.name);
        // Admin.show();


        //做爲函數的prototype和做爲對象的__proto__
        class User {
            static name = "user";
            static show() {
                console.log('show');
            }
        }
        class Admin extends User {

        }
        console.dir(Admin);

    </script>
</body>

</html>

 

使用instanceof檢測對象實現:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
</head>

<body>

    <script>
        // function User() { }
        // function Admin() { }
        // Admin.prototype = Object.create(User.prototype);
        // let a = new Admin;
        // console.log(a instanceof Admin);
        // console.log(a instanceof User);

        // console.log(a.__proto__ == Admin.prototype); // User

        // console.log(Admin.prototype.__proto__ == User.prototype);
        // console.log(a.__proto__.__proto__ == Admin.prototype.__proto__);

        // console.log(a.__proto__.__proto__.__proto__ == Object.prototype);

        // console.log(a.__proto__.__proto__.__proto__.__proto__); //null
        // console.log(Object.prototype.__proto__ == null);


        // function checkPrototype(obj, constructor) {
        //     if (!obj.__proto__) return false;
        //     if (obj.__proto__ == constructor.prototype) return true;
        //     return checkPrototype(obj.__proto__, constructor);
        // }
        // console.log(checkPrototype(a, Admin));
        // console.log(checkPrototype(a, User));
        // console.log(checkPrototype(a, Object));



        //做爲函數的prototype和做爲對象的__proto__
        class User { }
        class Admin extends User { }
        let a = new Admin;
        console.dir(a instanceof Admin);
        console.dir(a instanceof User);
    </script>
</body>

</html>

 

isPrototypeOf檢測繼承關係:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
</head>

<body>

    <script>
        // let a = {}
        // let b = {
        //     __proto__: a //b的原型是a
        // }
        // console.log(a.isPrototypeOf(b));//檢測a是否是b的原型


        class Common { }
        class Admin extends Common { }
        let a = new Admin;
        console.log(Admin.prototype.isPrototypeOf(a));
        console.log(Common.prototype.isPrototypeOf(a));
        console.log(Admin.isPrototypeOf(a));//必須是對象進行判斷
    </script>

</body>

</html>

 

內置類繼承的原型實現:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
</head>

<body>

    <script>
        function Arr(...args) {
            args.forEach(item => this.push(item));
            console.log(this);
            this.first = function () {
                return this[0];
            }
            this.max = function () {
                return this.sort((a, b) => b - a)[0];
            }
        }
        Arr.prototype = Object.create(Array.prototype);
        let a = new Arr(1, 44, 66, 88, 3);
        console.log(a.first());
        console.log(a.max());
    </script>

</body>

</html>

 

使用繼承加強內置類:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
</head>

<body>

    <script>
        class Arr extends Array {
            //可省略
            // constructor(...args) {
            //     super(...args);
            // }
            first() {
                return this[0];
            }
            max() {
                return this.sort((a, b) => b - a)[0];
            }
            add(item) {
                this.push(item);
            }
            remove(value) {
                let pos = this.findIndex(item => item == value);
                this.splice(pos, 1);
            }
        }
        let a = new Arr(1, 44, 66, 88, 3);
        // console.log(a.first());
        // console.log(a.max());

        a.push('cyy');
        console.log(a);
        a.remove(44);
        console.log(a);
    </script>

</body>

</html>

 

mixin混合模式使用技巧:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
</head>

<body>

    <script>
        let Tool = {
            max(key) {
                return this.data.sort((a, b) => b[key] - a[key])[0];
            }
        };
        let Arr = {
            count(key) {
                return this.data.reduce((t, c) => t + c[key], 0);
            }
        };

        let lessons = [
            { name: 'html', price: 100, click: 46 },
            { name: 'css', price: 44, click: 75 },
            { name: 'js', price: 22, click: 395 },
        ];

        class Lesson {
            constructor(lessons) {
                this.lessons = lessons;
            }
            get data() {
                return this.lessons;
            }
        }
        Object.assign(Lesson.prototype, Tool, Arr); //繼承Tool類的方法

        let l = new Lesson(lessons);
        console.log(l.max('click'));
        console.log(l.max('price'));
        console.log(l.count('price'));
        console.log(l.count('click'));
    </script>

</body>

</html>

 

靈活的動畫處理類:

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
    <style>
        .s1 {
            width: 200px;
            display: flex;
            flex-direction: column;

        }

        dt {
            background: orange;

        }

        dt:first-child {
            border-radius: 5px 5px 0 0;
        }

        dd {
            background: lightblue;

            margin: 0;
            height: 40px;
            overflow: hidden;
        }

        dd:last-child {
            border-radius: 0 0 5px 5px;
        }
    </style>
</head>

<body>
    <div class="slide s1">
        <dt>html</dt>
        <dd>html-html</dd>
        <dt>css</dt>
        <dd>css-css</dd>
        <dt>js</dt>
        <dd>js-js</dd>
    </div>

    <script>
        class Anima {
            constructor(el) {
                this.el = el;
                this.defaultHeight = this.height;
                this.isShow = true;
                this.timeout = 5;
            }
            get height() {
                return window.getComputedStyle(this.el).height.slice(0, -2) * 1;//去除後面的px,*1是轉數值類型
            }
            set height(height) {
                this.el.style.height = height + 'px';
            }
            hide(callback) {
                this.isShow = false;
                let id = setInterval(() => {


                    if (this.height <= 0) {
                        clearInterval(id);
                        callback && callback();
                        return;
                    }

                    this.height = this.height - 1;
                }, this.timeout);
            }
            show(callback) {
                this.isShow = true;
                let id = setInterval(() => {

                    if (this.height >= this.defaultHeight) {
                        clearInterval(id);
                        callback && callback();
                        return;
                    }

                    this.height = this.height + 1;

                }, this.timeout);
            }
        }
        // let el = document.querySelector('.s1');
        // let anim = new Anima(el);
        // console.log(anim);
        // anim.hide(() => {
        //     console.log('隱藏結束~');

        //     anim.show(() => {
        //         console.log('展現結束~');
        //     });
        // });


        class Slide {
            constructor(el) {
                this.el = document.querySelector(el);
                this.links = this.el.querySelectorAll('dt');
                this.panels = [...this.el.querySelectorAll('dd')].map(item => new Panel(item));
                // console.log(this.panels);
                this.bind();
            }
            bind() {
                this.links.forEach((item, i) => {
                    item.addEventListener('click', () => {
                        // console.log(i);
                        // console.log('click');
                        this.action(i);
                    })
                })
            }
            action(i) {
                Panel.hideAll(Panel.filter(this.panels, i), () => {
                    this.panels[i].show();
                });

            }
        }
        class Panel extends Anima {
            static num = 0;
            static hideAll(items, callback) {
                if (Panel.num > 0) return; //有動畫正在執行,則不繼續執行

                items.forEach(item => {
                    Panel.num++;
                    item.hide(() => {
                        Panel.num--;
                    });
                });

                callback && callback();
            }
            static filter(items, i) {
                return items.filter((item, index) => index != i);
            }
        }
        let s = new Slide('.s1');

    </script>

</body>

</html>

相關文章
相關標籤/搜索