lesson 5 面向对象及原型链 2021-04-29

课程标题 面向对象及原型链

课程目标

  1. 面向对象思想
  2. 原型及原型链
  3. 继承

知识点

  1. 面向对象思想
  2. 原型链的指向
  3. new关键字的步骤

new关键字做了什么?

  1. 创建一个新的对象,继承自构造函数的原型。
  2. 实例的proto指向构造函数的prototype。
  3. 将this指向新创建的对象。
  4. 返回这个新创建的对象。
    a. 如果构造函数没有显示的返回值或者返回值是基本类型,返回this
    b. 如果构造函数有显示的返回值且是对象类型,返回这个对象
 function Player(name){
   this.name = name;
 }

 function objectFactory(){
    let obj = new Object();
    let FunctionConstructor = [].shift.call(arguments);
    obj.__proto__ =FunctionConstructor.prototype;
    let resultObj = FunctionConstructor.apply(obj,arguments);
    return typeof resultObj === 'object' ? resultObj : obj;
 }

 const p1 = objectFactory(Player,'df');

 console.log(p1);

继承

原型链继承

function Parent(){
  this.name = 'parentName';
}

Parent.prototype.getName = function ()
 {
  console.log(this.name)
}

function Child () {}

// const child = new Child();
// child.__proto__ === Child.prototype
// Child.prototype.__proto__ === Parent.prototype
Child.prototype = new Parent();
Child.prototype.constructor = Child

const child1 = new Child()
child1.getName()

隐含的问题

  1. 如果一个实例操作了引用类型的原型属性,所有实例都会受到影响。
  2. 创建Child实例的时候无法传参

构造函数继承

针对上述问题1, 想办法把Parent上的属性和方法,添加/复制到Child上而不是都存在原型对象/Parent上,防止被实例共享。

实现

function Parent() {
  this.actions=['eat','sleep'];
  this.name = 'parentName';
}

function Child() {
  Parent.call(this)
  // 相当于执行了Parent内部的两个语句
  // this.actions = ['eat', 'sleep'];
  // this.name = 'parentName';
}
const child1 = new Child()
const child2 = new Child()
child1.actions.pop();
console.log(child1.actions)   // ['eat']
console.log(child2.actions)   // ['eat', 'sleep']

解决第二个不能传参的问题

function Parent(name,actions) {
  this.actions=actions;
  this.name = name;
}

function Child(id,name,actions) {
  Parent.call(this,name,actions)
  this.id = id; 
}
const child1 = new Child('c1','c1name',['eat'])
const child2 = new Child('c2','c2name',['play','jump','ball'])
console.log(child1.actions)   // ['eat']
console.log(child2.actions)   // ['play','jump','ball']

隐含的问题

  1. 属性或者方法被继承的话,只能在构造函数中定义。每次创建实例都会执行一遍构造函数,多占用内存。

组合集成

原型链继承,实现了基本的继承,方法存在prototype上,子类可以直接调用,但是引用类型的属性会被所有实例共享,并且不能传参。
构造函数继承,解决了原型链的两个问题,但是构造函数内重复创建方法,导致内存占用过多。

实现

function Parent(name,actions) {
  this.actions=actions;
  this.name = name;
}
Parent.prototype.eat = function (){
  console.log('eat')
}
function Child (id) {
     Parent.apply(this,Array.from(arguments).slice(1));
    this.id = id;
}

Child.prototype = new Parent();
Child.prototype.constructor = Child;

const c1 = new Child(1, 'c1',['xxx'])
const c2 = new Child(2, 'c2', ['hhhh'])

c1.eat();
c2.eat();

隐含的问题

  1. 调用了两次Parent的构造函数,做了无意义的重复操作。

寄生组合式继承

为了解决上述问题

function Parent(name,actions) {
  this.actions=actions;
  this.name = name;
}
Parent.prototype.eat = function (){
  console.log('eat')
}
function Child (id) {
    Parent.apply(this,Array.from(arguments).slice(1));
    this.id = id;
}

// Child.prototype = new Parent();
//let TempFunction = function () {};
//TempFunction.prototype = Parent.prototype;
//Child.prototype = new TempFunction();
Child.prototyp = Object.create(Parent.prototyp);

Child.prototype.constructor = Child;
const c1 = new Child(1, 'c1',['xxx'])
const c2 = new Child(2, 'c2', ['hhhh'])

c1.eat();
c2.eat();

class

class Parent {
    constructor(){
      this.name ='aaa';
    }
  getName() {
    console.log('getName');
  }
}

class Child extends Parent {
    constructor() {
      super() ;   
    }
}

const p1 = new Child ();
p1.getName()

课后问题

  1. new 做了什么? 如何实现
    a. 基于构造函数的prototype创建了一个对象。
    b. 将this指向这个对象。
    c. 实例的proto指向构造函数的prototype。
    c. 返回这个新创建的对象,如果构造函数没有返回或者返回了值类型,直接返回this,如果构造函数返回引用类型,返回这个对象。
  2. 4种继承方式的优缺点?如何实现?
    a.原型链
    b. 构造函数
    c. 组合模式
    d. 寄生组合模式 可用Objec.create()模拟
  3. 如何判断一个函数是否用了同一块内存地址?
    用严格相等 ===
  4. 如何在原型链上去找一个属性?

你可能感兴趣的:(lesson 5 面向对象及原型链 2021-04-29)