在 ES6 规范中,引入了 class 的概念。使得 JS 开发者终于告别了,直接使用原型对象模仿面向对象中的类和类继承时代。
但是JS 中并没有一个真正的 class
原始类型, class
仅仅只是对原型对象运用语法糖。所以,只有理解如何使用原型对象实现类和类继承,才能真正地用好 class
。
类语法不会为JavaScript引入新的面向对象的继承模型。
class类概念与语法
ES5之前不存在类的概念,创建对象使用的构造函数,通过new操作符来创建;
为使JS更像面向对象,ES6版本引入class概念,其基本语法:
class Cat{
constructor(name,age){
this.name = name;
this.age = age;
}
Say(){
return '我的名字是' + this.name;
}
}
var cat1 = new Cat('有鱼',2);
console.log(cat1.Say());//我的名字是有鱼
代码解析:
① constructor是一个构造函数方法,创建对象时自动调用该方法
② constructor是默认存在的,可以省略,程序亦可以调用
③ this指的是实例化对象
④类中声明的方法不能加function关键字
⑤方法之间不要用逗号分隔,否则会报错
二、class类与原型的关系
class类本质上就是一个函数,自身指向的就是构造函数,看代码:
console.log(typeof Cat);// function
console.log(Cat.prototype.constructor ===Cat);//true
class类是构造函数的另一种写法,仍然存在prototype方法
console.log(Cat.prototype);//object
可以通过原型prototype修改类方法和新增方法
Cat.prototype.Say = function(){
return return '我的名字是' + this.name+',我是原型prototype声明同样的Say方法,把原有Say方法覆盖了';
}
cat2 = new Cat('年年',5);
console.log(cat2.Say());//我的名字是年年,我是原型prototype声明同样的Say方法,把原有Say方法覆盖了
Cat.prototype.Go = function(){
return return '我的年龄是' + this.age;
}
console.log(cat2.Go());//我的年龄是5
还可以通过Object.assign方法来为对象动态增加方法
Object.assign(Cat.prototype,{
Eat:function(){
return this.name;
},
Run:function(){
return this.age;
}
})
cat3 =new Cat('卡卡',4);
console.log(cat3.Eat());//卡卡
console.log(cat3.Run());//4
也可以使用实例对象的__proto__属性新增类的方法
cat3 =new Cat('卡卡',4);
cat4 =new Cat('楼楼',10);
cat3.__proto__.Play = function(){
return this.name;
}
console.log(cat3.Play());// 卡卡
console.log(cat4.Play());// 楼楼
实例属性和原型属性
实例属性:constructor里面的属性为实例属性,即定义在this对象上
原型属性:除去实例属性都称为原型属性,即定义在class类上
hasOwnProperty方法:可以通过hasOwnProperty()方法进行判断属性是否是实例属性
in操作符:能够访问到属性时返回true,无论是实例属性还是原型属性
class Person(){
constructor(per1,per2){
this.per1 = per1;
this.per2 = per2;
}
Say(){
return per1+per2;
}
}
var box=new Person('年年','有鱼');
console.log(Person.hasOwnProperty("per1"));//true
console.log(Person.hasOwnProperty("per2"));//true
console.log(Person.hasOwnProperty("Say"));//false
console.log("per1" in Person);//true
console.log("per2" in Person);//true
console.log("Say" in Person);//true
console.log("Go" in Person);//false
class类的继承
通过extends关键字实现类的继承
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
getName(){
return this.name;
}
getAge(){
return this.age;
}
}
class Student extends Person{
getName(){
return '我覆盖了父级的方法,'+ this.name;
}
getScore(){
return '我是子级新增的方法,'+this.name;
}
}
var stu1 = new Student('有鱼',2);
console.log(sut1.getName());// 我覆盖了父级的方法,有鱼
console.log(sut1.getAge());//2
console.log(sut1.getScore());// 我是子级新增的方法,有鱼
通过super关键字进行拓展父类构造器或方法
super作用:
①子类使用构造器constructor的时候,必须使用super关键字,用来扩展构造器
②子类同名方法会覆盖父类同名方法,使用super关键字后则可以调用到父类的同名函数
class Person{
constructor(name){
this.name = name;
}
getName(){
console.log('我是父级类getName方法输出来的');
}
getAge(){
console.log(this.age);
}
}
class Student extends Person{
constructor(name,age,sex){
super();//必须先调用super,才能使用constructor,才能使用this对象
this.name = name;
this.age = age;
this.sex = sex;
}
getName(){
super.getName();//调用super,才能调用父类同名函数getName
console.log('我是子级类getName方法输出来的');
}
}
var stu1 = new Student('有鱼',2);
stu1.getName();
// 我是父级类getName方法输出来的
// 我是子级类getName方法输出来的
static关键字的使用
①static关键字是类的方法
②只能通过类名来调用,不能被实例对象调用
③static方法也可以被继承
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
static getAge(){
console.log('我是静态属性static');
}
}
class Student extends Person{}
var stu1 = new Student('有鱼',2);
Person.getAge();//我是静态属性static
Student.getAge();//我是静态属性static
stu1.getAge();//stu1.getAge is not a function
ES6:class
通过类来创建对象,使得开发者不必写重复的代码,以达到代码复用的目的。它基于的逻辑是,两个或多个对象的结构功能类似,可以抽象出一个模板,依照模板复制出多个相似的对象。就像自行车制造商一遍一遍地复用相同的蓝图来制造大量的自行车。
使用 ES6 中的 class
声明一个类,是非常简单的事。它的语法如下:
class Person {
constructor(name){
this.name = name
}
hello(){
console.log('Hello, my name is ' + this.name + '.');
}
}
var xiaoMing = new Person('xiaoMing');
xiaoMing.hello() // Hello, my name is xiaoMing.
xiaoMing
是通过类 Person
实例化出来的对象。对象 xiaoMing
是按照类 Person
这个模板,实例化出来的对象。实例化出来的对象拥有类预先订制好的结构和功能。
ES6 的语法很简单,但是在实例化的背后,究竟是什么在起作用呢?
class
实例化的背后原理
使用 class
的语法,让开发者告别了使用 prototype
模仿面向对象的时代。但是,class
并不是 ES6 引入的全新概念,它的原理依旧是原型继承。
typeof class
== “function”
通过类型判断,我们可以得知,class
的并不是什么全新的数据类型,它实际只是 function
(或者说 object
)。
class Person {
// ...
}
typeof Person // function
Person
的属性并不多,除去用 [[...]]
包起来的内置属性外,大部分属性根据名字就能明白它的作用。需要我们重点关注的是 prototype
和 __proto__
两个属性。
(关于 __proto__
可以在本文的姊妹篇 找到答案)
实例化的原理: prototype
先来讲讲 prototype
属性,它指向一个特殊性对象:原型对象。
原型对象所以特殊,是因为它拥有一个普通对象没有的能力:将它的属性共享给其他对象。
在 ES6 规范 中,对 原型对象 是如下定义的:
object that provides shared properties for other objects
原型对象是如何将它的属性分享给其他对象的呢?
这里使用 ES5 创建一个类,并将它实例化,来看看它的实质。
function Person() {
this.name = name
}
// 1. 首先给 Person.prototype 原型对象添加了 describe 方法 。
Person.prototype.describe = function(){
console.log('Hello, my name is ' + this.name + '.');
}
// 2. 实例化对象的 __proto__ 指向 Person.prototype
var jane = new Person('jane');
jane.__proto__ === Person.prototype;
// 3. 读取 describe 方法时,实际会沿着原型链查找到 Person.prototype 原型对象上。
jane.describe() // Hello, my name is jane.