Skip to content

类方法间不需要逗号

javascript
class User {
  show() {}
  get() {
    console.log("get method");
  }
}
const hd = new User();
hd.get();

构造函数

构造函数用于传递对象的初始参数,但不是必须定义的,如果不设置系统会自动设置,子构造器中调用完super 后才可以使用 this

javascript
constructor(...args) {
  super(...args);
}

原理分析

类其实是函数

javascript
class User {
}
console.log(typeof User); //function

函数与类的定义是一致的

javascript
class User {
  constructor(name) {
    this.name = name;
  }
  show() {
    console.log(this.name);
  }
}


function User(name) {
  this.name = name;
}
Hd.prototype.show = function() {
  console.log(this.name);
};

函数差异

class 中定义的方法不能枚举

javascript
class User {
  constructor(name) {
    this.name = name;
  }
  show() {
    console.log(this.name);
  }
}
let xj = new User("love");
//不会枚举出show属性
for (const key in xj) {
  console.log(key);
}

function Hd(name) {
  this.name = name;
}
Hd.prototype.show = function() {
  console.log(this.name);
};
let obj = new Hd("love");
for (const key in obj) {
  console.log(key);
}

严格模式

class 默认使用strict 严格模式执行

静态访问

静态属性

javascript
function User() {}
User.site = "love";
console.dir(User);
console.log(User.site); //love

class 中为属性添加 static 关键字即声明为静态属性

  • 可以把为所有对象使用的值定义为静态属性
javascript
class Request {
  static HOST = "https://www.dotohi.com";
  
  query(api) {
    return Request.HOST + "/" + api;
  }
}
let request = new Request();

静态方法

指通过类访问不能使用对象访问的方法,比如系统的Math.round()就是静态方法

  • 一般来讲方法不需要对象属性参与计算就可以定义为静态方法

下面是静态方法实现原理

javascript
function User() {
  this.show = function() {
    return "this is a object function";
  };
}
User.show = function() {
  return "welcome to loveagri";
};
const xj = new User();
console.dir(xj.show()); //this is a object function
console.dir(User.show()); //welcome to loveagri

class 内声明的方法前使用 static 定义的方法即是静态方法

javascript
class User {
  constructor(name) {
    this.name = name;
  }
  static create(name) {
    return new User(name);
  }
}
const xj = User.create("love");
console.log(xj);

访问控制

protected

protected是受保护的属性修释,不允许外部直接操作,但可以继承后在类内部访问。

命名保护

将属性定义为以 _ 开始,来告诉使用者这是一个私有属性,请不要在外部使用。实际还是可以访问的。外部修改私有属性时可以使用访问器 setter 操作。

Symbol

下面使用 Symbol定义私有访问属性,即在外部通过查看对象结构无法获取的属性

WeakMap

WeakMap 是一组键/值对的集,下面利用WeakMap类型特性定义私有属性

javascript
const _host = new WeakMap();
class Common {
  constructor() {
    _host.set(this, "https://baidu.com");
  }
  set host(url) {
    if (!/^https:\/\//i.test(url)) {
      throw new Error("网址错误");
    }
    _host.set(this, url);
  }
}
class Article extends Common {
  constructor() {
    super();
  }
  lists() {
    return `${_host.get(this)}/article`;
  }
}
let article = new Article();
console.log(article.lists()); //https://baidu.com/article
article.host = "https://dotohi.com";
console.log(article.lists()); //https://dotohi.com/article

private

private 指私有属性,只在当前类可以访问到,并且不允许继承使用

  • 为属性或方法名前加 # 为声明为私有属性,真访问不到
  • 私有属性只能在声明的类中使用
js
class User {
    //private
    #host = "https://dotohi.com";
    constructor(name) {
        this.name = name
        this.#check(name)
    }
    set host(url) {
        if (!/^https?:/i.test(url)) {
            throw new Error("非常网址")
        }
        this.#host = url
    }
    get host() {
        return this.#host
    }
    #check = () => {
        if (this.name.length <= 5) {
            throw new Error("用户名长度不能小于五位")
        }
        return true
    };
}
let hd = new User("在线教程")
hd.host = "https://www.baidu.com"
console.log(hd.host);

