JS中的函数声明和函数表达式的区别,即function(){}和var function(){},以及变量提升、作用域和作用域链
一、前言
Uncaught TypeError: ... is not a function
function max(){}表示函数声明,可以放在代码的任何位置,也可以在任何地方成功调用;
var max = function(){};表示函数表达式,即将一个匿名函数赋值给一个变量,实现通过变量来调用这个匿名函数,但它需要在声明过后才能进行调用,如果调用在声明之前就会报如上红色字体的错误。而这在函数声明中不会出现这样的错误。
二、正文
(一)、代码示例
//函数表达式
myFunc();//此处调用会报错,即 Uncaught TypeError: myFunc is not a function var myFunc = function(){
alert("函数表达式")
}
myFunc();//此处调用正确
=======================================================================
//函数声明
otherFunc();//对于函数声明,此处调用可以使用 var otherFunc = function(){
alert("函数声明")
}
otherFunc();//此处调用也可以
可见对于函数表达式的使用,不可以提前进行调用,而函数声明却可以,为什么呢?
(二)、原因分析===JS作用域和声明提前,以及作用域和作用域链
1. JS的作用域:
变量有全局变量和局部变量之分,两者的区别在于作用域的不同;
- 全局作用域:针对于全局变量来说,全局变量在整个上下文都有效,只是在没有赋值之前调用,会输出undefined;
- 函数作用域:是针对局部变量来说的,在函数中定义的变量在函数外不能获取;
- 块级作用域:概念“{}”中间的部分都是块级作用域ex:for while if ,js中没有块级作用域,但是可以用闭包实现类似功能。
alert(c);//输出undefind
// alert(d);报错 Uncaught ReferenceError: d is not defined
// alert(b);报错 Uncaught ReferenceError: b is not defined
var c=3;
function test(){
var a=1;
b=2; //没有var直接赋值的变量都属于全局变量
alert(c)//输出3,
}
// alert(b);报错 Uncaught ReferenceError: b is not defined
alert(c);//输出3
test();
// alert(a);报错 Uncaught ReferenceError: a is not defined
alert(b);//输出2,在test执行过后才能把b变为全局变量
function test(){
alert(a);//声明未赋值输出undefine
var a=1;
alert(a);//1
}
// alert(a);报错,外部获取不到
test();
//alert(a);报错,Uncaught ReferenceError: a is not defined
注意 :
- 如果在函数中去掉var进行声明(如代码中的b),则变量就会从局部变量升级为全局变量。
- 局部变量的优先级高于同名的全局变量 。如果在函数中声明一个局部变量同名,则全局变量就会被局部变量覆盖。
2. JS的变量提升:
变量在声明之前就已经可用,因为浏览器在进行“预解析”时,对每个函数作用域中的所有变量和函数都会先提取出来,提前进行解析。
我们称这种特性为声明提前,也就是预解析。
//变量提前,会对var和function,即变量和函数进行提前解析
console.log(a) //打印function a(){console.log("123")} var a=1 function a(){console.log("123")} var a=10 console.log(a) //预解析的过程a=undefined, a=function a(){...}, a=undefined
//预解析的结果是a=function a(){...} //最终执行的结果是a=10,后面的覆盖前面的
几点说明:
- 将变量声明提升,只提升变量,不提升所赋的值;
- 将函数声明及函数内容提升,既提升函数声明,又提升函数内容,可以理解为将整个function内容提升;
- 块内的变量声明和函数声明也会被提升,例如if语句。
三个重名冲突:
- 遇到重名,预解析后只留下一个;
- 如有重名变量和函数,留下函数,因为函数有内容;
- 如有两个重名函数,后一个函数覆盖前一个函数。
3. JS的作用域链
作用域:是一个函数在执行时期的执行环境
作用域嵌套:嵌套内函数的执行会引用其“父函数”的作用域,例如B()和C()的执行会引用A()的作用域;
作用域链:像这种函数作用域的嵌套就组成了所谓的函数作用域链;
当在自身作用域内找不到该变量的时候,会沿着作用域链逐步向上查找,若在全局作用域内部仍找不到该变量,则会抛出异常。
//name="123";
function A(){
var name="456";
function B(){
var name="789";
console.log(name); //首先在函数内查找name,查找到所以结果是“789”
}
function C(){
console.log(name); //首先在函数内没有找到,向作用域链的上一级查找,找到了,结果是“456”
}
s();
ss();
}
t();
console.log(name); //此处再次进行查找,当前作用域内没有,整个链上都没有,结果就是未定义
三、总结
函数声明和函数表达式相比,函数声明使用可以更加自由,可以放在随意的位置,因为它能够整体的变量提升;
而函数表达式使用就相对没有那么自由了,调用必须在声明的后面,因为变量提前只是将表达式的变量提前,并没有将表达式的内容提前。
作用域:Js是以函数范围作为基本的局部;
- 全局作用域:针对于全局变量来说,全局变量在整个上下文都有效,只是在没有赋值之前调用,会输出undefined;
- 函数作用域:是针对局部变量来说的,在函数中定义的变量在函数外不能获取;
变量提升:是相对于作用域来说的,在每个作用域中,无论全局还是函数作用域,声明的变量和函数,都能够变量提升,也就是可以在声明之前进行使用(调用)。
作用域链:函数的嵌套,使得子函数的执行会引用父函数的作用域,像这种函数作用域的嵌套就组成了所谓的函数作用域链;
当在自身作用域内找不到该变量的时候,会沿着作用域链逐步向上查找,若在全局作用域内部仍找不到该变量,则会抛出异常。
JS中的函数声明和函数表达式的区别,即function(){}和var function(){},以及变量提升、作用域和作用域链的更多相关文章
- JS中函数声明与函数表达式的不同
Js中的函数声明是指下面的形式: function functionName(){ } 这样的方式来声明一个函数,而函数表达式则是类似表达式那样来声明一个函数,如 var functionName ...
- JS中函数声明与函数表达式的异同
相同点 注:函数声明和函数表达式的相同点包括但不限于以下几点 函数是一个值,所以和其他值一样,函数也可以进行被输出.被赋值.作为参数传给其他函数等相关操作,不管函数是以什么方式被定义的,当然和其他值的 ...
- JS的一些总结(函数声明和函数表达式的区别,函数中的this指向的问题,函数不同的调用方式,函数也是对象,数组中的函数调用)
一.函数声明和函数表达式的区别: 函数声明放在if——else语句中,在IE8中会出现问题 函数表达式则不会 <script> if(true){ function f1(){ conso ...
- js中的函数声明和函数表达式的区别
目录 一.声明与表达式的格式 1.1 声明式的格式: 1.2 表达式的格式: 二.区别 2.1 函数表达式可以直接在后面加括号执行,而函数声明不可以. 2.2 函数表达式可以被提前解析出来 2.3 命 ...
- Js中函数声明和函数表达式的区别
先看以下几段烧脑的代码: f();//=>? var f = function () { console.log("var"); } function f() { conso ...
- js函数声明和函数表达式的区别
Javascript Function无处不在,而且功能强大!通过Javascript函数可以让JS具有面向对象的一些特征,实现封装.继承等,也可以让代码得到复用.但事物都有两面性,Javascrip ...
- 转载 js函数声明和函数表达式
在js中函数有两种表达方式.1 函数声明 2 函数表达式 函数声明 function sayname(){ alert("li lei"); } 函数表达式 var sayname ...
- JavaScript(js)函数声明与函数表达式的区别
在JavaScript中,函数是经常用到的,在实际开发的时候,我想很多人都没有太在意函数的声明与函数表达式的区别,但是呢,这种细节的东西对于学好js是非常重要的. 函数声明与函数表达式用代码写出来是这 ...
- 【JS】函数提升变量提升以及函数声明和函数表达式的区别
今天看js的变量提升问题,里面提到了函数提升.然后发现自己之前一直把函数声明和函数表达式弄错,导致函数提升出错 一.变量提升 console.log(a) var a=100 //undefined ...
随机推荐
- ballerina 学习十二 变量
ballerina 有两种方式进行变量的定义,类型加上名称以及初始值.,使用var 关键字 简单例子 代码 import ballerina/io; // 全局public 变量,使用类型定义 pub ...
- log4j打印mybatis执行sql,将占位符换成真实的参数输出
背景: 在我日常码代码的时候,由于对mybatis的动态sql,比较依赖,并且有时候需求复杂,导致sql较长,而且参数众多,当出现问题是,需要将sql,放到navicat里面去执行查看结果,但是对于复 ...
- 虚拟路由冗余协议VRRP
一.VRRP简介 虚拟路由冗余协议VRRP(Virtual Router Redundancy Protocol)通过把几台路由设备联合组成一台虚拟的路由设备,将虚拟路由设备的IP地址作为用户的默认网 ...
- PAT1055___排序神题
题目意思比较简单,按财富,年龄,姓名来排序 看似挺普通的,但被坑了20多次TLE 首先排序只要一次,就是按题目规定的进行排序 然后在查询的时候,不是从头扫到尾看是否符合年龄的限制,而是记录这个年龄组在 ...
- Oracle清理大表,降水位
背景:一张表的清理机制存在问题,导致该表的数据一直在增加,该表水位已很高,需要对该表的数据进行清理并降水位. 1.1 迁移前准备 步骤一.新建表 p_transaction_bak. oracle@l ...
- 竖屏拍照,但是sd卡中却是横屏解决方法
protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (resultCode) ...
- 项目中Map端内存占用的分析
最近在项目中开展重构活动,对Map端内存尽量要省一些,当前的系统中Map端内存最高占用大概3G左右(设置成2G时会导致Java Heap OOM).虽然个人觉得占用不算多,但是显然这样的结果想要试 ...
- 【转】WebAPI使用多个xml文件生成帮助文档
来自:http://www.it165.net/pro/html/201505/42504.html 一.前言 上篇有提到在WebAPI项目内,通过在Nuget里安装(Microsoft.AspNet ...
- MySQL 库、表
1.库 1.库的基本操作 1.查看已有的库 show databases; 2.创建库(指定默认字符集) create database 库名 default charset=utf8; 3.查看创建 ...
- module解析过程
加载一个核心模块时 直接require('模块名')即可 加载一个文件模块时 直接require('绝对路径/相对路径')即可,可省略文件后缀.js. 因为如果文件不存在,将试图找文件名.js的文件 ...