JavaScript之深入函数(一)
在任何编程语言中,函数的功能都是十分强大的,JavaScript也不例外。之前已经讲解了函数的一些基本知识,诸如函数定义,函数执行和函数返回值等,今天就带大家深入了解JavaScript中函数的原理及执行过程。
一 函数参数
1,声明函数时可以添加参数,相当于在函数内部隐式的声明了变量。它的学名叫形式参数,简称形参,在执行函数使实际传递的值叫实际参数,简称实参。
- function add(a,b){
- return a+b;
- }
- add(1,2);//
- //a和b就是形参,1和2就是实参
2,JS中函数的参数不限制个数和数据类型,这意味着函数的形参和实参个数可以不相等,都可以是无限多个。
- function add(a,b){
- return a + b;
- }
- add(1);//NaN 1+undefined
- add(1,2,3);//3 忽略3
- add(1,"3");//"13"
3,函数甚至可以没有形参,函数的arguments属性,以类数组的形式存储了函数执行时的实参。
- function add(){
- console.log(arguments);
- }
- add(1,2);//Arguments[1,2,...]
4,函数有一个length属性,表示函数的形参个数。
- function test(){
- console.log(arguments);
- console.log(test.length);
- }
- test(1,2,3);
- //[0:1,1:2,2:3,length:3,...]
- //
5,arguments中的实参和形参是相互绑定的,修改其中一个,另一个也会改变,但形参和实参实际是两个变量。实参列表的个数是不可更改的,函数执行时传递几个就是几个。
- function test(a,b){
- console.log(arguments);
- console.log(b);
- b = 2;
- console.log(arguments);
- console.log(b);
- a = 10;
- console.log(arguments);
- }
- test(1);
- /*
- [0:1,length:1,...]
- undefined
- [0:1,length:1,...]
- 2
- [0:10,length:1,...]
- */
二 预编译
前面介绍JavaScript时,提到它是单线程,解释性语言,即读到一行就执行一行。其实这只是它的表象,实际上JavaScript执行代码分为了3个大的步骤:
1, 语法分析
语法分析的工作大体就是检测是否有语法错误,是否符合版本规则等等。如果没有问题则进入预编译阶段,如果遇到问题则会抛出错误。
2, 预编译
在理解预编译之前,我们应该先明白两个概念:
a:如果变量未申明即访问将报错,但是变量未声明即赋值,那么该变量将自动升级成全局对象(window)的属性。
b:在全局申明的变量,也会自动升级成window的属性。
- console.log(a);//Reference error:a is not defined
- *************************************************
- a = 10;//
- a === window.a;//true
- var b;
- b === window.b;//true
程序预编译发生在即将执行之前,分为三步:
1) 创建一个GO(Global Object)对象(也称为全局作用域),实际上就是window对象。
2) 查找变量声明,并把他们作为GO对象的属性,值为undefined。
3) 查找是否有函数声明,若有,则把函数名作为GO对象的属性,并把函数体赋值给该属性,若函数名和变量名相同,则会覆盖他们。
函数也有预编译过程,函数的预编译发生在函数即将执行之前,分为四步:
1) 创建一个AO(Active Object)对象(即执行期上下文,也叫函数作用域或本地作用域)。
2) 查找形参和变量声明,并把他们作为AO对象的属性,值为undefined。
3) 将实参赋值给形参。
4) 查找函数内部是否有函数声明,若有,则把函数名作为AO对象的属性,并把函数体赋值给该属性,若函数名和形参或变量名相同,则会覆盖他们。
总结下来,可以简单概括为:变量声明时,声明提升。函数声明时,整体提升。函数声明优先级大于变量和形参。
- function fn(a) {
- console.log(a); //ƒ a() {}
- console.log(b); //undefined
- console.log(c); //ƒ c() {}
- var a = 123;
- console.log(a); //
- function a() {}
- console.log(a); //
- var b = function b() {};
- console.log(b); //ƒ b() {}
- function c() {}
- }
- fn(1);
- /*
- GO --> {fn:fn}
- AO
- 第一步:AO --> {}
- 第二步:AO --> {a:undefined,b:undefined}
- 第三步:AO --> {a:1,b:undefined}
- 第四步:AO --> {a:function a() {},b:undefined,c:function c() {}}
- 注意这里只是预编译过程,函数真正执行时,AO中的属性值会动态改变。所以:
- 第一行代码直接打印fn a
- 第二行打印undefined
- 第三行打印fn c
- 第四行声明变量a已经被提前执行了,这里直接赋值123
- 第五行打印123
- 第六行函数声明被整体提前了,这一行代码将被直接跳过
- 第七行依然打印123
- 第八行只执行赋值操作,b == fn b
- 第九行则打印fn b
- 第十行声明函数已经被整体提升了
- */
3, 解释执行
根据预编译后的代码顺序,一条一条的执行。
三 作用域和作用域链
每一个函数都有一个隐式的属性[[scope]],它存储的是函数在执行时创建的执行期上下文集合,即一堆AO和GO对象,当然他们是有顺序的,类似一个数组,它只能被系统调用,而不能被我们访问和使用。
当一个函数在全局被创建时(没有被执行,这时还没有产生它自身的AO对象),[[scope]]将被插入GO对象。
当函数执行时(这时已经创建了自己的AO对象),[[scope]]的头部将被插入一个自己的AO对象,类似数组的unshift()方法,这一过程在函数执行内部代码之前。
现在[[scope]]中已经有两个执行期上下文的对象了:第0位的AO,第1位的GO。
- function fn(){}
- //fn.[[scope]] --> {0:GO}
- fn();
- //fn.[[scope]] --> {0:AO,1:GO}
如果全局函数的内部定义了一个子函数(父函数正在执行),那么该子函数的[[scope]]属性类似的会存储:第0位父函数的AO,第1位GO(因为只有父函数执行才会产生子函数的定义,所以子函数被定义时就已经有两个执行期上下文对象了),这时子函数还没被执行,所以它只会存储这两个对象。
当它被执行时,子函数[[scope]]属性的头部将被插入它自己的AO对象。如果子函数内部还定义的有其他函数,那么它的[[scope]]属性生成方式和上面相同。
- function fn(){
- function son(){};
- }
- //fn.[[scope]] --> {0:GO}
- //son.[[scope]] --> {} fn还没执行,son都还没声明
- fn();
- //fn.[[scope]] --> {0:AO(fn),1:GO}
- //son.[[scope]] --> {0:AO(fn),1:GO} 这里只是声明了函数son,所以并没有AO(son),如果function son(){}后面还有一行代码:son();那么当执行到这一行时,son.[[scope]] --> {0:AO(son),1:AO(fn),2:GO}
这样就形成了函数的作用域链。当我们在函数内部访问变量时,实际上是在函数的[[scope]]属性里依次查找(从第0位开始),直到全局GO(window)对象。
函数作用域链的最终表现就是:函数访问变量的权限由声明环境决定,而非执行环境。子函数可以访问父函数的变量,父函数不能访问子函数的变量。
每次函数执行产生的执行期上下文都是独一无二的,当函数执行完毕,它自己的AO将被永远销毁,并更新自己的[[scope]]属性。下一次执行将产生一个新的AO对象,并添加到[[scope]]属性中。
JavaScript之深入函数(一)的更多相关文章
- JavaScript权威指南 - 函数
函数本身就是一段JavaScript代码,定义一次但可能被调用任意次.如果函数挂载在一个对象上,作为对象的一个属性,通常这种函数被称作对象的方法.用于初始化一个新创建的对象的函数被称作构造函数. 相对 ...
- 用javascript 写个函数返回一个页面里共使用了多少种HTML 标签
今天我无意间看到一个面试题: 如何用javascript 写个函数返回一个页面里共使用了多少种HTML 标签? 不知你看到 是否蒙B了,如果是我 面试,肯定脑子嗡嗡的响.... 网上搜了搜也没有找到答 ...
- JavaScript基础学习-函数及作用域
函数和作用域是JavaScript的重要组成部分,我们在使用JavaScript编写程序的过程中经常要用到这两部分内容,作为初学者,我经常有困惑,借助写此博文来巩固下之前学习的内容. (一)JavaS ...
- JavaScript 基础回顾——函数
在JavaScript中,函数也是一种数据类型,属于 function 类型,所以使用Function关键字标识函数名.函数可以在大括号内编写代码并且被调用,作为其他函数的参数或者对象的属性值. 1. ...
- JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解
二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...
- javascript立即执行函数
javascript和其他编程语言相比比较随意,所以javascript代码中充满各种奇葩的写法,有时雾里看花;当然,能理解各型各色的写法也是对javascript语言特性更进一步的深入理解. ( ...
- JavaScript 立即执行函数
js中(function(){…})()立即执行函数写法理解 javascript和其他编程语言相比比较随意,所以javascript代码中充满各种奇葩的写法,有时雾里看花,当然,能理解各型各色的写法 ...
- 前端学习 第六弹: javascript中的函数与闭包
前端学习 第六弹: javascript中的函数与闭包 当function里嵌套function时,内部的function可以访问外部function里的变量 function foo(x) { ...
- JavaScript学习09 函数本质及Function对象深入探索
JavaScript学习09 函数本质及Function对象深入探索 在JavaScript中,函数function就是对象. JS中没有方法重载 在JavaScript中,没有方法(函数)重载的概念 ...
- JavaScript中的函数表达式
在JavaScript中,函数是个非常重要的对象,函数通常有三种表现形式:函数声明,函数表达式和函数构造器创建的函数. 本文中主要看看函数表达式及其相关的知识点. 函数表达式 首先,看看函数表达式的表 ...
随机推荐
- 一键部署 Spring Boot 到远程 Docker 容器,就是这么秀!
不知道各位小伙伴在生产环境都是怎么部署 Spring Boot 的,打成 jar 直接一键运行?打成 war 扔到 Tomcat 容器中运行?不过据松哥了解,容器化部署应该是目前的主流方案. 不同于传 ...
- 表单模糊查询的三种简单方式(springboot-h2-mybatis)
前几天运营提到说后台管理系统有几个地方想要模糊查询.. 想了下是简单的,就是要注意以前方法的被调用情况,进行增量改动,以免牵一发而动全身.整理一波记录下(本次案例是按名字模糊查询学生信息). 三种 ...
- GIS基础知识 - 坐标系、投影、EPSG:4326、EPSG:3857
最近接手一个GIS项目,需要用到 PostGIS,GeoServer,OpenLayers 等工具组件,遇到一堆地理信息相关的术语名词,在这里做一个总结. 1. 大地测量学 (Geodesy) 大地测 ...
- Jira更改工作流后,敏捷看板里无法显示sprint对应的问题列表
转自:http://blog.csdn.net/computerheart/article/details/68924295 Jira更改工作流后,敏捷看板里无法显示sprint对应的问题列表 原创 ...
- E-Find the median_2019牛客暑期多校训练营(第七场)
题意 N次操作,每次塞入区间\([L,R]\)的每个数,并输出此时的中位数. 题解 如果题目不是每次塞入一整个区间,而是只塞入一个数,可以简单的建权值线段树查询区间第K大,由于每次都是查询整个区间就不 ...
- 2019DX#1
1001 Blank 题意 有一个长度为n(n<=100)的位子,填入四种颜色,有m个限制,某个区间的颜色个数要恰好等于x个.问颜色个数的方案数. 思路 DP 四维的DP,利用滚动数组优化一维空 ...
- CodeForces 103 D Time to Raid Cowavans
Time to Raid Cowavans 题意:一共有n头牛, 每头牛有一个重量,m次询问, 每次询问有a,b 求出 a,a+b,a+2b的牛的重量和. 题解:对于m次询问,b>sqrt(n) ...
- PAT L3-015. 球队“食物链”
L3-015. 球队“食物链” 时间限制 1000 ms 内存限制 262144 kB 代码长度限制 8000 B 判题程序 Standard 作者 李文新(北京大学) 某国的足球联赛中有N支参赛球队 ...
- springboot的最简创建方式
springboot是目前比较流行的技术栈之一,我在这里写一个springboot工程最简方式 首先开发工具是IDEA,双击打开IDEA,点击Create new Project 进入到这个页面,选择 ...
- try(){}自动释放资源,AutoCloseable
我们在使用资源的时候,必须关闭资源,比如使用jdbc连接或者inputStream的时候,必须在finally中将资源关闭.然而有的时候我们会忘记关闭资源.那么有没有更好的方法呢? SqlSessio ...