面向对象
对象是什么?为什么要面向对象?
通过代码抽象,进而描述单个种类物体的方式。
特点:面向对象——逻辑上迁移更加灵活、代码复用性更高、高度的模块化
对象的理解
- 对象是对于单个物体的简单抽象
- 对象是容器,封装了属性 & 方法
属性:对象的状态;方法:对象的能力 & 行为
const Course = {
teacher:'yy',
leader:'xh',
startCourse:name => {
reurn `开始${name}课`;
}
}
// A
Course.teacher = 'xxxxx';
Course.startCourse('react');
// B
Course.startCourse('vue');
// 函数对象
function Course () {
this.teacher = 'yy';
this.leader = 'xh';
this.startCourse = name => {
return `开始${name}课`;
}
}
构造函数 - 生成对象 => 实例
需要一个模板 - 表征了一类物体的共同特征,从而生成对象
类即对象模板
js其实本质上并不是基于类,而是基于构造函数 + 原型链
function Course () {
this.teacher = 'yy';
this.leader = 'xh';
this.startCourse = name => {
return `开始${name}课`;
}
}
const course = new Course(args);
console.log(course.__proto__ === Course.prototype)
Course 本质就是构造函数
- 函数体内使用的this,指向所要生成的实例
- 生成对象用new来实例化
- 可以初始化传参
追问
如果构造函数不初始化,可以使用具有相同能力吗?—— 无法具有
如果在项目中需要使用,且不希望外界进行感知的情况下。如何让外界直接拿到实例化后的对象? => 单例模式
function Course () {
const _isClass = this instanceof Course;
if(!_isClass) {
return new Course();
}
this.teacher = 'yy';
this.leader = 'xh';
this.startCourse = name => {
return `开始${name}课`;
}
}
//使用方法
const course = Course();
- 启发:编写底层api代码时,尽量做到不让外部去感知区分内部类型
引发思考:new是什么?/new的原理?/new时候做了些什么?
function Course() {};
const course = new Course();
- 结构上:创建了一个空对象,做为返回的对象实例
- 属性上:将生成空对象的原型对象指向了构造函数的prototype属性
- 关系上:将当前实例对象赋值给了内部的this;如果该函数没有返回对象,则返回 this
- 生命周期上:执行了构造函数的初始化代码
function usernew (obj,...args) {
const newObj = Object.create(obj.prototype);
const result = obj.apply (newObj,args);
return typeof result === 'object' ? result : newObj;
}
追问:实例属性影响——独立的
function Course (teacher, leader) {
this.teacher = teacher;
this.leader = leader;
}
const course1 = new Course('yy', 'xh'); // course1.leader => xh
const course2 = new Course('yy', 'bubu'); // course2.leader => bubu
course2.teacher = 'xxxx'; // course1.teacher => yunyin
construtor是什么?
function Course(teacher, leader) {
this.teacher = teacher;
this.leader = leader;
}
const course = new Course('yunyin', 'xh');
- 每个对象在创建时,会自动拥有一个构造函数属性constructor
- constructor源自原型对象,指向了构造函数的引用
- 实例获得了模板属性=>即:继承了类的属性
原型对象
function Course () { }
const course1 = new Course();
const course2 = new Course();
// 1. Course - 用来初始化创建对象的函数 | 类
course1.__proto__ === Course.prorotype
// 2. course1 - 根据原型创建出来的实例
course1.constructor === Course
prototype是什么?
function Course() {
this.teacher = 'yy';
this.leader = 'xh';
}
const course1 = new Course();
const course2 = new Course();
Course.prototype.startCourse = name => {
return `开始${name}课`;
}
追问,原型对象有自己的原型么?
- course1.proto.proto === Object.prototype
- Course.prorotype.proto === Object.prototype
- course1.proto.proto.proto === null
继承
js如何实现继承?
原型链继承
在原型对象的所有属性方法,都可以被实例所共享
function Game () {
this.name = 'lol';
}
Game.prototype.getName = function () {
return this.name;
}
// LOL
function LOL () { };
LOL.prototype = new Game();
LOL.prototype.constructor = LOL;
const game = new LOL();
const game2 = new Game();
console.log(game, game.name, game.getName())
console.log(game2, game2.name, game2.getName())
// 本质:重写了原型对象方式,将父对象的属性方法,作为自对象原型对象的属性方法,同时重写构造函数
追问:原型链直接继承有什么缺点
function Game () {
this.name = 'lol';
this.skin = ['s'];
}
Game.prototype.getName = function () {
return this.name;
}
// LOL
function LOL () { };
LOL.prototype = new Game();
LOL.prototype.constructor = LOL;
const game1 = new LOL();
const game2 = new LOL();
game1.skin.push('ss');
console.log(game1.skin, game2.skin)
- 父类属性一旦赋值给到子类的原型属性,此时属性属于子类的共享属性了
- 实例化子类时,无法向父类进行传参
解决方法:构造函数继承
经典继承:在子类的构造函数内部调用父类的构造函数
function Game (arg) {
this.name = 'lol';
this.skin = ['s'];
}
Game.prototype.getName = function () {
return this.name;
}
function LOL (arg) {
Game.call(this, arg);
}
const game3 = new LOL('arg');
//console.log(game3.getName());// 报错
// 解决了共享属性问题 + 子向父传参的问题
缺点:原型链上的共享方法无法被读取继承
追问:原型链上的共享方法无法被读取继承,如何解决?
解决方法:组合继承
function Game(arg) {
this.name = 'lol';
this.skin = ['s'];
}
Game.prototype.getName = function() {
return this.name;
}
function LOL(arg) {
Game.call(this, arg);
}
LOL.prototype = new Game();
LOL.prototype.constructor = LOL;
const game4 = new LOL('arg');
缺点:无论何种场景,都会调用两次父类的构造函数
解决方案:寄生组合继承
function Game (arg) {
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 game5 = new LOL('arg');
拔高:多重继承
function Game (arg) {
this.name = 'lol';
this.skin = ['s'];
}
Game.prototype.getName = function () {
return this.name;
}
function Store () {
this.shop = 'steam';
}
Store.prototype.getPlatform = function () {
return this.shop;
}
function LOL (arg) {
Game.call(this, arg);
Store.call(this, arg);
}
LOL.prototype = Object.create(Game.prototype);
//Object.assign方法将源对象自有的(不可以是原型链的属性Object.hasOwnProperty返回true)
//中所有可枚举的属性复制到目标对象
Object.assign(
Store.prototype,
LOL.prototype
);
LOL.prototype.constructor = LOL;