05.js高级基础知识总结

228 阅读10分钟

1- 箭头函数

1-1 函数简写

// 箭头函数是匿名函数,一般做为参数传递
// let test = function (a,b){
//     let sum = a + b 
//     return sum
// }
// let test = (参数) => {函数体}
// 几个小细节
// 1.如果函数体只有一句,那么可以省略{},同时默认会返回函数体的结果,不能写return
// 2.如果只有一个参数,那么可以省略()
// 3.如果没有参数,()也不能省略
// let test = (a,b) =>  a + b    // 不能有大括号
let test = a =>  a + 10 

let res = test(100)
console.log(res)

2 定义一个箭头函数 没有形参 没有返回值

        const func3 = () => {
            console.log('执行业务3');
        }

3 没有形参,没有返回值 业务只有一行代码 大括号都可以省略

const func4 = () => console.log('1234');

4 只有一个形参 没有返回值 业务只有一行代码

const func5 = num =>console.log(num+1)

5 两个或者多个参数(括号不能省略),没有返回值,业务只有一行代码

const func6 = (num1,num2) => console.log(num1+num2);

6 没有形参,有返回值 业务有两行代码

        const func7 = () => {
            let a = 100;
            return a+=100
        }

7 没有形参,有返回值,业务只有一行代码

const func8 = () => 100+200 //相等于return 100+200

1-2 返回对象

1650513284255

2- 常用的数组方法

2-1 forEach

数组每个元素都执行一次回调函数。遍历数组

1650513224071

2-2 map()

通过指定函数处理数组的每个元素,并返回处理后的数组。

1650513249861

2-3 伪数组转正数组

1650513395237

2-4 every()

检测数值元素的每个元素是否都符合条件

1650540603855

2-5 some()

检测数组元素中是否有元素符合指定条件

2-6 filter()

过滤或者筛选

2-7 find()

返回符合传入测试(函数)条件的数组元素。

2-8 findIndex()

返回符合传入测试(函数)条件的数组元素索引

2-9 includes

判断一个数组是否包含一个指定的值

2-10 indexOf()

搜索数组中的元素,并返回它所在的位置。

2-11 isArray()

判断对象是否为数组。

2-12 join()

把数组的所有元素放入一个字符串

2-13 reduce

将数组元素计算为一个值(从左到右)

2-14 reverse

反转数组的元素顺序

2-15 sort

对数组的元素进行排序

3- 面向对象

3-1 概念

  1. 一种编程行业通用的写项目级的代码的思维,引导我们如何编写高质量的代码,万物皆对象-看待事物的角度,(属性:数据,行为:动作(方法))代码特点是封装和继承

3-2 对象在内存中的示意图

1650526338058

4- 创建对象的几种方式

4-1 字面量

  1. 简单粗暴
  2. 不适合创建多个同样类型的对象的场景
const obj ={ name:"悟空",height:100,age:5000};

4-2 工厂函数

  1. 容易理解
  2. 失去血缘关系,无法简单分辨对象的特征
  3. 后期无法实现继承

img

    function createPerson(name, age, height) {
      return {
        name: name,
        age: age,
        height: height
      }
    }

    function createStudent(name, age, height, grade) {
      return {
        name1: name,
        age1: age,
        height1: height,
        grade: grade
      }
    }

    // 这个不是工厂模式
    function createStudent2(name, age, height, grade) {
      this.name = name;
      this.age = age;
      this.height = height;
      this.grade = grade;
    }


    const obj1 = createPerson("八戒", 18, 500);
    const obj2 = createStudent("悟能", 83, 231, 3);
    const obj3 = new createStudent2("悟能", 83, 231, 3);

    console.log(obj1);
    console.log(obj2);
    console.log(obj3);

4-3 构造函数

  1. 可以方便的创建对象
  2. 拥有血缘关系
  3. 还有后续更多的优势
    // 1 声明函数 
    function createStudent(name, age) {
      // 2 通过 this 赋值
      this.name = name;
      this.age = age;
    }

    // 3 通过 new 来创建对象
    const obj = new createStudent("悟能", 83);

    console.log(obj);
构造函数的工作原理
  1. 开辟空间
  2. 将新的创建的对象对象构造函数中的this
  3. 为对象赋值
  4. 将创建好的对象的地址返回

4-4 造函数的弊端

同一个 say 方法占据了两份内存

    function createStudent(name, age) {
      this.name = name;
      this.age = age;
      this.say = function () {
        console.log(this.name);
      }
    }

    const obj = new createStudent("悟能", 83);
    const obj1 = new createStudent("悟能1", 84);

    console.log(obj.say === obj1.say); // false  不是同一say方法 浪费了内存

1650587215288

