JavaScript 之有趣的函数(函数声明、调用、预解析、作用域)
前言:“函数是对象,函数名是指针。”,函数名仅仅是指向函数的指针,与其他包含函数指针的变量没有什么区别,话句话说,一个函数可能有多个名字。
-1.函数声明,function+函数名称。调用方法:函数名(参数);
function f1(x,y){
    return x+y;     //函数体
}
console.log(f1(2,3));
这是最常见的指定函数名声明函数,在函数体内返回参数值,函数调用时才会输出结果。既然说到函数,那就免不了提一提它的预解析以及作用域。
此类方法定义的函数,在代码开始执行之前会通过解释器进行一个函数声明提前的过程,并将其添加到执行环境中, JavaScript 引擎会将其提升到代码树的顶端,率先执行,所以即使声明函数的代码在函数调用代码的后面,也能正确访问,上述过程就被称为函数的预解析。例如:
console.log(sum); //控制台输出函数源代码,证明函数可以被调用
console.log(sum(3,2)); //
function sum(x,y){
return x+y;
}
执行环境定义了变量或者函数有权访问其他数据,每个环境中都有一个与之关联的变量对象,环境中定义的变量和函数都保存在这个对象中,解析器在处理数据时就会使用这个对象。
- 2.匿名函数,即没有命名的函数,通过给将函数赋值给变量的形式声明函数,也称之为函数表达式。调用方法:函数名(参数);
var f2 = function(x,y){
                return x+y;
            }
f2(4,5);                
此类方法定义的函数不能在函数声明前调用函数,因为在预解析机制中:
1.把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值。
2.把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用。
3.先提升var,在提升function
所以上述代码如果提前调用函数,在预解析的执行环境中是:
var f2; //变量声明提升,但赋值没有
f2(4,5); //报错
console.log(f2(4,5));
f2 = function(x,y){
return x+y;
}
匿名函数是用一个变量去接收函数,因此预解析只将变量的声明提前了,此时的函数位于一个初始化语句中,在执行到函数体代码之前变量不会保存对函数的引用,所以直接报错。
-3.自调用函数,顾名思义就是自己调用自己的函数,在声明的同时进行调用,虽然方便但只能执行一次。
(function(){
      console.log("这是一个自调用函数");
})();
/*
*
* 而它的演变过程也很简单,是由函数表达式演变而来。
* var f1 = function(){
*       console.log("这是一个自调用函数");
* }
* f1();
* 这里把调用时的 f1 替换成 function(){}
* 所以调用时就是function(){}();
* 为了保持代码的整体性,所以在最外层加了一个括号。
*
* */
//这里还有一个点需要注意,自调用函数前的一个函数如果没有输出调用,需要在函数表达式的结尾加上分号,以和自调用函数区分开来,防止将上一个函数也解析成自调用函数。
-4.Function 构造函数,Function 构造函数可以接受任意数量的参数,但最后一个参数始终被看做是函数体,而前面则看作是函数体的参数。调用:函数名();
var sum = new Function("sum1","sum2","return sum1+sum2");
但这种定义函数的方法并不推荐使用,因为在此类函数在执行时会先解析一次常规ECMAscript 代码,再解析传入函数中的字符串,反复调用时将会影响性能。
函数也是有数据类型的,所有函数的类型都是 function 。
关于作用域链
作用域链的用途是,保证执行环境中,有权访问的所有函数和变量的有序访问。简单来说就是变量的使用范围。
全局变量:在函数以外,用 var 声明的变量都是全局变量,在全局执行环境中都可以使用。 !!! 但需要注意,全局变量只有在整个程序退出或者销毁后才会被释放,否则就一直占内存。
局部变量:在函数内部定义的变量就是局部变量,只能在函数内部使用。
隐式全局变量:没有var 声明的,也是作用于全局,但是可以使用delete删除。
全局作用域:全局变量的使用范围,始终是作用域链中的最后一个对象。
局部作用域:局部变量的使用范围。
块级作用域:指的是在一对大括号内声明的变量,就只能在这对大括号中使用,但是js中全部都可以使用,所以js没有块级作用域,函数除外。
使用过程:1.作用域链的前端始终是当前执行代码所在环境的变量对象,解析过程是按照作用域链一级一级搜索的过程。
2.始终是从作用域的前端开始的,然后逐级向后回溯。
3.内部变量可以通过作用域链访问外部变量,但外部变量不能访问任何内部的变量或者函数。(自内向外访问)
最后给大家举个栗子,综合解说函数调用、预解析以及作用域:
f1();
console.log(c);
console.log(b);
console.log(a);
function f1(){
var a = b = c = 9;
console.log(a);
console.log(b);
console.log(c);
}
输出的结果为:9 9 9 9 9 报错,原因如下
function f1(){
       // var a = b = c = 9;   变量的声明也提升
       var a;
       a = 9;  // 此时的 a 是被 var 声明的,作为函数内的局部变量,只能在函数内被调用,所以最后的a会报错
       b = 9;
       c = 9;  // b,c 则是隐式全局变量
       console.log(a);
       console.log(b);
       console.log(c);
}
f1();  //调用f1函数,预解析后f1函数的声明提升到作用域的最前面,此时的代码为
console.log(c);
console.log(b);
console.log(a); 
第二个小栗子
f1();
var f1 = function (){
console.log(a);
var a = 10;
};
//结果是报错
//因为预解析的存在,所以函数和变量提前
//由于是函数表达式的形式,所以预解析后的代码为: // var f1;
// f1();
// function (){
// console.log(a);
// var a = 10;
// };
JavaScript 之有趣的函数(函数声明、调用、预解析、作用域)的更多相关文章
- JavaScript 基础(数据类型、函数、流程控制、对象)
		
