闭包治愈“全局变量恐惧症”,利用闭包实现JavaScript私有变量

news2024/10/6 18:18:07

在这里插入图片描述

文章目录

  • I. 介绍
    • 对闭包的定义和概述
    • 为什么理解闭包很重要
  • II. 函数与作用域
    • 函数的作用域和生命周期
    • 闭包是如何利用函数的作用域的
  • III. 闭包的实现
    • 闭包的实现方式
    • 如何创建闭包
    • 闭包的应用场景
  • IV. 闭包的优缺点
    • 闭包的优点
      • 数据的封装
      • 可以实现高阶函数
    • 闭包的缺点
      • 内存占用
      • 对程序员编写能力和理解能力要求更高
  • V. 总结与展望
    • 在实际开发中如何使用闭包
    • 未来闭包的发展趋势

I. 介绍

对闭包的定义和概述

闭包是指在函数内部定义函数,并且可以访问到外部函数的变量的一种机制

通俗来说,闭包就是“函数内部的函数”,且这个内部函数可以访问到外部函数的变量,即使外部函数已经执行完毕,内部函数仍然可以访问外部函数的作用域。

这种特性使闭包在JavaScript中非常有用,可以用于模块化代码、实现私有变量、保存函数状态等。理解和掌握闭包的机制有助于提高代码的质量和性能。但是,过度使用闭包也会导致一些问题,如内存泄漏和性能下降。因此,在使用闭包时需谨慎权衡利弊。

为什么理解闭包很重要

理解闭包很重要的原因有以下几点:

  1. JavaScript中的作用域和闭包是理解函数式编程和模块化编程的重要基础。如果没有理解闭包,那么很难掌握这些编程范式。

  2. JavaScript中的变量作用域和变量生命周期比较复杂,闭包可以用来解决一些变量作用域的问题,例如实现私有变量和避免全局变量的污染等。

  3. 闭包可以用于保持函数状态,并且可以根据需要动态地创建并存储函数状态。因此,理解闭包可以让我们写出更丰富、灵活和可维护的 JavaScript 代码。

  4. JavaScript 中闭包机制的正确性和利用是一个考验编程能力的问题,理解并正确使用闭包可以提高代码质量、代码可扩展性和代码安全性等方面的问题。

  5. 闭包在 JavaScript 中的应用非常广泛,包括 jQuery、React、Angular等热门框架中都有闭包的应用。掌握闭包不仅可以编写更好的代码,还可以方便我们理解和调试现有的项目。

II. 函数与作用域

函数的作用域和生命周期

JavaScript中,每个函数都有自己的作用域,即变量和函数在函数内部的活动范围。函数的作用域由函数声明所在的位置和函数参数的作用域组成。当函数返回后,它的作用域会被销毁,其中声明的变量和函数也会被销毁,这个过程叫做函数的生命周期

函数外部声明的变量叫做全局变量,它在整个程序都可见,并且不会在任何函数生命周期结束后被销毁,直到程序运行结束或者手动删除,才会被销毁。

函数内部声明的变量叫做局部变量,它只在函数内部的作用域范围内可见。当函数执行完毕并返回后,局部变量也会被销毁,释放其所占用的内存空间。

在函数中,如果某个变量被内部函数引用,即使这个内部函数已经在该函数执行后返回到外部,该变量仍然会存在于内部函数的作用域中,这就是闭包的一个特性。由于闭包会让变量继续存在于内存中,因此需要注意闭包的使用,避免出现内存泄漏等问题。

闭包是如何利用函数的作用域的

闭包利用了JavaScript中的函数作用域以及函数作用域链的特性。当内部函数引用外部函数的变量时,由于函数作用域链的存在,内部函数可以访问外部函数的变量。在外部函数返回后,如果内部函数仍然保持对外部函数变量的引用,那么这个内部函数就形成了闭包,可以访问外部函数变量,即使外部函数已经执行完毕并且变量已经离开作用域。

在JavaScript中,所有的函数都是闭包,因为它们都可以访问所在作用域的变量。但是在一般情况下,我们所称的“闭包”通常是指具有特定功能的内部函数,它可以访问外层函数的变量,即使外层函数已经执行完毕。

