一、什么是“类”?
所谓“类”就是对象的模板,对象就是“类”的实例。但是,JavaScript 语言的对象体系,不是基于“类”的,而是基于构造函数(constructor)和原型链(prototype)。所谓”构造函数”,就是专门用来生成实例对象的函数。
构造函数特点:
①、首字母大写
②、函数体内部使用了this
关键字,代表了所要生成的对象实例
③、生成对象的时候,必须使用new
命令
那什么是new命令呢?
new
命令的作用,就是执行构造函数,返回一个实例对象。
var Test = function(){ //Test就是一个构造函数
this.name = 'niubi'
}
var a = new Test() // a就是一个实例对象
new的原理是什么,创建一个新对象需要几步?
①、创建一个空对象,作为将要返回的对象实例
let newObject = {}
②、将这个空对象的原型,指向构造函数的prototype
属性
newObject.__prototype__ = XX.prototype
③、将这个空对象赋值给函数内部的this
关键字
XX.apply(obj)
④、返回新对象
构造函数作为模板,可以生成实例对象。但是,有时拿不到构造函数,只能拿到一个现有的对象。我们希望以这个现有的对象作为模板,生成新的实例对象,这时就可以使用Object.create()
方法。
二、“类”的写法
引用阮神的例子:
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
使用ES6 class复写一遍:
class Point {
constructor(x, y) { //构造方法,返回实例对象即this
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
typeof Point // "function"
Point === Point.prototype.constructor // true 证明类的数据类型就是函数,类本身就指向构造函数
三、“类”内部的this指向问题
默认指向类的实例。
如果在静态方法中,this指的是类,而不是实例。
四、class类的静态方法-static
static关键字一般作用于类的方法,用来定义一个工具函数。static方法不能被实例对象调用,只能通过类名来调用。同时static方法也可以被继承,而且也能在子类中用super对象来调用父类中的static方法。
class Person{ //没有constructor的类会默认生成一个constructor构造器
static sayName(){
console.log("我是static函数")
}
}
class Student extends Person{}
const student = new Student()
Person.sayName() //我是static函数
Student.sayName() //我是static函数
student.sayName() //用实例化的对象来调用static方法时,代码会报错
static的this指向问题:
class Foo {
static bar() {
this.baz();
}
static baz() {
console.log('hello');
}
baz() {
console.log('world');
}
}
const poo = new Foo()
Foo.bar() // hello
Foo.baz() // hello
poo.baz() // world
poo.bar() // 报错
参考阮神的例子,发现其中三点:
①、静态方法可以与非静态方法重名
②、无关顺序,会优先调用static baz方法
③、this的指向问题
static的继承问题:
static静态方法同样可以被继承,使用方法为利用super()直接调用
五、实例对象自身属性的新写法
旧写法:
class Test {
constructor(){
this.num = 00
}
}
新写法:
class Test {
num = 0
}
优点:更加清晰可见,代码整齐
六、class类的继承
依旧使用阮神的例子:
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}
ES6规定,子类必须在constructor方法中调用super方法
class ColorPoint extends Point {
}
// 等同于
class ColorPoint extends Point {
constructor(...args) {
super(...args);
}
}
七、super关键字的使用
super()只能用在子函数内部,用在其他地方会报错,有两种用法:
①、当作函数使用
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();
}
}
new A() // A
new B() // B
子类B
的构造函数之中的super()
,代表调用A构造函数。但是返回的是子类B
的实例,即super
内部的this
指的是B
的实例,因此super()
在这里相当于A.prototype.constructor.call(this)
②、当作对象使用
class A {
p() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.p()); // 2
}
}
上面代码中,子类B
当中的super.p()
,就是将super
当作一个对象使用。这时,super
在普通方法之中,指向A.prototype
,所以super.p()
就相当于A.prototype.p()
注意:由于**super**
指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过**super**
调用的。
class A {
constructor() {
this.p = 2;
}
}
class B extends A {
get m() {
return super.p;
}
}
let b = new B();
b.m // undefined
那么问题来了,里面的get是什么作用呢?
取值函数(getter)和存值函数(setter)
class MyClass {
constructor() {
// ...
}
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
let inst = new MyClass();
inst.prop = 123;
// setter: 123
inst.prop
// 'getter'
当子类调用父类方法时,如果父类对象内存在static方法名称与非静态名称相同,实例对象调用非静态,子类调用static。