关于js的原型与继承

1.原型

在这里插入图片描述
_proto_: 每个对象都会有隐式原型对象,它指向该对象的构造函数的prototype。
prototype: 只有函数对象才有,默认指向空对象。在prototype上添加属性或方法,该函数对应的实例都会获得改属性的使用权。
constructor: 原型对象里的 constructor 指向构造函数本身

function Computer(name, price) {
      this.name = name;
      this.price = price;
      this.text=function(){
         return `${name}:${price}`
       }
    }
    const huawei = new Computer('huawei', 1200)

图解:

  1. huawei是构造函数Computer的实例,所以huawei的__proto__指向Computer的prototype,即:
huawei.__proto__===Computer.prototype    // true
  1. 而构造函数中,Computer.prototype的constructor指向构造函数本身,即:
hauwei.__proto__.constructor === Computer   // true
  1. Computer.prototype本身也是对象,因为所有的__proto__指向构造函数的prototype,所以Computer.prototype的原型指向对象的prototype,即:
hauwei.__proto__.__proto__ === Object.prototype // true
  1. Object.prototype最终指向null

总结:如何理解原型?
每个对象都有一个 __proto__ 属性,该属性指向自己的原型对象
每个构造函数都有一个 prototype 属性,该属性指向实例对象的原型对象
原型对象里的 constructor 指向构造函数本身

2.构造函数

2.1 什么是对象?为什么面向对象?

为什么面向对象 : 逻辑迁移更加灵活、代码复用性高、高度的模块化

对象:

  • 对象是对于单个物体的简单抽象
  • 对象是一个容器,封装了属性 & 方法
    属性:对象的状态
    方法:对象的行为
  // 简单对象
    const Course = {
        teacher: 'a',
        leader: 'b',
        startCourse: function(name) {
            return `开始${name}`;
        }
    }

    // 函数对象
    function Course() {
        this.teacher = 'a';
        this.leader = 'b';
        this.startCourse = function(name) {
            return `开始${name}`;
        }
    }

2.2 什么是构造函数

构造函数:表征了一类物体的共同特征,从而生成对象。js本质上并不是基于类,而是基于构造函数+原型链。

2.3 构造函数的特点

  1. 函数体内使用的this,指向所要生成的实例
  2. 生成对象用new来进行实例化
  3. 可以做初始化传参

2.3 new做了什么?

function Course() {};
   const course = new Course();
  1. 创建了一个空对象,作为返回的对象实例
  2. 将生成空对象的原型对象指向了构造函数的prototype属性
  3. 将当前实例对象赋给了内部this
  4. 执行构造函数的初始化代码

追问1:使用构造函数,会有什么性能上的问题?
构造函数中的方法,会存在于每一个生成的实例里,重复的挂载其实是会导致资源浪费

function Cuorse(){
this.teacher='a';
this.course='web';
this.work=function(name){
      return `开始${name}`
   }
}
const course1 = new Course('es6');
const course2 = new Course('OOP');

优化:

function Cuorse(){
this.teacher='a';
this.course='web';
}
// 方法挂载于prototype上
    Course.prototype.work = function(name) {
        return `开始${name}`;
    }
const course1 = new Course('es6');
const course2 = new Course('OOP');

追问2:构造函数不初始化可以使用吗?
不可以

function Cuorse(){
this.teacher='a';
this.course='web';
this.work=function(name){
      return `开始${name}`
   }
}

追问3:如果项目需要使用,如何解决?

instanceOf():判断某个实例是否属于某构造函数;A instanceof B ,返回值为boolean类型,用来判断A是否是B的实例对象或者B子类的实例对象

function Cuorse(){
 const _isClass = this instanceof Course;
        if (!_isClass) {
            return new Course();
        }
   this.teacher='a';
   this.course='web';
   this.work=function(name){
      return `开始${name}`
   }
}

使用:

console.log(Course().name)

cosnt course1=Course()

3.原型链

