JS高级(一):类、原生tab栏切换、原型和原型链

news2025/3/3 4:43:20

JavaScript高级(一)

  • 一、类(之前ES6学过)
    • 1.类的用法
    • 2.类的继承
      • 2.1 extends关键字
      • 2.2 super关键字
        • (1)子类属性和父类一致,constructor可以省略
        • (2)子类要添加属性,必须要先写super
        • (3)super关键字调用父类中的普通函数
    • 3.类中this的指向问题
  • 二、面向对象tab栏
  • 三、原型和原型链
    • 1.构造函数中的方法浪费内存的问题
    • 2.原型对象
    • 3.对象的__proto__属性
    • 4.原型中的constructor属性
    • 5.原型链
    • 6.利用原型对象扩展内置对象的方法

一、类(之前ES6学过)

抽取了对象的公共部分,它泛指某一大类
对象特指某一个通过类实例化出来的东西

1.类的用法

ES6中的class其实就类似于ES5中的构造函数
比如下面两个写法是等价的:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.sum = function () {
  return this.x + this.y ;
};

var p = new Point(1, 2);
p.sum();
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  sum() {
    return this.x + this.y ;
  }
}

var p = new Point(1, 2);
p.sum();

定义sum()方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法与方法之间不需要逗号分隔,加逗号会报错

constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加。

class Point {
}

// 等同于
class Point {
  constructor() {}
}

2.类的继承

2.1 extends关键字

Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法。

class Father {
}

class Son extends Father {
}

2.2 super关键字

1、如果父类和子类属性一致,子类的constructor可以省略
2、子类在继承父类的方法后,可以重写父类的方法,也可以添加其他方法,super必须在子类的this之前调用。
3、其实super也可以理解成改变父类构造函数中的this指向,让this指向子类的实例。
4、所有子类继承的父类的属性都写到super里,子类独有的要在super后添加

(1)子类属性和父类一致,constructor可以省略

如果子类和父类在构造器函数中的属性一致,那么子类中的constructor完全可以省略

   class Person {
      constructor(name,age) {
          this.name = name;
          this.age = age;
      }

      speak() {
          console.log(this.name, this.age);
      }
   }

   class Student extends Person {
       say() {
           console.log(this.name + '11');
       }
   } 

   let zzy = new Student('zzy',18);
   console.log(zzy.age);  //18
   console.log(zzy.name);  //zzy
   zzy.speak();  //zzy 18
   zzy.say();  //zzy11

(2)子类要添加属性,必须要先写super

1、子类在继承父类的方法后,可以重写父类的方法,也可以添加其他方法,super必须在子类的this之前调用。
2、其实super也可以理解成改变父类构造函数中的this指向,让this指向子类的实例。
3、所有子类继承的父类的属性都写到super里,子类独有的要在super后添加
4、如果父类和子类属性一致,子类的constructor可以省略

  class Person {
       constructor(name,age) {
           this.name = name;
           this.age = age;
       }

       speak() {
           console.log(this.name, this.age);
       }
   }

   class Student extends Person {
       constructor(name,age,sex) {
           // super调用父类构造函数,也可以理解成让父类中this指向子类的实例
           super(name,age); //super表示和父类重复的属性,
           this.sex = sex; //添加子类独有的属性
       }
       //添加子类独有的方法
       say() {
           console.log(this.name + '666');
       }
       //重写父类的方法
       speak() {
           console.log(this); //Student
           console.log(this.name, this.age, this.sex);
       }
   } 

   let zzy = new Student('zzy',18,'男');
   console.log(zzy.name);  //zzy  super让父类中的this指向子类的实例了
   console.log(zzy.sex);  //男
   zzy.say();  //zzy666
   zzy.speak();  //zzy 18 男

(3)super关键字调用父类中的普通函数

通过super.函数名() 调用。
如果子类继承父类,子类和父类中有同名方法,那么子类中的实例调用时优先调用子类中的方法,如果子类没有该方法,才去调用父类的该方法。

class Father {
    say() {
        return '我是爸爸';
    }
}

