0

0

闭包有话说 - 大前端

高洛峰

高洛峰

发布时间:2017-02-08 17:57:48

|

1174人浏览过

|

来源于php中文网

原创

宠物商店
宠物商店

目前,PetShop已经从最初的2.0、3.0等版本,发展到了最新的4.0版本。PetShop 4.0使用ASP.NET 2.0技术开发,其中加入了众多新增特性,因此,在性能、代码数量、可扩展性等方面有了重大改善。可以说,学习PetShop 4.0是深入掌握ASP.NET 2.0技术的捷径。本节将引领读者逐步了解PetShop 4.0的方方面面,包括应用程序安装、功能和用户界面简介、解决方案和体系

下载

引言

刚学习前端的时候,看到闭包这个词,总是一脸懵逼,面试的时候,问到这个问题,也是回答的含含糊糊,总感觉有层隔膜,觉得这个概念很神奇,如果能掌握,必将功力大涨。其实,闭包没有这么神秘,它无处不在。

一个简短的的问题

首先,来看一个问题。

请用一句话描述什么是闭包,并写出代码进行说明。

如果能毫不犹豫的说出来,并能给出解释,那下面文字对你来说就没有往下看的必要了。
就这个问题,结合我查阅的资料和经验,在这里简单的说一下,如果哪里有不对的,欢迎指正。

先回答上面的问题,什么是闭包。

闭包是一个概念,它描述了函数执行完毕后,依然驻留内存的现象。

立即学习前端免费学习笔记(深入)”;

代码描述:

function foo() {

    var a = 2;

    function bar(){
        console.log(a);
    }

    return bar;
}

var test = foo();
test(); //2

上面这段代码,清晰的展示了闭包。

函数 bar() 的词法作用域能够访问 foo()的内部作用域。然后我们将bar()函数本身当作一个值类型进行传递。上面这个例子,我们将bar() 所引用的函数对象本身作为返回值。

foo() 执行完毕之后, 其内部作用域并没有被销毁,因为bar()依然保持着对内部作用域的引用,拜bar()的位置所赐,它拥有涵盖foo()内部作用域的闭包,使得该作用域能够一直存活,以供bar()在之后的任何时间进行引用。这个引用,其实就是闭包。
也正是这个原因,test被实际调用的时候,它可以访问定义时的词法作用域,所以,才能访问到a.

函数传递也可以是间接的:

    var fn;
    function foo(){

        var a = 2;

        function baz() {
            console.log( a );
        }
        fn = baz; //将baz 分配给全局变量
    }

    function bar(){
        fn();
    }
    foo();
    bar(); //2

所以,无论通过何种手段将内部函数传递到其所在的词法作用域外,它都会持有对原始定义作用域的引用。也就是说,无论在什么地方执行这个函数,都会使用闭包。也是这个原因,我们才可以很方便的使用回调函数而不用关心其具体细节。

其实,在定时器,事件监听器,ajax请求, 跨窗口通信,Web Workers 或者任何其他的同步 或 异步任务中,只要使用了回调函数,实际上就是在使用闭包。

到这里,或许你已经对闭包有个大概的了解,下面我再举几个例子来帮你加深对闭包的认识。

几个更具体的例子

首先,就先看一下所谓的立即执行函数.

var a = 2;

(function IIFE() { 
   console.log(a); 
 })();

//2

这个立即执行函数通常被认为是经典的闭包例子,它可以正常工作,但严格意义上讲,它并不是闭包。
为什么呢?

因为这个IIFE函数并不是在它本身的词法作用域之外执行的。它在定义时所在的作用域中执行了。而且,变量a 是通过普通的词法作用域查找的,而不是通过闭包。

另一个用来说明闭包的例子是循环。

    <p class="tabs">
        <li class="tab">some text1</li>
        <li class="tab">some text2</li>
        <li class="tab">some text3</li>
    </p>
var handler = function(nodes) {

    for(var i = 0, l = nodes.length; i < l ; i++) {
        
        nodes[i].onclick = function(){

            console.log(i);

        }
    }
}

var tabs = document.querySelectorAll('.tabs .tab');
    handler(tabs);

我们预期的结果是log  0 ,1,2;

