javascript基础(二)--(函数,作用域)

函数在javascript中无处不在,函数得功能:提高软件的开发效率,提高软件的可维护性,提高软件的重用性,控制程序设计的复杂性。函数重用封装的思路:尽量保证HTML代码结构一致,可以通过父级选取子元素,把核心主程序实现,用函数包起来,把每组不同的值找出来,通过传参实现.

JS 函数

函数是一段完成指定任务的已命名代码块

自定义函数

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
function function_name($arg1,$arg2){
fun_body
return arg_n
}
function_name() // 函数的调用可以在函数前也可以在函数后面
1.函数名要有意义,函数名不区分大小写,不能重复定义,和保留字相同的名称不能使用
2.被传入的参数称为实参,而函数用于接收的参数称为形参(相当于var arg1 = n),参数可有可无,
可以有多个","用逗号分隔 不定参 arguments 实参的集合 参数个数不固定时
function fn(){
console.log(arguments.length)//不定参
console.log(arguments[1])
}
function fn(a){ //形参
console.log(a)
}
fn(5) //实参
// js函数参数设置默认值
function simue (){
var a = arguments[0] ? arguments[0] : 1;
var b = arguments[1] ? arguments[1] : 2;
return a+b;
}
console.log(simue()) //输出3
console.log(simue(10)) //输出12
console.log(simue(10,20)) //输出30

3.函数分为执行过程 和 结果
// 函数返回值return后面紧跟要返回的一个值 不会直接输出 可以终止函数后面不执行
// 如果函数内不return结果 则返回值默认为undefined
ex:function a(){
var a = 1;
}
console.log((a) //函数体 function a(){ var a = 1 }
console.log((a()) //undefined
ex:function fn(){
var a = function(){
console.log(2) //执行过程
}
return a
}
fn() //没有任何显示
console.log(fn()) //函数体 function(){alert(2);}
fn()() //2
ex:function fn(){
var a = function(){
console.log(2)
}
return a()
}
fn() //2 a()函数执行弹出2
console.log(fn()) //2 undefined
fn()() //报错 fn()(即undefined) is not a function
4.变量函数:如果将一个函数名称给一个变量在变量后加上()就会调用对应的函数
function add(){} var a=add a()

函数声明

1
2
3
4
5
6
7
8
9
10
function fn(){
console.log(1)
}
console.log(fn) //弹出函数体 function fn(){alert(1);}
console.log(fn()) //弹出 1 (主动执行函数)
// 函数执行方式
a fn()
b document.onclick=fn
c setInterval('fn()',3000)
d setIntertval(fn,3000)

函数表达式

1
2
3
4
5
6
7
a var fn = function(){ alert(2);}
// 函数表达式不属于函数声明 b=function(){} 是一个赋值过程是一个函数赋给b变量
var a = function b(){
console.log(2)
}
a() //2
b() // b is not defined ie8以下弹出 2

函数自执行(立即执行函数)

1
2
3
4
5
6
7
// 自执行函数本身不进行变量提升 自执行函数执行形成私有作用域
a !function(){}
b (function(){})
c -function(){}
d +function(){}
e ~function(){}
注:立即执行函数 直接在末尾加()执行 (function(){alert(2))}()

闭包

作用域只能由内向外寻找得到变量,而不能从外向内得到变量
闭包:指有权访问另一个函数作用域中的变量的函数
闭包解决的问题
(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
25
ex: 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
2
ex: 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{}进行提前声明或者定义
    先去找 var function a(){} 形参数(相当于var)私有变量
    将var声明的变量名和function函数提升到作用域的最上方
    var a //声明变量不赋值,变量在正式运行之前,值为undefined
    function 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
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
// 示例一
console.log(a) //5 输出function a(){console.log(40)}
var a = 10 //1 定义var a 6 赋值 a = 10
console.log(a) //7 输出10
function a(){ //2 定义function a(){}
console.log(20)
}
console.log(a) //8 输出10
var a = 30; //3 定义var a但无效 9赋值 a = 30
console.log(a) //10 输出30
function a(){ //4 定义function a(){}覆盖前面
console.log(40)
}
console.log(a) //11 输出30

// 示例二
a() //5 执行 function a(){console.log(2)} 输出2
var a = function(){ //1 定义 var a 6 执行 将匿名函数赋值给a
console.log(1)
}
a() //7 执行 function(){console.log(1)}() 输出1
function a(){ //2 定义 function a(){}
console.log(2)
}
a() //8 执行 function(){console.log(1)}() 输出1
var a = function b(){ //3 定义 var a
console.log(3) //4 定义 var function b(){}
} //9 执行 将函数b赋值给a
a() //10 执行 a()相当于b() 输出3

// 示例三
var a = 1 //1 var a; 3 a = 1;
function fn(a){ //2 定义function fn(a){...} 5 function fn( var a){....}
console.log(a) //6 a = undefiend 函数局部作用域内a
a = 2 //7 a = 2 局部a
}
fn() //4 fn()
console.log(a) //8 a = 1 全局

// 示例四
var a = 1 //1 var a; 3 a = 1;
function fn(a){ //2 fn = function fn(a){...} 5 fn( var a){....} 6 fn(a = 1){....}
console.log(a) //7 a = 1 函数局部作用域内a
a = 2 //8 a = 2 局部a
}
fn(a) //4 fn()
console.log(a) //9 a = 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()定义时作用域

其他

  1. a href=”javascript:;”或a href=”javascript:void(0)” 点A标签的时候,让当前页不跳转。
  2. 在同一个网页中,放任意个数的<script>由上往下执行 一般放在</body>结束之前。原因:先呈现页面,加载DOM树结构,后加载脚本,提高用户体验 基于网站优化
  3. js中允许把 “.” 换成 “[‘’]”
  4. !-[-1,] 判断IE
  5. IE独有api isIE = document.documentMode
-------------本文结束感谢您的阅读-------------
0%