本文深入解析ES6中Class类的核心用法,对比ES5传统构造函数写法。详细阐述constructor构造器、静态方法static、私有属性提案及new.target属性。重点讲解继承机制中super关键字作为函数和对象的不同行为,以及this指向问题的解决方案,包括bind、箭头函数和Proxy代理。
ES6 Class 基础概念与ES5对比
在JavaScript语言中,生成实例对象的传统方式是通过构造函数。这种方式与C++和Java等传统面向对象语言存在较大差异。ES6引入了更接近传统语言语法的class(类)概念,将其作为对象的模板,通过class关键字可以定义类。
ES6中的class与ES5面向对象编程主要存在以下区别:
第一,语法写法不同,ES6直接使用class关键字。
第二,当使用new创建实例时,默认会执行constructor方法,且该方法默认返回实例对象(this)。不过,constructor也可以显式返回另一个对象。
第三,类的所有方法都定义在prototype属性上,这些方法不可枚举,且每个方法定义结束后不能使用分号。
第四,类的调用必须通过new关键字实例化,且类内部默认启用严格模式。
第五,类不存在变量提升,因此必须先声明类,之后才能调用。
第六,class中的this默认指向当前类本身。
第七,类的静态方法使用static关键字定义,无需实例化即可直接通过类名调用。
第八,关于实例属性和静态属性的写法:实例属性可以直接在类内部使用等号(=)定义,或者写在constructor方法中;静态属性则需在实例属性前加上static关键字。
第九,类的继承使用extends关键字,其继承机制与ES5完全不同。
ES5的继承原理是:先new出子类的实例对象this,再将父类的方法和属性添加到子类的this上(通过parents.call(this)实现)。
ES6的继承原理是:先创建父类的实例对象this。因此,在子类的constructor中访问父类属性时,必须先调用super()方法,然后再通过子类的constructor修改this。
第十,类可以继承原生构造函数,这是ES5所不支持的。
Class 语法糖与表达式写法
1. 一般写法(ES5与ES6对比)
//一.ES5写法:
function Animate(name){
this.name = name;
}
Animate.prototype.getname = function(){
console.log(this.name)
}
var p =new Animate("lity");
p.getname();
//二.ES6,面向对象的写法,calss,
class Person{
//constructor():构造方法是默认方法,new的时候回自动调用,如果没有显式定义,会自动添加
//1.适合做初始化数据
//2.constructor可以指定返回的对象
constructor(name,age){
this.name = name;
this.age = age;
}
getval(){
console.log(`你是${this.name},${this.age}岁`);
}
}
var c1 = new Person("lity",20);
c1.getval();
ES6的class本质上只是一个语法糖,其绝大部分功能在ES5中都能实现。
需要注意:class类的本质仍然是一个函数,类本身指向构造函数。
typeof Person //function
Person === Person.prototype.constructor // true
我们可以使用Object的一些属性或方法来检测用ES6编写的实例对象。
//1.查看实例对象c1的__proto__是否指向Person的原型(Person.prototype)
console.log(c1.__proto__==Person.prototype)//true
console.log(c1.__proto__)//原型对象的所有方法
//2.isPrototypeOf:检测实例对象是否是某个函数的原型
console.log(Person.prototype.isPrototypeOf(c1));//true
//3.constructor:查看某个对象的构造函数
console.log(c1.constructor);
//4.hasOwnProperty:检测某个属性是否是自己的属性;不是原型对象上的属性和方法
console.log(c1.hasOwnProperty("name"))//true;
//5.in:通过in可以检测属性是否在自己中(this)或者是原型中存在
console.log("getval" in c1)//原型上存在,true
console.log("name" in c1)//constructor(自己上存在),true
//6.自定义检测属性是否是存在
function hasproperty(attr,obj){
return obj.hasOwnProperty(attr)&&(attr in obj);
}
console.log(hasproperty("name",c1));//true;
2. 表达式写法
//class表达式
const Myclass = class Me{//这里的Me是没有作用的
constructor(name,jog){
this.name = name;
this.jog = jog;
}
getval(){
console.log(`name is ${this.name},job is a ${this.jog}`);
}
}
var obj1 = new Myclass("lylt","teacher");
obj1.getval();
私有成员与静态方法
3. class的私有方法(ES6未提供标准写法)和私有属性(ES6未提供标准写法,提案中使用#符号识别)。
所谓私有方法和私有属性,是指这些成员只能在类的内部使用,无法在类外部直接调用。
4. ES6规定Class类没有静态属性,只有静态方法:static。
所谓静态,是指不需要实例化对象,可以直接通过类名调用。
class Foo {
static classMethod() {
return 'lity';
}
}
console.log(Foo.classMethod()) // 'hello'
new.target 与 this 指向问题
5. new.target属性
new是构造函数生成实例的命令,ES6为new提供了一个属性.target。
该属性返回通过new命令实例对象的class(构造函数),通常用于类的内部。
//ES5:原始写法对象
function objtarge(name){
if(new.target==undefined){
throw new Error("必须实例化对象");
}else{
this.name = name
}
}
var targets = new objtarge("litys");
console.log(targets.name);//litys
//es6写法:class内部使用new.target,返回当前的calss
class caltartget{
constructor(name){
console.log(new.target==caltartget);//true
if(new.target!==caltartget){
throw new Error("实例化对象不是caltrget");
}else{
this.name = name;
}
}
}
var caltart = new caltartget("lity");
console.log(caltart.name);//lity
6. this指向问题
类的方法内部如果包含this,它默认指向类的实例。但必须非常小心,一旦单独使用该方法,很可能报错。
如下示例
class Logger {
printName(name = 'there') {
this.print(`Hello ${name}`);
}
print(text) {
console.log(text);
}
}
const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined
分析以上示例:prinName的方法中this默认指向类Logger,但如果将该方法单独调用,就会报错。此时this会指向运行环境,因此因找不到this.print()方法而报错。
针对以上this指向问题的解决方法:
(1)使用bind(this)绑定上下文。
(2)使用ES6的箭头函数()=>{}。
(3)使用Proxy代理。
//1.bind()方法
class Logger {
constructor() {
this.printName = this.printName.bind(this);
}
// ...
}
//2.箭头函数 ()=>{}
class Logger {
constructor() {
this.printName = (name = 'there') => {
this.print(`Hello ${name}`);
};
}
// ...
}
//3. Porxy()
.................
存取器方法与继承机制
7. class的get()和set()方法
与ES5一样,在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,从而拦截该属性的存取行为。
class MyClass {
constructor() {
// ...
}
get prop() {// 使用 get 拦截了该方法的返回值
return 'getter';
}
set prop(value) {//当对该方法赋值时能获取到该赋值
console.log('setter: '+value);
}
}
let obj = new MyClass();
obj.prop = 123;
// setter: 123
inst.prop
// 'getter'
8. 继承机制
Class可以通过extends关键字实现继承,这比ES5通过修改原型链实现继承要清晰和方便得多。
//es5 的继承
//父类
function Person(name,sex){
this.name = name;//属性
this.sex = sex;//属性
}
//定义一个原型方法
Person.prototype.show = function(){
console.log("我的姓名是"+this.name+"==="+"我的性别是"+this.sex)
}
//子类
function Worker(name,sex,job){
//构成函数伪装:使用call()方法绑定this,伪装继承父级的属性
Person.call(this,name,sex);
this.job = job;
}
//继承父类的原型方法:(介绍三种方法)
//写法一:通过遍历父级的原型一个个赋给子级的原型(es5 的原型是可枚举的,es6的不可以枚举)
(var i in Person.prototype){
Worker.prototype[i] = Person.prototype[i];
}
//写法:重新new一个父级对象赋给子级的原型
Worker.prototype = new Person();
Worker.prototype.constructor = Worker;
//写法三:创建一个原型对象赋给子级的原型;(es5 推荐)
Worker.prototype = Object.create(Person.prototype);
Worker.prototype.constructor = Worker;
var workers = new Worker("小明","男","job")
//es6 的继承
class Person{
constructor(name,sex){
this.name = name;//属性
this.sex = sex;//属性
}
}
class Worker extends Person{
constructor(name,sex,job){
super();
this.job =job;
}
}
var workers = new Worker("小明","男","job")
8.1: super关键字:在子类中不同情况下用法不同,既可以当作函数使用,也可以当作对象使用。
(1):super作为函数,只能在constructor中使用,代表父类,返回子类的this。
(2):super作为对象,在普通函数中,super指向父类的原型对象,可以访问原型对象的属性和方法。注意,父类的实例的属性和方法是访问不了的。
(3):super作为对象,在静态方法中,super指向的是父类,不是父类的原型对象。
示例分析如下:
//父类
class Aniamte{
constructor(){
if(new.target == Aniamte){
throw new Error("本类不能实例化,只能有子类继承");
}
}
//静态方法
static getval(mgs){
console.log("父类的static",mgs)
}
//普通方法
setname(){
console.log("该方法有子类重写")
}
}
//子类
class Dog extends Aniamte{
constructor(){
super();//调用此方法,this才用可以用,代表父类的构造函数,返回的却是子类
//super() ==父类.prototype.constructor.call(子类/this)
console.log(this)//Dog {}
this.age = 20;
}
//静态方法,super在静态方法中作为对象使用,指向父类;
static getval(mgs){
super.getval(mgs)//父类的static niceday
console.log("子类的static",mgs)//子类的static niceday
}
setname(name){
//普通方法,super作为对象使用,指向父类的原型对象,父类.prototype;
super.setname();//该方法有子类重写
this.name = name;
return this.name
}
};
Dog.getval("niceday");//静态方法,直接调用
//var parAni = new Aniamte();//报错
var dogs = new Dog();//new 一个示例对象
dogs.setname("DOYS");////DOYS
8.2. 原生构造函数的继承,ES5不支持,ES6利用extend可以继承原生构造函数。
//ESMAScript的构造函数有以下几种
/* Boolean()
* Unmber()
* String()
* Array()
* Date()
* Function()
* RegExp()
* Error()
* Object()
*/
//实例一:自定义类Myarray 继承了原生的数组的构造函数,拥有原生数组的属性和方法了
class Myarray extends Array{
constructor(){
super();
console.log(this.constructor.name)//Myarray
}
}
var myarr = new Myarray();
console.log(Object.prototype.toString.call(myarr));//[object Array]
myarr.push(1,2,1);
console.log(myarr.length)//3
总结
以上所述是小编给大家介绍的es6新特性之 class 基本用法解析,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!