对js函数的实参,形参以及闭包的理解

这篇文章主要介绍了关于对js函数的实参,形参以及闭包的理解,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下

可选形参

if(a === undefined) a = [];

登录后复制

等价于

a = a || [];

登录后复制

这两句是完全等价的,只不过后者需要提前声明a而已
如果参数没有传入,其余的填充undefined
可选的形式参数:通过注释/optional/来强调参数可选,并且要将其放在最后,否则就要使用null或者undefined来作为占位符来进行传入

可变长的实参列表

callee和caller

callee为指代当前正在执行的函数
caller指代当前正在执行函数的函数

将对象属性用作实参

>> function e(o){...   return o.Object;... }undefined> e;[Function: e]> var a = {Object:33};undefined> e(a);33>

登录后复制

作为值的函数

函数能作为值传入另外一个函数

自定义函数属性

函数属性可以自定义

o.a = 3;function o() {  return o.a;}

登录后复制

3917629040-5b4b6f30192be_articlex[1].png

作为命名空间的函数

在函数中声明的变量在整个函数体内都是可见的(包括在嵌套函数中),在函数外部是不可见的。不在任何函数内声明的变量为全局变量。在整个js程序中都是可见的。在js中无法声明只在一个代码块内可见的变量的。所以常常简单的定义一个函数用作临时的命名空间。在这个命名空间内定义的变量都不会污染到全局命名空间。
正是因为如果将变量定义在全局的时候,会出现变量的污染,污染到全局变量(好吧,这是动态语言的坑)导致出现一些未知的错误。所以呢将变量放置在函数中,在进行调用,这样放置其变量污染其全局空间,出现变量的冲突(尤其是在浏览器的环境下,很容易的,导致各种未知错误,所以必须要这样做)

定义完函数后直接调用函数

(  function() {    return 333;  }());

登录后复制

加()是必须的,因为如果不加()会让js解释器认为其为函数声明,function按照函数声明来进行解释,js解释器不允许创建一个匿名的函数声明,所以会报错。
加()变成一个函数表达式,js解释器运行创建一个匿名的函数表达式

闭包

终于到闭包了。(正经点Σ( ° △ °|||)︴)
(这是最难的地方,是函数式编程的基础,也是能否学好js的最关键的地方。。。。当然了,es6还有一个令人讨厌的箭头函数)
闭包是其函数式编程的重要的基础
和其他语言一样js采用的词法作用域,即函数的执行依赖于变量的作用域,作用域是在函数定义时确定的,不是在其调用所决定的
即js的函数对象的内部状态不仅仅包含函数的代码逻辑,还必须引用当前的作用域链(变量的作用域向下传递的,变量的作用域链在进行寻找的时候往上寻找,直到函数的顶部)函数对象可以通过作用域链相互关联起来,函数体内部的变量可以保存在函数作用域内,即闭包

很古老滴术语,指函数变量可以被隐藏于作用域链之内,因此看起来函数将变量包裹起来。

如何定义作用域链

作用域链为一个对象的列表,每次调用js函数的时候,都会创建一个新的对象来保存其局部变量,把这个对象添加到作用域链中,如果函数返回,就从作用域链中将绑定的对象删除,如果不存在嵌套函数,也不存在其引用指向这个绑定的对象,会被js解释器的垃圾回收机制不定时的回收,是不定时的,不是在没有完全引用的时候立马删除,如果定义了嵌套函数,每个嵌套函数都各自对应着一个作用域链,并且这个作用域链指向一个变量绑定的对象。如果这些嵌套函数对象在外部函数中保存下来,那么他们也会和所指向的变量绑定对象一样当做垃圾进行回收,如果这个函数定义了嵌套的函数,并将它作为返回值返回,或者存储在某处属性里,会有外部引用指向这个嵌套函数,即不会被当做垃圾回收,其变量所绑定的对象也不会当做垃圾进行回收。

函数执行完毕以后相关的作用域链不会删除,只有当不在有引用的时候,才会进行删除操作

884710466-5b4b6f3028f51_articlex[1].png

关于栈的说明

原始栈
栈顶 window
执行下列js脚本

function a() {  function f() {    return 333;  }  return f;}a()();

登录后复制

栈顶 a → window
开始调用,执行到return
发现需要调用f
继续加栈
栈顶 f → a → window
执行完f弹出f
继续执行a,执行完毕弹出a
最后全部执行完毕弹出window

算了文字解释太无力,直接上代码

var scope = "global scope"; // 一个全局变量function checkscope() {    var scope = "local scope";  // 定义一个局部变量    function f()   {    return scope; // 返回变量作用域中的scope的值  }    return f(); // 返回这个函数}

登录后复制

调用一下这个函数

checkscope();"local scope"

登录后复制

接着这样执行