执行之后的结果却是是三个3;

这是为什么呢?

首先解释下这个3是怎么来的,

看一下循环体,循环的终止条件是  i 因此 ,输出显示的是循环结束时 i 的最终值。 根据作用域的工作原理,尽管循环中的函数是在各个迭代中分别定义的,但是它们都被封闭在一个共享的全局作用域中,因此实际上是只有一个i.

handler 函数的本意是想把唯一的i传递给事件处理器,但是失败了。
因为事件处理器函数绑定了 i本身 ,而不是函数在构造时的i的值.

知道了这个之后,我们可以做出相应的调整:

var handler = function(nodes) {

    var helper = function(i){
        return function(e){
            console.log(i); // 0 1 2
        }
    }

    for(var i = 0, l = nodes.length; i < l ; i++) {
        
        nodes[i].onclick = helper(i);
    }
}

在循环外创建一个辅助函数,让这个辅助函数在返回一个绑定了当前i的值的函数,这样就不会混淆了。

明白了这点,就会发现,上面的处理就是为了创建一个新的作用域,换句话说,每次迭代我们都需要一个块作用域.

说到块作用域,就不得不提一个词,那就是let.

所以,如果你不想过多的使用闭包,就可以使用let:

var handler = function(nodes) {

    for(let i = 0, l = nodes.length; i < l ; i++) {
        
        //nodes[i].index = i;

        nodes[i].onclick = function(){

            console.log(i); // 0 1 2


        }
    }
}

jQuery中的闭包

先来看个例子

     var sel = $("#con"); 
     setTimeout( function (){ 
         sel.css({background:"gray"}); 
     }, 2000);

上边的代码使用了 jQuery 的选择器,找到 id 为 con 的元素,注册计时器,两秒之后,将背景色设置为灰色。

这个代码片段的神奇之处在于,在调用了 setTimeout 函数之后,con 依旧被保持在函数内部,当两秒钟之后,id 为 con 的 p 元素的背景色确实得到了改变。应该注意的是,setTimeout 在调用之后已经返回了,但是 con 没有被释放,这是因为 con 引用了全局作用域里的变量 con。

以上的例子帮助我们了解了更多关于闭包的细节,下面我们就深入闭包世界探寻一番。

深入理解闭包

首先看一个概念-执行上下文(Execution Context)。

执行上下文是一个抽象的概念,ECMAScript 规范使用它来追踪代码的执行。它可能是你的代码第一次执行或执行的流程进入函数主体时所在的全局上下文。

闭包有话说 - 大前端

在任意一个时间点,只能有唯一一个执行上下文在运行之中。

这就是为什么 JavaScript 是“单线程”的原因,意思就是一次只能处理一个请求。

一般来说,浏览器会用栈来保存这个执行上下文。

栈是一种“后进先出” (Last In First Out) 的数据结构,即最后插入该栈的元素会最先从栈中被弹出(这是因为我们只能从栈的顶部插入或删除元素)。

当前的执行上下文,或者说正在运行中的执行上下文永远在栈顶。

当运行中的上下文被完全执行以后,它会由栈顶弹出,使得下一个栈顶的项接替它成为正在运行的执行上下文。

除此之外,一个执行上下文正在运行并不代表另一个执行上下文需要等待它完成运行之后才可以开始运行。

有时会出现这样的情况,一个正在运行中的上下文暂停或中止,另外一个上下文开始执行。暂停的上下文可能在稍后某一时间点从它中止的位置继续执行。

一个新的执行上下文被创建并推入栈顶,成为当前的执行上下文,这就是执行上下文替代的机制。

闭包有话说 - 大前端

当我们有很多执行上下文一个接一个地运行时——通常情况下会在中间暂停然后再恢复运行——为了能很好地管理这些上下文的顺序和执行情况,我们需要用一些方法来对其状态进行追踪。而实际上也是如此,根据ECMAScript的规范,每个执行上下文都有用于跟踪代码执行进程的各种状态的组件。包括:

  • 代码执行状态:任何需要开始运行,暂停和恢复执行上下文相关代码执行的状态
     函数:上下文中正在执行的函数对象(正在执行的上下文是脚本或模块的情况下可能是null)

  • Realm:一系列内部对象,一个ECMAScript全局环境,所有在全局环境的作用域内加载的ECMAScript代码,和其他相关的状态及资源。

  • 词法环境:用于解决此执行上下文内代码所做的标识符引用。

  • 变量环境:一种词法环境,该词法环境的环境记录保留了变量声明时在执行上下文中创建的绑定关系。

