最近在学习javascript的函数,函数是javascript的一等对象,想要学好javascript,就必须深刻理解函数。本人把思路整理成文章,一是为了加深自己函数的理解,二是给读者提供学习的途径,避免走弯路。内容有些多,但都是笔者对于函数的总结。

1.函数的定义

  1.1:函数声明

  1.2:函数表达式

  1.3:命名函数的函数表达式

  1.4:函数的重复声明

2.函数的部分属性和方法

  2.1:name属性

  2.2:length属性

  2.3:toString()方法

3.函数作用域

  3.1:全局作用域和局部作用域

  3.2:函数内部的变量提升

  3.3:函数自身的作用域

1.函数的定义

  1.1:函数声明

  函数就是一段可以反复调用的代码块。函数声明由三部分组成:函数名,函数参数,函数体。整体的构造是function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数。函数体放在大括号里面。当函数体没有使用return关键字返回函数时,函数调用时返回默认的undefined;如果有使用return语句,则返回指定内容。函数最后不用加上冒号。

    function keith() {}console.log(keith())   // 'undefined'function rascal(){return 'rascal';}console.log(rascal())    // 'rascal'

  函数声明是在预执行期执行的,也就是说函数声明是在浏览器准备解析并执行脚本代码的时候执行的。所以,当去调用一个函数声明时,可以在其前面调用并且不会报错。

1     console.log(rascal())   // 'rascal'
2     function rascal(){
3         return 'rascal';
4     }

  其实这段代码没有报错的原因还有一个,就是与变量声明提升一样,函数名也会发生提升。函数名提升会在下面小节谈到。

  1.2:函数表达式

  函数表达式是把一个匿名函数赋给一个全局变量。这个匿名函数又称为函数表达式,因为赋值语句的等号右侧只能放表达式。函数表达式末尾需要加上分号,表示语句结束。

1     var keith = function() {
2         //函数体
3     };

  函数表达式与函数声明不同的是,函数表达式是浏览器解析并执行到那一行才会有定义。也就是说,不能在函数定义之前调用函数。函数表达式并不像函数声明一样有函数名的提升。如果采用赋值语句定义函数并且在声明函数前调用函数,JavaScript就会报错。

1     keith();
2     var keith = function() {};
3     // TypeError: keith is not a function

  上面的代码等同于下面的形式。

1     var keith;
2     console.log(keith());   // TypeError: keith is not a function
3     keith = function() {};

  上面代码第二行,调用keith的时候,keith只是被声明了,还没有被赋值,等于undefined,所以会报错。

  1.3:命名函数的函数表达式

  采用函数表达式声明函数时,function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效。

1     var keith = function boy(){
2       console.log(typeof boy);
3     };
4
5     console.log(boy);
6     // ReferenceError: boy is not defined
7
8     keith();
9     // function

上面代码在函数表达式中,加入了函数名boy。这个boy只在函数体内部可用,指代函数表达式本身,其他地方都不可用。这种写法的用处有两个,一是可以在函数体内部调用自身,二是方便除错(除错工具显示函数调用栈时,将显示函数名,而不再显示这里是一个匿名函数)。

  1.4:函数的重复声明

  如果同一个函数被多次声明,后面的声明就会覆盖前面的声明。

1     function keith() {
2         console.log(1);
3     }
4     keith(); //2
5     function keith() {
6         console.log(2);
7     }
8     keith(); //2

  上面代码中,后一次的函数声明覆盖了前面一次。而且,由于函数名的提升,前一次声明在任何时候都是无效的。JavaScript引擎将函数名视同变量名,所以采用函数声明的方式声明函数时,整个函数会像变量声明一样,被提升到代码头部。表面上,上面代码好像在声明之前就调用了函数keith。但是实际上,由于“变量提升”,函数keith被提升到了代码头部,也就是在调用之前已经声明了。

2.函数的部分属性和方法

  2.1:name属性

  name属性返回紧跟在function关键字之后的那个函数名。

1     function k1() {};
2     console.log(k1.name); //'k1'
3
4     var k2 = function() {};
5     console.log(k2.name); //''
6
7     var k3 = function hello() {};
8     console.log(k3.name); //'hello'

  上面代码中,name属性返回function 后面紧跟着的函数名。对于k2来说,返回一个空字符串,注意:匿名函数的name属性总是为空字符串。对于k3来说,返回函数表达式的名字(真正的函数名为k3,hello这个函数名只能在函数内部使用。)

  2.2:length属性

  length属性返回函数预期传入的参数个数,即函数定义之中的参数个数。返回的是个数,而不是具体参数。

