一、前言

  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;
  • 函数作用域:是针对局部变量来说的,在函数中定义的变量在函数外不能获取;

  

  变量提升:是相对于作用域来说的,在每个作用域中,无论全局还是函数作用域,声明的变量和函数,都能够变量提升,也就是可以在声明之前进行使用(调用)。

  作用域链:函数的嵌套,使得子函数的执行会引用父函数的作用域,像这种函数作用域的嵌套就组成了所谓的函数作用域链;

  当在自身作用域内找不到该变量的时候,会沿着作用域链逐步向上查找,若在全局作用域内部仍找不到该变量,则会抛出异常。

最新文章

  1. Android中Fragment的两种创建方式
  2. java范型集合中的成员排序
  3. python3安装
  4. 面试题-->写一个函数,返回一个数组中所有元素被第一个元素除的结果
  5. 关于getClass().getClassLoader()
  6. 实例源码--Android的ListView控件的总结
  7. CSS中背景图片定位方法
  8. FOJ 2170 花生的序列 dp
  9. 【安德鲁斯】基于脚本的数据库"增量更新",如果不改变,每次更新java代码、!
  10. 计算机程序的思维逻辑 (70) - 原子变量和CAS
  11. Base64图片编码优化
  12. Ubuntu Firefox HTML5
  13. 设计模式C++学习笔记之十一(Bridge桥梁模式)
  14. PHP 文件处理----fopen(),fclose(),feof(),fgets(),fgetc()
  15. AIMLBot (中文自动回复)文本自动回复机器人
  16. Hibernate_HQL
  17. Visual自动添加CSS兼容前缀
  18. 不同路径 II
  19. 测试ik分词效果
  20. javascript中用闭包递归遍历树状数组

热门文章

  1. 再谈zabbix 邮件通知配置(不用脚本,简单配置就可以了)
  2. Unit04: 实战技巧 、 登录
  3. JAVA课程设计(坦克大战)
  4. jq from表单 取值
  5. 杂项-DB:内存数据库
  6. Redis:目录
  7. Mysql无法创建外键的原因 !!!
  8. PHP中exit,exit(0),exit(1),exit('0'),exit('1'),die,return的区别
  9. 《Java核心技术》 -- 读书笔记 ② - 类 | 对象 | 接口
  10. 【BZOJ】1260 [CQOI2007]涂色paint(区间dp)