• JavaScript高级复习上(59th)


    1、类 constructor 构造函数

    constructor() 方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动给我们创建一个 constructor()

    语法:

    // 创建一个学生类
    class Student {
        constructor(uname, age, major) {
            this.uname = uname;
            this.age = age;
            this.major = major;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    类的实例化——创建对象

    let Ty = new Student("TY", 22, "CS");
    console.log(Ty.uname); // TY
    
    • 1
    • 2

    注意:

    1、通过 class 关键字创建类, 类名我们还是习惯性定义首字母大写
    2、类里面有个 constructor 函数,可以接受传递过来的参数,同时返回实例对象
    3、constructor 函数 只要 new 生成实例时,就会自动调用这个函数, 如果我们不写这个函数,类也会自动生成这个函数
    4、生成实例 new 不能省略
    5、最后注意语法规范, 创建类:类名后面不要加小括号。生成实例:类名后面加小括号, 构造函数不需要加 function

    2、类中添加方法

    语法:直接在类中写方法名和括号即可,如下所示。

    class Student {
        constructor(uname, age, major) {
            this.uname = uname;
            this.age = age;
            this.major = major;
        }
        // 类中添加方法
        sing() {
            console.log(this.uname + "会唱歌");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    创建实例:

    let Ty = new Student("TY", 22, "CS");
    Ty.sing(); // Ty会唱歌
    
    • 1
    • 2

    注意:
    方法之间不能加逗号分隔,同时方法不需要添加 function 关键字。

    3、类的继承

    1、继承

    1、现实中的继承:子承父业,比如我们都继承了父亲的姓。
    2、程序中的继承:子类可以继承父类的—些属性和方法。
    语法:使用 extends 关键字。

    class Son extends Father {
        // class body
    }
    
    • 1
    • 2
    • 3

    2、super 关键字

    super 关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。

    class Person {
        constructor (uname, age) {
            this.uname =uname;
            this.age = age;
        }
    }
    class Student extends Person {
        constructor (uname, age, major) {
            // super 将子类的参数传递给父类构造函数,减少代码量
            super(uname, age);
            // 子类可以有自己独有的属性
            this.major = major;
        }
    }
    let rick = new Student("Rick", 22, "数学");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3、super 调用父类普通函数

    class Parent {
        sayHi() {
            return "Father: hello";
        }
    }
    class Child extends Parent {
        sayHi() {
            // super 调用父类普通函数
            console.log(super.sayHi());
        }
    }
    let man = new Child();
    man.sayHi(); // Father: hello
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4、super 必须放到子类 this 之前

    子类在构造函数中使用 super, 必须放到 this 前面。

    class Father {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
    }
    class Son extends Father {
        constructor(x, y, z) {
            super(x, y, z);
            this.z = this.z;
        }
    }
    let obj = new Son();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4、使用类的注意点

    1、在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象
    2、类里面的共有属性和方法一定要加 this 使用
    3、类里面的 this 指向问题:constructor 里面的 this 指向实例对象, 方法里面的 this 指向这个方法的调用者

    5、构造函数存在的问题

    构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。

    虽然构造函数方法很好用,但是存在浪费内存的问题。
    在这里插入图片描述
    函数内容一样的函数sing(),会在内存中产生两份,占两份空间。 我们希望所有的对象使用同一个函数,这样就比较节省内存,于是就有了构造函数原型对象 prototype

    6、构造函数原型对象prototype

    1、构造函数通过原型分配的函数是所有对象所共享的。

    2、JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。注意这个 prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。

    3、我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。

    关键概念理解:

    1、原型是一个对象,我们也称为 prototype 为原型对象。
    2、原型的作用:共享方法

    function Star(uname, age) {
        this.uname = uname;
        this.age = age;
    }
    Star.prototype.sing = function () {
            console.log('我会唱歌');
    }
    var ldh = new Star('刘德华', 18);
    var zxy = new Star('张学友', 19);
    console.log(ldh.sing === zxy.sing); // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    上述代码将输出 true,因为 sing 方法已被共享。

    7、对象原型 __ proto __

    1、任何对象身上都会有一个属性 __ proto __,指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __ proto __原型的存在。

    2、 __ proto __ 对象原型和构造函数的原型对象 prototype 是等价的,都是对象。

    3、方法的查找规则: 首先先看ldh 对象身上是否有 sing 方法,如果有就执行这个对象上的sing, 如果没有sing
    这个方法,因为有__proto__ 的存在,就去构造函数原型对象prototype身上去查找sing这个方法

    4、__proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype
    在这里插入图片描述

    8、constructor 构造函数

    1、对象原型__proto__和构造函数原型对象prototype里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身。 constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。

    2、一般情况下,对象的方法都在构造函数的原型对象中设置。如果有多个对象的方法,我们可以 给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。

    function Human(uname, age) {
        this.uname = uname;
        this.age = age;
    }
    Human.prototype = {
        // 添加一个 constructor 指向原来的构造函数
        constructor: Human,      //
        sayHi: function () {
            console.log("Hey!");
        },
        eat: function () {
            console.log("Eat something.");
        }
    }
    let rick = new Human('rick', 35);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    9、构造函数、实例、原型对象三者之间的关系

    在这里插入图片描述

    10、原型链(重点)

    在这里插入图片描述
    1.只要是对象就有__proto__ 原型, 指向原型对象
    2.我们Star原型对象里面的__proto__原型指向的是 Object.prototype
    3.我们Object.prototype原型对象里面的__proto__原型 指向为 null

    11、JavaScript 的成员查找机制(规则)

    1.当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
    2.如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。
    3.如果还没有就查找原型对象的原型(Object的原型对象)。
    4.依此类推一直找到 Object 为止(null)。
    5.__proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。

    12、自己总结的原型链

    说起原型链就要先说一下构造函数,什么是构造函数呢,它可以用来创建对象实例,当我们new了构造函数就会创建一个实例,但是构造函数有一个问题就是里面存放的函数方法每new一次就会多开辟一份内存空间,为了解决这个问题构造函数里面有一个属性叫prototype原型对象,专门存放公有的方法,实例对象怎么调用这个方法呢,因为所有对象身上都有对象原型__proto__,就可以通过它来调用,同时对象身上都有一个constructor 属性可以指回构造函数本身,原型链其实就是一个查找的过程,当我们想要查找对象实例身上的属性时先在本身找,找不到再用对象原型__proto__去原型对象prototype上找,再找不到再去原型对象的prototype上找,最终找不到返回最终指向null

    13、扩展内置对象

    可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能
    注意:数组和字符串内置对象不能给原型对象覆盖操作 Array.prototype = {}
    只能是 Array.prototype.xxx= function(){} 的方式。

    14、改变函数内部 this 指向

    JavaScript 为我们专门提供了一些函数方法来帮我们更优雅的处理函数内部 this 的指向问题,常用的有 bind()call()apply() 三种方法。

    1、call方法

    call() 方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。

    fun.call(thisArg, arg1, arg2, ...);
    
    • 1

    参数说明:

    1、thisArg:在 fun 函数运行时指定的 this 值
    2、arg1,arg2:传递的其他参数
    3、返回值就是函数的返回值,因为它就是调用函数
    4、因此当我们想改变 this 指向,同时想调用这个函数的时候,可以使用 call,比如继承36

    2、apply方法(数组)

    apply() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。

    fun.apply(thisArg, [argsArray])
    
    • 1

    1、thisArg:在fun函数运行时指定的 this 值
    2、argsArray:传递的值,必须包含在数组里面
    3、返回值就是函数的返回值,因为它就是调用函数
    4、因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值

    let arr = [-10, 2, 12, 3, 1];
    let max = Math.max.apply(Math, arr);
    console.log(max); // 12
    
    • 1
    • 2
    • 3

    案例:

    function fn(...args){
        console.log(this,args);
    }
    let obj = {
        myname:"张三"
    }
    
    fn.apply(obj,[1,2]); // this会变成传入的obj,传入的参数必须是一个数组;
    fn(1,2) // this指向window
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    当第一个参数为null、undefined的时候,默认指向window(在浏览器中)

    3、bind方法(不会调用)

    bind() 方法不会调用函数。但是能改变函数内部 this 指向。

    let fn = fun.bind(thisArg, arg1, arg2, ...)
    
    • 1

    1、thisArg:在 fun 函数运行时指定的 this 值
    2、arg1,arg2:传递的其他参数
    3、返回由指定的 this 值和初始化参数改造的 原函数拷贝
    4、因此当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用 bind
    5、bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入)
    6、改变this指向后不会立即执行,而是返回一个永久改变this指向的函数

    unction fn(...args){
        console.log(this,args);
    }
    let obj = {
        myname:"张三"
    }
    
    const bindFn = fn.bind(obj); // this 也会变成传入的obj ,bind不是立即执行需要执行一次
    bindFn(1,2) // this指向obj
    fn(1,2) // this指向window
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4、apply、call、bind三者的区别

    1、三者都可以改变函数的this对象指向
    2、三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window
    3、三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入
    4、bind是返回绑定this之后的函数,不会调用函数,apply、call 则是立即执行

    15、数组新增方法

    迭代(遍历)方法:forEach()、map()、filter()、some()、every()

    1、forEach()

    forEach 方法用于遍历数组,不对原数组进行修改,没有返回值。

    array.forEach(function(Value, index, arr), thisArg);
    
    • 1

    该方法接收一个函数 function 参数,其中,该函数内又有三个参数:

    1、Value:数组当前项的值,最后输出Value就可以实现遍历了
    2、index:数组当前项的索引
    3、arr:数组对象本身
    一般可以省略后面回调函数中后两个参数和 thisArg 参数,即:

    array.forEach(function(Value));
    
    • 1
    <body>
        <script>
            // forEach 迭代(遍历) 数组
            var arr = [1, 2, 3];
            var sum = 0;
            arr.forEach(function(value, index, array) {
                console.log('每个数组元素' + value);
                console.log('每个数组元素的索引号' + index);
                console.log('数组本身' + array);
                sum += value;
            })
            console.log(sum);
        </script>
    </body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2、map()

    map() 方法遍历一个数组,首先创建一个新数组,新数组中的每个元素是是调用一次所提供的函数参数后的返回值,然后返回这个新数组。

    let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
    let mapResult = numbers.map((item, index, array) => item * 2);
    console.log(mapResult) // 2,4,6,8,10,8,6,4,2
    
    • 1
    • 2
    • 3

    3、filter()

    对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回

    let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
    let filterResult = numbers.filter((item, index, array) => item > 2);
    console.log(filterResult); // 3,4,5,4,3
    
    • 1
    • 2
    • 3

    filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组,注意它返回一个新数组。

    4、some()

    对数组每一项都运行传入的测试函数,如果至少有1个元素返回 true ,则这个方法返回 true

    let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
    let someResult = numbers.some((item, index, array) => item > 2);
    console.log(someResult) // true
    
    • 1
    • 2
    • 3

    通俗点:查找数组中是否有满足条件的元素

    5、every()

    对数组每一项都运行传入的测试函数,如果所有元素都返回 true ,则这个方法返回 true

    let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
    let everyResult = numbers.every((item, index, array) => item > 2);
    console.log(everyResult) // false
    
    • 1
    • 2
    • 3

    16、Object.defineProperty()

    Object.defineProperty() 定义对象中新属性或修改原有的属性。

    Object.defineProperty(obj, prop, descriptor)
    
    • 1

    参数:

    1、obj:必需。目标对象
    2、prop:必需。需定义或修改的属性的名字
    3、descriptor:必需。目标属性所拥有的特性
    Object.defineProperty() 第三个参数 descriptor 说明: 以对象形式 { } 书写。

    // descriptor 对象的参数值均为默认值
    Object.defineProperty(obj, prop, {
        value: undefined,
        writable: false,
        enumerable: false,
        configurable: false
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1、value:设置属性的值 默认为 undefined
    2、writable:值是否可以重写。true | false 默认为 false
    3、enumerable:目标属性是否可以被枚举。true | false 默认为 false
    4、configurable:目标属性是否可以被删除或是否可以再次修改特性 true | false,默认为 false

    descriptor 对象的参数默认值说明,使用 defineProperty 新增一个对象属性,该属性只读,不可迭代,不可再被修改特性。

  • 相关阅读:
    【JAVA刷题初阶】刷爆力扣第十一弹——二叉树
    VsFtpd的环境搭建,虚拟登录,Linux服务器
    spring cloud系统安装涉及的技术说明
    vim的基本操作
    AIGC 是通向 AGI 的那条路吗?
    python多维数组切片
    PDF格式分析(六十八)——注释(批注)概要
    java分布式项目需要进行注意的事项(代码层面)
    【短道速滑十】非局部均值滤波的指令集优化和加速(针对5*5的搜索特例,可达到单核1080P灰度图 28ms/帧的速度)。
    借鉴前端事件机制的Spring AOP
  • 原文地址:https://blog.csdn.net/weixin_55608297/article/details/128008712