模块与闭包

现在的开发都离不开模块化,下面说说模块是如何利用闭包的。

先看一个实际中的例子。
这是一个统计模块,看一下代码:

    define("components/webTrends", ["webTrendCore"], function(require,exports, module) {
    
    
        var webTrendCore = require("webTrendCore");  
        var webTrends = {
             init:function (obj) {
                 var self = this;
                self.dcsGetId();
                self.dcsCollect();
            },
    
             dcsGetId:function(){
                if (typeof(_tag) != "undefined") {
                 _tag.dcsid="dcs5w0txb10000wocrvqy1nqm_6n1p";
                 _tag.dcsGetId();
                }
            },
    
            dcsCollect:function(){
                 if (typeof(_tag) != "undefined") {
                    _tag.DCSext.platform="weimendian";
                    if(document.readyState!="complete"){
                    document.onreadystatechange = function(){
                        if(document.readyState=="complete") _tag.dcsCollect()
                        }
                    }
                    else _tag.dcsCollect()
                }
            }
    
        };
    
      module.exports = webTrends;
    
    })

在主页面使用的时候,调用一下就可以了:

var webTrends = require("webTrends");
webTrends.init();

在定义的模块中,我们暴露了webTrends对象,在外面调用返回对象中的方法就形成了闭包。

模块的两个必要条件:

  • 必须有外部的封闭函数,该函数必须至少被调用一次

  • 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态。

性能考量

如果一个任务不需要使用闭包,那最好不要在函数内创建函数。
原因很明显,这会 拖慢脚本的处理速度,加大内存消耗 。

举个例子,当需要创建一个对象时,方法通常应该和对象的原型关联,而不是定义到对象的构造函数中。 原因是 每次构造函数被调用, 方法都会被重新赋值 (即 对于每个对象创建),这显然是一种不好的做法。

看一个能说明问题,但是不推荐的做法:

    function MyObject(name, message) {
    
      this.name = name.toString();
      this.message = message.toString();
      
      this.getName = function() {
        return this.name;
      };
    
      this.getMessage = function() {
        return this.message;
      };
    }

上面的代码并没有很好的利用闭包,我们来改进一下:

    function MyObject(name, message) {
      this.name = name.toString();
      this.message = message.toString();
    }
    
    MyObject.prototype = {
      getName: function() {
        return this.name;
      },
      getMessage: function() {
        return this.message;
      }
    };

好一些了,但是不推荐重新定义原型,再来改进下:

function MyObject(name, message) {
    this.name = name.toString();
    this.message = message.toString();
}

MyObject.prototype.getName = function() {
       return this.name;
};

MyObject.prototype.getMessage = function() {
   return this.message;
};

很显然,在现有的原型上添加方法是一种更好的做法。

上面的代码还可以写的更简练:

    function MyObject(name, message) {
        this.name = name.toString();
        this.message = message.toString();
    }
    
    (function() {
        this.getName = function() {
            return this.name;
        };
        this.getMessage = function() {
            return this.message;
        };
    }).call(MyObject.prototype);

在前面的三个示例中,继承的原型可以由所有对象共享,并且在每个对象创建时不需要定义方法定义。如果想看更多细节,可以参考对象模型。

闭包的使用场景:

  • 使用闭包可以在JavaScript中模拟块级作用域;

  • 闭包可以用于在对象中创建私有变量。

闭包的优缺点

优点:

  • 逻辑连续,当闭包作为另一个函数调用的参数时,避免你脱离当前逻辑而单独编写额外逻辑。

  • 方便调用上下文的局部变量。

  • 加强封装性,第2点的延伸,可以达到对变量的保护作用。

缺点:

  • 内存浪费。这个内存浪费不仅仅因为它常驻内存,对闭包的使用不当会造成无效内存的产生。

结语

