js-函数(闭包、私有、递归、自调)、变量(隐士)和作用域

一、JavaScript 函数

将脚本编写为函数,就可以避免页面载入时执行该脚本。

打印 定义的函数名 就相当于打印这整个函数

函数包含着一些代码,这些代码只能被事件激活或者在函数被调用时才会执行。

你可以在页面中的任何位置调用脚本(如果函数嵌入一个外部的 .js 文件,那么甚至可以从其他的页面中调用)。

创建函数的语法:

function 函数名(var1,var2,...,varX){

    代码...

}

注意:无参数的函数必须在其函数名后加括号

函数的调用必须是函数名后面跟(),有参给参无参不给

函数名是什么?  函数名 == 整个函数。

函数加载问题?   JS加载的时候,只加载函数名,不加载函数体。所以如果想使用内部的成员变量,需要先调用这个函数。

第一种:函数声明(最常用)

function 函数名(){ 

      代码...

}

第二种:函数表达式

var text =  function (var1,var2,...,varX) {

    代码……

}

第三种:面向对象(构造函数)

var  fn = new  Function(var1,var2,...,varX);

第四种:匿名函数(即没有名字的函数)

function (){

    代码……

}

特殊的函数:

第一种:回调函数(一个函数作为另一个函数的参数进行传递,高阶函数)

function fn(a,b,){
    return a()+b();
}

var  one = function(){
    return 1;
}

var two = function(){
    return 2:
}

//函数调用
fn(one,two)

第二种:匿名回调函数(将匿名函数作为参数传递的函数)

第三种:自调函数/立即执行函数(自己定义自己调用,别人不能调用的函数)

(function () {
    alert("a");
}) ();

第四种:内部私有函数(函数里面还有函数)

内部私有函数的表现形式:

    function fn() {
        function fn1() {
            console.log("fn1");
        }
    }
    fn();
//    fn1();//如果执行fn1()会报错

内部私有函数的解决办法:(通过函数表达式的方式将内部的那个函数给放到外部)

var fn2 ;
function  fun() {
    fn2=function () {
        alert("fn2");
    }
}
fun();
fn2();

闭包:

/* 
    1.如何产生闭包?
        当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包
    2.闭包到底是什么?
        闭包 = 内层函数 + 外层函数的变量
        理解一:闭包是嵌套的内部函数(绝大部分人)
        理解二:包含被引用变量(函数)的对象(极少数人)
        注意:闭包存在于嵌套的内部函数中
    3.产生闭包的条件?
        函数嵌套
        内部函数引用了外部函数的数据(变量/函数)
    4.闭包的作用?
        封闭数据,实现数据私有,外部也可以访问函数内部的变量
        闭包很有用,因为它允许将函数与其所操作的某些数据(环境)关联起来
    5.使用场景
        1.创建私有变量
        2.延长变量的生命周期
    6.闭包可能引起的问题?
        内存泄漏
*/

function outer() {
    var a = 2;
    function fn() { // 执行函数定义就会产生闭包(不用调用内部函数)
        console.log(a);
    }
}
outer();
// 闭包的常规用法
/** 
 * 1.将函数作为另一个函数的返回值
 * 2.将函数作为实参传递给另一个函数调用
*/

function fn1() {
    var a = 2;
    function fn2() {
        a++;
        console.log(a);
    }
    return fn2;
}
var fn = fn1();
fn();
fn();

// 2.将函数作为实参传递给另一个函数调用
function showDelay(msg,time) {
    setTimeout(() => {
        alert(msg);
    }, time);
}
showDelay("4444",1000);

/** 
 * 闭包的作用:
 *  1.使用函数内部的变量在函数执行后,仍然存活在内存中(延长了局部变量的生命周期)
 *  2.让函数外部可以操作(读写)到函数内部的数据(变量/函数)
 * 
 * 问题:
 *  1.函数执行完后,函数内部声明的局部变量是否还存在?  一般是不存在,存在于闭包中的变量才可能存在
 *  2.在函数外部能直接访问函数内部的局部变量吗?  不能,但我们可以通过闭包让外部操作它
 * 
*/

/** 
 * 闭包的生命周期:
 *  1.产生:在嵌套内部函数定义执行完成时就产生了(不是在调用)
 *  2.死亡:在嵌套的内部函数成为垃圾对象时
*/
// fn = null; // 闭包死亡(包含闭包的函数对象成为垃圾对象---回收闭包)


/** 
 * 1.闭包的缺点:
 *  函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
 *  容易造成内存泄漏
 * 2.解决:
 *  能不用闭包就不用
 *  及时释放
*/
//作用域、上下文
function a() {
    var i=0;
    function b() {
        console.log(++i);
    }
    return b;
}
var c = a();
c();

首先函数c是无法访问函数a中的变量的,此时通过在函数a内构造一个内部函数b,b自然是可以读取a中的变量的,此时c便可以通过b来读取a中的变量,这便是闭包了。

如果之后再执行c(),此时会输出1,这是为什么呢?

这和js的垃圾回收机制有很大的关系,由于闭包c的存在,函数b依赖于a中的变量,使得函数a的i始终存在在内存。

