首页 > 其他分享 >深入浅出的介绍下作用域和闭包

深入浅出的介绍下作用域和闭包

时间:2022-11-28 10:14:16浏览次数:62  
标签:闭包 function 函数 作用域 深入浅出 对象 var createCounter

深入浅出的介绍下作用域和闭包

闭包

对前端的同学,经常被问道的一个问题就是, 什么是闭包。是不是很熟悉???
闭包是一个有权访问另外一个函数作用域中的变量的函数
关键的两点

  1. 是一个函数
  2. 能够访问另一个函数作用域中变量

闭包有以下三个特性

  1. 闭包可以访问当前函数以外的变量
function getMyName(){
  var name = 'king';
  function getName(str){
    console.log(str + name);  //访问外部的name
  }
  return getName('本帅哥是:'); //"本帅哥是:king"
}
getMyName();
  1. 即使外部函数已经返回,闭包仍能访问外部函数定义的变量
function getMyName(){
  var name = 'king';
  function getName(str){
    console.log(str + name);  //访问外部的name
  }
  return getName
}
let myName = getMyName();
myName('本帅哥是:');   //"本帅哥是:king"
myName('你不是:');   //"你不是:king"
  1. 闭包可以更新外部变量的值
function undateName () {
  var name = 'king'
  function getName (value) {
    name = value
    console.log(name)
  }
  return getName
}
undateName('china')
undateName('usa')

作用域链

Javascript中有一个执行上下文(execution context)的概念,它定义了变量或函数有权访问的其它数据,决定了他们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。

作用域链: 当访问一个变量时,解释器会首先在当前作用域查找标示符,如果没有找到,就去父作用域找,直到找到该变量的标示符或者不在父作用域中,这就是作用域链。

作用域链和原型继承查找时的区别:如果去查找一个普通对象的属性,但是在当前对象和其原型中都找不到时,会返回undefined;但查找的属性在作用域链中不存在的话就会抛出ReferenceError。

作用域链的顶端是全局对象,在全局环境中定义的变量就会绑定到全局对象中。

全局环境

1. 无嵌套函数

var foo = 1;
var bar = 2;

function myFunc() {
  
  var a = 1;
  var b = 2;
  var foo = 3;
  console.log("inside myFunc");
  
}

console.log("outside");
myFunc();

定义时:当myFunc被定义的时候,myFunc的标识符(identifier)就被加到了全局对象中,这个标识符所引用的是一个函数对象(myFunc function object)。

内部属性[[scope]]指向当前的作用域对象,也就是函数的标识符被创建的时候,我们所能够直接访问的那个作用域对象(即全局对象)。

myFunc所引用的函数对象,其本身不仅仅含有函数的代码,并且还含有指向其被创建的时候的作用域对象。

调用时:当myFunc函数被调用的时候,一个新的作用域对象被创建了。新的作用域对象中包含myFunc函数所定义的本地变量,以及其参数(arguments)。这个新的作用域对象的父作用域对象就是在运行myFunc时能直接访问的那个作用域对象(即全局对象)。

2. 有嵌套函数
当函数返回没有被引用的时候,就会被垃圾回收器回收。但是对于闭包,即使外部函数返回了,函数对象仍会引用它被创建时的作用域对象。

function createCounter(initial) {
  var counter = initial;
  
  function increment(value) {
    counter += value;
  }
  
  function get() {
    return counter;
  }
  
  return {
    increment: increment,
    get: get
  };
}

var myCounter = createCounter(100);
console.log(myCounter.get());   // 返回 100

myCounter.increment(5);
console.log(myCounter.get());   // 返回 105

当调用 createCounter(100) 时,内嵌函数increment和get都有指向createCounter(100) scope的引用。假设createCounter(100)没有任何返回值,那么createCounter(100) scope不再被引用,于是就可以被垃圾回收。

但是createCounter(100)实际上是有返回值的,并且返回值被存储在了myCounter中

