ES6 中的新引入的 class 具有正式定义类的能力,我们可以使用 class 关键字来定义类,和函数类型相似,类的定义的主要方式是 类声明 和 类表达式
类的两种定义的方式都需要使用到 class 关键字
// 类声明
class Person {}
// 类表达式
let Person = class {};
需要注意的是,类表达式和函数表达式类似,使用 var 关键字声明,在求值前引用,拿到的是undefined,如果是使用 let 关键字声明,则会报错,原因是使用 let 关键字声明的变量不会自动提升,并且不允许在变量声明之前使用,
console.log(person, personC); //undefined undefined
var person = function () {};
var personC = class {};
console.log(person, personC);
//Uncaught ReferenceError: Cannot access 'person' before initialization
let person = function () {};
let personC = class {};
我们知道,函数声明可以提升,我们可以在函数声明之前调用函数,但是类声明不能;
console.log(personFn); // ƒ personFn() {}
function personFn() {}
console.log(Person);
//Uncaught ReferenceError: Cannot access 'Person' before initialization 不能在 "Person"初始化之前访问
class Person {}
我们声明的类受块级作用域的影响,但是函数声明却不受块级作用域的影响
{
function personFn() {}
class Person {}
}
console.log(personFn); // ƒ personFn() {}
console.log(Person); // Uncaught ReferenceError: Person is not defined
补充一下作用域的知识,块级作用域是ES6 提供的,在此之前只有 全局作用域 和 函数作用域.在 ES6 中带有{}的就是块级作用域, 例如 if{}else{} 在块级作用域中我们使用 let 和 const 来声明变量,不会出现变量提升,但是对使用 var 声明的变量没有影响,还是会存在变量提升,在块级作用域中, var 声明和function 和在window中的表现一样,如果没有使用 let 和 const 进行声明的变量,我们就可以不考虑块级作用域,function(){}会形成函数作用域,在函数内部形成的作用域,如果在函数A内部定义函数B,则函数B可以访问到函数A内部定义的变量,但是函数A无法访问函数B内部定义的变量,接下来用代码来简单的复习一下函数作用域
function wrap() {
var wrapText = "wrap";
function inner() {
var innerText = "inner";
console.log(wrapText); // wrap
}
inner();
console.log(innerText); // Uncaught ReferenceError: innerText is not defined
}
wrap();
声明的类可以包含 构造函数方法, 实例方法, 设置函数, 获取函数, 静态类方法, 但是这些都不是必须的,即使是定义一个空的类也是有效的.和构造函数一样,建议类名的首字母要大写,可以区别通过它创建的实例
// 这个是一个空类定义,但是它是有效的
class Person{}
// 在此依然可以使用定义的空类创建实例 'xiaoming'
let xiaoming=new Person;
// 通过对比可以发现 xiaoming 的原型和定义的类 Person 的构造函数式相等的
console.log(xiaoming.prototype==Person.constorctor); // true
// 非空类的定义, 构造函数方法, 实例方法, 设置函数, 获取函数, 静态方法 这些都是非必须的,根据需要定义
class Person{
// constructor 构造函数
constructor(){}
// 设置函数
set name{}
// 获取函数
get name{}
// 静态方法
static mySecret(){}
}
在类中使用关键字 constructor 定义块内部创建类的构造函数,在使用 new 关键字创建类的新的实例是,会调用 constructor 这个方法.构造函数在类的定义中不是必须的,例如上面我们定义了一个空类,也是可以的;
使用 new 操作符实例化定义的类等于使用 new 调用了其构造函数,在使用 class 和 new 时,JavaScript 解释器知道需要使用 constructor 函数进行实例化;
在使用 new 关键字调用类的构造函数执行了如下操作
1.在内存中创建一个新的对象
2.这个新对象的 [[prototype]] 指针被赋值为构造函数的 prototype 属性
3.constructor 中的 this 指向新创建的对象
4. 执行 constructor 中的代码,给新的对象添加属性
5. 如果 constructor return 非空对象,则会返回该对象,否则,返回刚创建的新对象
注意: 调用类构造函数必须使用 new 关键词
// 内部执行简单的方法
class Person{
constructor(){
console.log('constructor 执行了');
}
}
// constructor 如果不需要传参, 类后面的括号是可选的
let xiaoming=new Person; // constructor 执行了
// constructor 内部有属性
class Person{
constructor(){
this.uerName="李煜"
}
}
let liyu=new Person;
console.log(liyu.uerName); // 李煜
// 类实例化传入参数,会用 constructor 函数的参数,需要在方法里面接收参数
class Person{
constructor(name){
this.uerName=name
}
}
let luyou=new Person('陆游');
console.log(luyou.uerName);
let baijuyi=new Person;
console.log(baijuyi.uerName); // undefined
// 这种写法,类立即实例化了,类名是可选的,不写页面有关系
let zhangjiuling = new class {
constructor(name) {
this.uerName = name;
}
}("张九龄");
console.log(zhangjiuling.uerName); // 张九龄
在默认的情况下,类构造函数会在执行后返回 this 对象,构造函数返回的对象会被用作实例化的对象,如果新创建的 this 对象没有被引用,那么这个对象会被销毁.如果返回的不是 this 对象,那么这个对象不能通过 instanceof 操作符检测出和类之间有关联,因为这个对象的原型指针没有被修改;
class Person {
constructor(flag) {
this.name = "陶渊明";
if (flag) {
return {
userName: "杜子美",
};
}
}
}
let taoyuanming=new Person;
let duzimei = new Person(true)
console.log(taoyuanming instanceof Person); // true
console.log(duzimei instanceof Person) // false
在使用 new 关键字创建类构造函数的实例后, 类构造函数会成为普通的实例方法(但作为构造函数,仍然可以被 new 调用),因此可以在实例上面引用他;
class Person{}
let litaibai=new Person;
let liboqin=new litaibai.constructor()
litaibai.constructor();
// Class constructor Person cannot be invoked without 'new'
// 在没有“new”的情况下无法调用类构造函数
如果使用对类进行类型检测,会发现他其实是 function 类型;
class Person{}
console.log(typeof Person); // function
console.log(Object.prototype.toString.call(Person)); // [object Function]
声明的类是有 prototype 属性,并且这个原型的 constructor 属性指向类本身;
class Person{}
console.log(Person.prototype);
根据上面打印的结果我们可以看到, Person.prototype 对象里面有一个 constructor属性,是指向我们声明的 Person 类的.
和普通的构造函数一样,可以使用 instanceof 操作符检测构造函数原型是否存在于实例的原型链中,这样我们就可以判断这个对象是不是类的实例
class Person{}
let xinqiji=new Person;
console.log(xinqiji instanceof Person); // true
类是 JavaScript 中的一等公民,可以想其他的对象或者是函数引用一样作为参数传递
let classList=[
class{
constructor(name){
this.name=name;
console.log('大家好! 我的名字是: '+this.name);
}
}
]
function createPerson(classfn,name){
return new classfn(name)
}
let person = createPerson(classList[0],'孟浩然,帅孟孟') // 大家好! 我的名字是: 孟浩然,帅孟孟
console.log(person.name); // 孟浩然,帅孟孟