前面对闭包做了一些简单的解释,最后再总结下,其实闭包没什么特别的,其特点是:

  • 函数嵌套函数

  • 函数内部可以访问到外部的变量或者对象

  • 避免了垃圾回收

欢迎交流,以上 ;-)

参考资料

让我们一起学习JavaScript闭包吧

弄懂JavaScript的作用域和闭包

Closures

引言

刚学习前端的时候,看到闭包这个词,总是一脸懵逼,面试的时候,问到这个问题,也是回答的含含糊糊,总感觉有层隔膜,觉得这个概念很神奇,如果能掌握,必将功力大涨。其实,闭包没有这么神秘,它无处不在。

一个简短的的问题

首先,来看一个问题。

请用一句话描述什么是闭包,并写出代码进行说明。

如果能毫不犹豫的说出来,并能给出解释,那下面文字对你来说就没有往下看的必要了。
就这个问题,结合我查阅的资料和经验,在这里简单的说一下,如果哪里有不对的,欢迎指正。

先回答上面的问题,什么是闭包。

闭包是一个概念,它描述了函数执行完毕后,依然驻留内存的现象。

立即学习前端免费学习笔记(深入)”;

代码描述:

function foo() {

    var a = 2;

    function bar(){
        console.log(a);
    }

    return bar;
}

var test = foo();
test(); //2

上面这段代码,清晰的展示了闭包。

函数 bar() 的词法作用域能够访问 foo()的内部作用域。然后我们将bar()函数本身当作一个值类型进行传递。上面这个例子,我们将bar() 所引用的函数对象本身作为返回值。

foo() 执行完毕之后, 其内部作用域并没有被销毁,因为bar()依然保持着对内部作用域的引用,拜bar()的位置所赐,它拥有涵盖foo()内部作用域的闭包,使得该作用域能够一直存活,以供bar()在之后的任何时间进行引用。这个引用,其实就是闭包。
也正是这个原因,test被实际调用的时候,它可以访问定义时的词法作用域,所以,才能访问到a.

函数传递也可以是间接的:

    var fn;
    function foo(){

        var a = 2;

        function baz() {
            console.log( a );
        }
        fn = baz; //将baz 分配给全局变量
    }

    function bar(){
        fn();
    }
    foo();
    bar(); //2

所以,无论通过何种手段将内部函数传递到其所在的词法作用域外,它都会持有对原始定义作用域的引用。也就是说,无论在什么地方执行这个函数,都会使用闭包。也是这个原因,我们才可以很方便的使用回调函数而不用关心其具体细节。

其实,在定时器,事件监听器,ajax请求, 跨窗口通信,Web Workers 或者任何其他的同步 或 异步任务中,只要使用了回调函数,实际上就是在使用闭包。

到这里,或许你已经对闭包有个大概的了解,下面我再举几个例子来帮你加深对闭包的认识。

几个更具体的例子

首先,就先看一下所谓的立即执行函数.

var a = 2;

(function IIFE() { 
   console.log(a); 
 })();

//2

这个立即执行函数通常被认为是经典的闭包例子,它可以正常工作,但严格意义上讲,它并不是闭包。
为什么呢?

因为这个IIFE函数并不是在它本身的词法作用域之外执行的。它在定义时所在的作用域中执行了。而且,变量a 是通过普通的词法作用域查找的,而不是通过闭包。

另一个用来说明闭包的例子是循环。

    <p class="tabs">
        <li class="tab">some text1</li>
        <li class="tab">some text2</li>
        <li class="tab">some text3</li>
    </p>
var handler = function(nodes) {

    for(var i = 0, l = nodes.length; i < l ; i++) {
        
        nodes[i].onclick = function(){

            console.log(i);

        }
    }
}

var tabs = document.querySelectorAll('.tabs .tab');
    handler(tabs);

我们预期的结果是log  0 ,1,2;

执行之后的结果却是是三个3;

这是为什么呢?

首先解释下这个3是怎么来的,

看一下循环体,循环的终止条件是  i 因此 ,输出显示的是循环结束时 i 的最终值。 根据作用域的工作原理,尽管循环中的函数是在各个迭代中分别定义的,但是它们都被封闭在一个共享的全局作用域中,因此实际上是只有一个i.

