目录
ES5构造函数和ES6类
构造函数和类都是用于创建对象的机制
-
在
ES5
之前:JavaScript
并没有class
类关键字,是使用function
来声明一个构造函数(类)的,之后通过new
关键字来对其进行调用来实现类似于类的功能;- 在其他面向的编程语言里面,构造函数是存在于类中的一个方法,称之为构造方法
- 但
ES5
之前JavaScript
中的构造函数有点不太一样,构造函数扮演了其他语言中类的角色
-
在
ES6
之后:JavaScript
可以像别的语言一样,通过class
来声明一个类,这是一种语法糖,类实际上是基于ES5
实现的一种高级语法
1. 构造函数
- 构造函数也称为构造器(
constructor
),通常是我们在创建对象时会调用的函数 - 构造函数也是一个普通的函数,命名以大写字母开头,从表现形式来说,和普通的函数没有任何区别
- 但如果这个函数被
new
操作符来调用了,那么这个函数就称之为是一个构造函数
实例
实例 是由类或构造函数创建的具体对象,通过 new
关键字调用构造函数创建,或通过 class
关键字定义类并创建
function Person(name, age, height) {
this.name = name;
this.age = age;
this.height = height;
}
var p1 = new Person("ablice", 20, 188);
p1
就是通过 new Person
创建的实例
new操作符
具体学习这篇文章:https://juejin.cn/post/7397399723601215488
实例方法
实例方法是定义在类或构造函数的原型对象(prototype
)上的方法
// 定义一个构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 定义实例方法
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};
// 创建实例
const person1 = new Person('Alice', 30);
const person2 = new Person('Bob', 25);
// 调用实例方法
person1.greet(); // 输出: Hello, my name is Alice and I am 30 years old.
person2.greet(); // 输出: Hello, my name is Bob and I am 25 years old.
类方法/静态方法
类方法也称为静态方法,它是定义在类或构造函数本身上的方法,通过类或构造函数调用,不能通过实例调用
// 定义一个构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 定义实例方法
Person.prototype.greet = function() {
console.log(`Hello, ${this.name}, ${this.age}`);
};
// 定义类方法
Person.jumping = function(){
console.log(this.name + '在jumping')
}
// 创建实例
const person1 = new Person('Alice', 30);
const person2 = new Person('Bob', 25);
// 调用类方法
Person.jumping(); // 输出: Person在jumping
// 调用实例方法
person1.greet(); // 输出: Hello, Alice, 30
person2.greet(); // 输出: Hello, Bob, 25
原型
构造函数是函数自然有一个 prototype
属性,该属性是一个对象,包含由该构造函数创建的所有实例共享的属性和方法
具体理解看这篇文章:https://blog.youkuaiyun.com/qq_45730399/article/details/141104727?spm=1001.2014.3001.5501
实现继承
具体学习这篇文章:https://juejin.cn/post/7399986979735781391
2. ES6类
在 ES6中,引入了 class
关键字,使得面向对象编程变得更加直观和易于理解。但 JavaScript
依旧是基于原型的语言,class
只是语法糖,它提供了一种更简洁、更清晰的方式来定义使用构造函数和原型继承
定义
按照构造函数形式创建类,不仅仅和编写普通的函数过于相似,而且代码并不容易理解
- 在ES6(
ECMAScript2015
)新的标准中使用了class
关键字来直接定义类 - 本质是构造函数、原型链的语法糖而已
- 类的原型和原型中的
constructor
指向和构造函数都是相同的 - 唯一不同是类不能像构造函数一样直接调用
- 可以使用两种方式来声明类:
typeof Person === function
- 类声明:
class Person {}
` - 类表达式:
var Student = class {}
- 类声明:
constructor
如果我们希望在创建对象的时候给类传递一些参数,这个时候应该如何做呢?
- 每个类都可以有一个自己的构造函数(方法),这个方法的名称为固定的
constructor
- 每个类只能有一个
constructor
,如果包含多个构造函数,那么会抛出异常 - 当我们通过
new
一个类的时候会调用这个类的constructor
,这时new
做的事情和new
一个构造函数做的事情是一样的
实例方法
// 定义一个类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 定义实例方法
greet() {
console.log(`Hello, ${this.name}, ${this.age}`);
}
}
// 创建实例
const person1 = new Person('Alice', 30);
const person2 = new Person('Bob', 25);
// 调用实例方法
person1.greet(); // 输出: Hello, Alice, 30
person2.greet(); // 输出: Hello, Bob, 25
类方法/静态方法
类方法也称为静态方法,它是定义在类或构造函数本身上的方法,通过类或构造函数调用,用于定义直接使用类来执行的方法,不需要有类的实例,使用static
关键字来定义
// 定义一个类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 定义实例方法
greet() {
console.log(`Hello, ${this.name}, ${this.age}`);
}
// 定义类方法
static jumping = function(){
console.log(this.name + '在jumping')
}
}
// 创建实例
const person1 = new Person('Alice', 30);
const person2 = new Person('Bob', 25);
// 调用类方法
person1.jumping(); // 输出: Person在jumping
// 调用实例方法
person1.greet(); // 输出: Hello, Alice, 30
person2.greet(); // 输出: Hello, Bob, 25
访问器方法
对象可以添加setter
和getter
函数的,那么类也是可以的
class Person {
constructor(name) {
this._name = name;
}
set name(newName) {
console.log("调用了name的setter方法");
this._name = newName;
}
get name() {
console.log("调用了name的getter方法");
return this._name;
}
}
var p1 = new Person("alice");
console.log(p1.name); // 调用了name的getter方法, alice
// 设置新的名字,会调用setter方法
p1.name = "bob"; // 调用了name的setter方法
// 再次获取名字,会调用getter方法
console.log(p1.name); // 调用了name的getter方法, bob
extends
在ES6
中新增了使用extends
关键字,可以帮助我们实现继承
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
running() {
console.log(this.name + "在running");
}
static jumping() {
console.log(this.name + "在jumping");
}
}
class Student extends Person {}
var s1 = new Student("bob", 20);
console.log(s1); // {name: 'bob', age: 20}
s1.running(); // bob在running
继承内置类
我们可以让自己的类继承内置类,比如Array,然后对Array进行扩展
// es5继承并扩展Array
Array.prototype.lastItem = function () {
return this[this.length - 1];
};
var a1 = [1, 2, 3];
// var a2 = new Array(1, 2, 3);
console.log(a1.lastItem()); // 3
// es6继承并扩展Array
class myArray extends Array {
lastItem() {
return this[this.length - 1];
}
}
var a2 = new myArray(10, 20, 30);
console.log(a2.lastItem()); // 30
super关键字
- 在子(派生)类的构造函数中,必须在使用
this
之前调用super
,以确保父类的构造函数被正确调用并初始化父类的属性 - 在子(派生)类的方法中,可以使用
super.methodName
调用父类的方法 super
的使用位置有三个:子类的构造函数、实例方法、静态方法
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
running() {
console.log(this.name + "在running");
}
static jumping() {
console.log(this.name + "在jumping");
}
}
class Student extends Person {
constructor(name, age, school, score) {
// 1. 在constructor中,子类想扩展自己的属性时,需要在扩展之前使用super
super(name, age);
this.school = school;
this.score = score;
}
running() {
// 2. 想在实例方法中写自己的逻辑,也想使用父类时
console.log(`考上${this.school}了`); // 考上清华了
super.running(); // bob在running
}
static jumping() {
// 2. 想在类方法中写自己的逻辑,也想使用父类时
console.log(`哇考了${this.score}分`); // 哇考了undefined分
super.jumping(); // Student在jumping
}
}
var s1 = new Student("bob", 20, "清华", 750);
console.log(s1); // {name: 'bob', age: 20, school: '清华', score: 750}
s1.running();
Student.jumping();
混入mixin
JavaScript
的类只支持单继承只能有一个父类,
在开发中需要在一个类中添加继承多个类时可以使用混入:
function mixin(...classes) {
if (classes.length === 0) {
throw new Error("At least one class should be provided");
}
return classes.reduce((CombinedClass, CurrentClass) => {
return class extends CombinedClass {
constructor(...args) {
super(...args);
Object.assign(this, new CurrentClass(...args));
}
};
});
}
// 测试代码
class CanEat {
eat() {
console.log('Eating');
}
}
class CanWalk {
walk() {
console.log('Walking');
}
}
class CanSwim {
swim() {
console.log('Swimming');
}
}
const MixedClass = mixin(CanEat, CanWalk, CanSwim);
const obj = new MixedClass();
obj.eat(); // 输出: Eating
obj.walk(); // 输出: Walking
obj.swim(); // 输出: Swimming
babel
中ES6
类转ES5
源码
在转换的源码中可以看到ES6
的类和继承的原理还是构造函数和原型,只是它们的语法糖
class
代码的转换
class
代码转换前:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
running() {
console.log(this.name + "在running");
}
static jumping() {
console.log(this.name + "在jumping");
}
}
var p1 = new Person('bob', 18)
转换后:
function _typeof(o) {
"@babel/helpers - typeof";
return (
(_typeof =
"function" == typeof Symbol && "symbol" == typeof Symbol.iterator
? function (o) {
return typeof o;
}
: function (o) {
return o &&
"function" == typeof Symbol &&
o.constructor === Symbol &&
o !== Symbol.prototype
? "symbol"
: typeof o;
}),
_typeof(o)
);
}
function _classCallCheck(a, n) {
// 如果a不是n的实例就抛错说class类不能直接调用
if (!(a instanceof n))
throw new TypeError("Cannot call a class as a function");
}
function _defineProperties(e, r) {
for (var t = 0; t < r.length; t++) {
var o = r[t];
(o.enumerable = o.enumerable || !1),
(o.configurable = !0),
"value" in o && (o.writable = !0),
Object.defineProperty(e, _toPropertyKey(o.key), o);
}
}
function _createClass(e, r, t) {
// e: 构造函数Person,r: 实例方法数组,t:静态方法数组
return (
r && _defineProperties(e.prototype, r), // 将实例方法定义Person原型上
t && _defineProperties(e, t), // 将静态方法定义Person上
Object.defineProperty(e, "prototype", { writable: !1 }), // prototype设为不可写
e
);
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == _typeof(i) ? i : i + "";
}
function _toPrimitive(t, r) {
if ("object" != _typeof(t) || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof(i)) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
// 纯函数
var Person = /*#__PURE__*/ (function () {
function Person(name, age) {
_classCallCheck(this, Person); // 判断this是不是Person的实例
this.name = name;
this.age = age;
}
return _createClass(
Person,
[
{
key: "running",
value: function running() {
console.log(this.name + "在running");
}
}
],
[
{
key: "jumping",
value: function jumping() {
console.log(this.name + "在jumping");
}
}
]
);
})();
var p1 = new Person("bob", 18);