类与对象

类(class)是对象(Object)的模板,定义了同一组对象共有的属性和方法
类更加的抽象,对象更加的具体

ES5的 类

类的名称使用 大写,是为了让别人知道这不是一个普通的方法

  1. function People() {
  2. this.name = '潜龙'
  3. this.age = 34
  4. }
  5. // 对象实例化
  6. const p1 = new People()
  7. console.log('p1', p1);

image.png

为了提高对象实例化的复用性,对象类属性的赋值采用形参的方式

function People(name, age) {
  this.name = name
  this.age = age
}

let p2 = new People('潜龙', 24);
console.log('p2', p2)
// p2 People {name: "潜龙", age: 24}

People 类 中 等号左边 是 类里面的属性, 右边是 传入的形参

上面的 p1 和 p2 就是 实例化后的 对象, 而 People 则是抽象类

类里面 this 指向

function People(name, age) {
  console.log(this)
  this.name = name
  this.age = age
}

let p1 = new People('潜龙', 24);
let p2 = new People('simpleSky12', 34);
console.log(p1, p2);

image.png

this 指向的 是, 实例化对象后 的本身

类中方法的定义

一般 es5 中定义 类的方法,不会将方法放在构造函数中定义,也就是通过 this.方法名() 的方式去定义,这样的做法会损耗性能,因为每次我们 new 实例化的时候都会渲染方法,但是可能我们用不到这个方法,这就会导致资源的浪费。
所以常规的做法是 将 类的方法,通过 原型链的方式 去定义, 即 prototype

function People(name, age) {
  this.name = name
  this.age = age
}

People.prototype.showName = function () {
  console.log('名字是:' + this.name);
};

let p1 = new People('潜龙', 24);
let p2 = new People('simpleSky12', 34);
p1.showName();
p2.showName();
//名字是:潜龙
//名字是:simpleSky12

常见的内置对象

常见的 内置对象 为 String, Object, Array, Math

let str = new String('潜龙');
console.log('str:', str);

let arr = new Array(1, 2, 3);
console.log('arr: ', arr);

let obj = new Object();
obj.name = '潜龙'
console.log('obj: ', obj);

// 静态方法
console.log(Math.max(4, 10));
console.log(Math.random() * 10);

image.png

Math对象下 方法 的调用方式中可以看出, Math 下的方法 属于静态方法,不需要对 Math 实例化,即可直接使用,很便捷

静态属性与静态方法

静态属性 是 直接定义在 类下面,而实例属性 则是定义在 类的 构造函数中
实例属性 => 构造函数
实例属性,会在类每次的实例化中,重复的调用

// People 类的 构造方法
function People(name, age) {
  // 实例属性
  this.name = name
  this.age = age
  // People 每实例化一次,静态属性 count 就自增一
  People.count++
}

// 静态属性
People.count = 0

console.log(People.count)
console.log(new People('潜龙', 24));
console.log(new People('simpleSky12', 34));
console.log(People.count);

image.png

静态方法是直接定义在类下面的,和静态属性类似,但是实例方法是定义在 类的 原型链上的

function People(name, age) {
  this.name = name
  this.age = age
}
People.count = 0

// 静态方法
People.getCount = function () {
  console.log('当前实例化的对象个数:', People.count)
};

// 实例方法
People.prototype.getAge = function () {
  console.log('年龄是:', this.age);
}

People.getCount() //当前实例化的对象个数: 0
let p1 = new People('潜龙', 24);
p1.getAge()  //年龄是: 24

静态方法只能调用 静态属性,如果调用 this下的属性,会返回 undefined, 因为 构造函数中的this指向 的 是实例化对象,我们调用 静态方法的时候,没有实例化对象

ES5 中 类的 继承

构造函数继承

这个方法只能继承 构造函数中 this 的属性,不能继承 类中的声明的方法

主要使用的方法 是 call() , 来改变 this 指向, 将原来子类中的this指向,改成 父类的this指向


// 父类
function Animal(name) {
  this.name = name
}

Animal.prototype.showName = function () {
  console.log('名字是:' + this.name);
};

// 子类
function Dog(name, color) {
  Animal.call(this, name) // 继承父类属性
  this.color = color
}

let dog1 = new Dog('旺财', 'white');
console.log('dog1', dog1);
dog1.showName()

image.png
这里我们可以看出来 dog1 对象,只是成功继承了 父类 Animal 中 构造函数中 关于 this下属性 name 的继承,并没有成功继承到 父类 Animal 中的方法showName()

原型继承

改进方法,利用 对象的 prototype 原型来进行 方法和构造方法的继承

这个方式只能继承 父类的方法,无法继承 父类 的 this 属性,即构造函数

// 父类
function Animal(name) {
  this.name = name
}
Animal.prototype.showName = function () {
  console.log('名字是:' + this.name);
};