handler 函数的本意是想把唯一的i传递给事件处理器,但是失败了。
因为事件处理器函数绑定了 i本身 ,而不是函数在构造时的i的值.

知道了这个之后,我们可以做出相应的调整:

var handler = function(nodes) {

    var helper = function(i){
        return function(e){
            console.log(i); // 0 1 2
        }
    }

    for(var i = 0, l = nodes.length; i < l ; i++) {
        
        nodes[i].onclick = helper(i);
    }
}

在循环外创建一个辅助函数,让这个辅助函数在返回一个绑定了当前i的值的函数,这样就不会混淆了。

明白了这点,就会发现,上面的处理就是为了创建一个新的作用域,换句话说,每次迭代我们都需要一个块作用域.

说到块作用域,就不得不提一个词,那就是let.

所以,如果你不想过多的使用闭包,就可以使用let:

var handler = function(nodes) {

    for(let i = 0, l = nodes.length; i < l ; i++) {
        
        //nodes[i].index = i;

        nodes[i].onclick = function(){

            console.log(i); // 0 1 2


        }
    }
}

jQuery中的闭包

先来看个例子

     var sel = $("#con"); 
     setTimeout( function (){ 
         sel.css({background:"gray"}); 
     }, 2000);

上边的代码使用了 jQuery 的选择器,找到 id 为 con 的元素,注册计时器,两秒之后,将背景色设置为灰色。

这个代码片段的神奇之处在于,在调用了 setTimeout 函数之后,con 依旧被保持在函数内部,当两秒钟之后,id 为 con 的 p 元素的背景色确实得到了改变。应该注意的是,setTimeout 在调用之后已经返回了,但是 con 没有被释放,这是因为 con 引用了全局作用域里的变量 con。

以上的例子帮助我们了解了更多关于闭包的细节,下面我们就深入闭包世界探寻一番。

深入理解闭包

首先看一个概念-执行上下文(Execution Context)。

执行上下文是一个抽象的概念,ECMAScript 规范使用它来追踪代码的执行。它可能是你的代码第一次执行或执行的流程进入函数主体时所在的全局上下文。

闭包有话说 - 大前端

在任意一个时间点,只能有唯一一个执行上下文在运行之中。

这就是为什么 JavaScript 是“单线程”的原因,意思就是一次只能处理一个请求。

一般来说,浏览器会用栈来保存这个执行上下文。

栈是一种“后进先出” (Last In First Out) 的数据结构,即最后插入该栈的元素会最先从栈中被弹出(这是因为我们只能从栈的顶部插入或删除元素)。

当前的执行上下文,或者说正在运行中的执行上下文永远在栈顶。

当运行中的上下文被完全执行以后,它会由栈顶弹出,使得下一个栈顶的项接替它成为正在运行的执行上下文。

除此之外,一个执行上下文正在运行并不代表另一个执行上下文需要等待它完成运行之后才可以开始运行。

有时会出现这样的情况,一个正在运行中的上下文暂停或中止,另外一个上下文开始执行。暂停的上下文可能在稍后某一时间点从它中止的位置继续执行。

一个新的执行上下文被创建并推入栈顶,成为当前的执行上下文,这就是执行上下文替代的机制。

闭包有话说 - 大前端

当我们有很多执行上下文一个接一个地运行时——通常情况下会在中间暂停然后再恢复运行——为了能很好地管理这些上下文的顺序和执行情况,我们需要用一些方法来对其状态进行追踪。而实际上也是如此,根据ECMAScript的规范,每个执行上下文都有用于跟踪代码执行进程的各种状态的组件。包括:

  • 代码执行状态:任何需要开始运行,暂停和恢复执行上下文相关代码执行的状态
     函数:上下文中正在执行的函数对象(正在执行的上下文是脚本或模块的情况下可能是null)

  • Realm:一系列内部对象,一个ECMAScript全局环境,所有在全局环境的作用域内加载的ECMAScript代码,和其他相关的状态及资源。

  • 词法环境:用于解决此执行上下文内代码所做的标识符引用。

  • 变量环境:一种词法环境,该词法环境的环境记录保留了变量声明时在执行上下文中创建的绑定关系。

