class是什么
- class是定义类的方法。ES6之前用构造函数的方式定义类,ES6引入了class。
- class是语法糖。
- class内部默认是严格模式。
使用
传统方式定义一个类
function Point1(x,y){
this.x = x
this.y = y
}
Point1.prototype.add = function(){
return this.x + this.y
}
var p1 = new Point1(3,9)
console.log(p1.add())
class方式定义一个类
class Point2{
constructor(x, y){
this.x = x
this.y = y
}
add(){ // 这里的add方法实际上是定义在原型上的。
return this.x + this.y
}
}
var p2 = new Point2(4,77)
console.log(p2.add())
类的数据类型
console.log(typeof Point1) // function
console.log(typeof Point2) // function
类的数据类型就是函数,类本身就是指向函数的
类的所有方法都是定义在类的prototype上的
class Point3{
say(){
//
}
eat(){
//
}
}
// 等同于以下
Point3.prototype.say = function(){
//
}
Point3.prototype.eat = function(){
//
}
// 等同于:
Point3.prototype = {
say(){},
eat(){},
}
所以:在类上调用方法,实际上是在原型上调用方法。
利用Object.assigin可以向类添加多个方法
Object.assign(Point3.prototype, {
say(){},
eat(){}
})
严格模式:在类的内部默认是严格模式,整个ES6都是严格模式
constructor方法
- constructor方法是类的默认方法。new生成实例时,会默认调用constructor方法
- 一个类必须有constructor方法,如果没有显式的声明,就会自动创建一个空的constructor方法
class Con{
}
// 等同于
class Con{
constructor(){
//
}
}
// JavaScript引擎会自动给Con类添加一个constructor方法, 这个方法默认会返回实例对象(this)
类的实例对象
- 和ES5一样,实例的属性除非显式的定义在本身(this上),否则都是定义在原型上的
- 所以类的所有实例都共享原型对象
var p1 = new Point2(1,2)
var p2 = new Point2(3,5)
console.log(p1.__proto__ === p2.__proto__) // true
// 因为p1.__proto__ === Point2.prototype
// p2.__proto__ === Point2.prototype
class表达式的形式
var person = new class{
constructor(name){
this.name = name
}
sayName(){
console.log(this.name)
}
}("小可爱")
person.sayName()
class不存在变量提升现象,必须要先声明后使用
this指向问题
如果方法里有this,this指向的是实例
类的静态方法
- 啥叫静态方法:类是实例的原型,所有类的方法都会被实例继承。但是,如果在一个方法前面加上static关键字,就代表这是一个静态方法,这个静态方法不会被实例继承,而是通过类直接调用
class Foo{
static say(){
console.log("hello!!")
}
eat(){
console.log("eat")
}
}
Foo.say(); // hello!!
var foo = new Foo();
// foo.say(); // 报错,这里报foo.say is not a function
// 这里说明静态方法是不可以被继承的
foo.eat(); // eat,这里说明非静态方法是可以被实例继承的。
如果静态方法有this关键字,静态方法的this指向的是类,而不是实例
静态方法可以与非静态方法可以重名
class Bar{
static say(){
this.foo()
}
foo(){
console.log("no foo")
}
static foo(){
console.log("static foo")
}
}
Bar.say() // static foo
var bar = new Bar()
// bar.say() // 报错,因为say是静态的方法,实例不能调用
// 可以看出:静态方法可以与非静态方法重名
综上所述
// 定义一个类
class Point{
// constructor方法,this代表实例对象
constructor(x,y){
this.x = x
this.y = y
}
// 类的方法,除了constructor方法,都定义在类的prototype属性上
add(){ // 定义在原型上
return this.x + this.y
}
}
const p = new Point(21,10);
const p2 = new Point(1,8);
console.log(typeof Point); // 'function'
// 这里在类调用方法时,调用的是原型上的方法
console.log(p.add()); // 31
console.log(Point.prototype.constructor === Point); // true
console.log(Point.prototype === p.__proto__); // true
// 这里在类调用方法时,调用的是原型上的方法
console.log(p.add === Point.prototype.add); // true
// 关于实例对象的属性方法问题
// 实例属性除非定义在本身(this对象)上,否则都定义在原型上
console.log(p.hasOwnProperty("x")); // true
console.log(p.hasOwnProperty("y")); // true
console.log(p.hasOwnProperty("add")); // false
console.log(p.__proto__.hasOwnProperty("add")); // true
// 所有实例都共享同一个原型对象
console.log(p.__proto__ === p2.__proto__); // true
// 所以说,可以通过p.__proto__来给类添加方法,但不推荐
class原型链的方式继承
class的继承是通过extends关键字实现的
基本语法
class Dog extends Animal{}
这里的Dog类,通过extends关键字,继承了Animal类所有的属性和方法,因为没有代码,所以相当于复制了一份
给继承添加代码
class Animal{
constructor(name, age){
this.name = name
this.age = age
}
sayName(){
return "it's name is " + this.name
}
}
class Cat extends Animal{
constructor(name, age, color){
super(name, age) // super关键字调用父元素的方法
this.color = color
}
sayColor(){
return "the color is " + this.color
}
}
var bosimiao = new Cat("bosi", 2, 'yellow')
console.log(bosimiao.sayName()) // it's name is bosi
console.log(bosimiao.sayColor()) // the color is yellow
关于super关键字:
1、在子构造函数中,必须用super方法,否则新建对象的时候会报错
2、子构造函数中,使用this前必须先用super,否则会报错
报错例子1
报错情况:extends继承了,但是没有用super
class Person{
constructor(name, age){
this.name = name
this.age = age
}
sayName(){
return this.name
}
}
class Worker extends Person{
constructor(name, age, job){
this.name = name
this.age = age
this.job = job
}
sayJob(){
return this.job
}
}
var xiaoming = new Worker("xiaoming", 28, "teacher")
console.log(xiaoming.sayName())
console.log(xiaoming.sayJob())
解决:在constructor中,先用一下super
constructor(name, age, job){
super()
this.name = name
this.age = age
this.job = job
}
报错例子2:先写了this,然后调用的super()
constructor(name, age, job){
this.name = name
super()
this.job = job
}
为啥这两种情况会报错呢?
ES6的继承机制:先创建父类的实例对象,再创建子类的实例,然后修改父类的this
子类没有自己的this对象,而是继承父类的this对象,然后对其加工。如果不调用super方法, 就得不到this对象
子类调用父类的方法:
class Person{
constructor(name, age){
this.name = name
this.age = age
}
sayName(){
return "person name is " + this.name
}
}
class Worker extends Person{
constructor(name, age, job){
super(name, age)
this.job = job
}
}
var p1 = new Worker("lanlan", 18, "student")
console.log(p1.sayName()) // worker name is lanlan
通过原型链实现继承
原理
我们每创建一个函数,都有一个prototype属性(自带的),这个prototype属性是一个指针,指向了一个对象,这个对象是原型对象。
这个原型对象上有一个constructor属性。这个constructor属性是一个指针,指向了构造函数。
构造函数的new出来的实例对象,有一个__proto__属性(内部属性),这个属性指向原型对象。
所以实例也能访问原型对象的属性和方法。
原型对象通过constructor指针指向了构造函数
所有的构造函数也是Function的实例
所有构造函数的原型对象是Object的实例
所以,实例,构造函数,原型对象的关系是:
[].__proto__ === Array.prototype === 原型对象
[].__proto__.constructor === Array
Animal.prototype.constructor === Animal
prototype:每个函数都有显式原型prototype
__proto__:每个对象实例都有隐式原型__proto__
constructor:构造函数的原型有一个constructor属性,指向创建该对象的构造函数
演示constructor
var arr = [];
console.log(arr.constructor === Array); // true
题目
手写class B继承class A
ES5的方式
function A(){
this.superType = "A"
}
A.prototype.getSuperType = function(){
console.log(this.superType)
}
function B(){
this.type = "B"
}
B.prototype = new A();
var b = new B();
b.getSuperType();
ES6的方式
class A{
constructor(superType){
this.superType = superType
}
getSuperType(){
return "superType is " + this.superType
}
}
class B extends A{
constructor(superType, type){
super(superType)
this.type = type
}
}
let b = new B("superType", "type")
console.log(b.getSuperType()); // superType is superType
网友评论