引言:
JavaScript当中并没有构造函数的概念,只是我们为了方便区分,就人为的约定了方法名首字母大写的为构造函数。
在ES6之前,如果写一个构造函数像下面这样:
function Person1(name,age){
this.name = name;
this.age = age;
this.show = function(){
console.log(this.name,this.age);
}
}
或者标准的写法是:把属性写在构造函数里,把方法写在原型链里,如下:
function Person2(name,age){
this.name = name;
this.age = age;
}
Person.prototype.show = function(){
console.log(this.name,this.age);
}
在ES6当中,引入了类(class)的概念,使用class写上面的代码如下:
class Person{
constructor(name,age){ //构造函数
this.name = name;
this.age = age;
}
show = function(){
console.log(this.name,this.age);
}
}
首先,使用class声明的Person:typeof(Person); // function
并且var p = new Person(); Person.prototype.show == p.show; // true
说明class上的方法都是写在原型链上的!
接着,看一下构造函数constructor直接:
class A{}
var a = new A();
是能够直接运行的!说明内部帮我们写了一个构造函数,只不过是空的:constructor(){}
ES6之前,函数可以这样写:var show = function(){}
,我们称之为函数表达式;
ES6同样为我们提供了类表达式:const Person = class{}
ES6之前有立即执行函数:(funcrion(){})()
ES6也同样提供了立即执行类:var p = new class{}
虽然上面说了typeof(Person); // function
,class是一个类,但class并不支持变量提升,必须先定义,在使用。
一、class的基本使用
- constructor、实例属性、实例方法
在constructor内定义的就是实例属性,如constructor(name){ this.name = "xm"; }
除constructor之外的函数。 - this指向
class Person {
constructor(){
this.name = "xm";
}
show(){
this.print(`my name is ${this.name}`);
}
print(data){
console.log(data);
}
}
var p = new Person();
此时p具有show()和print()这两个方法,并且p是一个对象(typeof p == "object"
),那么,对p进行解构赋值:
var {show : show } = p;
// 此时,执行p.show()与show():
p.show(); // my name is xm
show(); //报错,print undefined 此处show是单独执行,并没有被调用,所以不指向p
当我们把constructor改写为如下时:
// 方法1
constructor(){
this.name = ""xm;
this.show = this.show.bind(this);
}
在执行:
var {show : show } = p;
p.show(); // my name is xm
show(); // my name is xm
why???
来分析一下新增的那句this.show = this.show.bind(this);
,我们知道,谁调用this就指向谁:此时,后面的this.show
指的就是show方法,然后又使用bind()
将show方法内部的this改变了指向!
// 方法2 利用箭头函数改写
constructor(){
this.name = ""xm;
this.show = () => this.print(`my name is ${this.name}`);
}
同样的在执行:
var {show : show } = p;
p.show(); // my name is xm
show(); // my name is xm
这是因为箭头函数里this指向它所在的父级对象的作用域!
constructor是一个函数,具有独立的作用域,内部箭头函数的this指向的是constructor的父级person,而p是person的实例,所以指向p
举例:
var name ="window";
bar obj = {
name : "obj" ,
show :() => { console.log(this.name); };
};
// 执行:
obj.show(); // window
- 静态属性、静态方法 (关键字static)
class A{
static fn(){ //静态方法
console.log("aaa");
}
// 静态属性规定了在这个位置写,static abc = 123,但浏览器并没有实现
}
A.fn(); // aaa
A.abc = 123; // 但可以通过这种方法添加
A.abc; // 123
二、class的继承
- extends
ES6之前的继承:
function A(){}
var a = new A();
那么,a.__proto__ == A.prototype // true
ES6的继承:
class A {
construtor(){ this.name = "xm"; }
print(){ console.log(this.name); }
}
class B extends A{
construtor(){
super(); // 子类要先调用super()才能使用this,因为子类没有this 默认调用父级的构造函数
this.name = "B";
}
}
var b = new B();
b.print(); // B
- super 详解
通过super()调用父类的构造函数,把this绑定到自己身上。
super()当函数使用,就相当于父级的构造函数执行。
super()当对象使用,在父类写:print(){ console.log("A") }
在子类里写:print(){ super.print(); }
,执行b.print(); // A
,此时值的是父类的原型对象。但如果在子类写为:print(){ super.name; }
,执行:b.print() // undefined
,而父类的print方法是在原型链上的,但name不在,说明super当作对象时,只能访问父类原型链上定义的方法。
接下来:
class A {
construtor( name = "deng" ){ this.name = "xm"; }
print(){ console.log(this.name); }
}
A.prototype.name = "Father name";
class B extends A{
construtor(name){
super(name);
}
print(){
super.print();
}
}
var b = new B("xdend");
b.print(); // xdend 说明super绑定了子类的this
刚才说了:function A(){}; var a = new A();
,有这样的关系:a.__proto__ == A.prototype // true
,
那么在ES6当中,有些不同:
class A{}
class B extends A{}
//字类的__proto__表示构造函数的继承
B.__proto == A; // true
//字类的__proto__.prototype表示方法的继承
B.__proto__.prototype == A.prototype ; // true