class Son extends Father {
    say() {
        console.log(super.say() + '的儿子');
    }
}

let son = new Son();
son.say();  //我是爸爸的儿子   
//就近原则,如果子类有方法,优先调用子类,子类没有才去调用父类里的

3.类中this的指向问题

永远记住,谁调用函数,this就指向谁。不过下面这个如果我要调用公共方法,用箭头函数怎么解决this的问题?奇怪。其实可以用bind也行, this.btn.onclick = this.sing.bind(this)。这样调用的话,sing里面的this指向的就是constructor里面的this

<button>点击</button>
let that;
class Father {
    constructor(uname, uage) {
        that = this;
        this.name = uname;
        this.age = uage;
        // this.sing();
        this.btn = document.querySelector('button');
        this.btn.onclick = this.sing;  //这个this指向的是btn
        //谁调用方法,this就指向谁
    }

    sing() {
        console.log(this);  //<button>点击</button>
        console.log(that.name);  //zzy
    }
}

let f = new Father('zzy', 18);
f.sing();

二、面向对象tab栏

学完vue再来敲原生js感觉真的是非常麻烦,vue一行就能搞定的事情原生js要写几十行。
这个案例值得学习的地方就是老师很多代码的思路,比如利用逻辑短路去做优化。
可以去github下载:https://github.com/ForMyselfs/tabSwitch-by-nativeJs

var that;
class Tab {
    constructor(id) {
        that = this;
        //获取大的容器
        this.main = document.querySelector(id);

        //获取添加按钮
        this.add = this.main.querySelector('.tabadd');
        this.ul = this.main.querySelector('.fisrstnav ul');
        this.fsection = this.main.querySelector('.tabscon');

        this.init();
    }

    //获取标题和内容节点(由于更新dom需要重新获取,所以要单独定义个方法)
    updateNode() {
        this.lis = this.main.querySelectorAll('li');
        this.sections = this.main.querySelectorAll('section');

        //获取删除按钮
        this.remove = this.main.querySelectorAll('.iconfont');

        //获取修改元素
        this.spans = this.main.querySelectorAll('.fisrstnav li span:first-child');
    }

    //0.初始化操作,让相关的元素绑定事件
    init() {
        this.updateNode(); //重新获取更新后的节点
        //0.1切换绑定事件
        for (let i = 0; i < this.lis.length; i++) {
            this.lis[i].index = i;  //给每个li添加index属性保存索引值
            this.lis[i].onclick = this.toggleTab;
            this.remove[i].onclick = this.removeTab; /* 删除绑定事件 */
            this.spans[i].ondblclick = this.editTab; //编辑功能
            this.sections[i].ondblclick = this.editTab; //编辑功能
        }
        //0.2添加绑定事件
        this.add.onclick = this.addTab;
    }

    //1.切换
    toggleTab() {
        //排他思想
        that.clearClass();
        this.className = 'liactive';
        that.sections[this.index].className = 'conactive';
    }

    clearClass() {
        for (let i = 0; i < this.lis.length; i++) {
            this.lis[i].className = '';
            this.sections[i].className = '';
        }
    }

    //2.添加
    addTab() {
        that.clearClass();
        let random = Math.random();
        //2.1创建元素
        let li = `<li class="liactive"><span>测试${random}</span><span class="iconfont icon-guanbi"></span></li>`
        let section = `<section class="conactive">测试${random}</section>`
        //2.2追加到父元素中
        that.ul.insertAdjacentHTML('beforeend', li);
        that.fsection.insertAdjacentHTML('beforeend', section);
        //2.3重新初始化,获取节点并绑定事件(不重新初始化,获取的还是旧DOM)
        that.init();
    }

    //3.删除
    removeTab(e) {
        e.stopPropagation();  //阻止事件冒泡到li的切换,只删除就行
        let index = this.parentNode.index;
        that.lis[index].remove();
        that.sections[index].remove();
        that.init(); //重新获取更新后的DOM,避免出现不必要的问题

        //删除之后让它前边那个卡高亮
        if(document.querySelector('.liactive')) return;
        index--;
        that.lis[index] && that.lis[index].click(); //逻辑短路,如果index=-1就不点击
    }