var scope = "global scope"; // 一个全局变量function checkscope() {    var scope = "local scope";  // 定义一个局部变量    function f()   {    return scope; // 返回变量作用域中的scope的值  }    return f; // 返回这个函数}

登录后复制

继续调用函数

checkscope()();"local scope"

登录后复制

闭包有什么用

先看一个函数uniqueInteger()使用这个函数能够跟踪上次的返回值

var uniqueInteger = (  function() {    var count = 0;    return function() {return count++}  }());

登录后复制

这样子就使用闭包

uniqueInteger();0uniqueInteger();1

登录后复制

每次返回是其上一次的值,并随便直接将值加1
至于为什么要这样写,如果不使用闭包,那么恶意代码就可以随便的将计数器重置了。。

uniqueInteger.count = 0;function uniqueInteger() {  return uniqueInteger.count++;}

登录后复制

类似这样的,完全可以做到直接通过赋值,将其count的值重置。
而如果使用闭包,没有办法进行修改,为私有状态,也不会导致其一个页面内变量的冲突,或者是其覆盖。

立即调用的函数

var a = (function c(){  var a = 1;  a++;  console.log('已经执行');  return function b(){return a++};}())

登录后复制

额,我大概解释一下这段代码。
首先呢,解释最外层的圆括号,因为如果没有圆括号,则这个是一个赋值语句,将一个匿名函数赋值给变量a,实际上是在内存中完成了栈中变量a指向匿名函数存储的空间的地址,如果有圆括号,实际上是告诉js解释器这是一个语句,需要js执行,消除了其function带来的影响。(ps;貌似火狐上不加也可以,也可以正常的运行)执行和引用的关系下方有。
然后呢,最后的圆括号,代表着其执行这个函数,因为js解析器将()解析为调用前方的函数名称,类似于运算符吧。但是实际上并不是运算符,因为能往其内传值,注意,这点是将其执行的结果保存在堆中,并完成其指向
其后,当直接输入a;,实际上执行并完成了一次调用,其返回值为函数b,将函数b完成一次引用,即变量a引用函数b,由于其存在引用关系,即栈中变量a保存的为其函数a的返回结果,(因为其不是不是对象,如果写a()()表示将函数a调用后返回的对象保存在栈中,然后将栈中的内容再次调用,由于是保存,并不存在其应用关系,所以执行完毕后直接垃圾回收)由于其保存的是函数b的作用域链,而函数b的作用域链是继承自函数a的作用域链,但是由于函数a的作用域链并没有引用导致其执行完后被垃圾回收(当不在有变量指向的时候)。所以呢,函数其值是在函数b中进行保存,如果修改函数c此时函数c并不会影响到函数b中的保存,因为其函数c的变量列表已被销毁,
最后,继续讨论起嵌套函数的引用,由于其父函数已被销毁,但是嵌套函数被引用,(注意:因为其父已经没有,所以是另开辟一块新的堆空间,用于存储其函数c的返回结果,注意是返回结果,而不是函数b)此时另外指定变量保存其结果,无论指定多少个变量保存其结果,都是新的空间的执行,没有任何的干扰,详细了解看下面,继续讨论

ps;如果是()()则代表其会被其垃圾回收

ps 还需要注意一点点的是由于其引用的是result的值,并不是其

最后,这样就能完成其变量保存在函数中,貌似叫做记忆?

所以呢,借助堆和栈就很好的能理解了闭包

再继续看代码

function count() {  var n = 0;  return {    count: function() { return n++; },    reset: function() { n = 0; }  };}

登录后复制

var c = count(); var d = count();undefined

登录后复制

在分别执行一下下

c.count();0d.count();0c.count();1d.count();1c.reset();undefinedc.count();0d.count();2

登录后复制

这一点体现了其互不影响性,表明其由于其父被回收,导致其子分别开创了一块在堆中新的内存空间,并完成其指向,互相不干扰。
其作用域链互不干扰

使用getter和setter完成其闭包

function count(n) {  return {    get count() { return n++; },    set count(m) {       if ( m >= n)        n = m;      else        throw new Error( '请输入一个正确的值' );    },  };}

登录后复制

这个就不用解释啦,很简单啦

同一个作用域链中定义两个闭包

function test1() {  val = value = 111;  this.test = function() { return value - 1; };  this.test2 = function() { return value + 1; };  }

登录后复制

这同样是两个作用链域
不过这样写需要先执行其o.test1(),因为其方法在其函数内部,必须先执行一下,完成其方法的添加,否则会报错,

ee.test is not a function

登录后复制

提示找不到这个方法,
因为执行

ee.test1 = test1;function test1()

登录后复制

只是简单的进行赋值,并不能进行查看,所以导致其无法使用
所以嘛,要先执行一遍,让其方法添加进去