4-5 提取同一个 say 方法

  1. 解决了浪费内存的弊端
  2. 但是造成了 污染全局变量 的问题
    // 提前将say 声明好
    function say() {  // 污染全局变量
      console.log(this.name);
    }
    function createStudent(name, age) {
      this.name = name;
      this.age = age;
      this.say = say
    }

    const obj = new createStudent("悟能", 83);
    const obj1 = new createStudent("悟能1", 84);

    console.log(obj.say === obj1.say); // true

1650587297761

4-6 原型模式

在构造函数的原型上存放函数

  1. 解决了同一个say浪费内存的问题
  2. 解决了污染全局变量的问题
    function createStudent(name, age) {
      this.name = name;
      this.age = age;
    }
    // 将刚才的全局函数say 直接挂载到 构造函数的原型上 即可
    // prototype 是个对象 每一个构造函数都会内置有的. 我们称之为原型
    createStudent.prototype.say = function () {
      console.log(this.name);
    }

    const obj = new createStudent("悟能", 83);
    const obj1 = new createStudent("悟能1", 84);

    console.log(obj.say === obj1.say); // true
解释
  1. 原型的单词是 prototype, 原型的这个名字是行业内共同认可的名字。
  2. 原型本质是一个对象,理解为 JavaScript 自动帮我们添加的
  3. 原型是 JavaScript 自动帮我们在定义构造函数的时候添加的
  4. 所有构造函数的实例,共享一个原型
  5. 原型上一般是挂载函数

1650587549294

5- 原型链继承

利用代码的能力实现面向对象的特性封装和继承

初体验

  1. 子类student继承了父类Person的属性
    // 父类
    function Person(name, height) {
      this.name = name;
      this.height = height;
    }

    Person.prototype.say = function () {
      console.log(this.name);
      console.log(this.height);
    }

    // 子类
    function Student(grade, name, height) {
      // 借用了父类的构造函数,完成对自己的赋值
      Person.call(this, name, height)
      this.grade = grade;
    }

    // 赋值了父类原型上的所有的 属性和方法
    Student.prototype = Person.prototype;
    // 修改之类的指向
    Student.prototype.constructor = Student;

    // 创建子类的实例
    const stu = new Student("一年", "周星星", 170);
    stu.say();

需求

  1. 有一个负责创建元素的构造函数A
  2. 有一个负责创建图片的构造函数B
  3. 构造函数B可以使用构造函数A的原型上的所有的功能实现继承

效果

1650759934688

代码

      node.classList.add("element")
      node.innerText = text;
      this.node = node;
    }

    // 2 原型上挂载 共用的方法
    Element.prototype.appendTo = function (selector) {
      const parent = document.querySelector(selector);
      parent.appendChild(this.node);
    }

    // 3 创建一个实例
    const div = new Element("div", "做人开心就好");
    // 4 追加到父元素上
    div.appendTo("section");

    // 5 一个新的构造函数 用来创建图片
    function ElementImg(src) {
      // 6 借用了 1 中的构造函数,并且把参数传递了进去
      Element.call(this, "img", "");
      // 7 图片设置路径
      this.node.src = src;
    }

    // 8 继承了 父亲的构造函数上的原型上的所有 函数
    ElementImg.prototype=Element.prototype;			// 修改来原型,也就修改了构造函数
    // 9 重新将 constructor 的指向改回来
    ElementImg.prototype.constructor=ElementImg;

    // 10 创建一个图片实例:注意,实例化代码10不能在8和9之前
    const img = new ElementImg("images/01.png");
    
    // 11 创建到父元素上
    img.appendTo("section");

6- es6 class(重点)

es6 的class的出现基本上替代了es5的构造函数的原型,使代码结构上更简洁

关键字

  1. class
  2. 属性
  3. 方法
  4. 继承extends
  5. 构造函数constructor
  6. 方法重写override:子类方法覆盖父类,super:父类方法()
  7. 父类的构造函数 super :子类有构造方法且使用this前,必须使用super()

完整代码体验

    class Person {
      // 构造方法
      constructor(name) {
        // 属性
        this.name = name;
      }
      // 方法
      say() {
        console.log(this.name);
      }
    }
	// 继承
    class Student extends Person{
      constructor(name,height){
        // console.log(this);			// 语法错误:必须先调用super()才能使用this
        super(name);
        this.height=height;
      }
    }

    const s1=new Student("八神",180);
    s1.say();							// 八神

	class Saler extends Person{
        constructor(name,age){
            super(name);
            this.age = age;
        }
        // 覆盖(重写)
        say(){
            // 访问父类方法
            super.say();				// 马云
            console.log(this.age);
        }
    }

	const s2 = new Saler('马云',50);
	s2.say();							// 50

Tab栏切换

<!DOCTYPE html>
<html>