通过使用闭包,我们可以方便地创建私有变量和创建对象和构造函数等功能。由于闭包可以访问到外部函数的变量,所以可以通过闭包来创建私有变量和方法,以保护变量不被外部访问。同时,由于闭包可以保存对外部变量的引用,因此可以用来实现一些具有状态的函数,使得函数调用状态可以持久化,而不用依赖外部全局变量的存储。

III. 闭包的实现

闭包的实现方式

闭包的实现方式通常有两种。

第一种方式是使用函数表达式,将函数直接赋值给变量或者作为函数参数传递。在函数内部,再定义一个内部函数来使用外部函数的变量。

第二种方式是使用函数声明方式,在函数内部声明一个内部函数,并将其作为返回值返回出去,这个内部函数可以访问外部函数的变量,从而形成闭包。

例如,以下是使用函数表达式实现闭包的示例:

function createCounter() {
  let count = 0;
  return function() {
    count++;
    console.log(count);
  };
}

const counterA = createCounter();
counterA(); // 输出1
counterA(); // 输出2
counterA(); // 输出3

在这个示例中,createCounter 函数返回内部的匿名函数,该匿名函数可以访问外部 createCounter 函数的变量 countcounterA = createCounter() 把匿名函数赋值给变量 counterA,当 counterA 再次调用时,变量 count 仍然存在,并得到后续累加处理。从而实现了一个计数器。

另外一个使用函数声明方式实现闭包的示例如下:

function add(x) {
  return function(y) {
    return x + y;
  }
}

const addFive = add(5);
console.log(addFive(2)); // 输出 7

在这个示例中,add 函数返回一个匿名函数,该匿名函数可以访问外部函数 add 的参数 xaddFive = add(5) 把匿名函数赋值给变量 addFive,接下来再通过 addFive 的调用来获得 y 的值,再加上 x 的值,从而实现一个加法函数。

如何创建闭包

在JavaScript中创建闭包需要满足以下两个条件:

  1. 外部函数返回内部函数。
  2. 内部函数可以访问外部函数的变量。

实现闭包有多种方式,下面列举几个常见的方式:

1. 函数表达式

可以通过函数表达式的方式,将内部函数作为外部函数的一个属性或者直接赋值给另外一个变量,来实现闭包。

function outer() {
  let a = 10;
  return function() {
    console.log(a);
  }
}

const inner = outer();
inner(); // 输出 10

在这个例子中,内部函数访问外部函数的变量 a,形成了闭包,在 inner() 被调用的时候,依然可以访问 a 的值。

2. 立即执行函数(IIFE)

立即执行函数是指一种立即执行的 JavaScript 函数。常见的写法是将函数定义包裹在 (function() {})() 或者 (function() {}()) 中。这种方式也可以用来创建闭包。

const inner = (function() {
  let a = 10;
  return function() {
    console.log(a);
  }
})();

inner(); // 输出 10

在这个例子中,内部函数即立即执行函数的返回值,形成了闭包。

3. 对象方法

通过将内部函数作为对象的方法也可以形成闭包。

const object = {
  a: 10,
  inner: function() {
    console.log(this.a);
  }
};

object.inner(); // 输出 10

在这个例子中,内部函数是一个对象方法,因此可以访问对象属性 a 的值,形成闭包。

这些仅是常见的几种方式,实际上可以灵活运用JavaScript语言特性,来实现闭包。

闭包的应用场景

闭包在JavaScript中的应用非常广泛,以下是几个常见的应用场景:

1. 私有变量和方法

由于JavaScript语言并没有提供私有变量和方法的原生支持,因此可以利用闭包来实现私有变量和方法。外部函数的变量和方法只能在内部函数中访问,而外部作用域无法访问。这种方式可以有效地保护变量,防止它们被外部访问。

function createPerson(name) {
  let age = 0;

  return {
    setName: function(newName) {
      name = newName;
    },
    setAge: function(newAge) {
      age = newAge;
    },
    getAge: function() {
      return age;
    }
  }
}

