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>