<head lang="en">
    <meta charset="UTF-8">
    <title>tab栏切换</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        ul {
            list-style: none;
        }

        .box {
            width: 400px;
            height: 300px;
            border: 1px solid #ccc;
            margin: 100px auto;
        }

        .hd {
            height: 45px;
        }

        .hd span {
            display: inline-block;
            /*将行内元素转换成行内块元素,宽高才起作用*/
            width: 90px;
            background-color: pink;
            line-height: 45px;
            text-align: center;
            cursor: pointer;
        }

        .hd span.current {
            /*交集选择器,标签指定式选择器*/
            background-color: purple;
            /*紫色*/
        }

        .bd li {
            height: 255px;
            background-color: purple;
            display: none;
            /*设置隐藏*/
        }

        .bd li.current {
            display: block;
            /*显示*/
        }
    </style>
    <script src="js/TabExchange.js"></script>
</head>

<body>
    <div class="box" id="box">
        <div class="hd">
            <span class="current">体育</span>
            <span>娱乐</span>
            <span>新闻</span>
            <span>综合</span>
        </div>
        <div class="bd">
            <ul id="list">
                <li class="current">我是体育模块</li>
                <li>我的娱乐模块</li>
                <li id="li3">我是新闻模块</li>
                <li>我是综合模块</li>
            </ul>
        </div>
    </div>

    <script>
        let tab = new TabExchange('box', 'current');
        console.dir(tab);
    </script>

</body>

</html>
class TabExchange {

    // 构造函数
    constructor(id, className) {
        // 获取id对应的元素:规范,属于最外层
        this.id = document.querySelector(`#${id}`)
        // 规定内部有两层解构:div>span
        this.hd = this.id.querySelectorAll('div>span')
        // 规范内部有两层解构:div>ul>li
        this.bd = this.id.querySelectorAll('div>ul>li')
        // 保存当前要处理的样式类
        this.style = className;

        // 调用自己的tabClick 方法处理点击事件
        this.tabClick();
    }


    // 绑定点击事件
    tabClick() {
        // 因为接下来要绑定事件,事件里面也会出现this,所以先定义变量that来保存当前TabExchange对象的this
        let that = this

        // 循环遍历,绑定事件,并进行事件处理
        this.hd.forEach(function (item, key) {
            // item就是span,key就是span的下标
            item.addEventListener('click', function () {

                // 排他:清空hd全部样式
                that.hd.forEach(function (item1) {
                    item1.classList.remove(that.style)
                })

                // 清空bd的所有样式
                that.bd.forEach(function (item2) {
                    item2.classList.remove(that.style)
                })

                // 给当前事件元素添加样式
                that.hd[key].classList.add(that.style)
                that.bd[key].classList.add(that.style)
            })
        })
    }

}

元素创建继承

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style>
    .element {
        width: 200px;
        height: 200px;
        border-radius: 50%;
        border: 10px solid #00ffff;

        text-align: center;
        font-size: 30px;
        line-height: 200px;

        overflow: hidden;
        float: left;
    }
</style>

<body>
    <script>
        // 创建一个类:可以创建普通元素,并且可以实现将元素挂到指定父元素下
        class CreateElement {
            // 构造函数,传入标签名,文本内容,和元素默认类名
            constructor(element, text, className = 'element') {
                // 1.创建元素
                const node = document.createElement(element)
                // 2.添加类名
                node.classList.add(className)
                // 3.添加文本内容(双标签有效,单标签无效)
                node.innerText = text

                // 4.将创建的元素节点存入到属性node
                this.node = node
            }

            // 将当前创建的元素 添加到指定父元素下
            appendTo(selector) {
                // 获取父元素
                const parent = document.querySelector(selector)
                // 添加子元素
                parent.appendChild(this.node)
            }
        }

        // 直接创建一个普通元素
        const div = new CreateElement('div', '做人开心就好')
        div.appendTo('body')

        // 图片标签不适用于普通元素创建(有属性src),但是想使用默认的样式和创建过程
        // 创建子类 继承 父类
        class CreateImg extends CreateElement {
            // 自己的构造函数:只需要传入图片
            constructor(src, className = 'element') {
                // 父类构造函数
                super('img', '', className)

                // 给创建的元素增加src属性
                this.node.src = src
            }
        }

        // 改变了创建的内容,但是可以直接应用父类继承的方法
        const img = new CreateImg('images/02.jpg')
        img.appendTo('body')

    </script>
</body>

</html>

7- 函数参数默认值

定义函数的同时,可以给形参一个默认值

    // 定义函数的同时,可以给形参一个默认值
    function show(msg = "大家一起快活呀") {
      console.log(msg);
    }

    show();// 打印 大家一起快活呀
    show("搞笑不");// 打印 搞笑不

8- 对象简写

