第十二章 类
类是封装继承和多态的脚手架。拥有更加方便的功能,避免造原始的轮子。
其内部使用原型和继承的方式,本质上是函数,拥有函数的特性。
相对于函数来说,更加清晰。
类的类型为函数
js
声明:
class XXX{}
let obj = new lass();
js
添加方法
class obj{
func1(){}
func2(){}
func3(){}
}
let sm = new obj();
sm.func1();
sm.func2();
sm.func3();
js
初始化
class obj{
constructor(){
}
}
constructor会被自动执行。
js
练手
{
class My {
constructor() {
this.name = "yang";
this.age = 19;
this.spirit = "right";
}
get() {
console.log(this.name, this.age)
}
}
let my = new My();
my.get();
}
工作机制
js
1、继承机制
class obj{
show(){}
}
obj.prototype.show = function(){}
两种本质上是一样的。
类方法更加清晰
2、初始化机制
class obj{
constructor(name){
this.name = name
}
}
function obj(name){
this..name = name
}
属性定义
加上this指的是对对象的声明,而不是class本身的声明
js
class obj{
func1 = xxx
constructor(){}
}
前者定义是固定定义,在constructor里定义是接受参数定义
特征声明
使用class时会自动设置好特征。
在函数中使用forin循环会默认遍历出原型的方法。而这不是我们想要的,本质上是因为函数在创建的时候特征里的enumerable为true。
class声明时,会自动修改为不可遍历。所以遍历出来的为对象的属性,而不会遍历出原型的方法。
严格模式
class自带严格模式。
class中添加方法时,this默认为undefined,实际上就是严格模式
js
class User{
constructor(){
}
show(){
function test(){
console.log(this);
}
test();
}
}
let user = new User();
user.show();
静态属性
通过prototype为示例添加的为普通方法, 通过对对象/class/函数本身/原型添加的为静态方法
分配给构造函数的为静态属性。区分静态属性和对象属性。
静态属性属于class类。
使用场景:设置静态属性会给所有的对象使用。
js
class obj = {
static xxx = xxx;
static xxx = xxx;
static xxx = xxx;
}
通过构造函数创建的对象自动指向原型
函数有三种用途:1、普通函数 2、构造函数 3、对象
静态方法实现原理
js
{
class User {
show() {
console.log("对象调用的普通方法");
}
static show() {
console.log("类调用的静态方法")
}
}
User.show();
let usr = new User();
usr.show();
}
{
class Usr {
constructor(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
static create(...args) {
return new this(...args);
}
}
console.log(Usr.create("yang", 18, "男"));
}
js
使用静态方法的多数据处理
const data = [
{ name: "js", price: 100 },
{ name: "mysql", price: 212 },
{ name: "vue.js", price: 98 }
];
class lesson {
constructor(data) {
this.model = data;
}
name (){
return this.model.name;
}
price() {
return this.model.price;
}
static sum(data){
return data.reduce((a,b)=>{ return a + b.price()},0)
}
static createBatch(data) {
return data.map(item => new lesson(item))
}
static maxPrice(data) {
return data.sort((a, b) => b.price() - a.price())[0];
}
}
let all = lesson.createBatch(data);
console.log(lesson.maxPrice(all).name());
console.log(lesson.sum(all));
访问器(类)
对属性的控制通过函数控制。
js
class Request {
constructor() {
this.data = {};
this.host = {};
}
set host(url) {
if (!/^https?:\/\//i.test(url)) {
throw new Error("address error");
}
this.data.host = url;
}
get host() {
return this.data["host"];
}
}
let user = new Request("https://www.baidu.com");
console.log(user.host);
属性保护
开放属性可修改,有时候我们不希望发生这种情况。
js
名义约定:
以下划线开始命名为私有属性
class User{
_url = "https://www.baidu.com";
constructor(name){
this.name = name;
}
set url(url){
if(!/^https?:/i.test(url)){ throw new Error("address abnormal")}
this._url = url;
}
}
let usr = new User("yyyyyyyyyyy")
usr.name ="yang";
usr.url = "https:"
console.log(usr)
js
Symbol()约定 (子类可访问)
const protectes = Symbol();
class Common {
constructor() {
this[protectes] = {};
this[protectes].host = "https://www.baidu.com"
}
}
class User extends Common {
constructor(name) {
super();
this.name = name;
}
set url(url) {
if (!/^https?:/i.test(url)) { throw new Error("address abnormal") }
this[protectes].host = url;
}
get url(){
return this[protectes].host;
}
}
let usr = new User("yyyyyyyyyyy")
usr.name = "yang";
usr.url = "https://"
console.log(usr[Symbol()]);
js
WeakMap设置 (子类可访问)
const protecteds = new WeakMap();
class Common {
constructor() {
protecteds.set(this, {
host: "https://www.baidu.com"
});
}
set url(url) {
if (!/^https?:/i.test(url)) { throw new Error("address abnormal") }
protecteds.set(this, { ...protecteds.get(this), url });
}
get url() {
return protecteds.get(this)["host"];
}
}
class User extends Common {
constructor(name) {
super();
this.name = name;
}
set name(name) {
protecteds.set(this, { ...protecteds.get(this), name });
}
get name(){
return protecteds.get(this)["name"];
}
}
let usr = new User("yyyyyyyyyyy");
usr.name = "ya"
console.log(usr.name);
console.log(usr);
let usr2 = new User("zzzzzzzzz");
usr.protecteds = "https://jlsakjdl"
console.log(usr.protecteds);
console.log(usr2.protecteds);
js
私有属性 (子类不可访问,仅当前类中访问)
语法:#
场景:属性私有化,方法私有化
class Usr {
#host = "https://www.baidu.com"
constructor(name) {
this.name = name
this.check(name);
}
set(url) {
if (!/^http?:/i.test(url)) { throw new Error("wrong") }
this.#host = url;
}
check (){
if(this.name.length < 5){throw new Error ("your name is too short")};
}
}
let user = new Usr("aaaaaa");
继承
js
*extend方法
普通继承:
1、原型继承
function User(name){
this.name = name;
}
function Admin(name){
this.name = name;
}
Admin.prototype = Object.create(User.prototype);
console.dir(Admin);
2、属性继承
function User(name){
this.name = name;
}
function Admin(name){
this.name = name;
User.call(this,name);
}
Admin.prototype = Object.create(User.prototype);
console.dir(Admin);
3、方法继承(对象的原型上设置方法达成继承)
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继承
class User{
constructor(name){
console.log("run")
this.name = name;
}
}
class Admin extends User{
constructor(name){
super(name);
}
}
let adm = new Admin("demo_name")
console.dir(adm)
被继承对象调用super方法,使用extends继承父类,父类constructor自动调入。
js
函数继承:
function user() { }
user.prototype.show = function () {
console.log("user's show")
}
function admin() { }
admin.prototype = Object.create(user.prototype);
let yz = new admin();
yz.show();
语法糖继承:
class usr{
show(){
console.log("usr's show")
}
}
class admin extends usr{}
let yz = new admin();
yz.show();
本质上是一样的
class不过是简写的自带便捷继承的function函数罢了。
补充:call方法在对象继承的中的应用
let yz = {
name:"yz___",
show() {
console.log("yz's show");
console.log(this.name)
}
};
let zh = {
name:"zh____",
__proto__: yz,
show() {
this.__proto__.show()
console.log("zh's show");
}
};
zh.show()
本来的意愿是使用zh里的数据调用yz的方法。因为yz里的show方法永远是yz调用的,所以this永远指向yz。结果是调用的数据来源是yz的数据(name)。
解决方法:call 改变this指向
let yz = {
name:"yz___",
show() {
console.log("yz's show");
console.log(this.name)
}
};
let zh = {
name:"zh____",
__proto__: yz,
show() {
this.__proto__.show.call(this,name);
console.log("zh's show");
}
};
zh.show()
所以super是调用父级的方法,而使用当前的数据(this指向自己)。
js
上述的对象继承只适用于两层。
this.__proto__.show.call(this)对多继承将发生死循环
this还是指对象,super只是做原型攀升与方法调用
super可以解决多重继承的问题,在此super仅做为原型攀升的功能。
在function(){}
js
使用super方法必须放在constuctor的首位书写,逻辑上优先级需要是自身>父类,所以需要先调用父类方法然后再进行覆盖以达到优先级的排序。
静态继承原理
属性和方法在皮上没有不同,只是值不同。
class内部实现原理就是原型。
prototype 是函数作为构造函数的原型
__proto__是函数做为对象的原型。
js
class中的static属性是为class(本质上是函数)作为对象时写入的属性/方法。
class中的非static属性是class作为构造函数时为实例化对象添加的方法。
继承判断
js
a为原型对象:
a.isPrototypeOf(b) a是否能实现b b是否被a实现
a为对象:
a instanceOf b a的原型链上(对象为__proto__)是否存在b的prototype
内置类继承
js
class number extends Array {
constructor(...args) {
super(...args)
}
max() {
return this.sort((a, b) => b - a)[0];
}
add(item) {
return this.push(item);
}
remove(value) {
let post = this.findIndex(item => item == value);
this.splice(post,1);
}
}
let demo = new number(5,61,65,16,13,546,123,321);
demo.add(555)
console.log(demo.max());
demo.remove(555);
console.log(demo.max());
多继承mixn
js
通过Object.assign方法添加多个属性
const data = [
{click:156,name:"abc"},
{click:6541,name:"english"},
{click:520,name:"law"}
]
class Lesson{
constructor(lessons){
this.lessons = lessons;
}
get data(){
return this.lessons
}
}
let tool = {
max(key){
return this.data.sort((a,b)=>b[key]-a[key])[0];
}
}
let arr = {
count(key){
return this.data.reduce((a,b)=> a + b[key],0)
}
}
Object.assign(Lesson.prototype,tool,arr);
let demo = new Lesson(data);
console.dir(Lesson)
console.log(demo.count("click"));