特点:可以重复利用变量,并且这个变量不会污染全局的一种机制;这个变量是一直保存在内存中,被垃圾回收机制回收

使用闭包的注意点:

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除(i = null;)。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

使用场景:

1.防抖

2.节流

3.柯里化函数

4.函数嵌套函数避免全局污染的时候 

递归

递归:就是函数内部自己调用自己。  必须有跳出条件   必须在外部执行这个函数才能形成递归

注意:别忘记 JavaScript 中大小写字母的重要性。"function" 这个词必须是小写的,否则 JavaScript 就会出错另外需要注意的是,必须使用大小写完全相同的函数名来调用函数。

高阶函数

定义:一个函数的 参数/返回值 是一个函数的函数称为高阶函数。常见的像数组的map、reduce、filter这些都是高阶函数

高阶函数:如果一个函数符合下面2个规范中的任何一个,那么该函数就是高阶函数
    1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
    2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
    常见的高阶函数:Promise、setTimeout、arr.map()、arr.reduce()
// 简单的高阶函数
function add(x,y,fn){
    return fn(x) + fn(y);
}

function fn(num){
    return Math.abs(num);
}

add(-5,6,fu); // 11

函数柯里化

函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。

 柯里化(Currying)又称部分求值,一个柯里化的函数首先会接收一些参数,接收了这些参数后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要它们求值的时候,之前传入的所有参数都会被一次性用于求值。

柯里化是一种函数的转换,它是指将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)或者f(a, b)(c)或者f(a)(b, c)。

函数柯里化的主要作用和特点就是参数复用、提前返回和延迟执行。

// 普通的add函数
function add(x,y){
    return x + y;
}

// 柯里化后
function curryingAdd(x){
    return function(y){
        return x + y;
    }
}

add(1,2); // 3
curryingAdd(1)(2); // 3

return 语句

return 语句用来规定从函数返回的值。

因此,需要返回某个值的函数必须使用这个 return 语句。

function prod(a,b){
    var x=a*b
    return x;
}

注意:

1. 如果函数没有显示的使用 return 语句 ,那么函数有默认的返回值:undefined

2. 如果函数使用 return 语句,那么跟再return后面的值,就成了函数的返回值

3. 如果函数使用 return 语句,但是return后面没有任何值,那么函数的返回值   也是:undefined

4. 函数使用 return 语句后,这个函数会在执行完 return 语句之后停止并立即退出,也就是说return后面的所有其他代码都不会再执行。

变量和作用域(隐式全局变量和变量声明提升)

变量和作用域(函数中的变量需要函数执行后才能执行)

一、全局变量(成员变量)

哪里都可以访问到的变量。

(进入script立即定义的变量或函数外部和函数没有var的变量)

二、局部变量(只有局部能够访问的变量)

函数内部的变量,只有函数内部可以访问到。

(函数内部用var定义的变量和形参)

手动清除变量: aaa = null

作用域:全局变量一直存在,手动消失;局部变量函数调用完就被回收(特例:闭包不会消失需要手动消失)。

隐式全局变量

隐式全局变量就是隐藏的全局变量不好被发现。(没有被var声明的变量可看成是全局变量)

function fn(){
    var a  =  b =  c  = 1;   // b和c就是隐式全局变量
}

注意:

function fn(){
    var a  =  b =  c  = 1;   // b和c就是隐式全局变量(等号)
    var a = 1;  b = 2;  c = 3;    // b和c就是隐式全局变量(分号)
    var a = 1 ,  b = 2 ,  c = 3;   // b和c就不是隐式全局变量(逗号)
}

变量声明提升(出现原因:预解析)

函数中,定义变量在使用变量之后。

var/function 声明只提升变量名/函数体,不提升变量值,容易出现undefined。计算后形成NaN。

function fn(){
    // var aaa;
    console.log(aaa);
    var aaa = 1;
}

//提前看一眼这个习惯叫什么呢?  预解析!
//变量声明提升:在预解析的时候,成员变量和函数,被提升到最高位置,方便其他程序访问。
//变量声明提升特点:成员变量只提升变量名,不提升变量值。但是,函数是所有内容全部提升。(function直接定义的)
//函数范围内照样会出现变量声明提升
//什么情况容易出现变量声明提升:使用变量在定义变量之前。

函数范围内,照样适用。

预解析过程:

  1. 语法检查,如果有错误,直接停止后续步骤不再运行。

  2. 把变量和函数的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值和调用。

  3. 先提升变量后提升函数,如果函数和变量同名,则变量会被函数替换;

代码执行过程:

变量的赋值,函数的调用,循环判断等,根据代码由上往下顺序执行;

小知识:

1.函数不调用不执行

2.function函数名等于整个函数

3.加载函数的时候,只加载函数名,不加载函数体(原因:函数里面的值不加载不执行)

4.就近原则使用变量(同名变量)

5.两个平级的函数变量不会相互影响,但可以使用相同的参数。

请描述JS中的作用域及其作用域链。

JavaScript中的作用域是指变量、函数等在程序中被访问的范围JS采用的是词法作用域,即代码的作用域在定义时就已经确定了。作用域链是指变量在代码中查找的过程,JavaScript引擎在查找变量时会先在当前作用域内查找,如果没有找到,就会逐级向上查找直到全局作用域。