const person = createPerson('小明');
console.log(person.getAge()); // 输出 0
person.setAge(18);
console.log(person.getAge()); // 输出 18

在这个例子中,外部函数 createPerson 返回一个对象,该对象包含三个方法,其中 setAge 方法可以访问外部函数 createPerson 的变量 age,而变量 name 则只能在外部函数 createPerson 内部使用。

2. 事件绑定

在JavaScript中,事件处理程序通常需要访问元素的一些属性或者其他变量。由于事件处理程序通常作为全局函数定义,因此它无法访问事件处理程序函数之外的变量。但是通过使用闭包,可以在事件处理程序内部访问外部的变量。

function addClickHandler(el, fn) {
  el.addEventListener('click', function() {
    fn();
  });
}

const button = document.querySelector('#myButton');
const name = '张三';
addClickHandler(button, function() {
  alert(`你好, ${name}`);
});

在这个例子中, addClickHandler 函数通过闭包,允许事件处理程序内部访问外部变量 name,而且这种方式不会导致全局变量的污染。

3. 循环变量绑定

在循环语句中,经常需要将循环变量绑定到按钮或者链接等元素中。使用循环变量绑定时,需要注意循环变量和事件处理程序之间的作用域问题。使用闭包,可以解决循环变量和事件处理程序之间的作用域问题。

function createHandler(i) {
  return function() {
    console.log(`按钮 ${i} 被点击了`);
  }
}

for(let i = 0; i < 5; i++) {
  const button = document.createElement('button');
  button.innerText = `按钮 ${i}`;
  button.addEventListener('click', createHandler(i));
  document.body.appendChild(button);
}

在这个例子中, createHandler 函数使用闭包来创建一个可以访问循环变量的事件处理程序。而不使用闭包的话,事件处理程序将只能访问循环结束时循环变量的值。

IV. 闭包的优缺点

闭包的优点

数据的封装

闭包在数据封装上有以下优点:

1. 私有化变量和方法

闭包可以将变量和方法封装在一个函数作用域内,使得这些变量和方法只能在函数内部被访问,从而实现数据的封装。外部作用域无法直接访问闭包内部的变量和方法,只能通过相关的公有方法来访问或者修改数据。

function Counter() {
  let count = 0;

  return {
    increment: function() {
      count++;
    },
    decrement: function() {
      count--;
    },
    getCount: function() {
      return count;
    }
  };
}

const counter = Counter();
counter.increment(); // count+1
console.log(counter.getCount()); // 输出 1

在这个例子中, count 变量和三个方法都被封装在 Counter 函数内部,只能通过 Counter 函数返回的方法来访问或者修改 count 变量的值。

2. 避免全局变量的污染

使用闭包可以避免全局变量的污染。在函数作用域内部定义变量和方法时,它们不会影响全局作用域中已经存在的同名变量和方法,从而避免了命名冲突等问题。

let count = 0;
function Counter() {
  count = 10;

  return {
    increment: function() {
      count++;
    },
    decrement: function() {
      count--;
    },
    getCount: function() {
      return count;
    }
  };
}

const counter = Counter();
counter.increment(); // count+1
console.log(counter.getCount()); // 输出 11
console.log(count); // 输出 10

在这个例子中, count 变量已经被定义在全局作用域中,但是通过在 Counter 函数内部重新定义一个同名的 count 变量,实现了局部变量的封装,避免了对全局 count 变量的修改。

因此,使用闭包可以实现对数据的封装,并且避免全局变量的污染,有助于提高代码的可读性和可维护性。

可以实现高阶函数

闭包可以实现高阶函数,并且在高阶函数中发挥重要作用,具有以下优点:

1. 强大的表现力

闭包可以将函数作为参数和返回值传递,实现更加灵活的函数组合和转换,从而快速构建复杂的功能模块。这种能力称为函数式编程中的柯里化(currying)和组合(composition)。

function add(x) {
  return function(y) {
    return x + y;
  };
}

function multiply(x) {
  return function(y) {
    return x * y;
  };
}

const add5 = add(5);
const multiply2 = multiply(2);

const result = add5(2); // 5 + 2 = 7
const result2 = multiply2(result); // 7 * 2 = 14

