目录

  • 对象是什么
  • 构造函数
  • 原型对象
  • 实现继承以及不同继承方式

对象

为什么要面向对象编程

代码逻辑迁移更加灵活、代码复用性高、高度模块化

构造函数

function Person(name) {
    this.name = name
    
    this.getName = function(name) {
        return name
    }
}
const person = new Person()
  • 函数体内部的this指向生成的实例对象
  • 生成对象用new关键词进行实例化
  • 可以做初始化传参

new实例的过程

  1. 创建一个空对象,作为返回的对象实例
  2. 将生成空对象的原型对象指向了构造函数的prototype属性
  3. 将当前实例对象赋值给内部的this
  4. 执行构造函数初始化代码
function myNew(Fn, params) {
    // 创建一个空对象,作为返回的对象实例,将实例对象的__proto__属性指向构造函数的原型(Fn.prototype)
    const obj = Object.create(Fn.prototype)
    // 将当前实例对象赋值给内部的`this`,执行构造函数初始化代码
    const result = Fn.apply(obj, params)
    return (result && (typeof result === 'object' || typeof result === 'function')) ? result : obj
}

如何创建没有news实例化的构造函数(不被外部感知)

function Person() {
    // 判断是否为new实例对象
    if(!this instanceof Person) {
         return new Person()  
    }
    this.name = "Tom";
    this.getName = function() {
        return this.name
    }
}

const person = Person();

使用构造函数的缺点

  • 构造函数中的方法,会存在每一个生成的实例对象中,重复挂载其实是会导致资源浪费。

所以要使用原型对象来解决上面的问题。

原型对象

每个函数都有一个属性——prototype。这个prototype的属性值是一个对象(属性的集合),默认只有一个叫做constructor的属性,指向这个函数本身。 如下图所示:

这里写图片描述

上图中,SuperType是一个函数,右侧的方框就是它的原型

原型既然作为对象(属性的集合),除了constructor外,还可以自定义许多属性,比如下面这样的:

这里写图片描述

function Person(name) {
    this.name = name;
}
// 赋值原型对象的属性做为实例对象上的继承属性,避免重复挂载方法
Person.prototype.getName = function() {
    return this.name
}

“隐式原型”proto

每个对象都有一个__proto__属性,指向创建该对象的构造函数的prototype。

注意: Object.prototype确实一个特例——它的__proto__指向的是null,切记切记!!!

这里写图片描述

从上图可以看出:自定义函数Foo.__proto__指向Function.prototypeObject.__proto__指向Function.prototype

但是,为什么有Function.__proto__指向Function.prototype呢?
其实原因很简单:Function也是一个函数,函数是一种对象,也有__proto__属性。既然是函数,那么它一定是被Function创建。所以Function是被自身创建的。所以它的__proto__指向了自身的Prototype

最后一个问题:Function.prototype指向的对象,它的__proto__是不是也指向Object.prototype
答案是肯定的。因为Function.prototype指向的对象也是一个普通的被Object创建的对象,所以也遵循基本的规则。

这里写图片描述

继承

  • 原型链继承
function Person() {
    
}
Person.prototype.getName = function() {
    return this.name
}

function Man() {
    
}
Man.prototype = new Person();
Man.prototype.constructor = Man

本质: 重新原型对象的方式,将父对象的属性和方法,作为子对象原型对象的属性和方法

缺点:

  1. 父类属性一旦赋值给子类的原型属性,此时属性属于子类的共享属性了
  2. 实例化子类时,无法向父类传参
  • 构造函数继承
function Person() {
    
}
Person.prototype.getName = function() {
    return this.name
}

function Man(arg) {
    Person.call(this,arg)   
}
// 解决了共享属性的问题 + 子向父传参问题
  • 组合继承
function Person() {
    
}
Person.prototype.getName = function() {
    return this.name
}

function Man(arg) {
    Person.call(this,arg)   
}
Man.prototype = new Person();
Man.prototype.constructor = Man

缺点:无论何种场景,都会调用2次父构造函数

  1. 初始化子类原型
  2. 子类调用函数内部call父类的时候
  • 寄生组合继承
function Person() {
    
}
Person.prototype.getName = function() {
    return this.name
}

function Man(arg) {
    Person.call(this,arg)   
}
Man.prototype = Object.create(Person.prototype);
Man.prototype.constructor = Man

如何实现多重继承

function Person(name) {
    this.name = name
}
Person.prototype.getName = function() {
    return this.name
}
function Worker(salary) {
    this.salary = salary
}
Worker.prototype.getSalary = function() {
    return this.salary
}

function Man(arg) {
    Person.call(this,arg) 
    Worker.call(this,arg)   
    
}
Man.prototype = Object.create(Person.prototype);
Object.assign(Man.prototype, Worker.prototype);
Man.prototype.constructor = Man

参考文章


看见了
876 声望16 粉丝

前端开发,略懂后台;