JavaScript 中继承合集

需要的几个Object对象的方法

开始之前先介绍几个Object对象的方法。

  • Object.create(原型[ , 属性配置 ]):创建对象并指定对象的原型;
        let web = Object.create(user, {
            name: {
                value: "wed of name", // name的值
                configurable: true, // 可配置属性
                enumerable: true, // 该属性可遍历
                writable: true // 该属性可更改值
            },
        }
  • Object.getOwnPropertyDescriptors(object):查看object对象的属性描述信息;
    类似下图的信息。
    在这里插入图片描述
  • Object.setPrototypeOf(target, proto) :设置target对象的原型为proto;
  • Object.getPrototypeOf(target):获取target对象的原型;
        // 把js的原型设置为user
        Object.setPrototypeOf(js, user);
        console.log(Object.getPrototypeOf(js));
  • Object.defineProperty(obj , 属性 , 配置对象) :

Object.defineProperty(obj,atrr,cfigObj)
obj :参数是设置属性的对象
atrr:是指设置的属性名
cfigObj:是指设置属性的配置属性,四个属性
1)value : 属性值
2)enumerable:是否可遍历
3)writable:是否可更改
4)configurable:是否可配置,简单理解就是能不能对属性进行增、删、该、以及是否可以把属性修改为访问器属性。

  • Object.assign(target,…source):获取source对象的所以属性和方法添加到target对象中;

圣杯模式

我的这篇文章——圣杯模式详细的写了整个过程,再次学习Object对象时,发现一个问题,就是子类继承了父类之后,实例化出来的对象的原型中constructor属性是可遍历的,假如我们使用 for of语句遍历对象,则会把 constructor 属性遍历出来,这样就不好了,这里更改一下 constructor 属性的设置。
采取Object.defineProperty() 方法定义属性。

        // 继承封装函数
        function extend(Sub, Super) {
            // 圣杯模式继承 
            let F = function () {};
            F.prototype = Super.prototype;
            Sub.prototype = new F();
            Sub.prototype.uber = Super.prototype;
            // Son.prototype.constructor = Son;
            // 存在问题,constructor是可遍历的,我们需要设置constructor不可遍历
            Object.defineProperty(Sub.prototype, "constructor", {
                value: Sub,
                enumerable: false, // 不可遍历
                writable: true, // 可更改
                configurable: true // 可配置
            })
       }

原型工厂

使用 Object.create() 方法设置对象的原型,最后要定义一下对象的原型中constructor属性不可遍历

		function extend(Sub,Super){
		// 2. 新创建一个对象 并为其指定原型为Super.prototype 原型工厂
            Sub.prototype = Object.create(Super.prototype);
            Object.defineProperty(Sub.prototype, "constructor", {
                value: Sub,
                // 存在问题,constructor是可遍历的,我们需要设置不可遍历
                enumerable: false, // 不可遍历
                writable: true, // 可更改
                configurable: true // 可配置
            })
            // 需要在原型上添加方法或者属性时,
            // 一定要在继承之后添加,否则会覆盖,导致新添加的属性和方法消失
        }

原型的继承

简单的一句话,子类构造函数的原型的原型对象设置为父类的构造函数的原型
函数它本身也是一个对象,构造函数同样是一个对象,所以构造函数也有自己的__proto__ 属性,指向构造函数的原型。
继承的核心思想就是让原型继承原型,而不是构造函数继承原型。

	function extend(Sub,Super){
		Sub.prototype.__proto__ = Super.prototype;
	}

测试