console.log(result2); // 输出 14

在这个例子中,addmultiply 函数都是返回一个闭包,而且这两个闭包接受一个参数并进行计算。通过组合这两个闭包,可以实现一个更加复杂的计算过程,从而实现高阶函数的功能。

2. 支持延迟求值

闭包支持延迟求值,也就是说,在某些情况下,可以将函数的执行延迟到需要的时候再执行,从而避免不必要的计算和内存占用。这种方法称为惰性求值(lazy evaluation)。

function createSquares(max) {
  const squares = [];

  function getSquare(i) {
    return function() {
      return i * i;
    };
  }

  for(let i = 0; i <= max; i++) {
    squares.push(getSquare(i));
  }

  return squares;
}

const squares = createSquares(10000);

console.log(squares[0]()); // 输出 0
console.log(squares[1]()); // 输出 1
console.log(squares[2]()); // 输出 4

// 将一次性计算所有的平方数改成延迟计算
const squares2 = createSquares(10000);

console.log(squares2[0]()); // 输出 0
console.log(squares2[1]()); // 输出 1

在这个示例中,createSquares 函数返回一个闭包数组,这些闭包可以在需要时延迟计算实际的值,解决了在一次性计算所有平方数时可能面临的性能问题,从而提高了运行效率。对于大数据量的计算,使用惰性求值可以节省大量的内存和计算资源。

因此,利用闭包可以创建高阶函数,实现更加灵活的函数组合和转换,还可以支持惰性求值,提高代码性能和运行效率。

闭包的缺点

内存占用

闭包在解决问题的同时,也存在一些缺点,其中之一就是内存占用问题:

1. 内存泄漏

由于闭包会引用外部函数的变量,而且这些变量不会被垃圾回收器所回收。因此,如果这些变量的内存空间不被手动释放,就会导致内存泄漏问题。

function createCounter() {
  let count = 0;

  return function() {
    count++;
    return count;
  };
}

const counter = createCounter();

console.log(counter()); // 输出 1
console.log(counter()); // 输出 2
console.log(counter()); // 输出 3

在这个例子中, count 变量被闭包引用,在每次调用 createCounter 函数时都会创建一个新的闭包。但是由于闭包一直被引用,所以 count 变量的内存空间不会被释放。

2. 过度使用闭包可能导致内存爆炸

由于闭包的特性,在一些情况下过度使用闭包会导致大量内存占用。如果闭包所引用的外部变量包含了大量的数据,那么每个闭包都会占用大量的内存空间,从而导致内存使用率飙升,甚至造成内存溢出的问题。

因此,在使用闭包时需要注意,适当地释放闭包的内存空间,避免因过度使用闭包而导致的内存泄漏和内存爆炸问题。

综上所述,闭包在解决问题的同时,也存在一些缺点,其中之一就是内存占用问题。在使用闭包时需要注意内存泄漏和内存爆炸问题,避免因过度使用闭包导致的内存占用过多的问题。

对程序员编写能力和理解能力要求更高

闭包虽然具有许多优点,但是由于其特殊的工作原理,也会对程序员的编写能力和理解能力要求更高,缺点主要表现在以下方面:

1. 代码可读性差

由于闭包的特殊工作机制,它可能会在不同的作用域中访问和修改变量,这样会使代码的逻辑更加复杂,降低代码的可读性。另外,不恰当的闭包使用也可能会导致代码中的变量命名污染问题。

function count() {
  let i;
  for(i = 0; i < 5; i++) {
    setTimeout(function() {
      console.log(i);
    }, i * 1000);
  }
}

count();

在这个例子中,使用闭包实现了一个计时器,但是因为闭包所引用的变量在多个作用域中进行访问和修改,导致代码的逻辑比较复杂,不容易理解和调试。

2. 容易出现内存泄漏和内存爆炸问题

由于闭包所引用的变量在闭包函数执行结束后不会自动释放,而是在内存中长期存在,这种特性可能会导致内存泄漏和内存爆炸的问题。

function createArray() {
  const arr = [];

  for(let i = 0; i < 1000000; i++) {
    arr.push(function() {
      return i;
    });
  }

  return arr;
}