即使createCounter(100)已经返回,但是其作用域仍在,并且只能被内联函数访问。可以通过调用myCounter.increment() 或 myCounter.get()来直接访问createCounter(100)的作用域。

当myCounter.increment() 或 myCounter.get()被调用时,新的作用域对象会被创建,并且该作用域对象的父作用域对象会是当前可以直接访问的作用域对象。

调用get()时,当执行到return counter时,在get()所在的作用域并没有找到对应的标示符,就会沿着作用域链往上找,直到找到变量counter,然后返回该变量。

单独调用increment(5)时,参数value保存在当前的作用域对象。当函数要访问counter时,没有找到,于是沿着作用域链向上查找,在createCounter(100)的作用域找到了对应的标示符,increment()就会修改counter的值。除此之外,没有其他方式来修改这个变量。闭包的强大也在于此,能够存贮私有数据。

创建两个函数:myCounter1和myCounter2

function createCounter(initial) {
  /* ... see the code from previous example ... */
}

//-- create counter objects
var myCounter1 = createCounter(100);
var myCounter2 = createCounter(200);


myCounter1.increment和myCounter2.increment的函数对象拥有着一样的代码以及一样的属性值(name,length等等),但是它们的[[scope]]指向的是不一样的作用域对象。

下面是我的小程序体验码,希望能和大家共同学习进步

标签:闭包,function,函数,作用域,深入浅出,对象,var,createCounter
From: https://www.cnblogs.com/eyesstar/p/16931449.html

相关文章

  • 作用域和闭包常见的面试题
    作用域和闭包常见的面试题作用域变量提升varscope="global";functionscopeTest(){console.log(scope);varscope="local"}scopeTest();//undefined......
  • C语言支持闭包
    前言c语言不支持闭包函数,因此需要通过参数结构体保存所有参数,将上下文传递给业务概述下面是一个例子,用于重试#include<stdio.h>#include<unistd.h>typedefenum{......
  • Rust闭包
    很多语言中都有闭包的概念,闭包就是一个能够捕获周围作用域中变量的函数,它们通常以简洁的形式展现,比如lambda表达式。Rust的Lambda表达式Rust中的闭包也是lambda表达式形......
  • 深入浅出学习透析Nginx服务器的基本原理和配置指南「Keepalive性能分析实战篇」
    Linux系统:Centos7x64Nginx版本:1.11.5Nginx是一款面向性能设计的HTTP服务器,能反向代理HTTP,HTTPS和邮件相关(SMTP,POP3,IMAP)的协议链接。并且提供了负载均衡以及HTTP......
  • 变量,常量,作用域
    变量、常量、作用域变量变量就是剋以变化的量在内存中的一块位置Java是一种强类型语言,每个变量都必须声明其类型Java变量是程序中最基本的存储单元,其要素包括变量名,......
  • spring::ioc作用域
    Bean的作用域singleton单实例prototype多实例,每次都是新建一个prototype,<!--Beans.xml文件--><?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www......
  • 10:生成器 迭代器 装饰器 闭包
    一:生成器#生成器:为了节约内存,拿到内存地址一边循环一边计算a=(x*2forxinrange(1,8))print(a)print(next(a))print(next(a))<generatorobject<genexpr>at0x00934......
  • 闭包以及内存泄露
    什么是闭包闭包是一种函数结构的统称,就是一群长相结构大差不差的函数群体就叫做闭包。那么它有啥特点呢?最大的特点就是儿子函数使用或者访问了祖宗函数的私有变量,这样的玩......
  • Java Web中requset,session,application 的作用域及区别
    三者概述requset概述:request是表示一个请求,只要发出一个请求就会创建一个request用处:常用于服务器间同一请求不同页面之间的参数传递,常应用于表单的控件值传递。sessio......
  • PHP闭包之变量作用域
    在项目中,难免会遇到闭包的形式,那么在闭包中,变量的作用域到底是怎么样的呢。下面有几个简单的例子。functiontest_1(){$a='php';$func=function($b)use($a)......