面向对象oop、原型原、型链

76 阅读2分钟

面向对象

对象是什么?为什么要面向对象?优势? ==》简化了对流程部分的岔路准备

特点:代码迁移更加灵活,代码复用性更高,高度模块化

对象的理解

  • 对象对单个物体的简单抽象 => 对象产生关联
  • 对象是基础,对象是容器 => 属性、方法 => 模块、接口
// 简单对象 - 本身开放
const Course = {
  teacher: "yy",
  class: "oop",
  startCourse: function (name) {
    return `开始${name}课程`;
  },
};

// 函数对象
function Course() {
  this.teacher = "yy";
  this.class = "oop";
  this.startCourse = function (name) {
    return `开始${name}课程`;
  };
}
const course1 = new Course();
course1.teacher;
course1.class;
course1.startCourse("js");

// 构造实例的函数 => 构造函数
// => ES class

构造函数

需要一个模板 => 构造即模板

js 的对象并不是基于类,基于 构造函数 + 原型传递 的方式 => constructor + prototype

追问:

new 的过程发生了什么?new 是什么?new 的原理

function Course() {}
const course1 = new Course();
  1. 结构上:创建一个新的空对象,用于承载返回的对象实例
  2. 属性上:生成的空对象的原型对象__proto__指向构造函数的 prototype 属性 course1.__proto__===Course.prototype
  3. 关系上:将当前的实例对象赋值给了内部 this
  4. 生命周期上:执行构造函数中的初始化代码

追问:实例化生成的对象之间有没有直接的联系?

function Course(teahcer) {
  this.teacher = teahcer;
  this.class = "oop";
  this.startCourse = function (name) {
    return `开始${name}课程`;
  };
}
const course1 = new Course("yy");
const course2 = new Course("zz");
course1.teacher = "aa";
console.log(course2.teahcer); // zz
// 实例化后个体之间独立自主
// 追问:如何共用?

追问:constructor 是什么?存在的意义是什么?

  1. 每个实例对象被创建时,会自动拥有一个证明身份的属性 constructor
  2. constructor 来自于原型对象,指向了构造函数的引用 course1.constructor===Course
  • 实例获得类的属性 => 继承

追问:使用构造函数创建对象有什么问题吗》性能上有什么问题?

function Course(teahcer) {
  this.teacher = teahcer;
  this.class = "oop";
  // 有多少实例,该方法就会被执行多少次,性能消耗
  //   this.startCourse = function (name) {
  //     return `开始${name}课程`;
  //   };
}
// 原型传递 => Course.prototype => 原型链
Course.prototype.startCourse = function (name) {
  return `开始${name}课程`;
};

const course1 = new Course("yy");
const course2 = new Course("zz");

继承

实现继承

重写原型法

function Game() {
  this.name = "LOL";
}
Game.prototype.getName = function () {
  return this.name;
};
function LOL() {}
LOL.prototype = new Game();
LOL.prototype.constructor = LOL;
const lol = new LOL();
lol.getName();

追问: 重写原型法继承有什么缺点?

function Game() {
  this.name = "LOL";
  this.skin = ["s"];
}
Game.prototype.getName = function () {
  return this.name;
};
function LOL() {}
LOL.prototype = new Game();
LOL.prototype.constructor = LOL;
const lol1 = new LOL();
const lol2 = new LOL();
lol1.skin.push("ss");
console.log(lol2.skin); // ['s','ss']
  1. 父类属性一旦赋值给子类的原型属性,此时属于子类的共享属性 -- 存在继承者之间实例篡改
  2. 实例化无法向父类进行传参

解决方法: 构造函数继承(经典继承)

function Game() {
  this.name = "LOL";
  this.skin = ["s"];
}
Game.prototype.getName = function () {
  return this.name;
};
function LOL(arg) {
  Game.call(this, arg);
}
const lol1 = new LOL();
const lol2 = new LOL();
lol1.skin.push("ss");
console.log(lol2.skin); // ['s']

// 解决了 共享属性 和 传参问题

追问:原型链上的共享方法无法继承?怎么解决?

组合继承

function Game() {
  this.name = "LOL";
  this.skin = ["s"];
}
Game.prototype.getName = function () {
  return this.name;
};
function LOL(arg) {
  Game.call(this, arg); // 执行1遍
}
LOL.prototype = new Game(); // 执行第二遍
LOL.prototype.constructor = LOL;
const lol1 = new LOL();
const lol2 = new LOL();
lol1.skin.push("ss");
console.log(lol2.skin); // ['s']

// 解决了 拿不到原型链上的方法的问题

追问:父类构造函数执行 2 遍,怎么解决?

寄生组合继承 (较为完美的方案)

function Game() {
  this.name = "LOL";
  this.skin = ["s"];
}
Game.prototype.getName = function () {
  return this.name;
};
function LOL(arg) {
  Game.call(this, arg);
}
LOL.prototype = Object.create(Game.prototype);
LOL.prototype.constructor = LOL;
const lol1 = new LOL();
const lol2 = new LOL();
lol1.skin.push("ss");
console.log(lol2.skin); // ['s']

// 解决了 父类构造函数执行2遍的问题

多重继承

function Game() {
  this.name = "LOL";
  this.skin = ["s"];
}
Game.prototype.getName = function () {
  return this.name;
};
function Store() {
  this.shop = "steam";
}
Game.prototype.getShop = function () {
  return this.shop;
};
function LOL(arg) {
  Game.call(this, arg);
  Store.call(this, arg);
}
LOL.prototype = Object.create(Game.prototype);
Object.assign(Game.prototype, Store.protutype);
LOL.prototype.constructor = LOL;
const lol1 = new LOL();
const lol2 = new LOL();
lol1.skin.push("ss");
console.log(lol2.skin); // ['s']

// 解决了 父类构造函数执行2遍的问题

引申

对象 => 模块 继承 => 传递 => 闭包 => 状态