• 【知识总结】金九银十offer拿到手软的前端面试题——Javascript篇(一)


    Javascript面向对象&继承

    1、原型链

    原理: 使用父类的示例重写子类的原型。

    function SuperType(){
    	this.prototype = true
    }
    SuperType.prototype.getSuperValue = function(){
    	return this.prototype
    }
    function SubType(){
    	this.subproperty = false
    }
    //继承
    SuperType.prototype = new SuperType()
    SuperType.prototype.getSuperValue = function(){
    	return this.subproperty()
    }
    var instance = new SubType()
    alert(instance.getSubValue)  //true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    缺点:

    1. 父类包含引用类型值的原型属性会被子类共享
    2. 在创建子类实例时,不能向父类的构造函数中传递参数

    2、借用构造函数

    原理: 在子类构造函数的内部调用父类的构造函数,将子类执行环境this用call方法传到父类的构造函数中,使父类中的属性和方法被重写到子类上。

    function SuperType(){
    	this.colors = ["red", "green", "blue"]
    }
    function SubType(){	//继承了SuperType
    	SuperType.call(this)
    }
    
    var instance1 = new SubType()
    instance1.color.push("black")
    alert(instance1.colors) 	//"red, green, blue, black"
    var instance2 = new SubType()
    alert(instance2.colors)		//"red, green, blue"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    缺点:

    • 父类中的方法无法被复用,每个方法都要在子类上重新创建

    优点:

    • 属性和方法不会被子类共享
    • 可以向父类构造函数中传递参数

    3、组合式继承

    原理: 使用原型链实现对原型属性和方法的继承,借用构造函数实现对实例属性的继承

    function SuperType(name){
    	this.name = name
    	this.color = ["red", "blue"]
    }
    SuperType.prototype.sayName = function(){
    	alert(this.name)
    }
    function SubType(name, age){ //继承属性
    	SuperType.call(this.age)
    	this.age = age
    }
    
    //继承方法
    SubType.prototype = new SuperType()
    SubType.prototype.constructor = SubType
    SubType.prototype.sayAge = function(){
    	alert(this.age)
    }
    var instance1 = new SubType("Gracy" , 29)
    instance1.colors.push("black")
    alert(instance1.colors) //"red,blue,black"
    instance1.sayName() //"Gracy"
    instance1.sayAge()	//29
    var instacne2 = new SubType("Jam", 25)
    alert(instance2.colors) //"red,blue"
    instance1.sayName() //"Jam"
    instance2.sayAge() //25
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    优点:

    • 使用原型链和借用构造函数继承两种方法,取长补短。实现了函数复用,又能保证每个子类不会共享父类的引用类型属性。

    四、寄生组合式继承

    原理: 通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。就是使用寄生式继承父类的原型,然后再将结果指定给子类的原型。

    //原型式继承,相当于 Object.create()
    function object(o){
        function F(){}
        F.prototype = o;
        return new F();
    }
     
    //寄生式继承
    function inheritPrototype(subType, superType){
        var prototype = object(superType.prototype); //创建对象
        prototype.constructor = subType; //增强对象
        subType.prototype = prototype; //指定对象
    }
     
    //寄生组合式继承
    function SuperType(name){
        this.name = name;
        this.colors = ["red", "blue", "green"];
    }
    SuperType.prototype.sayName = function(){
        alert(this.name);
    };
    function SubType(name, age){
        SuperType.call(this, name);
        this.age = age;
    }
    inheritPrototype(SubType, SuperType);
    SubType.prototype.sayAge = function(){
        alert(this.age);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    优点:

    • 改进组合模式中父类构造函数被调用两次这个缺陷。寄生组合式继承是引用类型最理想的继承模式。

    5、class

    在es6 中,官方给出了 class 关键字来实现面向对象风格的写法,但本质上是寄生组合式继承的语法糖。

    class  Person(){
    	constructor(age){
    		this.age_ = age
    	}
    	sayAge(){
    		console.log(this.age_)
    	}
    	//静态方法
    	static create(){
    		return new Person(Math.floor(Math.random()*100))
    	}
    }
    //继承普通构造函数
    class Doctor extends Person{}
    
    const doctor = new Doctor(32)
    doctor.sayAge() //32
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    原型和原型链

    1、原型

    1)原型是什么?

    给其他对象提供共享属性的对象,简称为原型。

    所有对象,都有一个隐式引用,它被称之为这个对象的prototype原型。当访问对象的某个属性时,会先在对象本身的属性上寻找,没找到就会去它的原型对象上寻找。

    __proto__ & constructor 概念简介:

    • proto:隐式原型, js标准里对象的原型无法直接通过属性名访问,但多数浏览器实现了__proto__属性来访问对象的原型
    • constructor:构造器,创建一个函数时会为它增加一个prototype属性,指向原型对象,原型对象自动获得一个名为constructor的属性,指回与之关联的构造函数。

    2)原型的作用

    作用: 共享属性

    如果在一个对象上找不到某个属性,就会去它的原型对象上找,以此类推直至找到,或者寻找到原型链的终点都没找到则不存在这个属性;这个特性让任意创建的对象都拥有通用的各种方法,例如:toString、hasOwnProperty、isPrototypeOf等

    3)原型的应用

    1. 属性委托:普通对象上默认的方法都是来自于Object.prototype上的方法
    const obj = {};
    obj.toString();
    
    • 1
    • 2
    1. 面向对象(类)封装:Javscript本身没有类,es6中的类也不过是寄生组合式封装的语法糖。
    function Person(){}
    Person.prototype.name = 'tom'
    Person.prototype.sayName = function(){
    	console.log(this.name)
    }
    const person1 = new Person()
    person1.sayName() //"tom"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 继承:子类通过原型实现对父类属性和方法的继承,本质上是在子类实例上找不到的属性方法在子类的原型上找到。js的继承不是复制,而是委托。委托行为意味着某些对象在找不到属性或方法引用时会把这个请求委托给另一个对象。
    function SuperType(){
    	this.name = 'tom'
    }
    SuperType.prototype.sayName = function(){
    	alert(this.name)
    }
    function SubType(){}
    
    //继承了SuperType()
    SubType.prototype = new SuperType()
    const instance = new SubType()
    instance.sayName() // 'tom'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4)优缺点

    优点

    • 节约空间:Object.prototype上的众多方法(toString、hasOwnProperty、isPrototypeOf等)都不需要重新复制,通过原型链就可以找到并使用。
    • 灵活度高:自由度大

    缺点

    • 难理解:别的语言没有,原型的原理细究起来也较为复杂。
    • 易被覆盖和修改:引用类型的原型属性会被所有实例共享,所以修改也会被共享。

    2、原型链

    什么是原型链: 如果要访问对象中并不存在的一个属性,就会查找内部prototype关联的
    对象。在查找属性时对它进行层层寻找遍历,这个关联关系实际上定义了一条原型链。

    原型链最终会指向Object.prototype,原型链的终点是Object.prototype.__proto__ 也就是null

    3、使用

    原型属性判断&设置

    1)getPrototypeOf

    Object.getPrototypeOf(obj)间接访问指定对象的prototype对象。

    2)setPrototypeOf

    Object.setPrototypeOf(obj, obj1) 间接设置指定对象的prototype对象。

    3)hasOwnProperty

    是否仅属于对象本身的属性。

    枚举方法

    1)getOwnPropertyNames

    返回所有实例本身的属性。

    2)in

    只要通过对象可以访问,in操作符就返回true,也就是说在对象或者原型链上存在,就会返回true

    3)for…in

    可以通过对象访问,且可以枚举出来的属性都会被返回。包括实例属性和原型属性。

    4)Object.keys

    对象上所有可枚举的实例本身属性。

    5) Object.values

    返回对象值的数组 版本:es8+

    6)Object.entries

    返回键/值对的数组 版本:es8+

    闭包

    定义: 闭包是指有权访问另一个函数作用域中的变量的函数。

    1、变量的作用域

    变量的作用域,就是指变量的有效范围。随着代码执行环境创建的作用域链往外层逐层搜索,一直搜索到全局对象为止。

    2、变量的生存周期

    • 对于全局对象来说,生存周期是永久的,除非主动销毁
    • 一般对于函数作用域或局部作用域(let、const),会随着函数调用的结束而被销毁
    • 当函数内部调用外部变量就产生闭包,这时候变量的回收策略可以参考引用计数和垃圾回收策略

    3、内存泄漏

    由于IE的js对象和dom对象使用不同的垃圾收集方法,因此闭包在IE中会导致内存泄露问题,也就是无法销毁在内存中的dom元素。

    因此我们在不需要使用dom时,应将dom解除引用来避免内存泄露:

    function closure(){
        // div用完之后一直存在内存中,无法被回收
        var div = document.getElementById('div');
        div.onclick = function () {
           console.log(div.innerHTML);// 这里用oDiv导致内存泄露
        };
    }
    
    // 解除引用避免内存泄露
    function closure(){    
        var div = document.getElementById('div');    
        var test = div.innerHTML;
        div.onclick = function () {
            console.log(test);
        };
        div = null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    4、闭包总结

    • 形成:函数中嵌套函数
    • 作用:函数内部调用外部变量、构造函数的私有属性、延长变量的生命周期
    • 优点:希望一个变量长期存在内存中,模块化代码避免全局变量的污染,私有属性
    • 缺点:无法回收闭包中引用变量,容易造成内存泄漏

    5、闭包的应用

    闭包可以使代码组织方式的自由度大大提升,在日常使用中有非常广泛的用途。

    简单的有:

    • ajax调用的成功回调
    • 事件绑定的回调方法
    • setTimeout的延时回调
    • 函数内部返回另一个匿名函数

    举例一些应用场景:

    1)构造函数的私有属性:由于js没有类的实现,某些不希望被外部修改的私有属性可以通过闭包的方式实现。

    function Person(param) {
        var name = param.name; // 私有属性
        this.age = 18; // 共有属性
    
        this.sayName = function () {
            console.log(name);
        }
    }
    
    const tom = new Person({name: 'tom'});
    tom.age += 1; // 共有属性,外部可以更改
    tom.sayName(); // tom
    tom.name = 'jerry';// 共有属性,外部不可更改
    tom.sayName(); // tom
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2)计算缓存

    // 平方计算
    var square = (function () {
        var cache = {};
        return function(n) {
            if (!cache[n]) {
                cache[n] = n * n;
            }
            return cache[n];
        }
    })();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3)函数节流、防抖

    // 节流
    function throttle(fn, delay) {
        var timer = null, firstTime = true;
        return function () {
            if (timer) { return false;}
            var that = this;
            var args = arguments;
            fn.apply(that, args);
            timer = setTimeout(function () {
                clearTimeout(timer);
                timer = null;
            }, delay || 500);
        };
    }
    // 防抖
    function debounce(fn, delay) {
        var timer = null;
        return function () {
            var that = this;
            var args = arguments;
            clearTimeout(timer);// 清除重新计时
            timer = setTimeout(function () {
                fn.apply(that, args);
            }, delay || 500);
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    函数节流和防抖

    1、节流

    只在开始执行一次,未执行完成过程中触发的可忽略,核心在于关闭锁。

    举个例子:多次点击按钮提交表单,第一次有效,按钮防重

    // 节流
    function throttle(fn, delay) {
        var timer = null;
        return function () {
            if (timer) { return false;}
            var that = this;
            var args = arguments;
            fn.apply(that, args);
            timer = setTimeout(function () {
                clearTimeout(timer);
                timer = null;
            }, delay || 500);
        };
    }
    
    
    // 使用
    function clickHandler() {
        console.log('节流click!');
    }
    const handler = throttle(clickHandler);
    document.getElementById('button').addEventListener('click', handler);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2、防抖

    只执行最后一次被触发的,清除之前的异步任务,核心在于清零。

    举个例子:页面滚动处理事件,搜索框输入联想,最后一次有效

    // 防抖
    function debounce(fn, delay) {
        var timer = null;
        return function () {
            var that = this;
            var args = arguments;
            clearTimeout(timer);// 清除重新计时
            timer = setTimeout(function () {
                fn.apply(that, args);
            }, delay || 500);
        };
    }
    
    
    // 使用
    function clickHandler() {
        console.log('防抖click!');
    }
    const handler = debounce(clickHandler);
    document.getElementById('button').addEventListener('click', handler);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    ⚠️关于容易搞混的问题,就记住:节流第一次有效,防抖反之

    Javascript 中的this

    1、this的指向

    this总是指向执行时的当前对象。

    js的this总是指向一个对象,而具体指向哪个对象是运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。

    也就是说this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

    场景的几种情况:

    • 作为对象的方法调用
    • 作为普通函数调用
    • 构造器调用
    • Function.prototype.callFunction.prototype.apply 调用。

    1)作为对象的方法调用

    对象的方法被调用时,this指向该对象。

    var obj ={
    	a:1,
    	getA(){
    		alert(this==obj)
    	}
    }
    obj.getA()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2)作为普通函数的调用

    当函数不作为对象的属性被调用时,也就是我们常说的普通函数方式,此时的this总是指向全局对象。

    //在浏览器的JavaScript 里,这个全局对象是window 对象。
    window.name = 'globalName';
    var getName = function(){
        return this.name;
    };
    console.log( getName() ); // 输出:globalName
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3)构造器调用

    通常情况下,构造器里的this就是指向返回的这个对象

    • 如果构造器不显式的返回任何数据,或者返回一个非对象的数据类型,this指向返回的这个对象
    • 如果构造器显式的返回了一个对象,则实例化的结果会返回这个对象,而不是this
    //构造器里的this 就指向返回的这个对象,见如下代码:
    var MyClass = function(){
        this.name = 'sven';
    };
    var obj = new MyClass();
    alert ( obj.name ); // 输出:sven
    
    
    var MyClass = function(){
        this.name = 'sven';
        return { // 显式地返回一个对象
            name: 'anne'
        }
    };
    var obj = new MyClass();
    alert ( obj.name ); // 输出:anne
    
    
    var MyClass = function(){
        this.name = 'sven'
        return 'anne'; // 返回string 类型
    };
    var obj = new MyClass();
    alert ( obj.name ); // 输出:sven
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    4)call 或 apply 调用

    apply和call可以动态地改变传入函数的 this

    var obj1 = {
        name: 'sven',
        getName: function(){
            return this.name;
        }
    };
    var obj2 = {
        name: 'anne'
    };
    console.log( obj1.getName() ); // 输出: sven
    console.log( obj1.getName.call( obj2 ) ); // 输出:anne
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2、call, apply 和 bind

    1)简介和区别

    call和apply的作用一模一样,区别仅在于传入参数形式的不同。

    apply

    apply接收两个参数,第一个参数指定了函数体内this对象的指向,第二个参数为一个带下标的集合,可以为数组,也可以为类数组(对象本身可以存储属性且对象的length属性可以读写)

    call

    call传入的参数数量不固定,第一个参数也是代表函数体内的this指向,从第二个参数往后,每个参数被依次传入函数。

    bind

    bind参数类型和call相同,不过它不会执行函数,而是修改this后返回一个新的函数

    ar func = function( a, b, c ){
        alert ( [ a, b, c ] );
    };
    func.apply( null, [ 1, 2, 3 ] );
    func.call( null, 1, 2, 3 );
    
    • 1
    • 2
    • 3
    • 4
    • 5

    手动实现apply

    Function.prototype.apply = function(context){
    	var context = context || window
    	var args = arguments[1] || []
    	context.fn = this
    	var result = context.fn(...args)
    	delete context.fn
    	return result
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    call和bind是包装在apply上面的语法糖

    Function.prototype.call = function( context ){ 
        var argus = [];
        for (var i = 1; i < arguments.length; i++) {
            argus.push(arguments[i]);
        }
        this.apply(context, argus);
    };
    
    Function.prototype.bind = function( context ){ 
        var self = this; // 保存原函数
        // 返回一个新的函数
        return function(){ 
            // 执行新的函数的时候,会把之前传入的 context // 当作新函数体内的 this
            return self.apply( context, arguments );
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2)用途

    • 改变 this 指向
    • 借用其他对象的方法: 在操作类数组(比如 arguments) 的时候,可以找 Array.prototype 对象借用方法Array.prototype.push.call(a, 'first' )

    自己动手实现Promise

    简介

    promise有两大特点:

    • 对象的状态不受外界影响,promise对象代表一个异步操作。有三种状态:pending、fulfilled、rejected。只有异步操作的结果,可以决定当前是哪一种状态,其他任何操作都无法改变这个状态。
    • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。状态的改变只有两种情况:从pending->fulfilled,或从pending->rejected,只要这两种清空发生了,状态就凝固了,不会再变化,这称之为 resolved(已定型)。

    优点:

    • promise是一种异步编程的解决方案,比传统的解决方案(回调函数和事件)更合理和更强大
    • 链式操作相对于回调函数体验更好

    缺点:

    • 无法取消promise,一旦开始立即执行,无法中途暂停
    • 若不设置回调函数,promise内部抛出错误,不会反应到外部
    • 当pending时,无法得知进行到哪个阶段

    实现功能:then、catch

    // 私有变量,三个状态
    const PENDING = 'pending';
    const FULFILLED = 'fulfilled';
    const REJECTED = 'rejected';
    /**
     * 自己实现Promise
     */
    class MyPromise {
        constructor(calllback) {
            // 状态
            this.status = PENDING;
            // 值
            this.value = undefined;
            // 成功回调列表
            this.resolveList = [];
            // 失败回调列表
            this.rejectList = [];
    
            const resolve = (value) => {
                // 状态仅允许修改一次
                if (this.status !== PENDING){
                    return;
                }
    
                setTimeout(() => {
                    this.value = value;
                    this.status = FULFILLED;
                    for (const func of this.resolveList) {
                        func(this.value);
                    }
                }, 0);
    
            }
    
            const reject = (reason) => {
                // 状态仅允许修改一次
                if (this.status !== PENDING){
                    return;
                }
                setTimeout(() => {
                    this.value = reason;
                    this.status = REJECTED;
                    for (const func of this.rejectList) {
                        func(this.value);
                    }
                }, 0);
            }
    
            try {
                calllback(resolve, reject);
            } catch (err) {
                reject(err);
            }
        }
        // 处理执行结果
        then(onResolve, onReject) {
            let promise = null;
            if (this.status === PENDING) {
                promise = new MyPromise((resolve, reject) => {
                    // 解决值穿透
                    onResolve = typeof onResolve === 'function' ? onResolve : (value) => value;
                    onReject = typeof onReject === 'function' ? onReject : (reason) => {throw reason};
                    this.resolveList.push(function(innerValue) {
                        try {
                            const value = onResolve(innerValue);
                            // resolve(value);
                            runThen(promise, value, resolve, reject);
                        } catch (err) {
                            reject(err);
                        }
                    });
                    this.rejectList.push(function(innerReason) {
                        try {
                            const value = onReject(innerReason);
                            // resolve(value);
                            runThen(promise, value, resolve, reject);
                        } catch (err) {
                            reject(err);
                        }
                    });
                });
            } else {
                const innerValue = this.value;
                const isFulfilled = this.status === STATUS.FULFILLED;
                promise = new MyPromise((resolve, reject) => {
                    try {
                        const value = isFulfilled ? onResolve(innerValue) : onReject(innerValue); // 失败状态调用 onReject
                        // resolve(value);
                        runThen(promise, value, resolve, reject);
                    } catch (error) {
                        reject(error)
                    }
                })
            }
            return promise;
            // 运行promise,处理then返回新promise和循环引用问题
            function runThen(promise, value, resolve, reject) {
                if (promise === value) {
                    reject(new TypeError('Chaining cycle detected for promise'));
                    return;
                }
                if (value instanceof MyPromise) {
                    value.then((val) => runThen(promise, val, resolve, reject), (reason) => reject(reason));
                } else {
                    resolve(value);
                }
            }
        }
        // 处理失败
        catch(onReject) {
            return this.then(null, onReject);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
  • 相关阅读:
    python获取文件夹下所有图片目录
    代码随想录训练营第47天休息日|210.课程表II
    线性代数 化简矩阵和零空间矩阵
    C. Yet Another Card Deck-Educational Codeforces Round 107 (Rated for Div. 2)
    ubuntu16因swap分区uuid错误启动慢排查
    【SpringBoot】yaml配置文件语法—总结回顾
    力扣-459.重复的子字符串
    斜率优化之李超线段树
    【Python项目】毕业设计必备——Python实现一个GUI版本的学生信息管理系统 | 附源码
    ubuntu22.04安装公司安全VPN的方案
  • 原文地址:https://blog.csdn.net/weixin_42224055/article/details/126210391