Js函数写法

js函数写法

函数声明

1
2
3
4
5
6
fn(111);

funciton fn(param){
alert(param);
}
//js编译器会优先编译声明式函数,然后再逐上而下执行js

函数表达式

1
2
3
4
5
6
fn(111);

var fn = function fun (param){
alert(param);
}
//会报错,因为没有声明fn函数,如果放在下面则可以

匿名函数

1
2
3
4
5
6
7
8
9
function (){
alert(111);
}();
//因为没有把函数赋给变量,所以被编辑器当做声明式表达式,声明式要有方法名

function kk(){
alert(111);
}()
//声明式函数无法直接执行,要加扩号,把他变成对象

闭包

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

1
2
3
4
5
6
7
8
9
10
function fn1() {
var name = 'iceman';
function fn2() {
console.log(name);
}
return fn2;
}
var fn3 = fn1();

fn3();

这样就清晰地展示了闭包:

  • fn2的词法作用域能访问fn1的作用域

  • 将fn2当做一个值返回

  • fn1执行后,将fn2的引用赋值给fn3

  • 执行fn3,输出了变量name

经典代码

1
2
3
4
5
6
for (var i = 1; i <= 10; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
//这是因为setTimeout中的匿名函数执行的时候,for循环都已经结束了

目标

1
2
3
4
5
6
7
8
for (var i = 1; i <= 10; i++) {
(function (j) {
setTimeout(function () {
console.log(j);
}, 1000);
})(i);
}
//我们可以让i在每次迭代的时候,都产生一个私有的作用域,在这个私有的作用域中保存当前i的值。

闭包的应用

闭包的应用比较典型是定义模块,我们将操作函数暴露给外部,而细节隐藏在模块内部:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function module() {
var arr = [];
function add(val) {
if (typeof val == 'number') {
arr.push(val);
}
}
function get(index) {
if (index < arr.length) {
return arr[index]
} else {
return null;
}
}
return {
add: add,
get: get
}
}
var mod1 = module();
mod1.add(1);
mod1.add(2);
mod1.add('xxx');
console.log(mod1.get(2));

在for循环中运行setTimeout的三种情况

1
2
3
4
5
for(var i=0;i<10;i++){
setTimeout(console.log(i),0);
}

//直接console.log是个对象,他是个匿名函数得出来的对象(自我理解)
1
2
3
4
5
6
for(var i=0;i<10;i++){
setTimeout(function(){
console.log(i); //连续的10个10
},0);
}
//匿名函数,在settimeout里要等异步执行
1
2
3
4
for(var i=0;i<10;i++){
setTimeout("console.log(i)",1000);//连续的10个10
}
//console.log完了以后进行转换字符串工作,所以遵循settimeout的异步操作

异步执行的运行机制如下:

  • 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
  • 主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。
  • 一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
  • 主线程不断重复上面的第三步。
  • 主线程从”任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。只要主线程空了,就会去读取”任务队列”,这就是JavaScript的运行机制。这个过程会循环反复。

一般来说,有以下四种会放入异步任务队列:

  • setTimeout和setlnterval
  • DOM事件
  • ES6中的Promise
  • Ajax异步请求

JavaScript的垃圾回收机制

在js中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第三者引用,那么这两个互相引用的对象也会被回收。

一键三连!