ee.test1();undefinedee.test();110ee.test2();112

登录后复制

这就是两个闭包,这两个闭包互相平行,同时继承于其父,但是又不受其父影响,很神奇吧,(@ο@)

叮 又发现一个莫名奇妙的东东 https://us.leancloud.cn 貌似目前水平能看懂一些了

关于this的问题

this在父闭包显示的即为使用该方法的对象。
但是子就不一定了。

function test1() {  val = value = 111;  this.test = function() { return this.x - 1; };  this.test2 = function() { return this.x + 1; };}

登录后复制

执行一下

ee.test();4443

登录后复制

这就尴尬了。
好吧。只能说是一般不这样用
一般这样写

var self = this;

登录后复制

将其值保存进一个self中

相关推荐:

js的函数声明和函数表达式的分析

如何使用JS求数组差集的方法

以上就是对js函数的实参,形参以及闭包的理解的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2745937.html

(0)
上一篇 2025年3月8日 03:51:22
下一篇 2025年2月28日 06:18:21

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 对于javascript中运算符的方法介绍

    js中的运算符包括算数运算符和关系运算符,其中算术运算符又包括了一元运算符和二元运算符,关系运算符则包括大于,等于,小于以及恒等运算符。 算术运算符 javascript中的算术操作主要通过算术运算符来实现,算术运算符包括一元算术运算符和二…

    编程技术 2025年3月8日
    200
  • 浅析ECMAScript中对象的两种属性

    ecmascript对象有两种属性,分别为数据属性和访问器属性,接下来我们就分别对着两种属性进行分析。 1、数据属性数据属性包含一个数据值的位置。这个位置可以读取和写入值。数据属性有4个描述其行为的特性。[ [ Configurable ]…

    编程技术 2025年3月8日
    200
  • 浅析Vue的异步组件函数

    这篇文章给大家带来的内容是关于浅析vue的异步组件函数,有着相应的代码实例,有需要的朋友可以参考一下。 export default new Router({ routes: [ { path: ‘/live’, name: ‘live’,…

    编程技术 2025年3月8日
    200
  • 一篇文章教你看懂关于JavaScript中this的软绑定

    首先,什么是软绑定? 所谓软绑定,是和硬绑定相对应的一个词,在详细解释软绑定之前,我们先来看看硬绑定。在javascript中,this的绑定是动态的,在函数被调用的时候绑定,它指向什么完全取决于函数在哪里调用,情况比较复杂,光是绑定规则就…

    编程技术 2025年3月8日
    200
  • 遍历器Iterator访问数据集合的统一接口的方法

    这篇文章给大家介绍的内容是关于遍历器iterator访问数据集合的统一接口的方法,有着一定的参考价值,有需要的朋友可以参考一下。 导语 遍历器Iterator是ES6为访问数据集合提供的统一接口。任何内部部署了遍历器接口的数据集合,对于用户…

    编程技术 2025年3月8日
    200
  • React的使用:react框架的五大特点

    这篇文章给大家介绍的内容是关于react的使用:react框架的五大特点,有着一定的参考价值,有需要的朋友可以参考一下。 01. React 出现的时代背景 世界上的事情都有因才有果,一个框架的出现也必然离不开特定的时代背景。而对于 Rea…

    编程技术 2025年3月8日
    200
  • react的使用: React如何渲染UI

    这篇文章给大家介绍的内容是关于react的使用: react如何渲染ui,有着一定的参考价值,有需要的朋友可以参考一下。 01. React 渲染界面的方式 在 React 等大型前端框架出现之前,我们渲染 UI 元素的方式是使用字符串模板…

    编程技术 2025年3月8日
    200
  • React的使用:React组件内部的状态管理

    这篇文章给大家介绍的内容是关于React的使用:React组件内部的状态管理,有着一定的参考价值,有需要的朋友可以参考一下。 在本文中,我们将把目光聚焦于 React 组件内部的状态管理,去认识或重新思考以下三个核心概念: props 和 …

    编程技术 2025年3月8日
    200
  • vue-cli的单元测试的示例解析

    这篇文章给大家介绍的内容是关于vue-cli的单元测试的示例解析,有着一定的参考价值,有需要的朋友可以参考一下。 vue-cli的单元测试 最近项目开发临近结尾,反思之前做的不足的地方,想着应该引入测试类的做法,于是乎开始学习前端测试之类的…

    编程技术 2025年3月8日
    200
  • JavaScript中Object.defineProperty()方法的解析

    这篇文章给大家介绍的内容是关于javascript中object.defineproperty()方法的解析,有着一定的参考价值,有需要的朋友可以参考一下。 =与Object.defineProperty 为JavaScript对象新增或者…

    编程技术 2025年3月8日
    200

发表回复

登录后才能评论