对象是什么?
- 对象是对于单个物体的抽象
- 对象本质就是一个容器,封装了属性 & 方法
- 属性 ➡ 可以描述为是对象的状态 who am I
- 方法的 ➡ 可以描述为对象的行为 what I do
为什么要面向对象编程(OOP)?
- 特点: 逻辑迁移灵活、代码可复用性高、高度模块化
构造函数
- 构造函数需要一个模板,代表了一类相同物体的特征,从而可以生成对象
- 类本质也是对象模板
- js其实本质上不是基于类,而是基于构造函数 + 原型链 constructor + prototype
function People() {
this.name = 'James',
this.age = 18
}
const p = new People()
new 做了什么动作?
- 创建了一个空对象, 作为返回的对象实例
- 将生成空对象的原型对象指向了构造函数的prototype属性
- 上面的例子
p.__proto__ === People.prototypetrue - 将当前实例对象赋给了内部this
- 执行构造函数初始化代码
思考:构造函数,不new,可以直接使用么???
function People() {
this.name = 'James',
this.age = 18
}
const p = People() // 可以拿到 p对象吗
我是犟驴,我就想这么用呢➰
// 兼容一下就ok了
function People() {
const isInstance = this instanceof People
if(!isInstance){
return new People()
}
this.name = 'James',
this.age = 18
}
const p = People()
// 这样就可以正常使用了
插播:实例对像的属性和方法是相互隔离的,不会相互影响
constructor 是什么?
constructor 属性返回对创建此对象的数组函数的引用
- 每个对象创建时会自动拥有一个构造函数属性constructor
- constructor继承自原型对象,指向构造函数的引用
p.contructor ==== People
原型对象
function People() {}
const p1 = new People();
const p2 = new People();
- 构造函数:用来初始化创建对象的函数 - People
- 自动给构造函数赋予一个属性
prototype,该属性实际等于实例对象的原型对象
- 自动给构造函数赋予一个属性
- 实例对象:p1 就是实例对象,根据原型创建出来的实例
- 每个对象中都有个
__proto__ - 每个实例对象都有个
constructor属性 constructor由继承而来,并指向当前构造函数
- 每个对象中都有个
- 原型对象:People.prototype
在JavaScript中,prototype 对象是实现面向对象的一个重要机制。每个函数就是一个对象(Function),函数对象都有一个子对象 prototype 对象,类是以函数的形式来定义的。prototype 表示该函数的原型,也表示一个类的成员的集合
构造函数,原型对象,实例对象三者之间的关系
-
每创建一个函数,该函数都会自动带有一个
prototype属性。该属性是一个指针,指向一个对象,该对象称之为原型对象(后期我们可以使用这个原型对象帮助我们在js中实现继承) -
原型对象上默认有一个属性
constructor,该属性也是一个指针,指向其相关联的构造函数 -
通过调用构造函数产生的实例对象,都拥有一个内部属性,指向了原型对象。其实例对象能够访问原型对象上的所有属性和方法。
// 定义了一个构造函数 People()
function People(){
this.type='人'
}
// People.prototype指向原型对象,其自带属性construtor又指回了People 即 People.prototype.constructor==People
People.prototype.showType=function(){
alert(this.type);
}
// 实例对象person由于其内部指针指向了原型对象
var person=new People();
//实例对象可以调用原型对象上面的方法
person.showType()
总结:三者的关系是,每个构造函数都有一个原型对象,原型对象上包含着一个指向构造函数的指针,而实例都包含着一个指向原型对象的内部指针。通俗的说,实例可以通过内部指针访问到原型对象,原型对象可以通过
constructor找到构造函数。
继承
在原型对象的所有属性和方法,都能被实例所共享
// People 类
function People() {
this.name = 'lol';
}
People.prototype.getName = function() {
return this.name;
}
// Person 类
function Person() {
this.hobby = ['eat', 'sport']
}
// Person 继承 People 类
Person.prototype = new People();
// Person 原型对象可以通过constructor找到构造函数 Person
Person.prototype.constructor = Person;
const person = new Person();
person.getName()
// 本质:重写原型对象,将父对象的属性方法,作为子对象原型对象的属性和方法
原型链继承有什么缺点?
- 父类属性一旦赋值给子类的原型属性,此时属性属于子类的共享属性了
- 在创建 LOL 的子类的时候,无法传递参数
function People() {
this.name = 'lol'
this.hobby = ['s']
}
// Person 类
function Person() {}
// Person 继承Game类
Person.prototype = new People();
Person.prototype.constructor = Person;
const person1 = new Person();
const person2 = new Person();
person1.hobby.push('ss');
// 这个时候person2子类拿到的 hobby 也会改变
如何解决上面的问题呢?
构造函数继承 : 在子类构造函数内部调用父类构造函数
function People(arg) {
this.name = 'lol';
this.hobby = ['s'];
}
People.prototype.getName = function() {
return this.name;
}
// Person 类
function Person(arg) {
// 构造函数内部 this 指向实例
People.call(this, arg);
}
// Person 继承 People 类
const person = new People();
// 解决了共享属性问题&传参问题
上面People对象上的方法getName又无法被读取继承,如何解决呢?
组合继承
function People(arg) {
this.name = '灭霸';
this.hobby = ['eat'];
}
People.prototype.getName = function() {
return this.name;
}
// Person 类
function Person(arg) {
People.call(this, arg);
}
Person.prototype = new People();
// 让Persond的构造函数指针指向自己
Person.prototype.constructor = Person;
// Person 继承 Person 类
const person = new Person();
组合继承的缺点:每次使用都会调用两次父类构造函数
- Person.prototype = new People()
- const person = new Person() // People.call(this, arg)
寄生组合继承
function People(arg) {
this.name = '灭霸';
this.hobby = ['eat'];
}
People.prototype.getName = function() {
return this.name;
}
// Person 类
function Person(arg) {
People.call(this, arg);
}
// 使用Object.create创建一个新对象,并让新对象的__proto__指向People的prototype,既保证了继承又不会执行父类 People 的构造函数
Person.prototype = Object.create(People.prototype);
Person.prototype.constructor = Person;
// Person 继承 Person 类
const person = new Person();
扩展小知识:js 如何实现多重继承
function People(arg) {
this.name = '灭霸';
this.hobby = ['eat'];
}
People.prototype.getName = function() {
return this.name;
}
function War() {
this.weapon = 'Ak47';
}
War.prototype.getWeapon = function() {
return this.weapon;
}
// 想要同时继承 People 和 War 的子类 Person
function Person(arg) {
People.call(this, arg);
War.call(this, arg);
}
Person.prototype = Object.create(People.prototype);
// Person.prototype = Object.create(War.prototype);// 这种写法太没意思了吧,失去了封装多重继承的意义
// 使用 Object.assgin 合并原型上的属性
Object.assign(Person.prototype, War.prototype);
Person.prototype.constructor = Person;
// Person实例化
const person = new Person();
person.getName()
person.getWeapon()
写文章好累啊,走过路过点个赞吧🧡💛💚💙💜💜💗💖💘💝💔