你不知道的JavaScript--作用域闭包

目录

5.1 启示

5.2 实质问题

5.3 现在我懂了

5.4 循环和闭包

5.5 模块 

模块模式的两个必要条件:

 单例模式模块

公共API

5.5.1 现代的模块机制

模块依赖加载器 / 管理器


关键词:词法作用域外部引用、外围函数、内部函数、单例模式

5.1 启示

闭包是基于词法作用域书写代码时所产生的自然结果,它在JavaScript中无处不在
 
 

5.2 实质问题

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用
域之外执行
 
 

 看代码: 

function foo() {
    var a = 2;
  
    function bar() { 
        console.log( a ); 
    }

    return bar; 
}

var baz = foo(); 

baz(); // 2 —— 朋友,这就是闭包的效果。

调用 baz() 时,bar() 被执行了,它是在它被声明的词法作用域外部被执行的。

问:在执行 var baz = foo() 后,我们会期望 foo() 的整个内部作用域消失(垃圾回收)。但并没有,bar() 执行时依旧在使用内部的作用域。为何?

答:这得益于 bar() 声明的位置,bar() 拥有一个词法作用域闭包覆盖着 foo() 的内部作用域,闭包为了能使 bar() 在以后任意的时刻可以引用这个作用域而保持它的存在。

bar() 依然拥有对那个作用域的引用,而这个引用称为闭包。

 

再来点闭包:

var fn;

function foo() {
    var a = 2;
    
    function baz() { 
        console.log( a ); 
    }
    
    fn = baz; // 将 baz 分配给全局变量 
}
    
function bar() { 
        fn(); // 妈妈快看呀,这就是闭包! 
}

foo(); 

bar(); // 2

 

无论通过何种手段将内部函数 传递 到所在的词法作用域以外,它都会持有对原始定义作用
域的引用,无论在何处执行这个函数都会使用闭包。
 
 

5.3 现在我懂了

常见的工具函数 setTimeout(..)是闭包:

function wait(message) {
    
    setTimeout( function timer() {
        console.log( message );
    }, 1000)
}

wait( "Hello,closuer" ); // 1000毫秒后,timer还拥有wait的内部作用域

或者很熟悉的jQuery写法:

function setupBot(name,selector) {
    $( selector ).click( function activator() {
        console.log( "Activating:" +name );
    })
}

setupBot( "Closure Bot 1","#bot_1"); // click时还持有setupBot作用域
setupBot( "Closure Bot 2","#bot_2");

 只要使用回调函数,就是在使用闭包

 

5.4 循环和闭包

for (var i=1;i<=5;i++){
    setTimeout( function timer() {
        console.log( i );
    }, i*1000 );
}

思考下这段代码的答应结果,是不是我们想象的分别输出1、2、3、4、5、6?若不是该如何改进?

解题思路: IIFE

最优解:     IIFE 结合 LET 块作用域

答案见文尾

 

5.5 模块 

JS模块也是用了闭包的威力:

function CoolModule() {
    var something = "cool";

    var anoher = [1,2,3];

    function doSomething() {
        console.log(something);
    }
    
    function doAnother() {
        console.log(another.join("!"));
    }

    return {
        doSomething: doSomething,
        doAnother: doAnother
    };
}

var foo = CoolModule();

foo.doSomething(); // cool
foo.doAnother();  // 1!2!3
通过 调用 CoolModule  来创建一个模块实例,同时创建内部作用域和闭包
 
从模块中返回一个实际的对象并不是必须的,也可以直接 返回一个内部函
jQuery 就是一个很好的例子。 jQuery $ 标识符就是 jQuery 模块的公
API ,但它们本身都是函数(由于函数也是对象,它们本身也可以拥有属
性)。
 
var myModule1 = (function($, global){
    ...//
}(jQuery, window))

模块模式的两个必要条件:

  1. 必须有外部的封闭函数,该函数必须至少被调用一次(每次调用创建一个模块实例)
  2. 封闭函数必须至少返回一个内部函数,这样内部函数才能在私有作用域中形成闭包,并可以访问、修改私有的状态

 经常会看到一个对象封装并返回一些函数。因为没有闭包,所以此种写法不是模块

 单例模式模块

通过 IIFE 创建单例模式:

var foo = (function CoolModule(id) {
    var something = "cool";
    var another = [1,2,3];

    function doSomething() {
        console.log( something + id);
    }

    function doAnother() {
        console.log( another.join(" ! ") );
    }

    return {
        doSomething: doSomething,
        doAnother: doAnother
    };
})();

// 也可以传参
foo.doSomething("123"); // cool123
foo.doAnother(); // 1!2!3

公共API

通过在模块实例的内部保留对公共 API 对象的内部引用,可以 从内部 对模块实例进行修
改,包括添加或删除方法和属性, 以及修改它们的值:
var foo = (function CoolModule(id) {
    function change() {
        // 修改公共API
        publicAPI.identify = identify2;
    }

    function identify1() {
        console.log( id );
    }

    function identify2() {
        console.log( id.toUpperCase );
    }

    var publicAPI = {
        change: change,
        identify: identify1
    };

    return publicAPI;
})( "foo module" );

foo.identify(); // foo module
foo.change();
foo.identify(); // FOO MODULE

 

5.5.1 现代的模块机制

模块依赖加载器 / 管理器

大多数模块依赖加载器 / 管理器本质上都是将这种模块定义封装进一个友好的 API
var MyModules = (function Manager() {
    var modules = {};
    
    function define(name, deps, impl) {
        for (var i=0; i<deps.length; i++){
            //将依赖的模块转换为modules列表内的一个元素
            deps[i] = modules[deps[i]];
        }
        //定义模块并传入依赖模块参数
        modules[name] = impl.apply( impl, deps);
    }

    function get(name) {
        return modules[name]; 
    }

    return {
        define: define,
        get: get
    };
})();

 在模块依赖中,有个声明模块 define ,有三个参数:

  • name   声明的模块名称
  • deps    该模块所依赖的模块名称集合
  • impl      模块主体函数,返回该模块数据内容

modules[name] = impl.apply( impl, deps ) : 通过 apply deps 中的依赖注入到主函数 impl

通过 get函数 获取到模块

MyModules.define( "bar", [], function() {
    function hello(who) {
        return "Let me introduce: " + who;
    }

    return {
        hello: hello
    };
});

MyModules.define( "foo", ["bar"], function(bar) {
    var hungry = "hippo";

    function awesome() {
        console.log( bar.hello( hungry ).toUpperCase() );
    }

    return {
        awesome: awesome
    };
});

var bar = MyModules.get( "bar" );
var foo = MyModules.get( "foo" );

console.log(
    bar.hello( "hippo" )
);  // Let me introduce: hippo

foo.awesome(); // LET ME INIRODUCE: HIPPO

 

 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值