const arr = createArray();

在这个例子中,由于闭包所引用的 i 变量在循环结束后不会被释放,而是长期存在于内存中,可能会导致内存爆炸问题。

3. 其他问题

使用闭包还可能会面临一些其他问题,如闭包中的变量作用域、作用域链的影响等,程序员需要具备更高的编写能力和理解能力才能正确地使用闭包来完成任务。

综上所述,闭包具有一些优点,但同时也存在一些缺点,如代码可读性差、容易出现内存泄漏和内存爆炸问题、需要更高的编写能力和理解能力等。因此在使用闭包时需要慎重考虑,避免滥用闭包,防止因为闭包使用不当而导致的问题。

V. 总结与展望

在实际开发中如何使用闭包

在实际开发中,可以使用闭包来解决一些特定的问题,例如:

1. 封装私有变量和方法

使用闭包来封装变量和方法,防止其被外部代码误用或访问,从而保证代码的安全性和稳定性。

function Counter() {
  let count = 0;

  function increment() {
    count++;
  }

  function decrement() {
    count--;
  }

  function getCount() {
    return count;
  }

  return {
    increment,
    decrement,
    getCount,
  };
}

const counter = Counter();
counter.increment();
console.log(counter.getCount()); // 输出 1

2. 简化柯里化和高阶函数的实现

使用闭包可以方便的实现柯里化(currying)和高阶函数的功能,实现代码的简洁和可读性。

function add(x) {
  return function(y) {
    return x + y;
  };
}

const add5 = add(5);
console.log(add5(3)); // 输出 8

3. 实现单例模式

使用闭包可以实现单例模式,确保某个类在一个程序中只有一个实例,并提供对该实例的全局访问。

const Singleton = (() => {
  let instance;

  function init() {
    // 一些初始化逻辑
    return {
      // 实例的公有方法和变量
    };
  }

  return {
    getInstance: () => {
      if(!instance) {
        instance = init();
      }

      return instance;
    }
  };
})();

const singletonA = Singleton.getInstance();
const singletonB = Singleton.getInstance();

console.log(singletonA === singletonB); // 输出 true

4. 实现模块化

使用闭包可以实现模块化,将代码按照功能模块封装起来,提供对外公有的接口。

// module.js
const module = (() => {
  let privateVar = 0;

  function privateMethod() {
    console.log('私有方法');
  }

  return {
    publicVar: '公有变量',
    publicMethod: () => {
      console.log('公有方法');
      privateVar++;
    },
    getPrivateVar: () => privateVar,
  };
})();

export default module;

// index.js
import module from './module.js';

console.log(module.publicVar);
module.publicMethod();
console.log(module.getPrivateVar()); // 输出 1

在实际开发中,可以使用闭包来优化代码的实现和设计,但也需要注意闭包可能存在的问题,合理的使用闭包来实现代码的封装、函数的柯里化和高阶功能、单例模式的实现、模块化等。

未来闭包的发展趋势

闭包作为一种编程概念和技术手段,已经成为现代编程中不可或缺的一部分。随着编程语言和技术的发展,未来闭包可能会出现以下发展趋势:

1. 更高效的内存管理

随着硬件性能的提升,现代编程语言和虚拟机已经具备了更高效的内存管理能力,如自动化垃圾回收等。未来的编程语言和技术可能会进一步提升内存管理的能力,从而在使用闭包时避免内存泄漏和内存爆炸问题。

2. 更安全的代码设计

由于闭包特殊的工作机制,存在可能造成变量污染和访问权限泄漏的问题。未来的编程语言和技术可能会将闭包和作用域的概念进一步完善和细化,从而设计更加安全的闭包。

3. 更优秀的语言和工具支持

未来的编程语言和工具可能会提供更多的闭包相关的语言特性、API和工具支持,帮助程序员更加方便地利用闭包来实现代码的封装、模块化、柯里化和高阶功能等。

综上所述,随着编程语言和技术的不断发展,未来闭包有望出现更高效的内存管理、更安全的代码设计以及更优秀的语言和工具支持等趋势。未来的核心是为了提高代码质量和开发效率,使闭包成为更加强大和稳定的编程工具。

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

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