    //4.修改
    editTab() {
        let value = this.innerHTML;
        // 双击禁止选定文字
        window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
        //双击生成文本框
        this.innerHTML = `<input type="text" value=${value} >`;
        let input = this.children[0];
        input.select();
        //离开文本框时把文本框的值给span
        input.onblur = function() {
            this.parentNode.innerHTML = this.value; 
        }
        input.onkeyup = function(e) {
            if(e.key === 'Enter') {
                this.blur();
            }
        }
    }
}

new Tab('#tab');

三、原型和原型链

1.构造函数中的方法浪费内存的问题

每次new一个实例,就会单独开辟一个内存空间存放实例中的sing函数,如果我们new一百个实例,就要开辟100个内存空间来存放sing函数,这显然很low

function Star(name, age) {
    this.name = name;
    this.age = age;
    this.sing = function() {
        console.log('我会唱歌');
    }
}

let ldh = new Star('刘德华','18');
let zxy = new Star('张学友','17');
console.log(ldh.sing === zxy.sing);  //false…………

2.原型对象

1、原型对象prototype可以解决上面的问题,每一个构造函数都有一个prototype原型对象。
2、一般情况下,公共属性定义在构造函数中,公共方法定义在原型对象上。
3、我们可以把那些不变的方法,直接定义在prototype对象上,这样所有的实例都可以共享这些方法,不需要再另外开辟内存空间。
4、可以看到,写到原型对象上后,ldh.sing === zxy.singtrue

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

Star.prototype.sing = function() {
    console.log('sing');
}

let ldh = new Star('刘德华','18');
let zxy = new Star('张学友','17');
console.log(ldh.sing === zxy.sing);  //true!!!

3.对象的__proto__属性

对象身上系统会自动添加__proto__属性,这个属性会自动指向构造函数的原型对象

console.log(ldh.__proto__ === Star.prototype)  //true

方法的查找规则:
1、首先看实例上有没有sing方法, 如果有就执行。
2、如果实例没有sing方法, 因为有__proto__的存在,就通过__proto__去构造函数的prototype身上去查找sing这个方法。
3、实际开发中不使用__proto__这个属性,它只是提供了一条路线指向prototype

在这里插入图片描述

4.原型中的constructor属性

对象的原型(__ proto__)和构造函数的原型对象(prototype)里都有一个属性constructor,我们成为构造函数,因为它指向构造函数本身(无限套娃,原型对象里有constructor,constructor里是构造函数,构造函数又有原型对象,原型对象又有constructor…………)。

在这里插入图片描述

有的时候我们需要手动让原型对象重新指向构造函数,比如我们要重写prototype对象

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

Star.prototype = {
    constructor: Star,  //这句话如果不加,就不知道实例指向谁了
    sing: function () {
        console.log('sing');
    }
}

let ldh = new Star('刘德华', '18');
console.log(ldh.__proto__);  //加constructor:{constructor: ƒ, sing: ƒ}
console.log(ldh.__proto__);  //不加constructor:{sing: ƒ}

不过这个地方有点没搞懂constructor有什么用。。。目前的理解是可以通过constructor找到当前对象指向谁。

三角关系:

在这里插入图片描述

5.原型链

理解下面这个图,原型链你就明白了
在这里插入图片描述

console.log(Star.prototype.__proto__ === Object.prototype);  //true
console.log(Object.prototype.__proto__);  //null

对象成员的查找规则:先从自身找,如果没有 => 通过__proto__向上找原型对象,如果没有 => 继续通过__proto__向上找原型对象,一直找到null => 如果都没就是undefined

console.log(ldh.dj);  //undefined

6.利用原型对象扩展内置对象的方法

比如我可以在数组对象中添加一个求和的方法,数组就可以直接调用

Array.prototype.sum = function() {
    let sum = 0;
    //这里面的this指向的是函数的调用者
    for (let i = 0; i < this.length; i++) {
        sum += this[i];
    }
    return sum;
}

let arr = [1,2,3,4,5];
console.log(arr.sum());  //15

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/9201.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

