学习了coderwhy的JavaScript高级语法视频课的笔记
如有错误或者不合适的地方,敬请见谅,欢迎指出和拓展,谢谢各位了
一、面向对象的继承
我们先回顾一下之前讲的几种面向对象继承的方法,之后就会学习ES6中类的使用和类实现面向对象继承的方法。
1、原型链继承
function Person() {
this.name = 'Person'
this.arr = []
}
Person.prototype.eat = function () {
console.log(this.name + '吃饭了')
}
function Student() {
this.sno = 123456789
}
Student.prototype = new Person()
//因为Student.prototype指向的是new Person()实例对象,所以没有constructor,指向也就不会是Student构造函数
//到时候就会根据原型链找Person.prototype的constructor,指向的是Person构造函数
//这里就是解决constructor这个问题
Object.defineProperty(Student.prototype, 'constructor', {
enumerable: false, //不可枚举
configurable: true, //可删除/重新定义
writable: true, //可赋值
value: Student
})
Person.prototype.study = function () {
console.log(this.name + '学习了')
}
var stu = new Student()
stu.eat() //Person吃饭了
console.log(stu.__proto__.constructor) //[Function: Student]
console.log(stu) //Student { sno: 123456789 }
console.log('------------------')
var stu2 = new Student()
stu.arr.push('引用类型,被多个对象共享')
console.log(stu.arr) //[ '引用类型,被多个对象共享' ]
console.log(stu2.arr) //[ '引用类型,被多个对象共享' ]
//弊端:
//1、继承的属性在子类中不可枚举,也就是打印出来看不到
//2、父类中,引用类型,被多个对象共享
//3、不能给Person传递参数
2、构造函数继承
function Person(name) {
this.name = name
}
Person.prototype.eat = function () {
console.log(this.name + '吃饭了')
}
function Student(name) {
Person.call(this, name)
this.sno = 123456789
}
Student.prototype.study = function () {
console.log(this.name + '学习了')
}
var stu = new Student('xxx')
console.log(stu) //Student { name: 'Person', sno: 123456789 }
console.log(stu.__proto__) //{ study: [Function (anonymous)] }
stu.study() //xxx学习了
stu.eat() //报错:stu.eat is not a function
//弊端:1、不能继承父类Person的原型属性和方法
3、组合式继承
上面两种方式的组合
function Person(name) {
this.name = name
}
Person.prototype.eat = function () {
console.log(this.name + '吃饭了')
}
function Student(name, sno) {
Person.call(this, name)
this.sno = sno
}
Student.prototype = new Person()
//因为Student.prototype指向的是new Person()实例对象,所以没有constructor,指向也就不会是Student构造函数
//到时候就会根据原型链找Person.prototype的constructor,指向的是Person构造函数
//这里就是解决constructor这个问题
Object.defineProperty(Student.prototype, 'constructor', {
enumerable: false, //不可枚举
configurable: true, //可删除/重新定义
writable: true, //可赋值
value: Student
})
Person.prototype.study = function () {
console.log(this.name + '学习了')
}
var stu = new Student('xxx', 123456789)
console.log(stu) //Student { name: 'xxx', sno: 123456789 }
console.log(stu.__proto__.constructor) //[Function: Student]
stu.eat() //xxx吃饭了
//弊端:1、Person至少被调用了两次
//2、在new Person()中,就在实例中添加了name:undefined,这是不必要的属性
4、原型式继承—对象
var obj = {
eat: function () {
console.log('该干饭了')
}
}
//第一种方式
function inheritProrotype(o) {
var newObj = {}
// newObj.__proto__ = o
Object.setPrototypeOf(newObj, o)
return newObj
}
var info = inheritProrotype(obj)
//第二种方式
function inheritProrotype2(o) {
var newObj = function Foo() {}
newObj.prototype = o
return new newObj()
}
var info2 = inheritProrotype2(obj)
// 第三种方式—ES6
var info3 = Object.create(obj)
info.eat() //该干饭了
info2.eat() //该干饭了
info3.eat() //该干饭了
5、寄生式继承
var obj = {
name: 'obj对象',
eat: function () {
console.log('该干饭就干饭')
}
}
function createStudent(sno) {
// 这里用的是原型式继承中第三中方法—ES6
var newObj = Object.create(obj)
newObj.sno = sno
return newObj
}
var stu = createStudent(132456789)
console.log(stu) //{ sno: 132456789 }
stu.eat() //该干饭就干饭
6、寄生组合继承
function Person(name) {
this.name = name
}
Person.prototype.eat = function () {
console.log('去吃饭吧')
}
function Student(name, sno) {
Person.call(this, name)
this.sno = sno
}
//立即执行函数
;(function (Student, Person) {
Student.prototype = Object.create(Person.prototype)
//因为Student.prototype指向的是new Person()实例对象,所以没有constructor,指向也就不会是Student构造函数
//到时候就会根据原型链找Person.prototype的constructor,指向的是Person构造函数
//这里就是解决constructor这个问题
Object.defineProperty(Student.prototype, 'constructor', {
enumerable: false, //不可枚举
configurable: true, //可删除/重新定义
writable: true, //可赋值
value: Student
})
})(Student, Person)
var stu = new Student('xxx', 123456789)
console.log(stu) //Student { name: 'xxx', sno: 123456789 }
stu.eat() //去吃饭吧
二、ES6中类的使用
1. class定义类的方式
//类的声明式
class Person {}
// 类的表达式
// var Person = class {}
console.log(Person.prototype) //{}
var p = new Person()
console.log(p.__proto__ === Person.prototype) // true
console.log(Person.prototype.__proto__) //[Object: null prototype] {}
console.log(Person.prototype.constructor) //[class Person]
console.log(typeof Person) // function
2. class的构造方法
class Person {
constructor(name) {
this.name = name
}
}
var stu = new Person('xxx')
var stu2 = new Person('zzz')
console.log(stu) //Person { name: 'xxx' }
console.log(stu2) //Person { name: 'zzz' }
3. class中方法的定义
// 1、
class Person {
constructor(name) {
this.name = name
}
// 普通的实例方法,eat添加到了Person.prototype原型对象上
eat() {
console.log(this.name + '该吃饭了喂')
}
}
var stu = new Person('xxx')
stu.eat() //xxx该吃饭了喂
// 用来获取一个对象的所有自身属性的描述符。
console.log(Object.getOwnPropertyDescriptors(Person.prototype))
// {
// constructor: {
// value: [class Person],
// writable: true,
// enumerable: false,
// configurable: true
// },
// eat: {
// value: [Function: eat],
// writable: true,
// enumerable: false,
// configurable: true
// }
// }
// 2、
class Person {
constructor(name, sno) {
this.name = name
this._sno = sno
}
// 类的访问器方法,sno添加到了Person.prototype原型对象上
get sno() {
return this._sno
}
set sno(newVal) {
this._sno = newVal
}
}
var stu = new Person('xxx', 123456789)
console.log(stu) //Person { name: 'xxx', _sno: 123456789 }
console.log(stu.sno) //123456789
stu.sno = 888888888
console.log(stu.sno) //888888888
// 用来获取一个对象的所有自身属性的描述符。
console.log(Object.getOwnPropertyDescriptors(Person.prototype))
// {
// constructor: {
// value: [class Person],
// writable: true,
// enumerable: false,
// configurable: true
// },
// sno: {
// get: [Function: get sno],
// set: [Function: set sno],
// enumerable: false,
// configurable: true
// }
// }
//3、
var arr = ['a', 'b', 'c']
class Person {
constructor(name, sno) {
this.name = name
}
//类的静态方法(类方法),huoqu添加到了Person上
static huoqu(num) {
var val = arr[num]
return val
}
}
console.log(Person.huoqu(1)) //b
// 用来获取一个对象的所有自身属性的描述符。
console.log(Object.getOwnPropertyDescriptors(Person))
// {
// length: { value: 2, writable: false, enumerable: false, configurable: true },
// prototype: {
// value: {},
// writable: false,
// enumerable: false,
// configurable: false
// },
// huoqu: {
// value: [Function: huoqu],
// writable: true,
// enumerable: false,
// configurable: true
// },
// name: {
// value: 'Person',
// writable: false,
// enumerable: false,
// configurable: true
// }
// }
4. class中实现继承的extends
- 面向对象的继承前面复习就讲到了好几种方法,现在又新添一种面向对象继承
- super的使用位置有三个:子类的构造函数、实例方法、静态方法
- 注意:在子(派生)类的构造函数中使用this或者返回默认对象之前,必须先通过super调用父类的构造函数
class Person {
constructor(name) {
this.name = name
}
eat() {
console.log(this.name + '在Person的干饭')
}
eat2() {
console.log(this.name + '在Person的干饭2')
}
run() {
console.log('Person的跑路1')
console.log('Person的跑路2')
}
static staticFoo() {
console.log('Person的staticFoo1')
console.log('Person的staticFoo2')
}
}
class Student extends Person {
constructor(name, sno) {
super(name)
this.sno = sno
}
// 方法重写
eat2() {
console.log(this.name + '在Student的干饭2')
}
// 方法的复用
run() {
super.run()
console.log('Student的跑路3')
console.log('Student的跑路4')
}
// 重写静态方法
static staticFoo() {
// 静态方法的复用
super.staticFoo()
console.log('Student的staticFoo3')
console.log('Student的staticFoo4')
}
}
var stu = new Student('xxx', 123456789)
stu.eat() //xxx在Person的干饭
stu.eat2() //xxx在Student的干饭2
stu.run()
// Person的跑路1
// Person的跑路2
// Student的跑路3
// Student的跑路4
Person.staticFoo()
// Person的staticFoo1
// Person的staticFoo2
Student.staticFoo()
// Person的staticFoo1
// Person的staticFoo2
// Student的staticFoo3
// Student的staticFoo4
5. 转成ES5代码
6. 继承内置类
class hyArray extends Array {
getStart() {
return this[0]
}
getEnd() {
return this[this.length - 1]
}
}
var arr = new hyArray(1, 2, 3, 4, 5)
console.log(arr.getStart()) //1
console.log(arr.getEnd()) //5
// 除了自定义的方法外,还继承了Array
arr.push(666)
console.log(arr.getEnd()) //666
7. 类的混入mixin
class Person {
person() {
console.log('Person类')
}
}
// 在JS中类只能有一个父类: 单继承
class Student extends Person {
student() {
console.log('student类')
}
}
function mixinStudent2(cl) {
return class Student2 extends cl {
student2() {
console.log('student2类')
}
}
}
function mixinStudent3(cl) {
return class Student3 extends cl {
student3() {
console.log('student3类')
}
}
}
// 返回一个class类赋给stu4
var stu4 = mixinStudent3(mixinStudent2(Student))
var s = new stu4()
s.person() //Person类
s.student() //student类
s.student2() //student2类
s.student3() //student3类
三、面向对象的多态
- 当对不同的数据类型执行同一个操作时, 如果表现出来的行为(形态)不一样, 那么就是多态的体现。
// 传统的面向对象多态是有三个前提:
// 1> 必须有继承(是多态的前提)
// 2> 必须有重写(子类重写父类的方法)
// 3> 必须有父类引用指向子类对象
// 当对不同的数据类型执行同一个操作时, 如果表现出来的行为(形态)不一样, 那么就是多态的体现。
// 1、
function foo(val) {}
var obj = {
bar: function () {
return 100
}
}
class person {
bar() {
return 200
}
}
var p = new person()
foo(obj)
foo(p)
//2、
function sum(a, b) {
return a + b
}
sum(2, 3)
sum('a', 'b')