类
类方法间不需要逗号
class User {
show() {}
get() {
console.log("get method");
}
}
const hd = new User();
hd.get();
构造函数
构造函数用于传递对象的初始参数,但不是必须定义的,如果不设置系统会自动设置,子构造器中调用完super
后才可以使用 this
constructor(...args) {
super(...args);
}
原理分析
类其实是函数
class User {
}
console.log(typeof User); //function
函数与类的定义是一致的
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
中定义的方法不能枚举
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
严格模式执行
静态访问
静态属性
function User() {}
User.site = "love";
console.dir(User);
console.log(User.site); //love
在 class
中为属性添加 static
关键字即声明为静态属性
- 可以把为所有对象使用的值定义为静态属性
class Request {
static HOST = "https://www.dotohi.com";
query(api) {
return Request.HOST + "/" + api;
}
}
let request = new Request();
静态方法
指通过类访问不能使用对象访问的方法,比如系统的Math.round()
就是静态方法
- 一般来讲方法不需要对象属性参与计算就可以定义为静态方法
下面是静态方法实现原理
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
定义的方法即是静态方法
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
类型特性定义私有属性
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
指私有属性,只在当前类可以访问到,并且不允许继承使用
- 为属性或方法名前加
#
为声明为私有属性,真访问不到 - 私有属性只能在声明的类中使用
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);
详解继承
属性继承的原型如下
function User(name) {
this.name = name;
}
function Admin(name) {
User.call(this, name);
}
let hd = new Admin("baidu");
console.log(hd);
这就解释了为什么在子类构造函数中要先执行super
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
指向当前对象,结果并不是admin
的name
值
let user = {
name: "user",
show() {
return this.name;
}
};
let admin = {
__proto__: user,
name: "admin",
show() {
return this.__proto__.show();
}
};
console.log(admin.show());
为了解决以上问题,需要调用父类方法时传递this
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
原型循环调用
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
查找原型造成死循环- 也就是说把查询原型方法的事情交给了
super
,this
只是单纯的调用对象在各个继承中使用
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
判断一个对象是否在另一个对象的原型链
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
扩展内置类
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
类不用来继承做为其它类的父类