类的声明
- es5中
function Class1(){
this.name = 'class1'
}
- es6中
class Class2(){
constructor(){
this.name = 'class2'
}
}
类的继承
借助构造函数继承
function Teacher(){
this.title = '老师'
}
Teacher.prototype.beat = '打学生'
function Student(){
Teacher.call(this) <-
this.type = '学生'
}
var s1 = new Student()
console.log(s1)
//Student {title: "老师的", type: "学生"}
console.log(s1.beat) <-
//undefined
原理:通过改变父类构造函数this的指向,使其指向子类的实例 缺点:无法继承父类原型上的方法和属性,只能继承父类构造函数上的方法属性
借助原型链实现继承
function Teacher2(){
this.title = '老师'
this.array1 = [1,2,3]
}
Teacher2.prototype.beat = '打学生'
function Student2(){
this.type = '学生'
this.array2 = [7,8,9]
}
Student2.prototype = new Teacher2() <-
Teacher2.prototype.say() = function(){
console.log('666')
}
var s2 = new Student2()
console.log(s2)
//Student2 {type: "学生"}
// -type: "学生"
// -__proto__: Teacher2
// -title: "老师"
// -__proto__:
// -beat: "打学生"
// -constructor: ƒ Teacher2()
// -__proto__: Object
console.log(s2.beat)
// 打学生
s2.say()
// 666
原理:子类的原型引用了父类的实例,此时JS会顺着原型链在
子类实例->子类原型 === 父类实例->父类原型中依次寻找方法属性
能通过instanceof
给原型添加方法的语句一定要放在原型指向了父类实例之后
var s3 = new Student2()
var s4 = new Student2()
s3.title = '老师76'
console.log(s3.title) //老师76
console.log(s4.title) //老师
s3.__proto__.title = '老师76'
console.log(s3.title) // 老师76 <-
console.log(s4.title) // 老师76 <-
s3.type = '毕业了的学生'
console.log(s3.type) //毕业了的学生
console.log(s4.type) //学生
s3.array1.push('abc')
console.log(s3.array1) // [1, 2, 3, "abc"] <-
console.log(s4.array1) // [1, 2, 3, "abc"] <-
s3.array2.push('def')
console.log(s3.array2) // [7, 8, 9, "def"]
console.log(s4.array2) // [7, 8, 9,]
//罪魁祸首:
s3.__proto__ === s4.__proto__ // true
记住JS顺着原型链往上寻找方法和属性,先在子类实例中(由子类构造函数获得的属性方法)找,再去子类的原型(已经指向了父类实例)中找......同时父类实例又有自己的
__proto__...真是绕.....
缺点:子类的实例共享同一个原型,修改一个子类的属性另一个也会改变
组合继承
function Teacher3(){
this.title = '老师'
this.array3 = [1,2,3]
}
function Student3(){
Teacher3.call(this) <-
this.type = '学生'
}
Student3.prototype = new Teacher3() <-
var s5 = new Student3()
var s6 = new Student3()
s5.array3.push(4)
console.log(s5.array3) // [1,2,3,4]
console.log(s6.array3) // [1,2,3]
缺点,实例化了三次
优化版本1
function Teacher4 () {
this.title = '老师'
this.array4 = [1,2,3]
}
function Student4 () {
Teacher4.call(this) // <-
this.type = '学生'
}
Student4.prototype = Teacher4.prototype // <-
var s7 = new Student4()
var s8 = new Student4()
s7.array3.push(4)
console.log(s7.array3) // [1,2,3,4]
console.log(s8.array3) // [1,2,3]
只在实例化的时候才实例类
__上述两种共同的缺点:最终产生的实例的构造函数都会指向父类构造函数
原因:
Student4.prototype = Teacher4.prototype或者Student3.prototype = new Teacher3()__
s5.constructor = function Teacher3(){...}
s7.constructor = function Teacher4(){...}
优化版本2
function Teacher5 () {
this.title = '老师'
this.array5 = [1,2,3]
}
function Student5 () {
Teacher5.call(this) // <-
this.type = '学生'
}
Student5.prototype = Object.create(Teacher5.prototype) // <-
// Object.creat() 创造的中间对象的原型对象就是传入的参数
// 此时中间对象的原型也就是父类的原型
// 中间对象通过类似在原型链上“加一段”的方式把父子类的构造器隔开
var s9 = new Student5()
console.log(s9.constructor) //依然为父类构造器
最终版本
function Teacher6 () {
this.title = '老师'
this.array5 = [1,2,3]
}
function Student6 () {
Teacher6.call(this) // <-
this.type = '学生'
}
Student6.prototype = Object.create(Teacher6.prototype) // <-
Student6.prototype.constructor = Student6 // <-!!!!!
var s10 = new Student6()
console.log(s10 instanceof Teacher6,s10 instanceof Student6)
// true true
console.log(s10.constructor) // Student5 () {..}
KILL IT!