- 函数声明和函数表达式是什么,又有什么区别呢?
我们来看如下的代码:
1
2
3
4
5
6
7
8
9fn();
function fn () {
//声明
}
fn1();
var fn1 = function () {
//表达式
}
我们知道函数声明和变量都会进行提升,在函数声明之前调用函数不会报错,因为编译器已经知道这个是一个函数了,可以调用。
但是对于函数表达式来说,将函数表达式赋值给变量的时候,变量是得到提升了,但是赋值操作是在执行过程中进行的,所以这里在表达式执行之前,fn1变量其实是undefined的。调用就会报错。
- 我们再来看一个例子:
1
2console.log(a);
var a = 100;
如果你觉得输出的结果是undefined,那么恭喜你已经对提升有了较好的理解了。
a作为一个全局变量得到了提升,在a未赋值之前,a都被认为是undefined。
函数在执行之前就已经确定了this和arguments的值
- 先了解一下执行上下文的概念:一段script或者函数都可以创建执行上下文,对于script来说,生成全局执行上下文,执行之前将变量定义和函数声明拿出来。
对于函数来说,会在函数执行之前将函数中的变量定义、函数声明,this和arguments拿出来。
- this会在执行时才能确定值,定义时无法确认,比如下面的例子。
1
2
3
4
5
6
7
8
9
10var a = {
name: 'A',
fn: function () {
console.log(this.name);
}
};
a.fn(); // this === a
a.fn.call({name: 'B}); // this === {name: 'B'}
var fn1 = a.fn;
fn1(); // this === window
变量提升如何理解
变量定义
函数声明
this几种使用场景
- 作为构造函数执行
1 | function Foo(name) { |
- 作为对象属性执行
1 | var obj = { |
- 普通函数执行
1 | function fn() { |
- call,apply,bind
1 | function fn1(name) { |
注意bind方法只用于函数表达式
创建十个a标签,点击弹出相应序号
这是一种错误的写法
1 | var i, a; |
我们点击的时候肯定是在代码执行完毕过后,那么我们给每个a标签绑定的点击事件的回调函数是alert(i),此时i为一个自由变量,就会向父作用域查找,而此时执行完毕for循环后,i的值为10,所以无论点击哪个a标签产生的结果都为10。
下面是正确的写法
1 | var i; |
如何理解作用域
- JS没有块级作用域
1 | // 没有块级作用域 |
在java或者c#中在大括号内定义的变量在外部是访问不到的,称之为块级作用域。
js中在块内和块外声明变量是一样的,所以不推荐在块内声明变量。
- 只有函数和全局作用域(相对于低级语言)
1 | // 函数和全局作用域 |
函数作用域可以保证变量不被污染。
下面这段代码解释了作用域链
1 | var a = 100; |
1 | var a = 100; |
总结一下:执行环境定义了变量或者函数有权访问的其他数据。每个执行环境都有一个变量对象(variable object),环境中定义的所有变量和对象都保存在这个对象中,但是我们的代码不能够访问这个对象,解析器在处理数据时在后台使用它。
而全局执行环境是最外围的执行环境,根据ECMAScript宿主环境的不同,表示执行环境的对象不同,在浏览器中,全局执行环境对象被认为是window对象。
每个函数也有自己的执行环境,当执行流进入一个函数时,函数的环境就被推入到一个环境栈中。执行结束之后,栈就将其环境弹出,把控制权交给之前的执行环境。
当代码在一个环境中执行时,就会创建变量对象的一个作用域链。他的用途,就是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行代码所在环境的变量对象。如果是函数,则将其活动对象作为变量对象。活动对象一开始只包含arguments对象。作用域链的下一个变量对象就来自于包含环境,再下一个变量对象来自于下一个包含环境,这样一层层追溯到全局执行环境。
实际开发闭包的应用
首先来说明一下闭包的概念:闭包是指有权访问另一个函数作用域中的变量的函数。首先是函数,然后可以访问其他函数作用域中的变量。
闭包的两个场景
- 函数作为返回值
1
2
3
4
5
6
7
8
9
10
11
12function F1 () {
var a = 100;
return function () {
console.log(a); // 自由变量去父级作用域查找,定义的父级作用域就是F1的函数作用域
}
}
// f1得到一个函数
// 函数的父级作用域是定义时候的作用域,而不是执行时候的父级作用域
var f1 = F1();
var a = 200;
f1();// 100 - 函数作为参数传递
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function F1 () {
var a = 100;
return function () {
console.log(a);
}
}
var f1 = F1();
function F2 (fn) {
var a = 200;
fn();
}
F2(f1);
封装变量和收敛权限
1 | function isFirstLoad () { |
表现为_list不能随意被外部修改。
2018 年 1月 5日