一.JavaScript概述 1.1 JavaScript的历史 1992年Nombas开发出C-minus-minus(C--)的嵌入式脚本语言(最初绑定在CEnvi软件中).后将其改名Script ...
 - function——函数声明头的提升和预解析
		
函数: 即function语句的集合,就是将多个语句封装到一起: 函数的执行要会自己遍历,遇见函数 a():执行语句,就要移交控制权,函数执行完毕之后,控制权又移交回来了! 函数的参数要罗列在func ...
 - ECMAScript1.3 数组 | 函数 | 作用域 | 预解析
		
数组array 数组可以存储很多项,有顺序,很多项形成一个集合,就是数组. 数组字面量是:[] 如何获取数组中的数据:索引/下标,数组中的第一项的索引是从0开始的. ['kay', 'andy', 1 ...
 - 函数(定义、参数、return、变量、作用域、预解析)
		
一.函数定义 1.方式一 function 函数名(参数){ 函数体 }——————函数声明的方法 function fn(a){ console.log(a); }: 2.方式二 ...
 - 从var func=function 和 function func()区别谈Javascript的预解析机制
		
var func=function 和 function func()在意义上没有任何不同,但其解释优先级不同:后者会先于同一语句级的其他语句. 即: { var k = xx(); function ...
 - javascript解析机制——预解析
		
JavaScript解析机制是什么? JavaScript解析过程分为两个阶段,一个是编译阶段,另外一个就是执行阶段. * 编译阶段 编译阶段就是我们常说的JavaScript预解析( ...
 - 轻松搞定javascript预解析机制(搞定后,一切有关变态面试题都是浮云~~)
		
hey,guys!我们一起总结一下JS预解析吧! 首先,我们得搞清楚JS预解析和JS逐行执行的关系.其实它们两并不冲突,一个例子轻松理解它们的关系: 你去酒店吃饭,吃饭前你得看下菜谱,点下菜(JS预解 ...
 - JavaScript函数的声明与调用方式
		
入职第一天小记 对于初入前端的程序猿来说,对于函数的理解与使用可谓是相当浅薄的,回顾这自己近几年的工作以及学习经历,准备对JavaScript来个系统的总结. 如果要我们对H5中的表单做个简单的校验, ...
 - javascript函数的声明和调用
		
× 目录 [1]函数的声明方式 [2]函数的调用方式 [3]两种声明方式的区别 函数:将完成某一特定功能的代码集合起来,可以重复使用的代码块. ---------------------------- ...
 
随机推荐
- 潜移默化学会WPF(绚丽篇)--热烈欢迎RadioButton,改造成功,改造成ImageButton,新版导航
			
原文:潜移默化学会WPF(绚丽篇)--热烈欢迎RadioButton,改造成功,改造成ImageButton,新版导航 本样式 含有 触发器 和 动画 模板 ,多条件触发器,还有布局 本人博 ...
 - VisualStateManager
			
管理控件状态和管理控件状态的转换逻辑 <Window.Resources> <Style TargetType="Button" x:Key="Anim ...
 - XF 按钮控件
			
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http:/ ...
 - Lexer的设计--上(3)
			
lexer的构造函数 有了上一节Token做铺垫, 可以开始设计lexer, 首先应该想到的是, 源代码是以文件流的格式传到编译器中的, 所以作为编译器的前段的第一个阶段, lexer必须负责处理输入 ...
 - delphi 判断目录是否可写
			
FUNCTION WritableDir(CONST Dir : STRING) : BOOLEAN; VAR FIL : FILE; N : STRING; I : Cardinal; BEGIN ...
 - Ubuntu16.04下配置laravel
			
参考 http://tecadmin.net/install-laravel-framework-on-ubuntu/# 本人亲试,完全正确 注意: Step 4 – Set Encryption K ...
 - UWP开发-在UWP中使用sqlite
			
原文:UWP开发-在UWP中使用sqlite sqlite是一种轻量级的数据库,对于一些资源紧张又需要数据库的开发非常好用. SQLite 是一个开源的无服务器嵌入式数据库. 这些年来,它已作为面向存 ...
 - Delphi 10.2的 更新说明,所有官方资料:新特征和Bugfix列表,所有工具开发说明
			
TMS东京版控件更新情况http://www.tmssoftware.com/site/radstudio10_2tokyo.asp RAD Studio 10.2 更新说明http://blog.q ...
 - Qt编程中QDiaog的ESC建
			
最近使用QDialog时,按了下Esc键,导致QDialog被关闭,而后续的数据处理出现了问题.原来在QDialog中按下Esc键会默认调用reject()方法而不是closeEvent(QClose ...
 - SharePoint Add-in Model (App Model) 介绍 – 概念、托管方式、开发语言
			
SharePoint Add-in Model 是自 2013 版本以来引入的新的扩展性开发模型, SharePoint 开发者可以利用这种新模型来实现往常利用场解决方案 (Farm Solution ...