1     function keith(a, b, c, d, e) {}
2     console.log(keith.length)    // 5

  上面代码定义了空函数keith,它的length属性就是定义时的参数个数。不管调用时输入了多少个参数,length属性始终等于5。也就是说,当调用时给实参传递了6个参数,length属性会忽略掉一个。

  2.3:toString()方法

  函数的toString方法返回函数的代码本身。

1     function keith(a, b, c, d, e) {
2         // 这是注释。
3     }
4     console.log(keith.toString());
5     //function keith(a, b, c, d, e) { // 这是注释。 }

  可以看到,函数内部的注释段也被返回了。

3.函数作用域

  3.1:全局作用域和局部作用域

  作用域(scope)指的是变量存在的范围。Javascript只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取,在全局作用域中声明的变量称为全局变量;另一种是局部作用域,变量只在函数内部存在,此时的变量被称为局部变量。

  在全局作用域中声明的变量称为全局变量,也就是在函数外部声明。它可以在函数内部读取。

1     var a=1;
2     function keith(){
3         return a;
4     }
5     console.log(keith())    //1

  上面代码中,全局作用域下的函数keith可以在内部读取全局变量a。

  在函数内部定义的变量,只能在内部访问,外部无法读取,称为局部变量。注意这里必须是在函数内部声明的变量。

1     function keith(){
2         var a=1;
3         return a;
4     }
5     console.log(a)    //Uncaught ReferenceError: a is not defined

  在上面代码中,变量a在函数内部定义,所以是一个局部变量,外部无法访问。

  函数内部定义的变量,会在该作用域下覆盖同名变量。注意以下两个代码段的区别。

 1     var a = 2;2 3     function keith() {4         var a = 1;5         console.log(a);6     }7     keith(); //18 9     var c = 2;
10
11     function rascal() {
12         var c = 1;
13         return c;
14     }
15     console.log(c); //216    console.log(rascal());  //1

  上面代码中,变量a和c同时在函数的外部和内部有定义。结果,在函数内部定义,局部变量a覆盖了全局变量a

  注意,对于var命令来说,局部变量只能在函数内部声明。在其他区块声明,一律都是全局变量。比如说if语句。

1     if (true) {
2         var keith=1;
3     }
4     console.log(keith);    //1

  从上面代码中可以看出,变量keith在条件判断区块之中声明,结果就是一个全局变量,可以在区块之外读取。但是这里如果采用ES6中let关键字,在全局作用域下是无法访问keith变量的。

  3.2:函数内部的变量声明提升

  与全局作用域下的变量声明提升相同,局部作用域下的局部变量在函数内部也会发生变量声明提升。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。

 1     function keith(a) {2         if (a > 10) {3             var b = a - 10;4         }5     }6 7     function keith(a) {8         var b;9         if (a > 10) {
10             b = a - 10;
11         }
12     }

  上面两个函数段是相同的。

  3.3:函数本身的作用域

  函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。

 1     var a = 1;2     var b = function() {3         console.log(a);4     };5     function c() {6         var a = 2;7         b();8     }9     c(); //1
10
11     var a = 1;
12     var b = function() {
13         return a;
14     };
15     function c() {
16         var a = 2;
17         return b();
18     }
19     console.log(c()); //1

  以上两个代码段相同。函数b是在函数c外部声明的。所以它的作用域绑定在函数外层,内部函数a不会到函数c体内取值,所以返回的是1,而不是2。

  很容易犯错的一点是,如果函数A调用函数B,却没考虑到函数B不会引用函数A的内部变量。

 1     var b = function() {2         console.log(a);3     };4     function c(f) {5         var a = 1;6         f();7     }8     c(b); //Uncaught ReferenceError: a is not defined9
10
11     var b = function() {
12         return a;
13     };
14     function c(f) {
15         var a = 1;
16         return f();
17     }
18     console.log(c(b)); //Uncaught ReferenceError: a is not defined

  上面代码将函数b作为参数,传入函数c。但是,函数b是在函数c体外声明的,作用域绑定外层,因此找不到函数c的内部变量a,导致报错。

  同样的,函数体内部声明的变量,作用域绑定在函数体内部。

 1     function keith() {2         var a = 1;3 4         function rascal() {5             console.log(a);6         }7         return rascal;8     }9
10     var a = 2;
11     var f = keith();
12     f(); //1

  上面代码中,函数keith内部声明了rascal变量。rascal作用域绑定在keith上。当我们在keith外部取出rascal执行时,变量a指向的是keith内部的a,而不是keith外部的a。这里涉及到函数另外一个重要的知识点,即在一个函数内部定义另外一个函数,也就是闭包的概念。下次有机会会分享。

转载于:https://www.cnblogs.com/chenliyang/p/6548355.html