详解继承

属性继承的原型如下

javascript
function User(name) {
  this.name = name;
}
function Admin(name) {
  User.call(this, name); 
}
let hd = new Admin("baidu");
console.log(hd);

这就解释了为什么在子类构造函数中要先执行super

javascript
class User {
  constructor(name) {
    this.name = name;
  }
}
class Admin extends User {
  constructor(name) {
    super(name);
  }
}
let hd = new Admin("baidu");
console.log(hd);

方法继承

  • 继承时必须在子类构造函数中调用 super() 执行父类构造函数
  • 子类中super.show() 执行父类方法

super

表示从当前原型中执行方法,

  • super 一直指向当前对象

下面是使用 this 模拟super,会有以下问题

  • this指向当前对象,结果并不是 adminname
javascript
let user = {
  name: "user",
  show() {
    return this.name;
  }
};
let admin = {
  __proto__: user,
  name: "admin",
  show() {
    return this.__proto__.show();
  }
};
console.log(admin.show());

为了解决以上问题,需要调用父类方法时传递this

javascript
let user = {
  name: "user",
  show() {
    return this.name;
  }
};
let admin = {
  __proto__: user,
  name: "admin",
  show() {
    return this.__proto__.show.call(this);
  }
};
console.log(admin.show());

上面看似结果正常,但如果是多层继承时,会出现新的问题

  • 因为始终传递的是当前对象this ,造成从 this 原型循环调用
javascript
let common = {
  show() {
    console.log("common.init");
  }
};
let user = {
  __proto__: common,
  name: "user",
  show() {
    return this.__proto__.show.call(this);
  }
};
let admin = {
  __proto__: user,
  name: "admin",
  get() {
    return this.__proto__.show.call(this);
  }
};
console.log(admin.get());

为了解决以上问题 js 提供了 super 关键字

  • 使用 super 调用时,在所有继承中 this 始终为调用对象
  • super 是用来查找当前对象的原型,而不像上面使用 this 查找原型造成死循环
  • 也就是说把查询原型方法的事情交给了 superthis 只是单纯的调用对象在各个继承中使用
javascript
let common = {
  show() {
    return this.name;
  }
};
let user = {
  __proto__: common,
  name: "user",
  show() {
    return super.show(this);
  }
};
let admin = {
  __proto__: user,
  name: "admin",
  get() {
    return super.show();
  }
};
console.log(admin.get());

super 只能在类或对象的方法中使用,而不能在函数中使用,下面将产生错误

constructor

super` 指调父类引用,在构造函数`constructor` 中必须先调用`super()
  • super() 指调用父类的构造函数
  • 必须在 constructor 函数里的this 调用前执行 super()

父类方法

使用super 可以执行父类方法

isPrototypeOf

使用 isPrototypeOf 判断一个对象是否在另一个对象的原型链

javascript
const a = {};
const b = {
  __proto__: a
};
const c = {
  __proto__: b
};
console.log(a.isPrototypeOf(b)); //true
console.log(a.isPrototypeOf(c)); //true


class User {}
class Admin extends User {}
let hd = new Admin();
console.log(Admin.prototype.isPrototypeOf(hd));
console.log(User.prototype.isPrototypeOf(hd));

使用 class扩展内置类

javascript
class NewArr extends Array {
  constructor(...args) {
    super(...args);
  }
  first() {
    return this[0];
  }
  add(value) {
    this.push(value);
  }
  remove(value) {
    let pos = this.findIndex(curValue => {
      return curValue == value;
    });
    this.splice(pos, 1);
  }
}
let hd = new NewArr(5, 3, 2, 1);
console.log(hd.length); //4
console.log(hd.first()); //5

hd.add("love");
console.log(hd.join(",")); //5,3,2,1,love

hd.remove("3");
console.log(hd.join(",")); //5,2,1,love

mixin

JS不能实现多继承,如果要使用多个类的方法时可以使用mixin混合模式来完成。本质是使用Object.assign

  • mixin 类是一个包含许多供其它类使用的方法的类
  • mixin 类不用来继承做为其它类的父类

手风琴示例

手风琴