Javascript面向对象(八)——Class
类结构允许使用简洁、漂亮的方式定义基于原型的类模式。
class语法
类的语法有多种形式,我们从简单的开始。
这里是基于原型的类User:
function User(name) {
this.name = name;
}
User.prototype.sayHi = function() {
alert(this.name);
}
let user = new User("John");
user.sayHi();
然后,我们同样使用类的语法:
class User {
constructor(name) {
this.name = name;
}
sayHi() {
alert(this.name);
}
}
let user = new User("John");
user.sayHi();
很容易看到两个示例相似。那么class到底做了什么?我们可能认为这定义了新语言级的实体,但并非如此。
class User {…}确切地说做了两件事:
1、声明了变量User,其引用constructor函数。
2、把定义的方法放入User.prototype,这里包括sayHi和constructor。
下面这段代码进行检测:
class User {
constructor(name) { this.name = name; }
sayHi() { alert(this.name); }
}
// proof: User is the "constructor" function
alert(User == User.prototype.constructor); // true
// proof: there are two methods in its "prototype"
alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi
User类的图示如下:
所以class是定义构造函数和原型方法的特定语法。但不仅如此,有一些差别用来确保用法正确。
举例,constructor不能不使用new调用:
class User {
constructor() {}
}
alert(typeof User); // function
User(); // Error: Class constructor User cannot be invoked without 'new'
输出类
如果输出类,如alert(User),一些引擎显示”class User”,而另一些显示”function User…”。
这里不要困惑,字符串显示可能不同,其实仍然是函数,在javascript语言中没有单独class实体。
类方法是非枚举的
类定义对所有原型中的方法设置enumerable标志为false,这时好事,因为如果我们使用for…in遍历对象,我们通常不想得到class方法。
如果没有构造函数怎么样?
如果在class结构没有构造函数,那么一个空的构造函数自动生成,写法如:constructor(){}。
class总是使用严格模式
所有在class结构的代码自动使用严格模式。
getter/setter
类也可以包括getter/setter,这里示例使用user.name,实现代码:
class User {
constructor(name) {
// invokes the setter
this.name = name;
}
get name() {
return this._name;
}
set name(value) {
if (value.length < 4) {
alert("Name too short.");
return;
}
this._name = value;
}
}
let user = new User("John");
alert(user.name); // John
user = new User(""); // Name too short.
只有方法
与对象定义不同,类定义代码不能有属性:值方式赋值。只能有方法(方法间没有逗号分割)和getter/setter。
根据这个思想:类中定义的所有内容都是在原型中定义,因为原型应该只存储方法,其在对象之间共享。数据是描述具体对象的状态,应该存储在各自对象中。
如果我们真坚持方非函数值至原型中,那么class无法做到。只能手工修改原型,如下:
class User { }
User.prototype.test = 5;
alert( new User().test ); // 5
虽然技术上可行,但我们应该知道我们为什么要做。使用getter的替代方式:
class User {
get test() {
return 5;
}
}
alert( new User().test ); // 5
从外部代码看是一样的,但通过getter方式应该慢点。
Class 表达式
和函数一样,类能够定义在另一个表达式内部,用于传递、返回等。这里是一个返回类的函数(类工厂):
function getClass(phrase) {
return class {
sayHi() {
alert(phrase);
};
};
}
let User = getClass("Hello");
new User().sayHi(); // Hello
如果还记得class就是函数原型模式定义的特殊形式,就会觉得这很正常。
如命名函数表达式一样,class也可以有一个名称,只能在class定义内部可见。
let User = class MyClass {
sayHi() {
alert(MyClass);
}
};
new User().sayHi(); // works, shows MyClass definition
alert(MyClass); // error, MyClass is only visible in methods of the class
静态方法
静态方法与类函数绑定,而不是原型。且可以使用this关键字(this代表类,java是不能使用的),示例:
class User {
static staticMethod() {
alert(this == User);
}
}
User.staticMethod(); // true
在User.staticMethod()方法内部的this值为类User的构造器自身(点前面对象的规则)。
通常static方法用来表达与类相关的功能,不是类的特定对象。
举例,我们有Article对象,并需要一个比较函数,那自然的选择应该是Article.compare,如下:
class Article {
constructor(title, date) {
this.title = title;
this.date = date;
}
static compare(articleA, articleB) {
return articleA.date - articleB.date;
}
}
// usage
let articles = [
new Article("Mind", new Date(2016, 1, 1)),
new Article("Body", new Date(2016, 0, 1)),
new Article("JavaScript", new Date(2016, 11, 1))
];
articles.sort(Article.compare);
alert( articles[0].title ); // Body
这里的Article.compare是在所有articles对象之上的,意味这比较他们。
另一个示例被称为工厂方法,使用特定的参数创建一个对象,如Article.createTodays():
class Article {
constructor(title, date) {
this.title = title;
this.date = date;
}
static createTodays() {
// remember, this = Article
return new this("Todays digest", new Date());
}
}
let article = article.createTodays();
alert( articles.title ); // Todays digest
现在,我们每次需要一个“当天摘要”对象,我们直接调用Article.createTodays()
。
静态方法用于数据库相关的类,用于从数据库中查询/保存/删除实体,代码示例:
// assuming Article is a special class for managing articles
// static method to remove the article:
Article.remove({id: 12345});
本文参考链接:https://blog.csdn.net/neweastsun/article/details/70568326