在 ES6 规范中,引入了 class 的概念。使得 JS 开发者终于告别了,直接使用原型对象模仿面向对象中的类和类继承时代。
但是 JS 中并没有一个真正的 class 原始类型, class 仅仅只是对原型对象运用语法糖。所以,只有理解如何使用原型对象实现类和类继承,才能真正地用好 class。
通过类来创建对象,使得开发者不必写重复的代码,以达到代码复用的目的。它基于的逻辑是,两个或多个对象的结构功能类似,可以抽象出一个模板,依照模板复制出多个相似的对象。就像自行车制造商一遍一遍地复用相同的蓝图来制造大量的自行车。
class 声明类的语法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class User { constructor (title ) { this .title = title; } getTitle (name ) { return name + this .title; } } console .log(typeof User); let us = new User("李四" );console .log(us.getTitle("你好" ));
类的工作机制就是原型机制
Object.getOwnPropertyNames()
, 方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组
Object.keys()
, 返回的是所有可枚举属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class User {}console .dir(User);console .log(User === User.prototype.constructor); function getName ( ) {}console .dir(getName);class Name { show ( ) {} } console .dir(Name);function getuser ( ) {}getuser.prototype.show = function ( ) {}; console .dir(getuser);console .log(Object .getOwnPropertyNames(Name));console .log(Object .getOwnPropertyNames(getuser));
对象属性的声明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class user { age = 10 ; constructor (val ) { this .name = val; } chageage (newage ) { this .age = newage; } getinfo ( ) { return this .age + this .name; } } let u = new user("lisi" );console .log(u);u.chageage(60 ); console .log(u.getinfo());
class 中声明的方法不能被遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class user { constructor (name ) { this .name = name; } age = 10 ; show ( ) {} } let u = new user();console .dir(user);for (const key in u) { console .log(key); } console .log(Object .getOwnPropertyDescriptor(user.prototype, "show" ));function hide ( ) {}hide.prototype.show = function ( ) {}; let h = new hide();for (const key in h) { console .log(key); } console .log(Object .getOwnPropertyDescriptor(hide.prototype, "show" ));
严格模式下运行的 class 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 "use strict" ;function show ( ) { console .log(this ); } show(); class user { name = "555" ; show ( ) { function test ( ) { console .log(this ); } test(); } } let u = new user();u.show();
静态属性:static 的使用 在类中属性前面加上 static 表示声明了静态属性,静态属性在类外面无法使用,是能在类内部使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class user { static host = "http://www.baidu.com" ; url = "http://taobao.com" ; api (url ) { return user.host + `/${url} ` ; } } let u = new user();let u2 = new user();console .log(u.host); console .log(u.api("userList" )); console .dir(user);u.url = "http://jingdong.com" ; console .log(u.url); console .log(u2.url);
静态方法实现原理 类中的静态方法声明和静态属性声明方式一样,在方法前添加 static 即为静态方法,通过 “类名.方法名” 的方式调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 function user ( ) {}user.show = function ( ) { console .log("方法的静态方法" ); }; user.prototype.show = function ( ) { console .log("构造函数的方法" ); }; user.show(); let u = new user();u.show(); class host { show ( ) { console .log(this === h); console .log("构造函数方法" ); } static show ( ) { console .log(this === host); console .log("静态方法" ); } } host.show(); let h = new host();h.show(); console .dir(host);console .dir(u);class createName { constructor (name, age, sex ) { this .name = name; this .age = age; this .sex = sex; } static create (...args ) { return new this (...args); } } let zs = createName.create("张三" , "18" , "男" );console .log(zs);for (const key in zs) { console .log(key + ":" + zs[key]); }
静态属性练习课程管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 const batchdata = [ { name : "js" , price : 300 }, { name : "vue.js" , price : 212 }, { name : "python" , price : 98 }, ]; class Lesson { constructor (data ) { this .model = data; } get name () { return this .model.name; } get price () { return this .model.price; } static createBatch (batchdata ) { return batchdata.map((item ) => new this (item)); } static maxPrice (data ) { return data.sort((a, b ) => b.price - a.price)[0 ]; } } let lesson = Lesson.createBatch(batchdata);console .log(lesson);console .log(Lesson.maxPrice(lesson).name); console .log(Lesson.maxPrice(lesson).price);
类中使用访问器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class user { constructor ( ) { this .data = {}; } set host (url ) { if (!/^http?:\/\//i .test(url)) { throw new Error ("地址格式错误" ); } this .data.host = url; } get host () { return this .data.host; } } let u = new user();u.host = "http://baidu.com" ; console .log(u.host);
使用 Symbol 定义 protected 属性 子类继承父类时在子类的 constructor 中必须声明 super()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 const protecteds = Symbol ();class userFather { constructor ( ) { this [protecteds] = {}; } set host (url ) { if (!/http:\/\//i .test(url)) { throw new Error ("地址错误" ); } this [protecteds].host = url; } get host () { return this [protecteds].host; } } class user extends userFather { constructor ( ) { super (); } } let u = new user();u.host = "http://sdfsdf" ; console .log(u);console .log(u.host);
使用 WeakMap 保护属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const host = new WeakMap ();class userFather { constructor ( ) { host.set(this , "http://baidu.com" ); console .log(this ); console .log(host); } set host (url ) { host.set(this , url); } get host () { return host.get(this ); } } class user extends userFather { constructor (name ) { super (); this .name = name; } } let u = new user("form-user" );u.host = "55555" ; console .log(u.host); console .log(u.name); let x = new user();x.host = "66666" ; console .log(x.host);
类的私有属性
私有属性 :只能在自己的类中使用,父类也无法继承使用
在属性前添加 # 号完成私有设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class user { #host = "http://baodu.com" ; constructor ( ) {} set host (name ) { this .#host = name; } get host () { return this .#host; } #gethsot ( ) { return this .#host + "私有方法" ; } gethost ( ) { return this .#gethsot(); } } let u = new user();u.host = "555" ; console .log(u.host);console .log(u.gethost());
class 属性继承 super()会自动执行 User 类的 constructor()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 function user ( ) {}user.prototype.show = function ( ) { return this .name; }; function admin (name ) { this .name = name; } admin.prototype = Object .create(user.prototype); let ad = new admin("555" );console .log(ad); console .log(ad.show()); class User { constructor (name ) { console .log(name + "这是User类" ); } } class Admin extends User { constructor (name ) { super (name); console .log(name + "这是Admin类" ); } } let a = new Admin("李四_" );
类中的方法继承 extends 关键字继承, A extends B 表示 A 继承 B
1 2 3 4 5 6 7 8 9 10 11 12 13 class user { show ( ) { console .log("我来自user方法" ); } } class admin extends user {}let ad = new admin();console .dir(ad);ad.show();
super 原理解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class user { classname = "user" ; show ( ) { console .log(this ); console .log(this .name); } } class admin extends user { classname = "admin" ; constructor (name ) { super (); this .name = name; } } let ad = new admin("lisi" );ad.show(); let User = { name: "user" , show ( ) { console .log("user的方法" ); console .log(this .name); }, }; let Admin = { __proto__: User, name: "admin" , show ( ) { console .dir(this ); console .log(this .__proto__ === User); this .__proto__.show.call(this ); console .log("admin的方法" ); }, }; Admin.show();
多重继承中 super 的魅力 super 只用来做原型攀升,和 this 指向无关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 let user = { name: "user" , show ( ) { console .log("user的方法," + this .name); }, }; let admin = { __proto__: user, name: "admin" , show ( ) { super .show(); console .log("admin的方法," + this .name); }, }; let commit = { __proto__: admin, name: "commit" , show ( ) { super .show(); console .log("commit的方法," + this .name); }, }; commit.show();
为什么子类的 constructor 中使用 super 子类使用 super()表示要改变父类 this 指向,指向子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function user (name ) { this .name = name; } function admin (name ) { user.call(this , name); } let a = new admin("Lisi" );console .log(a);class User { constructor (name ) { this .name = name; } } class Admin extends User { constructor (name ) { super (name); this .name = "new name" ; } } let ad = new Admin("ss" );console .log(ad);
使用 super 访问父类方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class controller { sum ( ) { return this .data.reduce((o, n ) => o + n.price, 0 ); } } class commit extends controller {}class Lesson extends commit { constructor (data ) { super (); this .data = data; } sumPrice ( ) { return { priceCount: super .sum(), data: this .data, }; } } let data = [ { name: "js" , price: 50 , }, { name: "vue" , price: 68 , }, { name: "jquery" , price: 99 , }, ]; let less = new Lesson(data);console .log(less.sumPrice());
方法重写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class commit { getByKey (key ) { return this .data.filter((item ) => item.name.includes(key)); } } class Lesson extends commit { constructor (data ) { super (); this .data = data; } getByKey (key ) { return super .getByKey(key).map((item ) => item.name); } } let data = [ { name: "js" , price: 50 , }, { name: "vue.js" , price: 68 , }, { name: "jquery" , price: 99 , }, ]; let les = new Lesson(data);console .log(les.getByKey("js" ));
静态继承原理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function user ( ) {}user.size = "user.size" ; user.show = () => { console .log(user.size); }; user.show(); function admin ( ) {}admin.__proto__ = user; console .dir(admin);admin.show(); class User { static size = "Class.User.size" ; static show ( ) { console .log(this .size); console .dir(this ); } } class Admin extends User {}Admin.show();
使用 instanceof 检测对象 a instanceof b,检测 a 是否属于 b
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 function user ( ) {}user.prototype.show = () => { console .log("object" ); }; function admin ( ) {}admin.prototype = Object .create(user.prototype); let ad = new admin();ad.show(); console .dir(ad);console .log(ad instanceof admin); console .log(ad instanceof user); console .dir(user);console .log(ad.__proto__ === admin.prototype); console .log(ad.__proto__.__proto__ === user.prototype); function newinstan (obj, fun ) { if (!obj.__proto__) { return false ; } if (obj.__proto__ === fun.prototype) { return true ; } return newinstan(obj.__proto__, fun); } console .log(newinstan(ad, user)); class User {}class Admin extends User {}let adm = new Admin();console .log(adm instanceof Admin); console .log(adm instanceof User);
isPrototypeOf 检测继承关系 a.isPrototypeOf(b),检测 a 是否在 b 的原型链上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 let a = { name: "55" , }; let b = { __proto__: a, }; let c = { __proto__: b, }; console .dir(b);console .log(a.isPrototypeOf(b)); console .log(a.isPrototypeOf(c)); class User {}class Admin extends User {}let ad = new Admin();console .dir(Admin.prototype.isPrototypeOf(ad));
内置类继承原理实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function myArr (...arg ) { console .log(arg); arg.forEach((item ) => { this .push(item); }); this .oneNumber = () => { return this [0 ]; }; this .maxNumber = () => { return this .sort((a, b ) => b - a)[0 ]; }; } myArr.prototype = Object .create(Array .prototype); console .dir(myArr);let m = new myArr(100 , 5 , 48 , 4 , 86 , 48 , 468 , 4 , 54 , 8 , 8 , 65 );console .log(m);console .log(m.maxNumber());console .log(m.oneNumber());
mixin 混合模式使用技巧 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 let libObj = { max (key ) { return this .data.sort((a, b ) => b[key] - a[key])[0 ]; }, min (key ) { return this .data.sort((a, b ) => a[key] - b[key])[0 ]; }, count (key ) { return this .data.reduce((l, f ) => l + f[key], 0 ); }, }; let lessondata = [ { name: "js" , price: 50 , }, { name: "vue" , price: 68 , }, { name: "jquery" , price: 99 , }, ]; class lesson { constructor (data ) { this .less = data; } get data () { return this .less; } } Object .assign(lesson.prototype, libObj);console .dir(lesson);let les = new lesson(lessondata);console .log(les.max("price" )); console .log(les.min("price" )); console .log(les.count("price" )); console .warn("创建第二个类" );class Admin { constructor (data ) { this .userinfo = data; } get data () { return this .userinfo; } } let userdata = [ { name : "张三" , age : 20 }, { name : "李四" , age : 32 }, { name : "王五" , age : 18 }, ]; Object .assign(Admin.prototype, libObj);let ad = new Admin(userdata);console .log(ad.max("age" )); console .log(ad.count("age" ));