函数在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{}进行提前声明或者定义
先去找var
function 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