函数在javascript中无处不在,函数得功能:提高软件的开发效率,提高软件的可维护性,提高软件的重用性,控制程序设计的复杂性。函数重用封装的思路:尽量保证HTML代码结构一致,可以通过父级选取子元素,把核心主程序实现,用函数包起来,把每组不同的值找出来,通过传参实现.
JS 函数
函数是一段完成指定任务的已命名代码块
自定义函数
| 1 | function function_name($arg1,$arg2){ | 
函数声明
| 1 | function fn(){ | 
函数表达式
| 1 | a var fn = function(){ alert(2);} | 
函数自执行(立即执行函数)
| 1 | // 自执行函数本身不进行变量提升 自执行函数执行形成私有作用域 | 
闭包
作用域只能由内向外寻找得到变量,而不能从外向内得到变量
闭包:指有权访问另一个函数作用域中的变量的函数
闭包解决的问题
(1) 在函数外面可以读取到函数内部的变量
(2) 让这些变量始终保存在内存中
闭包的基本形式
(1) 在外部函数内创建函数
(2) 将外部函数的返回值设置为新创建的函数
(3) 在外部函数就可以接收到返回值
(4) 使用这个函数就可以在外部访问到内部函数的数据
特点: 如果内部函数引用了外部函数的参数或者变量,外部函数的参数不会被回收
作用: 变量需要长期存在与内存中(创建私有变量)    避免全局变量的污染垃圾回收机制闭包下的变量和参数是不会被回收的,而是一直存在内存当中。
而普通局部变量和参数是会使用完以后被垃圾回收机制回收回去,不会存留在内存当中。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25ex: function fn(){
  var a = 3;
  console.log(a);
}
fn() //等fn执行完以后 a会被回收
// 闭包运用,存索引,let声明 获取索引值
ex: for(let i=0;i<oLi.length;i++){
  oLi[i].onclick = funciton(){
    console.log(i)
  }
}
ex: for(var i=0;i<oLi.length;i++){
  oLi[i].index = i;
  oLi[i].onclick = funciton(){
    console.log(this.index)
  }
}
ex: for (var i=0;i<oLi.length;i++){
  !function(i){
    oLi[i].onclick = function(){
      console.log(i)
    }
  }(i)
}
堆栈内存
- 堆内存: 存储引用数据类型(对象,函数)
- 栈内存: 提供JS代码执行的环境和存储基本数据类型值
- 堆内存释放:让所有引用堆内存空间地址的变量赋值为null即可(没有变量占用堆内存,浏览器在空闲时就会把它释放掉)
- 栈内存释放:普通局部变量和参数使用完以后被垃圾回收机制回收回去,不会存留在内存当中。
eval函数
  eval函数用于计算某个字符串得到结果或执行其中的javascript代码
  eval只有一个参数 如果传入的参数不是字符串,他直接返回这个参数1
2ex: eval("2"+"3")   //5
    eval("alert('hello')")   //hello
递归
一个过程或函数直接或间接调用自己本身,成为递归过程或递归函数
递归的特点
1.递归就是在过程或函数里调用自身
2.在使用递归从过程中必须有明确的递归结束条件 成为递归出口
3.递归算法解题运行效率较低
4.递归次数过多容易造成栈溢出  入栈出栈的过程1
2
3
4
5
6
7
8// 斐波那契数列第n项
function fib(n){
  if(n<=2){
    return 1
  }
  return fib(n-1) + fib(n-2)
}
console.log(fib(5))
callee关键字
在函数内部使用,代表当前函数的引用(名字)。
作用:降低代码的耦合度。
耦合度:一处代码的修改会导致其他代码也要发生改变(耦合度高),在项目里边要开发低耦合度的代码(一处代码修改尽量少地引起其他代码的变化)。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 未使用callee关键字
function jiecheng(n){
  if(n == 1) return 1
  return n*jiecheng(n-1)
}
console.log(jiecheng(5)) // 120
var jc = jiecheng // 对象赋值 引用传递
jiecheng = null // 销毁jiecheng函数对象
jc(5) //  jiecheng is not a function
// 使用callee降低代码的耦合度
function jiecheng(n){
  if(n == 1) return 1
  // 调用本函数(或者f1())代表当前运行的函数
  return n * arguments.callee(n-1)
}
console.log(jiecheng(5)) // 120
var jc = jiecheng // 对象赋值 引用传递
jiecheng = null // 销毁jiecheng函数对象
console.log(jc(5)) // 120
JS 代码执行顺序(变量提升或预解析)
- 定义(预解析) 在当前作用域下, 会把带var和function a{}进行提前声明或者定义
 先去找varfunction a(){}形参数(相当于var)私有变量将var声明的变量名和function函数提升到作用域的最上方var a//声明变量不赋值,变量在正式运行之前,值为undefinedfunction a(){}//函数块,函数在正式运行之前,都是整个函数块参数(相当于var)如果是函数内则先分析参数 对形参赋值形成私有变量
- 特殊 - 当函数名和变量名相同时, 不会重新的声明, 但是会重新的定义(重新赋值)
- 函数表达式只会提升变量名,不会提升后面的函数(只对等号左边进行变量提升)
- 在条件判断语句中不管判断语句是否成立,都会发生变量提升,但不会提升函数整体, 只提升函数名称(即声明未定义),提前使用会报错
- 函数执行形成私有作用域,形参是私有变量,在私有作用域中,先进行形参赋值形成私有变量,在变量提升
- 块级作用域: 判断体 循环体等大括号都是块级作用域(只对let/const/function起作用, 对var不起作用)- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60- /* 
 在当前作用域下,不管判断条件是否成立都要进行变量提升
 => 带var还是只声明
 => 带function的在老版本浏览器渲染机制下, 声明加定义都处理了,在新版本浏览器中对于函数(在条件判断中的函数),不管条件是否成立, 都只是先声明, 没有定义,类似于var
 */
 // 示例一
 console.log(a) // undefined
 if (false) {
 var a = 1;
 }
 // 示例二
 console.log(a) // undefined
 if (true) {
 var a = 1;
 }
 // 示例三
 console.log(typeof f) // undefined
 f() // f is not a function
 if(false){
 function f(){ console.log(111) }
 }
 // 示例四
 console.log(typeof f) // undefined
 f() // f is not a function
 if(true){
 function f(){ console.log(111) }
 }
 // 示例五
 console.log(typeof f) // undefined
 if(true){
 function f(){ console.log(111) }
 }
 f() // 111
 // 示例六
 console.log(typeof f) // undefined
 if(false){
 function f(){}
 }
 f() // f is not a function
 // 实例七
 // 1 变量提升: 无
 f = function () { return true } // 2 window.f = function () { return true }
 g = function () { return false } // 3 window.g = function () { return false }
 !function(){
 // 4 私有作用域 变量提升:
 // 新版本: 只声明私有变量 function g 未定义 此时私有变量g是undefined
 // 老版本: 声明加定义私有变量 function g() { return true } 如IE10及以下
 // 5 新版本 g() 报错 g is not a function 程序结束
 // 6 老版本 g() 返回 true [] == ![] => 先执行 ![] 为 false 得 => [] == false => 在将两边转化为number => 0 == 0 => true
 if (g() && [] == ![]) {
 // 7 老版本 条件成立 首先给已声明的赋值定义 function gg() { return true }
 // 8 老版本 给全局的 window.f = function () { return false }
 f = function () { return false }
 function g() { return true }
 }
 }()
 // 老版本 fasle false
 // 新版本报错 报错 g is not a function 程序结束
 console.log(f())
 console.log(g())
 
- 执行 (逐行解析代码) 
 表达式: = + - * / ++ – ! 参数等 表达式会修改预解析的值
| 1 | // 示例一 | 
JS 作用域与作用域链(es5)
作用域就是变量的作用范围   if for没有块级作用域 只有函数能开辟作用域
静态作用域:变量或函数作用域与定义时的作用域有关,跟在哪执行时的作用域无关
动态作用域:this跟在哪定义时的无关,跟在哪执行有关,只有在执行阶段才能决定变量的作用域
在JavaScript中,如果函数的调用者不存在,那么它的调用者将会是全局window对象(或其他环境中的全局变量)
所有的全局变量或对象都是在window对象上定义的, 所有的全局变量和对象都属于window对象1.全局作用域   在代码任何地方都能访问到的对象拥有全局作用域
a.最外层函数和在最外层函数外面定义的变量拥有全局作用域(可以在任何地方被改变)
b.所有未定义直接赋值的变量自动声明为拥有全局作用域
c.所有window对象的内置属性都拥有全局作用域2.局部作用域(函数作用域) 只有在函数内部可以访问到,在局部作用域(函数)外无法访问
局部作用域内的对象(函数或变量),所有未定义直接赋值的变量可以作为window的属性,不存在返回undefined,未定义也未赋值的变量寻找不到则直接报错xxx is not defined
函数的形参本身就是一个局部变量   相当于function b(var a){}3.作用域链 (函数)域先从自己的作用域找数据变量,找不到再去上级作用域,直到script还没找到则报错script是最大的域,有多个script,从上往下依次执行 函数可作为小的作用域 域内先定义后执行1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// 作用域链
var a = 0           //1  定义 var a    3 执行 赋值a=0
function fn(){	    //2  定义 function fn(){...}
  console.log(a)    //7  执行 console.log(a) //undefined
  var a = 1         //6  定义 var a    8 赋值 a=1
  console.log(a)    //9  执行 console.log(a) //1
}
console.log(a)      //4  执行 console.log(a)  //0
fn()                //5  执行 fn()
console.log(a)      //10 执行 console.log(a)  //0
// 函数作用域: 定义过程中的作用域 而不是调用时的作用域
var age = 80
function fn1(){
  console.log(age)
}
function fn2(){
  var age = 40
  fn1()
}
fn2() // 80  fn1()定义时作用域
其他
- a href=”javascript:;”或a href=”javascript:void(0)” 点A标签的时候,让当前页不跳转。
- 在同一个网页中,放任意个数的<script>由上往下执行 一般放在</body>结束之前。原因:先呈现页面,加载DOM树结构,后加载脚本,提高用户体验 基于网站优化
- js中允许把 “.” 换成 “[‘’]”
- !-[-1,] 判断IE
- IE独有api isIE = document.documentMode