相关文章

MedLSAM:定位和分割任何3D医学图像模型

文章目录 MedLSAM: Localize and Segment Anything Model for 3D Medical Images摘要本文方法模型学习过程模型推理过程 实验结果 MedLSAM: Localize and Segment Anything Model for 3D Medical Images 摘要 SAM (Segment Anything Model)是近年来出现的一种具有开创性的图像…

【Linux】gcc/g++ 调试学习记录

这是目录 gcc编译选项二、实战1、编译加上 CFLAGS -ggdb三级目录 gcc编译选项 1、-g 编译debug debugging 选项&#xff1a;-g gcc -g手册: 点击这里 -g一共分为4个等级&#xff1a;-g、-g0、-g1、-g3 其中g和g0是一个性质&#xff0c;不打开调试信息&#xff0c;g3保留所有信…

react-native-camera插件的使用,以及实现人脸识别

一、git地址和环境版本 &#xff08;1&#xff09;Git地址&#xff1a;https://github.com/react-native-camera/react-native-camera/tree/master &#xff08;2&#xff09;node版本&#xff1a;14 &#xff08;3&#xff09;react-native版本&#xff1a;0.72 二、环境配…

Linux操作系统——第五章 进程信号

目录 信号概念 用kill -l命令可以察看系统定义的信号列表 信号处理常见方式概览 产生信号 1. 通过终端按键产生信号 2. 调用系统函数向进程发信号 3. 由软件条件产生信号 4. 硬件异常产生信号 阻塞信号 1. 信号其他相关常见概念 2. 在内核中的表示 3. sigset_t 4.…

spring.aop 随笔4 如何借助jdk代理类实现aop

0. 下了有一个月的雨&#xff0c;这对鼻炎来说来吗&#xff1f;不好 其实这也算6月份的博客&#xff0c;之前一直疏于整理 本文仅关注jdk代理所实现的spring.aop下&#xff0c;两者的关系完整的aop源码走读请移步相关 spring.aop 的其他随笔 1. 反编译追踪源码 1.1 jdk代理类…

BPMN2.0规范简介

1 概述 BPMN(Business Process Model & Notation)&#xff0c;中文名为业务流程模型与符号。BPMN2.0是OMG(Object Management Group&#xff0c;对象管理组织)制定的&#xff0c;其主要目的是既给用户提供一套简单的、容易理解的机制&#xff0c;以便用户创建流程模型&…

项目性能优化-内存泄漏检测与修改

最近终于有空优化一波项目的性能了&#xff0c;第一波借助Android Studio自带的Profiler工具检测内存泄漏。 第一步、创建Profiler的SESSIONS 第二步、进入MEMORY内存监控 右侧带有绿色原点的就是此时运行的Profiler的SESSION,点击右侧MEMORY进入内存监控的详情模块 第三步…

缓存三击-缓存穿透、缓存雪崩、缓存击穿

缓存三击-缓存穿透、缓存雪崩、缓存击穿 ⭐⭐⭐⭐⭐⭐ Github主页&#x1f449;https://github.com/A-BigTree 笔记链接&#x1f449;https://github.com/A-BigTree/Code_Learning ⭐⭐⭐⭐⭐⭐ Spring专栏&#x1f449;https://blog.csdn.net/weixin_53580595/category_12279…

【产品设计】掌握“4+X”模型,从0到1构建B端产品

“4X”模型是什么 4个阶段&#xff1a;规划阶段&#xff0c;设计阶段&#xff0c;实现阶段&#xff0c;迭代阶段 X:项目管理&#xff0c;数据分析&#xff0c;产品运营 1、规划阶段 这是一个产品的开始&#xff0c;它决定了产品的设计方向和基调。主要包括用户分析、市场分…

爬虫入门指南(4): 使用Selenium和API爬取动态网页的最佳方法

文章目录 动态网页爬取静态网页与动态网页的区别使用Selenium实现动态网页爬取Selenium 的语法及介绍Selenium简介安装和配置创建WebDriver对象页面交互操作 元素定位 等待机制页面切换和弹窗处理截图和页面信息获取关闭WebDriver对象 使用API获取动态数据未完待续.... 动态网页…

