本文以对象、原型、继承为线索编写,其中对象分为以下三种:
简单对象:是一种基本的数据结构,由属性和方法组成,用于封装一组相关的数据和行为。函数对象:是一种具有特定功能的对象,通常用来封装一组操作或行为,并且可以被调用执行。类对象:是ES6中新增的概念,用于创建具有相同属性和方法的对象实例,通过类可以更方便地实现对象的创建和管理。
其次,原型和原型链是重要的概念:原型:每个对象都有一个指向其原型对象的内部链接,原型对象包含对象的共享属性和方法。原型链:是通过原型对象之间的链接实现继承的一种机制,当访问对象的属性或方法时,JavaScript引擎会沿着原型链向上查找,直到找到对应的属性或方法。
最后,继承分为以下三种:经典继承:通过在子类的构造函数内部调用父类的构造函数,实现子类继承父类的属性。组合继承:结合原型链和经典继承的方式,解决了原型链上的共享属性和方法无法被读取继承的问题。寄生组合继承:是组合继承的一种优化方式,避免了多次调用父类构造函数的问题,提高了性能。
面向对象编程
对象作为容器,封装属性和方法,属性封装对象的状态,方法封装对象的能力和行为。
简单对象、函数对象、类对象
简单对象
const Course = {
teacher: 'yunyin',
leader: 'xh',
startCourse: name => {
return `开始${name}课`;
}
}
// A
Course.teacher = 'xxxx';
Course.startCourse('react');
// B
Course.startCourse('vue');
函数对象
function Course() {
this.teacher = 'yunyin';
this.leader = 'xh';
this.startCourse = name => {
return `开始${name}课`;
}
}
上述函数,本质就是构造函数:1.函数体内使用this,指向所要生成的实例;2.生成对象用new来实例化;3.可以初始化传参。
函数对象的constructor是什么?
每个对象在创建时,会自动拥有一个构造函数属性constructor,每个constructor源自原型对象,指向了构造函数的引用,即对应的表达式为:A.prototype.constructor = A;
类对象
class Course {
constructor(teacher,leader){
this.teacher = teacher;
this.leader = leader;
}
startCourse(name){
return `开始${name}课`;
}
}
var course1 = new Course('vue');
course1.startCourse('vue');
类对象是ES6标准中新定义的,其本质还是函数对象。放在class对象中的公共函数,对应放在函数对象的原型prototype上。类就是对象模版!Js本质并不是基于类,而是基于构造函数+原型链。
如果构造函数不初始化,可以使用具有相同能力?
无法具有。构造函数初始化指的是在创建对象的时候,使用构造函数来为对象的属性进行初始化的过程,在Js中使用new关键字调用构造函数时,会创建一个新的对象,并且调用构造函数来初始化这个对象的属性。
构造函数初始化的过程通常包括以下步骤: 在构造函数内部,使用this关键字来引用将要初始化的对象; 在构造函数内部,为对象的属性赋予初始值或执行其他必要的操作; 当使用 new 关键字调用构造函数时,会创建一个新的对象,并且调用构造函数来初始化这个对象的属性。构造函数执行完毕后,会返回新创建的对象实例。
如果在项目中需要使用,且不希望外界进行感知情况下。如何让外界直接拿到实例化后的对象?
function Course() {
const _isClass = this instanceof Course;
if (!_isClass) {
return new Course();
}
this.teacher = 'yunyin';
this.leader = 'xh';
this.startCourse = name => {
return `开始${name}课`;
}
}
// 使用方
const course = Course();
// 启发:编写底层api代码时,尽量做到不让外部去感知区分内部类型
引发思考: new是什么?/ new的原理?/ new时候做了些什么?
function Course() {};
const course = new Course();
-
-
- 结构上:创建了一个空对象,作为返回的对象实例
-
- 属性上:将生成空对象的原型对象指向了构造函数的prototype属性
-
- 关系上:将当前实例对象赋给了内部的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() {}
const course1 = new Course();
const course2 = new Course();
* 1. Course - 用来初始化创建对象的函数 | 类
course1.__proto__ === Course.prorotype
* 2. course1 - 根据原型创建出来的实例
course1.constructor === Course
prototype是什么?
function Course() {
this.teacher = 'yunyin';
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中实现继承的一种机制,每个对象都有一个指向其原型对象的内部链接,这个链接称为原型链。当访问一个对象的属性时,如果对象本身没有这个属性,js就会顺着原型链向上查找,直到找到属性或原型链顶端。
原型链详解: 对象:People
【People.prototype = People.prototype】
对象原型:People.prototype
【People.proyotype.constructor = People】
对象实例:people
【people = new People()】
【people.proto = People.prototype】
继承
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();
// 本质:重写了原型对象方式,将父对象的属性方法,作为自对象原型对象的属性方法,同时重写构造函数
追问:原型链直接继承有什么缺点
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');
// 本质:重写了原型对象方式,将父对象的属性方法,作为自对象原型对象的属性方法,同时重写构造函数
-
- 父类属性一旦赋值给到子类的原型属性,此时属性属于子类的共享属性了
-
- 实例化子类时,无法向父类进行传参
解决方法:构造函数继承
经典继承:在子类的构造函数内部调用父类的构造函数
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');
// 解决了共享属性问题 + 子向父传参的问题
追问:原型链上的共享方法无法被读取继承,如何解决?
- 组合继承
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';
}
Game.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(
Store.prototype,
LOL.prototype
);
LOL.prototype.constructor = LOL;