axios 实现 POST/PUT接口入参是 query的形式而非JSON的形式——querystring的使用

先看下什么是query的形式入参&#xff1a; 1.常规的GET请求 一般是GET请求才会是query string parameters的形式入参 参数是query string parameters的展示&#xff0c;最终效果是拼接到url地址中&#xff0c;如下图所示&#xff1a; 2.常规的POST/PUT请求 一般是POST/PUT请…

云小课|使用SQL加密函数实现数据列的加解密

摘要&#xff1a;数据加密作为有效防止未授权访问和防护数据泄露的技术&#xff0c;在各种信息系统中广泛使用。作为信息系统的核心&#xff0c;GaussDB(DWS)数仓也提供数据加密功能&#xff0c;包括透明加密和使用SQL函数加密。本文分享自华为云社区《看GaussDB(DWS)如何使用S…

电感单位亨利H单位换算

1H 1e-18EH 1e-15PH 1e-12TH 1e-9GH 1e-6MH 0.001kH 1H 1,000mH 1,000,000uH 1,000,000,000nH 1,000,000,000,000pH 1,000,000,000,000,000fH 在线H亨利单位换算 (toolxq.com)http://www.toolxq.com/front/tools/trans/unit/H 亨利是电感的国际单位制导出单位&#xff0c;符…

2022年《微信小程序从基础到uni-app项目实战》

教程推荐&#xff1a;黑马程序员前端微信小程序开发教程&#xff0c;微信小程序从基础到发布全流程_企业级商城实战(含uni-app项目多端部署)_ 一、起步 1 uni-app 简介 uni-app 是一个使用 Vue.js 开发所有前端应用的框架。开发者编写一套代码&#xff0c;可发布到 iOS、And…

Word不计算封面、目录页数将正文页码修改为第几页共几页的格式

本文介绍在Word文档中&#xff0c;不考虑封面、目录、前言等的页数&#xff0c;为正文添加“第X页&#xff0c;共X页”样式页码的方法。 在Word中&#xff0c;我们可以双击页眉或页脚部分&#xff0c;选择“页码”选项并添加页码。 其中&#xff0c;可以添加如下图所示形式的页…

Nginx配置虚拟主机

虚拟主机指的是,在一台服务器中,我们使用Nginx,来配置多个网站。 如何区分不同的网站: 端口不同域名不同 1、通过端口区分不同的虚拟主机 1.1、Nginx配置文件 &#xff08;1&#xff09;Nginx配置文件的位置 cd /usr/local/nginx/conf nginx.conf 就是Nginx的配置文件 &a…

Docker学习

Docker学习 Docker概述 Docker安装 Docker命令 Docker镜像 Docker的数据卷 DockerFile Docker网络原理 IDEA整合Docker 集群-Docker Compose Docker Swarm CI\CD jenkins Docker概述 安装教程&#xff1a;(https://docs.docker.com/engine/install/centos/) 文档…

CSS初阶语法

