面向对象
对象是什么?为什么要面向对象?优势? ==》简化了对流程部分的岔路准备
特点:代码迁移更加灵活,代码复用性更高,高度模块化
对象的理解
- 对象对单个物体的简单抽象 => 对象产生关联
- 对象是基础,对象是容器 => 属性、方法 => 模块、接口
// 简单对象 - 本身开放
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();
- 结构上:创建一个新的空对象,用于承载返回的对象实例
- 属性上:生成的空对象的原型对象
__proto__指向构造函数的 prototype 属性course1.__proto__===Course.prototype - 关系上:将当前的实例对象赋值给了内部 this
- 生命周期上:执行构造函数中的初始化代码
追问:实例化生成的对象之间有没有直接的联系?
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 是什么?存在的意义是什么?
- 每个实例对象被创建时,会自动拥有一个证明身份的属性 constructor
- 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']
- 父类属性一旦赋值给子类的原型属性,此时属于子类的共享属性 -- 存在继承者之间实例篡改
- 实例化无法向父类进行传参
解决方法: 构造函数继承(经典继承)
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遍的问题
引申
对象 => 模块 继承 => 传递 => 闭包 => 状态