在定义对象的时候,如果属性名和变量名一直,那么可以实现简写

    const name = "悟空";
    const skill = "72变";
    const say = function () { }
    const obj = {
      name, skill, say
    }
    console.log(obj);// {name:"悟空",skill:"72变",say:function(){}}

对象的方法也可以简写

    const obj = {
      say() {
        console.log(this);
      }
    }

9- 解构

提供更加方便获取数组中元素或者对象中属性的写法

获取数组中的元素

      const [a, b, c, d] = [1, 2, 3, 4];
      console.log(a, b, c, d);// 1,2,3,4

元素交互顺序

    let a = 1111;
    let b = 2222;
    [b, a] = [a, b];
    console.log(a, b);// 2222 1111

获取对象中的属性(重点)

      const obj = {
        name: "悟空",
        skill: "72变",
        say() { }
      }
      const { name, skill,say } = obj;
      console.log(name, skill,say);// 悟空 72变 function(){}

10-拓展运算符 || 剩余运算符

通过 ...符号来获取剩下的参数

函数内获取

    function show(a, ...all) {		// 只能放最后
      console.log(a);
      console.log(all);
    }


    show(1);// 1 []
    show(1, 2, 3);// 1 [2,3]

数组内获取

    const [a, ...rest] = [1, 2, 3, 4, 5];
    console.log(a); // 1
    console.log(rest);// [2, 3, 4, 5]

对象内获取

    const obj={
      name:"悟空",
      skill:"72变",
      say(){}
    }

    const {name,...others}=obj;
    console.log(name); // 悟空
    console.log(others); // {skill: "72变", say: ƒ}

11-set

永远不会有重复元素的对象

可以理解为不重复的数组

    const set = new Set([1, 5, 3, 4]);
    set.add(5);
    set.add(5);
    console.log(set);

Set对象转为数组

    const set = new Set([1, 5, 3, 4]);
    set.add(5);
    set.add(5);
    console.log(set);

    const arr = [...set];// 将set对象转数组
    console.log(arr);

12-函数的四种调用模式

根据函数内部this的指向不同,可以将函数的调用模式分为四种

  1. 函数调用模式
  2. 方法调用模式
  3. 构造函数调用模式
  4. 上下文调用模式(借用方法模式)

函数调用模式

如果一个函数不是一个对象的属性时,就是被当做一个函数来进行调用的,此时this的指向了window

function fn(){
  console.log(this);// 指向window 
}
fn();

方法调用模式

当一个函数被保存为对象的一个属性时,我们称之为一个方法。当一个方法被调用时,this被绑定到当前对象

const obj = {
  sayHi:function(){
    console.log(this);//在方法调用模式中,this指向调用当前方法的对象。
  }
}
obj.sayHi();

构造函数调用模式

如果函数是通过new关键字进行调用的,此时this被绑定到创建出来的新对象上。

function Person(){
  console.log(this);
}
Person();//this指向什么?
var p = new Person();//this指向什么?

方法借用模式

也叫上下文模式,分为 apply 与 call

call

call方法可以调用一个函数,并且可以指定这个函数的this指向

    const RichWumon = {
      name: "富婆",
      say: function () {
        console.log(this.name, " 我要重金求子");
      }
    }

    const obj = {
      name: "屌丝"
    }

    RichWumon.say();			// 富婆
    RichWumon.say.call(obj);	// 屌丝
call应用
  1. 将伪数组转换成数组
	
    let divs = document.querySelectorAll('div');
    // let divs = document.body.children;
    console.log(divs);

    function change(nodelist) {
        console.log(Object.prototype.toString.call(nodelist));
        return Array.prototype.slice.call(nodelist);

    }
apply

就是apply()方法接受的是一个包含多个参数的数组。而call()方法接受的是若干个参数的列表

可以利用apply 将 刚才的call 的代码修改一下

   const RichWumon = {
     name: "富婆",
     say: function () {
       console.log(this.name, " 我要重金求子");
     }
   }

   const obj = {
     name: "屌丝"
   }

   RichWumon.say();			// 富婆
   RichWumon.say.apply(obj);	// 屌丝
apply应用
  1. 简化log方法
	// 简化log方法
    function log() {
        // 不需要改变this
        console.log.apply(console, arguments);
    }
bind方法
  1. **bind()**方法创建一个新的函数, 可以绑定新的函数的this指向
var name = '张三';
function Fn(){
    this.age = 1;
    
    console.log(this.name + this.age);
}

Fn();			// 张三 1

// 返回值:新的函数
// 参数:新函数的this指向,当绑定了新函数的this指向后,无论使用何种调用模式,this都不会改变。
let obj = {
    name:'小强',
}
const newFn = Fn.bind(obj);
newFn();		// 小强 1
区别

1650766349734