CSS初阶语法 文章目录CSS初阶语法1.CSS介绍2.CSS引入方式3.CSS基础选择器3.1 标签选择器(标签名字)3.2 类选择器(class)3.3 id选择器(id)3.4 通配符选择器(*)4.文字基本样式4.1 字体样式(font-size、font-weight、font-style、font-family、font)4.2 文本样式(text-indent、tex…

SpringBoot SpringBoot 开发实用篇 2 配置高级 2.1 第三方bean 属性绑定

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇2 配置高级2.1 第三方bean 属性绑定2.1.1 ConfigurationProperties2.…

ERP系统有哪些品牌?

企业资源规划 (ERP) 系统可以帮助企业在一个系统中管理其整个运营。凭借 CRM、人力资源、会计、供应链管理等功能&#xff0c;优秀的 ERP 系统可以集中并简化每位员工的日常工作。 那么&#xff0c;ERP系统有哪些品牌&#xff1f;哪个好用&#xff1f;下面就来简单介绍几个主流…

Android Studio App开发之网络通信中移动数据格式JSON的讲解以及实战(附源码 超详细必看)

运行有问题或需要源码请点赞关注收藏后评论区留言~~~ 一、移动数据格式JSON 网络通信的交互数据格式有两大类&#xff0c;分别是JSON和XML&#xff0c;前者短小精悍&#xff0c;后者表现力丰富&#xff0c;对于App来说&#xff0c;基本采用JSON格式与服务器通信。一是手机流量…

计算机网络的常见面试题

GET 和 POST 的区别 作用 GET用来获取资源&#xff0c;POST用于传输实体主体 参数位置 GET的参数放在URL中&#xff0c;POST的参数存储在实体主体中&#xff0c;并且GET方法提交的请求的URL中的数据多是2048字节&#xff0c;POST请求没有大小限制 安全性 GET方法因为参数放在…

二极管:Irush与我相干!

故事还没结束,我们必须继续讲,继续挖,边说边究。 即使时间us级不会让FUSE同学Open,FUSE后面的兄弟姐妹呢,可以扛住事吗?那可有3.4A的能量冲击啊! 不确定,就有可能。墨菲老人家曾说过:会出错的事总会出错。也就是会发生的事总会发生,只要不给时间加上一个期限,非要给…

外卖项目03---分类管理业务开发

目录 一、公共字段自动填充 41 1.1问题分析 41 ​编辑1.2公共字段自动填充---代码实现并测试 42 1.3公共字段自动填充---功能完善 43 二、新增分类 44 2.1需求分析 44 三、分类信息查询 45 3.1需求分析 45 3.2代码开发 流程分析 四、删除分类 46 4.1需求分析 46 4.2…

教你打造高性能的 Go 缓存库

我在看一些优秀的开源库的时候看到一个有意思的缓存库 fastcache&#xff0c;在它的介绍主要有以下几点特点&#xff1a; 读写数据要快&#xff0c;即使在并发下&#xff1b;即使在数 GB 的缓存中&#xff0c;也要保持很好的性能&#xff0c;以及尽可能减少 GC 次数&#xff1…

基于Fluent求解器进行二次开发到底怎么做?

作者 | 小田老师 Ansys Fluent软件作为通用的流体仿真软件&#xff0c;功能模型虽然多而全&#xff0c;但是缺乏相应的行业专业性。同时仿真分析具有一定的技术门槛&#xff0c;既需要技术人员具备行业基础&#xff0c;也需要积累相关仿真经验&#xff0c;对软件操作要有较高的…

Docker安装Redis

文章目录1.Docker搜索Redis镜像2.Docker拉取镜像3.Docker挂载配置文件4.启动Redis 容器5.测试Redis6.Docker删除Redis1.Docker搜索Redis镜像 命令&#xff1a;docker search <镜像名称> docker search redis可以看到有很多redis的镜像&#xff0c;此处因没有指定版本&…

干货|数据安全治理白皮书4.0(文末附下载链接)

数据安全治理白皮书是国内最早的数据安全治理白皮书系列&#xff0c;围绕数据安全治理&#xff0c;“新理论、新技术、新实践”&#xff0c;一次系统汇总、梳理分析与集中呈现&#xff0c;2018年-2021年&#xff0c;已发布1.0/2.0/3.0三个版本&#xff0c;2022年&#xff0c;已…

线性二分类的实现

神经网络结构 从视觉上判断是线性可分的&#xff0c;使用单层神经网络&#xff1b; 输入层设置两个输入单元&#xff0c;表示经纬度&#xff1a;&#x1d44b;&#x1d465;1,&#x1d465;2 输出层设置一个单元&#xff0c;表示地盘所属阵营&#xff1a; &#x1d467;&…

STC15W单片机防丢语音报警GPS北斗定位测距双机LORA无线手持可充电

实践制作DIY- GC0091-防丢语音报警 一、功能说明&#xff1a; 基于STC15W单片机设计-防丢语音报警 功能介绍&#xff1a; 主机硬件&#xff1a; STC15W4K48S4&#xff08;或者STM32F103CxT6&#xff09;系列最小系统板OLED显示器LORA无线通讯模块GPS北斗双模定位模块蜂鸣器…