深入理解javascript函数定义与函数作用域相关推荐

  1. 理解javascript中的回调函数(callback)【转】

    在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实 ...

  2. 理解javascript中的回调函数(callback)

    理解javascript中的回调函数(callback) 在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Obje ...

  3. kitten编程猫里的函数定义,函数实现和函数调用原理

    kitten编程猫 函数定义和函数实现: var 我的成语库 = [];function 成语初始化() {我的成语库.splice('' - 1, 1);我的成语库.push(1);我的成语库.pu ...

  4. 程序设计C语言函数定义,编程函数的定义之C语言

    编程函数的定义之C语言 类型标识符指明了本函数的类型,函数的类型实际上是函数返回值的类型.接下来,小编为您介绍了编程函数的定义之C语言,感谢您的阅读! 无参函数的定义 无参函数定义的`一般形式如下:类 ...

  5. 函数定义、函数声明、函数原型

    函数定义.函数声明.函数原型:变量声明.变量定义:类型声明.类型定义 1.函数定义.函数声明.函数原型 2.变量声明.变量定义 3.类型声明.类型定义 1.函数定义.函数声明.函数原型 函数声明中不但 ...

  6. 多个数字数组_1分钟彻底理解JavaScript的数组与函数

    1 - 数组 1.1 数组的概念 - 数组可以把一组相关的数据一起存放,并提供方便的访问(获取)方式. - 数组是指**一组数据的集合**,其中的每个数据被称作**元素**,在数组中可以**存放任意类 ...

  7. javaScript原生定义的函数

    1.JavaScript中的算术运算 包括加(+).减(-).乘(*).除(/)和求余(取模)(%)运算,除了这些基本的运算外,JavaScript还支持更加复杂的算术运算,这些复杂算术运算作为Mat ...

  8. python中的以简单例子解释函数参数、函数定义、函数返回值、函数调用

    python-函数 1.函数定义 2.自定义函数,基本规则 3.语法 4.参数 4.1必备参数 4.2默认参数 4.3不定长参数 4.4匿名参数 5.函数举例代码 1.函数定义 函数是组织好的,可重复 ...

  9. Shell函数(函数定义、函数变量、函数调用、函数传参、函数返回值、获取函数返回值)

    分享知识 传递快乐 1.函数定义 linux shell 可以用户定义函数,然后在shell脚本中可以随便调用.Shell 函数定义的语法格式如下: [function] funname [()]{函 ...

  10. Go 学习笔记(15)— 函数(01)[函数定义、函数特点、多值返回、实参形参、变长参数,函数作为参数调用]

    1. 函数定义 Go 语言最少有个 main() 函数.函数声明告诉了编译器函数的名称,返回类型和参数. func funcName(parameter_list)(result_list) {fun ...

最新文章

  1. 史上最全的 Linux Shell 文本处理工具集锦,快收藏!
  2. linux c 内存泄露 检查工具
  3. 服务器与浏览器数据传输过程中编码问题
  4. shell-4.bash的变量:用户自定义变量
  5. 并发基础篇(一): Java 并发性和多线程
  6. arcgis api for javascript创建webscene
  7. 基于verilog贪吃蛇游戏设计
  8. leetcode1328. 破坏回文串
  9. php的在线问卷调查_基于php技术的问卷调查系统
  10. android fragment 优势,Android Fragment详解
  11. php 命令设计模式示例,php设计模式(十九)命令模式-Fun言
  12. Kafka集成Kerberos之后如何使用生产者消费者命令
  13. n160ii打印机查看ip地址_芯烨ip地址设定软件下载|
  14. 【云原生 | Docker篇】 Docker容器配置阿里云镜像加速器
  15. Java基础-子类和父类之间的诡异事件
  16. ASP 模板引擎,ASP 模板类 (Taihom.Template.class)
  17. C++ 中的隐含 *this
  18. error C4996: ‘strcpy‘: This function or variable may be unsafe. Consider using strcpy_s instead.
  19. 随手记-记录一些技术点
  20. 智能网联汽车——智能化

热门文章

  1. hdu 3925 一道复杂的简单题
  2. SqlServer中获取字符串中的数字部分
  3. oracle 恢复坏块,Oracle 修复坏块,关掉闪回
  4. 什么软件可以让头发变黑_头发特别干枯毛躁,请问什么方法可以让头发恢复到顺滑状态?...
  5. 数据结构——>稀疏数组
  6. dsh linux,通过dsh批量管理Linux服务器(一)【感谢作者的无私分享】
  7. linux mysql libc.so_Linux libc依赖包问题如何解决?
  8. jquery开关灯案例_JS/jQuery实现简单的开关灯效果【案例】_輕微_前端开发者
  9. python的输入输出语句_Python基本输出语句/输入语句/变量解析
  10. php上js实现ajax请求,原生JS实现ajax与ajax的跨域请求实例