模块与闭包

现在的开发都离不开模块化,下面说说模块是如何利用闭包的。

先看一个实际中的例子。
这是一个统计模块,看一下代码:

    define("components/webTrends", ["webTrendCore"], function(require,exports, module) {
    
    
        var webTrendCore = require("webTrendCore");  
        var webTrends = {
             init:function (obj) {
                 var self = this;
                self.dcsGetId();
                self.dcsCollect();
            },
    
             dcsGetId:function(){
                if (typeof(_tag) != "undefined") {
                 _tag.dcsid="dcs5w0txb10000wocrvqy1nqm_6n1p";
                 _tag.dcsGetId();
                }
            },
    
            dcsCollect:function(){
                 if (typeof(_tag) != "undefined") {
                    _tag.DCSext.platform="weimendian";
                    if(document.readyState!="complete"){
                    document.onreadystatechange = function(){
                        if(document.readyState=="complete") _tag.dcsCollect()
                        }
                    }
                    else _tag.dcsCollect()
                }
            }
    
        };
    
      module.exports = webTrends;
    
    })

在主页面使用的时候,调用一下就可以了:

var webTrends = require("webTrends");
webTrends.init();

在定义的模块中,我们暴露了webTrends对象,在外面调用返回对象中的方法就形成了闭包。

模块的两个必要条件:

  • 必须有外部的封闭函数,该函数必须至少被调用一次

  • 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态。

性能考量

如果一个任务不需要使用闭包,那最好不要在函数内创建函数。
原因很明显,这会 拖慢脚本的处理速度,加大内存消耗 。

举个例子,当需要创建一个对象时,方法通常应该和对象的原型关联,而不是定义到对象的构造函数中。 原因是 每次构造函数被调用, 方法都会被重新赋值 (即 对于每个对象创建),这显然是一种不好的做法。

看一个能说明问题,但是不推荐的做法:

    function MyObject(name, message) {
    
      this.name = name.toString();
      this.message = message.toString();
      
      this.getName = function() {
        return this.name;
      };
    
      this.getMessage = function() {
        return this.message;
      };
    }

上面的代码并没有很好的利用闭包,我们来改进一下:

    function MyObject(name, message) {
      this.name = name.toString();
      this.message = message.toString();
    }
    
    MyObject.prototype = {
      getName: function() {
        return this.name;
      },
      getMessage: function() {
        return this.message;
      }
    };

好一些了,但是不推荐重新定义原型,再来改进下:

function MyObject(name, message) {
    this.name = name.toString();
    this.message = message.toString();
}

MyObject.prototype.getName = function() {
       return this.name;
};

MyObject.prototype.getMessage = function() {
   return this.message;
};

很显然,在现有的原型上添加方法是一种更好的做法。

上面的代码还可以写的更简练:

    function MyObject(name, message) {
        this.name = name.toString();
        this.message = message.toString();
    }
    
    (function() {
        this.getName = function() {
            return this.name;
        };
        this.getMessage = function() {
            return this.message;
        };
    }).call(MyObject.prototype);

在前面的三个示例中,继承的原型可以由所有对象共享,并且在每个对象创建时不需要定义方法定义。如果想看更多细节,可以参考对象模型。

闭包的使用场景:

  • 使用闭包可以在JavaScript中模拟块级作用域;

  • 闭包可以用于在对象中创建私有变量。

闭包的优缺点

优点:

  • 逻辑连续,当闭包作为另一个函数调用的参数时,避免你脱离当前逻辑而单独编写额外逻辑。

  • 方便调用上下文的局部变量。

  • 加强封装性,第2点的延伸,可以达到对变量的保护作用。

缺点:

  • 内存浪费。这个内存浪费不仅仅因为它常驻内存,对闭包的使用不当会造成无效内存的产生。

结语

前面对闭包做了一些简单的解释,最后再总结下,其实闭包没什么特别的,其特点是:

  • 函数嵌套函数

  • 函数内部可以访问到外部的变量或者对象

  • 避免了垃圾回收

更多闭包有话说 - 大前端相关文章请关注PHP中文网!

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Golang 生态工具与框架:扩展开发能力
Golang 生态工具与框架:扩展开发能力