// 子类
function Dog(name, color) {
  this.color = color
}

Dog.prototype = new Animal()
Dog.prototype.constructor = Dog

let dog2 = new Dog('小白', 'white')
console.log(dog2);
dog2.showName()

image.png
从控制台的 打印结果可以看出 父类 的构造方法 对于 name的设置并没有生效

组合式继承

这个方式是将 原型继承和 构造函数继承结合在一起,这样就能既可以 继承 类中的 方法,也能继承构造函数中的属性了

// 父类
function Animal(name) {
  this.name = name
}
Animal.prototype.showName = function () {
  console.log('名字是:' + this.name);
};

// 子类
function Dog(name, color) {
  Animal.call(this, name)
  this.color = color
}

Dog.prototype = new Animal()

let dog3 = new Dog('小白', 'white')
console.log(dog3);
dog3.showName()

image.png

ES6 中的类

class 就是 原来 es5 中声明类的 一种语法糖,更好更方便的去定义类

class People {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  showName() {
    console.log('名字:', this.name)
  }
}

let p1 = new People('潜龙', 24);
console.log('p1', p1);
p1.showName()

image.png

类的继承

我们通过 extend 来继承父类, 构造函数中 通过 spuer() 方法 继承父类属性

class People {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  showName() {
    console.log('名字:', this.name)
  }
}

class Coder extends People{
  constructor(name, age, company) {
    super(name, age); // 将 父类中的 name 和 age 继承下来
    this.company = company
  }

  showCompany() {
    console.log('工作的公司:', this.company);
  }
}

let c1 = new Coder('simpleSky12', 34, '慕课网');
console.log('c1', c1);
c1.showName();
c1.showCompany();

image.png

类中的 get 与 set 方法

class People {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this._sex = -1;
  }

  get sex() {
    return this._sex;
  }

  set sex(val) {
    this._sex = val;
  }

  showName() {
    console.log('名字:', this.name)
  }
}

let p1 = new People('潜龙', 24);
p1.sex = '男'
console.log(p1.sex);

这里使用 set() 以及和 sex 别名 _sex 相结合 是为了起到一个 避免死循环的出现

image.png
image.png
当我们不使用 _sex 当做 sex的别名的时候,就会出现死循环,因为 this.sex 等于是触发了 get方法,this.sex = ‘’’ 相当于触发了 set方法,所以就会导致死循环的出现,内存溢出

get() 与 set() 方法的优势 就是,我们在使用 get与set 方法的时候可以对其进行业务操作,这是我们在 构造函数中 使用 this.xxx = xxx 所达不到的效果,例如我们可以 对属性的设置 和获取 进行一次拦截

class People {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this._sex = -1
  }

  get sex() {
    if (this._sex === 1) {
      return 'male'
    }else if (this._sex === 0) {
      return 'female'
    }
    return 'error';
  }

  set sex(val) {
    if (val === 0 || val === 1) {
      this._sex = val
    }
  }

  showName() {
    console.log('名字:', this.name)
  }
}

let p2 = new People('潜龙', 24);
console.log(p2.sex); // error

p2.sex = 1;
console.log(p2.sex); // male

一开始因为 没有使用 set() 方法去设置 _sex 属性的值,默认值是 -1, 所以返回的是 error
后面利用 set方法 设置了 _sex 为 1, 当我们调用get方法时,返回的就是 male

get 与 set 的继承

People 中定义的 get 与 set 方法可以看做是 顶层属性,是可以被子类继承的

class People {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this._sex = -1
  }

  get sex() {
    if (this._sex === 1) {
      return 'male'
    }else if (this._sex === 0) {
      return 'female'
    }
    return 'error';
  }

  set sex(val) {
    if (val === 0 || val === 1) {
      this._sex = val
    }
  }
}

class Coder extends People {
  constructor(name, age) {
    super(name, age);
  }
}

let c2 = new Coder('simpleSky12', 34);
c2.sex = 1
console.log(c2.sex); // male

静态属性与静态方法

静态方法只能通过类名.的方式来调用

ES6 中 主要是通过 使用 关键字 static 来定义静态的属性 和 静态方法

静态方法

class Count{
  static getCount() {
    return 5
  }
}
console.log(Count.getCount()) // 5

静态方法是否可以被继承

class Num extends Count{

}
console.log(Num.getCount()) // 5

静态方法支持继承

静态方法错误的调用

let count = new Count()
console.log(count.getCount())

let num = new Num();
console.log(num.getCount())

image.png
实例化后的对象是无法调用 类中定义的 静态方法

静态属性

可以在类中直接使用 static 定义 静态属性吗?

class Count {
  static count = 5
}

console.log(Count.count) // 5

image.png
static 暂时还不能用来修饰 静态属性,

如何声明 静态属性?

定义方式和 ES5 相同

class Count {
}
Count.count = 66

console.log(Count.count) // 6