函数封装完毕后,写程序测试一下。

       
       User.prototype.run = function () {
            this.show();
        }
        User.prototype.sayAge = function () {
            console.log(this.age);
        }

        function User(name, age) {
            this.name = name;
            this.age = age;
        };

        extend(Vip, User);
                // 重写父类的show方法
        Vip.prototype.show = function () {
            console.log("Vip show meothd");
        }

        // ...在接收参数时是聚合成数组,args接收参数组成的数组;...在使用参数时是打散数组,展开
        function Vip(...args) { // 接收参数时是一个数组
            // 借用父类属性
            User.apply(this, args); // 在使用的时候args是数组,若需要使用每个元素,展开即可,...args
        }
        
        // 测试继承
        let user1 = new User("ff", 18);
        let vip = new Vip("tt", 15);
        vip.run();
        vip.sayAge();
        // console.log(Vip.prototype.constructor); // 指回自己的构造器

        // 测试constructor属性不可遍历
        console.dir(Object.getOwnPropertyDescriptors(Vip.prototype));
        for (const key in vip) {
            console.log(key); // 没有遍历出constructor
        }

解决多继承问题 —— 混合 (mixin)

多继承会导致整个代码结构很混乱,代码结构不健壮,而且可读性比较差,可以使用混合,其实混合这个概念一听比较高大上,听起来有点混乱,但是当我看到是 assign() 方法时,瞬间放松了。混合核心就是把目标对象所需要的功能或者属性添加到目标对象中,刚好assign() 方法能够实现属性的合并。

Object.assign ( ) 方法

    Object.assign(targetObj,...source)
    assign方法 : 
    参数1: 目标对象
    参数2: 源对象
    返回值: 目标对象
    函数作用: 把一个或多个源对象中所有可枚举的所有属性的值复制到目标对象
    简单理解可是属性的合并

混合的实现

定义两个构造函数,User构造出来的实例对象,有请求后台的功能、获取积分的功能、登录的功能…
如果把每个功能都写成函数,就太乱了,我们应该把每个功能封装成一个对象,需要其中的属性或方法之间调取即可,这样结构清晰,可读性高,代码显得更加健壮。

        function User(name, age) {
            this.name = name;
            this.age = age;
        };

        function Vip(...args) { // 接收参数时是一个数组
            // 借用父类属性
            User.apply(this, args); // 在使用的时候args是数组,若需要使用每个元素,展开即可,...args
        }

分别定义各个功能模块的对象

        // 把各个模块功能全部变成对象,功能中的小功能变成方法
        const Request = {
            quest() {
                return "请求后台";
            }
        };
        const Integral = {
            total() {
                console.log( "获取积分");
            }
        };
        const Login = {
            login() {
                console.log("跳转登录页面");
            }
        };
        const VipPic = {
            showVip() {
                console.log(this.name + "是vip会员");
            }
        }
对象添加 功能模块对象

使得User实例化的对象能够调用各个功能模块中的方法;
Vip实例化对象可以获得vip相关信息的功能。

        let user = new User("ff", 18);
        // User需要 Request Integral Login中的方法
        User.prototype = Object.assign(User.prototype, Request, Integral, Login);
        user.total();

        let vip = new Vip("tt", 18);
        // Vip需要使用 VipPick对象的方法
        Vip.prototype = Object.assign(Vip.prototype, VipPic);
        vip.showvip();

在这里插入图片描述

功能模块对象之间相互添加

在获取积分时,肯定是请求后台,我们接收后台返回的数据最终获取,这时候就需要在获取积分的时候去请求后台了,换句话说就是需要在获取积分的功能块中添加请求后台的功能。
使用super关键字,实现功能与功能之间的添加使用。
两步操作:

  1. 目标对象的__proto__ 属性指向添加的功能对象;
  2. 使用super关键字调用相应的属性或方法。
        const Integral = {
            __proto__: Request,
            // super == >this.__proto__
            total() {
                console.log(super.quest() + ": 获取积分");
            }
        };

此时再去实例化对象,调用积分的total方法时,就能调用请求后台的方法了。

        let user = new User("ff", 18);
        // User需要 Request Integral Login中的方法
        User.prototype = Object.assign(User.prototype, Request, Integral, Login);
        user.total();

在这里插入图片描述
继承的学习一开始会有点绕,多看几遍就可以。
加油!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值