《Golang 生态工具与框架》系统梳理 Go 语言在实际工程中的主流工具链与框架选型思路,涵盖 Web 框架、RPC 通信、依赖管理、测试工具、代码生成与项目结构设计等内容。通过真实项目场景解析不同工具的适用边界与组合方式,帮助开发者构建高效、可维护的 Go 工程体系,并提升团队协作与交付效率。

1

2026.02.24

Golang 性能优化专题:提升应用效率
Golang 性能优化专题:提升应用效率

《Golang 性能优化专题》聚焦 Go 应用在高并发与大规模服务中的性能问题,从 profiling、内存分配、Goroutine 调度、GC 机制到 I/O 与锁竞争逐层分析。结合真实案例讲解定位瓶颈的方法与优化策略,帮助开发者建立系统化性能调优思维,在保证代码可维护性的同时显著提升服务吞吐与稳定性。

2

2026.02.24

Golang 面试题精选:高频问题与解答
Golang 面试题精选:高频问题与解答

Golang 面试题精选》系统整理企业常见 Go 技术面试问题,覆盖语言基础、并发模型、内存与调度机制、网络编程、工程实践与性能优化等核心知识点。每道题不仅给出答案,还拆解背后的设计原理与考察思路,帮助读者建立完整知识结构,在面试与实际开发中都能更从容应对复杂问题。

1

2026.02.24

Golang 运行与部署实战:从本地到云端
Golang 运行与部署实战:从本地到云端

《Golang 运行与部署实战》围绕 Go 应用从开发完成到稳定上线的完整流程展开,系统讲解编译构建、环境配置、日志与配置管理、容器化部署以及常见运维问题处理。结合真实项目场景,拆解自动化构建与持续部署思路,帮助开发者建立可靠的发布流程,提升服务稳定性与可维护性。

3

2026.02.24

Golang 疑难杂症解决指南:常见问题排查与优化
Golang 疑难杂症解决指南:常见问题排查与优化

《Golang 疑难杂症解决指南》聚焦开发过程中常见却棘手的问题,从并发模型、内存管理、性能瓶颈到工程化实践逐步拆解。通过真实案例与调试思路,帮助开发者定位问题根因,建立系统化排查方法。不只给出答案,更强调分析路径与工具使用,让你在复杂 Go 项目中具备持续解决问题的能力。

1

2026.02.24

Golang 入门学习路线:从零基础到上手开发
Golang 入门学习路线:从零基础到上手开发

Golang 入门路线涵盖从零到上手的核心路径:首先打牢基础语法与切片等底层机制;随后攻克 Go 的灵魂——接口设计与 Goroutine 并发模型;接着通过 Gin 框架与 GORM 深入 Web 开发实战;最后在微服务与云原生工具开发中进阶,旨在培养具备高性能并发处理能力的后端工程师。

0

2026.02.24

中国研究生招生信息网官方网站入口 研招网网页版在线入口
中国研究生招生信息网官方网站入口 研招网网页版在线入口

中国研究生招生信息网入口(https://yz.chsi.com.cn) 此网站是研究生报名入口的唯一官方网站

95

2026.02.24

苹果官网入口与在线访问指南_中国站点快速直达与iPhone查看方法
苹果官网入口与在线访问指南_中国站点快速直达与iPhone查看方法

本专题汇总苹果官网最新可用入口及中国站点访问方式,涵盖官网直达链接、iPhone官方页面查看方法与常见访问说明,帮助用户快速进入苹果官方网站,便捷了解产品信息与官方服务。

14

2026.02.24

Asianfanfics官网入口与访问指南_AFF官方平台最新登录地址
Asianfanfics官网入口与访问指南_AFF官方平台最新登录地址

本专题系统整理Asianfanfics(AFF)官方网站最新可用入口,涵盖官方平台最新直达地址、官网登录方式及中文访问指引,帮助用户快速、安全地进入AFF平台浏览与使用相关内容。

15

2026.02.24

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
CSS3 教程
CSS3 教程

共18课时 | 6.1万人学习

PostgreSQL 教程
PostgreSQL 教程

共48课时 | 9.7万人学习

NumPy 教程
NumPy 教程

共44课时 | 3.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号