类
类可以帮助我们进行实例化出对象,帮助我们面向对象的封装,继承和多态,类可以使面向对象的操作更加的舒服
使用类的形式,其内部还是使用了原型和继承的知识,只不过使用类的方式相较于使用函数的形式会更加的清晰,实质上类的方式可以理解为语法糖的形式,其内部的类型还是一个函数:
class User {}
console.log(typeof User); // function
类的创建
声明类的两种方式:
class User {};
let Hd = class {};
在类的内部,我们可以定义我们的属性和方法:
class User {
show() {
console.log("jlc");
}
get() {
console.log(24)
}
}
let my = new User;
// 调用类中的方法
my.show();
和定义对象中的方法不一样,对于每个不同的方法之间,我们不需要进行添加逗号进行分割,加了逗号会报错
类的传参
对于类的形式进行参数的传递,我们需要使用特殊函数constructor
进行参数的传递,这个特殊函数会自动被执行,为我们的对象做属性的初始值
class User {
constructor(name){
this.name = name;
}
// 我们也可以写一些方法
getName() {
return this.name;
}
}
// 对象属性的声明
let my = new User("jlc");
console.log(my.name); // jlc
console.log(my.getName()); // jlc
constructor
函数是为我们的对象做属性初始值的
类的机制
类的内部工作机制就是原型的操作,我们通过typeof User
可以看出类就是一个函数,其结构和函数是一样的,内部都是有原型链的
class User {}
console.log(User === User.prototype.constructor);
// true
因此,类就是一个语法糖的结构,其内部还是一个函数,使用类进行定义其内部方法后,就能自动的将这个定义的方法放到原型链当中了,而不需要像构造函数一样,需要对原型进行方法的定义,使用类的方式可以使我们后续写起来更加的方便
获取类中属性的名字(获取对象的内容):
class User {
constructor(name){
this.name = name;
}
show() {}
}
let hd = new User('jlc');
// 打印实例化出对象的属性
console.log(Object.getOwnPropertyNames(hd));
// ["name"]
// 打印原型对象中的属性和方法
console.log(Object.getOwnPropertyNames(User.prototype)); // ["constructor", "show"]
对象属性的声明:
class User {
site = '111'; // 声明默认属性
// 在constructor函数里面声明的属性,可以后续接收参数来改变这个属性值,大多数的情况,属性声明都会放在这个函数里面
constructor(name) {
this.name = name;
}
changeSite(value) { // 通过自定义方法来修改属性值
this.site = value;
}
show() {
return `${this.site}:${this.name}`;
}
}
let hd = new User('jlc');
hd.show(); // 111:jlc
hd.changeSite('222');
hd.show(); // 222:jlc
使用
let hd = new User('jlc');
(类会接收参数来声明对象的属性)后,name
属性就赋值给了新增的对象hd
(在hd
中就有这个name
属性,这个属性就是只属于这一个对象的,如果新实例化一个对象并且传递值,这个对象也会有独立的name
属性)(每一个对象声明之后的属性是这个对象独有的,不同的对象之间是不会共享的)
类声明的方法不能被遍历
对于函数,其声明的方法是可以被遍历的:
function Hd() {}
Hd.prototype.show = function() {}
let h = new Hd();
for (const key in h) {
console.log(key);
}
// show
在函数中,其放到原型中的方法是可以进行遍历的,我们可以打印其原型方法的属性:
jsconsole.log( JSON.stringify( Object.getOenPropertyDescriptor(Hd.prototype, "show", null, 2) ) )
看到
enumerable
属性是true
,表示其是允许进行遍历的,但是一般情况下,我们是需要这个属性是不能进行遍历的
因此,对于类,系统就会帮我们设置好不能遍历类中方法的特征(enumerable
属性是false
),更加的符合我们的使用规范:
class User {
constructor(name) {
this.name = name;
}
show() {}
}
let u = new User('jlc')
for (const key in u) {
console.log(key);
}
// name
对类进行遍历,只能得到其类中的属性,不能遍历类中的方法,因为对于类,系统设置其方法是不允许被遍历的
在严格模式下运行
我们推荐在写代码的时候在严格模式下进行,可以有效的提高我们的代码质量,类默认情况下就是在严格模式下执行的,不需要我们进行特殊的声明:
class Hd {
show() {
function test() {
console.log(this);
}
test();
}
}
let hd = new Hd();
hd.show(); // undefined
如果是函数,在非严格模式下执行时,直接调用方法中的
this
,其对应的时一个windows
窗口;在严格模式下执行,其对应的是一个undefined
静态属性
在函数中,给构造函数创建的属性(分配给构造函数的属性),我们称之为静态属性
先了解一下对象属性,将属性分配给不同的对象后,改变一个对象的该属性值,另外对象的该属性值不会发生变化:
class Request {
host = 'www.baidu.com';
}
let obj = new Request();
console.log(obj); // Request {host:'www.baidu.com'}
let obj2 = new Request();
obj.host = 'www.abc.com';
console.log(obj2.host); // 'www.baidu.com'
如果我们构造的是一个静态属性,这个属性就不会随着对象的创建而出现在对象中,只有打印这个类才能看到这个静态属性,一般情况下,如果想要一个值可以给所有的对象使用,我们可以将这个值设置为一个静态属性(这样只需写一份即可,可以减少我们的内存占用):
class Request {
static host = 'www.baidu.com';
}
let obj = new Request();
console.log(obj); // Request {}
console.dir(Request); // 可以看到声明的host静态属性
静态属性在前端开发的时候,一般写接口请求时用到的比较多,后台的地址一般是固定的,我们可以将后台的地址设置为一个静态属性:
jsclass Request { static host = 'www.baidu.com'; api(url) { return Request.host + `/${url}`; } } let obj = new Request(); console.log(obj.api("a")); // www.baidu.com/a
静态方法
从函数角度进行分析,直接将方法定义到构造函数中的方法,叫做静态方法,这个方法是存在于后续通过这个构造函数实例化出的对象上
function User() {
this.show = function() {} // 这个方法是存在于后续实例化出来的新对象上的
}
let hd = new User; // 之前定义的方法存在于新实例化的hd对象上
每
new
一个新的对象都会将这个方法添加进去,但是我们一般是将这些方法放到构造函数的原型上jsfunction User() {} User.prototype.show = function() {}
这样实例化出来的对象中是没有
show
这个方法,但是在原型链中是有这个方法的(通过构造函数创建的对象自动指向这个构造函数的原型)
直接将方法定义到函数上的方法,叫做静态方法(区别于将方法定义在原型链上的方法,通过原型方式定义的方法,我们称之为普通方法)
function User() {
}
User.show = function() {}
在类中使用静态方法和普通方法:
class User {
// 定义普通方法,定义到原型上的方法
show() {
console.log('show')
}
// 定义静态方法
static change() {
console.log('change')
}
}
// 上面的定义静态方法等价于下面的方式,但是下面的方式不美观,不推荐使用
User.__proto__.change = function() {
console.log('change');
}
let hd = new User();
console.log(hd); // 可以在通过类构造出对象的原型链__proto__中找到show这个普通方法
hd.show(); // show 原型(普通)方法的调用
User.change(); // change 静态方法只能通过类进行调用
原型方法可以和静态方法命名相同,通过不同的方式进行调用不同的方法
静态的方法和属性不会被实例所继承,只能在构造函数或类中找到
小案例:通过User
类中的create
静态方法创建用户:
class User {
constructor(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
static create(...args) {
return new this(...args);
}
}
let hd = User.create("jlc", 24, "男");