JVM-垃圾回收-基础知识

基础知识 什么是垃圾 简单说就是没有被任何引用指向的对象就是垃圾。后面会有详细说明。 和C的区别 java&#xff1a;GC处理垃圾&#xff0c;开发效率高&#xff0c;执行效率低 C&#xff1a;手工处理垃圾&#xff0c;如果忘记回收&#xff0c;会导致内存泄漏问题。如果回…

Linux Mint 21.2“Victoria”Beta 发布

导读近日消息&#xff0c;Beta 版 Linux Mint 21.2 “Victoria” 于今天发布&#xff0c;用户可以访问官网下载镜像。 Linux Mint 21.2 代号 “Victoria” &#xff0c;基于 Canonical 长期支持的 Ubuntu 22.04 LTS&#xff08;Jammy Jellyfish&#xff09;操作系统&#xff0…

2023年第三届工业自动化、机器人与控制工程国际会议

会议简介 Brief Introduction 2023年第三届工业自动化、机器人与控制工程国际会议&#xff08;IARCE 2023&#xff09; 会议时间&#xff1a;2023年10月27 -30日 召开地点&#xff1a;中国成都 大会官网&#xff1a;www.iarce.org 2023年第三届工业自动化、机器人与控制工程国际…

JAVA http

javahttp 请求数据格式servletservlet生命周期servletrequest获取请求数据解决乱码response相应字符&字节数据 请求数据格式 servlet servlet生命周期 servlet request获取请求数据 解决乱码 response相应字符&字节数据 response.setHeader("content-type",…

A. Portal(dp优化枚举)

Problem - 1580A - Codeforces CQXYM发现了一个大小为nm的矩形。矩形由n行m列的方块组成&#xff0c;每个方块可以是黑曜石方块或空方块。CQXYM可以通过一次操作将黑曜石方块变为空方块&#xff0c;或将空方块变为黑曜石方块。 一个大小为ab的矩形M被称为传送门&#xff0c;当…

【Linux】程序员的基本素养学习

这是目录 写在前面一、内存管理1、分段2、分页 二、线程管理三、静态库1、编译1.1、预处理1.2、编译1.3、汇编1.4、链接2、编译器3、目标文件**.text****.data****.bss****__attribute__** 3.1、符号3.2、兼容C语言 -- extern C4、链接 -- ld 写在前面 本文记录自己的学习生涯…

五.组合数据类型

目录 1、数组类型 声明数组 初始化数组 数组赋值 访问数组元素 2、切片类型 1、定义切片 2、切片初始化 3、访问 4、空(nil)切片 5、切片的增删改查操作&#xff1a; 3、指针类型 1、什么是指针 2、如何使用指针、指针使用流程&#xff1a; 3、Go 空指针 4、指…

chatgpt赋能python:如何将Python打包-一个SEO优化指南

如何将Python打包 - 一个SEO优化指南 作为一名拥有10年Python编程经验的工程师&#xff0c;我意识到很多Python开发者面临一个共同的问题&#xff1a;如何将他们的Python项目打包并发布到PyPI上&#xff1f;打包一个Python项目不仅可以让您的代码更加组织化&#xff0c;也可以…

如何拆分PDF?拆分PDF软件分享!​

那么如何拆分PDF&#xff1f;PDF是一种流行的电子文档格式&#xff0c;它可以在不同的操作系统和设备上进行查看和共享&#xff0c;而不会因为不同的软件或硬件而出现兼容性问题。同时&#xff0c;在使用的过程中&#xff0c;PDF拆分PDF文件是一个比较常见的需求&#xff0c;它…

threejs入门

个人博客地址: https://cxx001.gitee.io 前言 随着HTML5的发布&#xff0c;我们可以通过WebGL在浏览器上直接使用显卡资源来创建高性能的二维和三维图形&#xff0c;但是直接使用WebGL编程来创建三维场景十分复杂而且还容易出问题。而使用Three.js库可以简化这个过程&#xff…