面向对象

javaScript

找个对象吧

类与实例

类的声明通过构造函数class A{}2种方式,用new生成实例。

类与继承

① 借助构造函数实现继承(无法继承父类原型对象上的方法与属性)

function Parent1() { this.name = 'parent1' } function Child1() { Parent1.call(this) this.type = 'child1' } console.log(new Child1())

②、借助原型链实现继承(一个实例改变原型上的数据,会影响到所有实例)

function Parent2() { this.name = 'parent2' this.play = [1, 2, 3] } function Child2() { this.type = 'child2' } Child2.prototype = new Parent2(); const newObj1 = new Child2() const newObj2 = new Child2() newObj1.play.push(4) newObj2.play // 被影响属性,得到 1,2,3,4

③、组合方式(父类构造函数在new的时候执行了2次)

function Parent3() { this.name = 'Parent3' this.play = [1, 2, 3] } function Child3() { Parent3.call(this) this.type = 'Child3' } Child3.prototype = new Parent3() const s3 = new Child3() const s4 = new Child3() s3.play.push(4) console.log(s3.play, s4.play)

④、组合继承(影响判断实例是由父类实例化还是子类实例的,子类添加方法或属性会影响到父类。)

function Parent4() { this.name = 'Parent4' this.play = [1, 2, 3] } function Child4() { Parent4.call(this) this.type = 'Child4' } // 子类原型继承父类原型 Child4.prototype = Parent4.prototype const s5 = new Child4() const s6 = new Child4() s5.play.push(4) console.log(s5.play, s6.play)

⑤、寄生组合继承

function Parent5() { this.name = 'Parent5' this.play = [1, 2, 3] } function Child5() { Parent5.call(this) this.type = 'Child5' } Child5.prototype = Object.create(Parent5.prototype) // 修正constructor指向 Child5.prototype.constructor = Child5 const s7 = new Child5() const s8 = new Child5() s7.play.push(4) console.log(s7.play, s8.play)

PS: 关于构造函数调用 new 运算符
① 创建一个新对象foo;
② 并将它的__proto__指向其构造函数的prototype,foo.__ proto __ = Foo.prototype;
③ 动态将this指向新对象,Foo.call(foo,arguments);
④ 执行函数体中的代码;
⑤ 放回新对象foo;

const new2 = function(func){ const o = Object.create(func.prototype) const k = func.call(o) if (typeof k === 'object'){ return k } else { return o } }

构造函数与 class 调用的不同点

1)、class父类的静态方法,可以被子类继承。

class Foo{ static classMethod() { return 'hello' } } class Bar extends Foo{} Bar.classMethod() // 'hello'

2)、class不存在变量提升(预解析),因为ES6不会把类的声明提升到代码头部,这种规则的原因是必须要保证子类在父类之后定义。

new Foo() // ReferenceError class Foo { sayName() { console.log('yxf') } }

3)、class必须使用new调用,否则会报错。

class Foo { sayName() { console.log('yxf') } } Foo() // TypeError: Class constructor Foo cannot be invoked without 'new'

4)、class内部定义的所有方法都是不可枚举的

class Foo { sayName(){ console.log('yxf') } } const objFoo = new Foo() Object.keys(objFoo) // []

ES5 的继承和ES6的继承区别?

ES5 继承机制: 先创建子类的实例对象,然后在将父类的方法添加到 this 上 (Parent.apply(this))
ES6 继承机制: 先创建父类的实例对象 this (必须先调用父类的 super() 方法),然后再用子类的构造函数修改this

具体:ES6 通过 class 关键字定义类,里面有构造方法,类之间通过 extends 关键字实现继承,子类必须在 constructor 方法中调用 super 方法,否则新建实例报错。因为子类没有自己的 this 对象,而是继承了父类的 this 对象, 然后对其进行加工,如果不调用 super 方法,子类得不到 this 对象

PS: super 关键字指父类的实例,即父类的 this 对象,子类中写 constructor 就必须要有 super关键字, 没有的话系统会隐士调用。

使用装饰器把多个对象上的方法合并到 class 上

代码链接