访问一个对象的属性时,现在自身属性中查找,找到则返回;如果没找到,再沿着_proto_这条链向上查找,proto_指向他构造函数的prototype;prototype本身也是对象,会找到Object的_proto(即null),如果最终没找到,找不到属性返回undefined,找不到方法会报错。

4.prototype如何实现继承

js如何实现继承:现有构造函数Game,需要创建一个构造函数LOL,实现通过LOL新建的实例,可以在实例上同时继承LOL和Game的属性

实现:重写原型对象方式。将父对象的属性方法,作为子对象原型对象的属性和方法

 // Game类
    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 game = new LOL();

追问1:这个方法,原型链继承有什么缺点?
使用如上方法创建两个实例,改变实例1的skin,实例2的skin也会被改变。(玩家1给游戏充值皮肤,玩家2不花钱也能收到)并且,实例化子类时,无法向父类做传参。
如下:

 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)
    console.log(game2.skin)

在这里插入图片描述
缺点:

  1. 父类属性一旦赋值给子类的原型属性,此时属性属于子类的共享属性了
  2. 实例化子类时,无法向父类做传参

追问2:如何解决上述问题
解决方法:
构造函数继承法:在子类构造函数内部调用父类构造函数。

重写原型对象方式的解决方法,将Game的属性放在LOL的prototype上。此次的方法,是将Game的属性放在LOl里。解决了共享属性的问题 + 子向父传参问题

    function Game(arg) {
        this.name = 'lol';
        this.skin = ['s'];
    }
    Game.prototype.getName = function() {
        return this.name;
    }

    // LOL类
    // 通过call改变this指向,将Game的属性放在LOL中,arg可换为子类向父类传递的参数
    // 用class如何实现,可使用super
    function LOL(arg) {
        Game.call(this, arg);
    }

    const game3 = new LOL('arg');

追问3:原型链上的共享方法无法被读取继承,如何解决?
解决方法:组合继承

追问2中的方法解决了子类传值与共享属性问题,但是新建的实例无法继承Game原型链上的方法。此次的方法,将Game再次放在LOl的prototype上。LOL本身找不到的属性,可以通过prototype访问Game的原型链。

  function Game(arg) {
        this.name = 'lol';
        this.skin = ['s'];
    }
    Game.prototype.getName = function() {
        return this.name;
    }

    // LOL类
    function LOL(arg) {
        Game.call(this, arg);
    }
    LOL.prototype = new Game();
    LOL.prototype.constructor = LOL;

    const game3 = new LOL();

追问4:组合继承就没有缺点么?无论何种场景都会调用两次父类构造函数(Game构造函数)
第一次:初始化子类原型时
第二次:子类调用函数内部call父类时

解决方案:
寄生组合继承

 function Game(arg) {
        this.name = 'lol';
        this.skin = ['s'];
    }
    Game.prototype.getName = function() {
        return this.name;
    }

    // LOL类
    function LOL(arg) {
        Game.call(this, arg);
    }
    //区别处:
    LOL.prototype = Object.create(Game.prototype);
    LOL.prototype.constructor = LOL;

区别处说明:用父类的原型对象把子类的原型对象覆盖掉,object.create 创建一个对象,Game.prototype 父类的原型对象

提高追问5:看起来完美解决了继承,但是如何实现多重继承?
现有构造函数Game,构造函数Store;需要创建一个构造函数LOl;要求通过LOL新建的实例可以访问到Game和Store的所有属性和原型链

Object.assign() 是对象的静态方法,可以用来复制对象的可枚举属性到目标对象,利用这个特性可以实现对象属性的合并。它将返回目标对象。

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);
    // LOL.prototype = Object.create(Store.prototype);
    //使用上面一行代码实现会覆盖,丢失Game的prototype
    Object.assign(LOL.prototype, Store.prototype);
    LOL.prototype.constructor = LOL;
    // LOL继承两类
    const game3 = new LOL();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值