JavaScript中的作用域有全局作用域和局部作用域。在函数内部定义的变量,只在该函数内部可见,称为局部变量;在函数外定义的变量,称为全局变量,在整个程序中都可以访问。

作用域链是由当前执行环境的变量对象和上一级执行环境的变量对象构成,这些变量对象有序地排列在一个链表结构中。当执行环境需要访问一个变量时,会首先在当前的变量对象中查找,如果没有找到,则会到上一级的变量对象中查找,直到找到该变量或者到达全局作用域。如果在全局作用域中也没有找到该变量,则会抛出“未定义”错误

作用域和作用域链的概念在理解JavaScript中的变量、函数、闭包等重要概念时非常重要。

如有不足请多多指教!希望给您带来帮助!

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Python函数中的变量作用域指的是变量的可见范围。在函数中定义的变量可以分为两种:局部变量和全局变量。 局部变量指的是在函数内部定义的变量,只能在函数内部使用,函数外部无法访问。当函数执行完毕后,局部变量的值会被销毁。 全局变量指的是在函数外部定义的变量,可以在函数内部和外部使用。但是,在函数内部如果要修改全局变量的值,需要使用global关键字进行声明。 在Python中,变量作用域遵循LEGB规则,即:Local(局部变量)-> Enclosing(闭包函数外的函数中的变量)-> Global(全局变量)-> Built-in(内置变量)。 当函数内部使用变量时,Python会按照LEGB规则从内到外查找变量,直到找到为止。如果在函数内部没有找到变量,则会继续向外查找,直到找到为止。如果最终还是没有找到变量,则会抛出NameError异常。 因此,在编写Python函数时,需要注意变量作用域,避免出现变量名冲突等问题。 ### 回答2: Python的函数中,变量作用域并不像其他编程语言那样严格。在Python中,变量作用域很容易受到内层作用域的影响,而无法访问外层的变量,这部分属于局部变量。下面我们从全局变量和局部变量两个方面来讲解变量作用域。 一、全局变量作用域 在Python中,如果变量未定义在任何函数内,即在全局作用域内,那么在各个函数内都可以访问该变量。 例如: ``` count = 0 def test(): global count count += 1 print(count) test() ``` 以上代码中,count变量未定义在函数内部,属于全局作用域,在调用函数`test()`时,可以使用`global`关键字来声明该变量为全局变量,然后在函数内部可以直接对该变量进行修改和访问。 二、局部变量作用域 在Python中,如果变量定义在函数内部,则该变量作用域只限于函数内部,外部无法访问该变量,称为局部变量。 例如: ``` def test(): count = 0 count += 1 print(count) test() ``` 以上代码中,count变量定义在函数`test()`内部,属于局部变量。在函数内部对count进行修改和访问也是可以的,但是在函数外部是无法访问到该变量的,否则会报错。 需要注意的是,函数内的变量名如果和全局变量变量名相同,那么在函数内访问该变量时,默认会访问局部变量,而非全局变量。如果仍要在函数内部访问全局变量,可以使用`global`关键字进行声明。 例如: ``` count = 0 def test(): count = 1 print("count in local:", count) test() print("count in global:", count) ``` 以上代码中,函数内部定义了一个名为count的局部变量,调用函数后,输出的是局部变量count的值,而不是全局变量count的值0。如果要访问全局变量count的值,可以在函数内部使用`global count`声明该变量为全局变量,再进行访问。 总之,Python的变量作用域相对比较宽松,可以根据具体情况进行灵活使用,但是在使用局部变量和全局变量时要避免命名冲突,同时合理使用`global`关键字来声明全局变量,以免出现意想不到的错误。 ### 回答3: 在Python中,变量作用域指的是变量所能被访问到的范围。在一个函数中定义的变量只能在函数内部被访问到,而在函数外定义的变量则可以在整个程序中被访问到。 Python中的变量作用域分为两种:局部作用域和全局作用域。局部变量指的是在一个函数内部定义的变量,只能在该函数内部访问。全局变量指的是在函数外部定义的变量,可以在整个程序中被访问到。如果在函数内部要访问全局变量,则需要使用global关键字进行声明。 在Python中,变量作用域可以遵循 LEGB 原则,即 Local(局部)、Enclosing(闭包)、Global(全局)、Built-in(内置)的顺序进行查找。这意味着变量首先在函数内部被查找,然后在函数外部被查找,之后在内置变量中被查找。 当在函数内部定义与全局变量同名的变量时,Python会优先使用局部变量而不是全局变量。如果需要在函数内部修改全局变量,则必须使用global关键字声明。 在使用闭包时,可以通过在函数内部再定义一个函数内部函数可以访问外部函数中的变量。这样的变量作用域称为嵌套作用域。在Python中,使用nonlocal关键字可以实现在内部函数中修改外部函数中定义的变量。 总之,学习变量作用域对于编写规范化的程序来说非常重要,特别是在编写复杂的函数时。了解变量作用域可以帮助我们更好地管理变量,并避免不必要的错误和问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值