函数和作用域
函数
函数是一块JavaScript代码,被定义一次,但可执行和调用多次。
JS中的函数也是对象,所以JS函数可以像其它对象那样操作和传递,所以我们也常叫JS中的函数为函数对象。
函数调用方式:
在JavaScript中一共有四种调用模式:
函数定义:函数声明与函数表达式
1.函数声明:1
2
3function fnA(ar0,arg1,arg2){
//函数体
}
2.函数表达式:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18var a = function(arg0,arg1,arg2){
//函数体
};
// IEF(Immediately Executed Function),立即执行函数
/*在function前面加!、+、 -甚至是逗号等到都可以起到函数定义后立即执行的效果,而()、!、+、-、=等运算符,都将函数声明转换成函数表达式,消除了javascript引擎识别函数表达式和函数声明的歧义,告诉javascript引擎这是一个函数表达式,不是函数声明,可以在后面加括号,并立即执行函数的代码。
加括号是最安全的做法,因为!、+、-等运算符还会和函数的返回值进行运算,有时造成不必要的麻烦。*/
(function() {
// do sth
})();
// first-class function
return function() {
// do sth
};
// NFE (Named Function Expression)
var add = function foo (a, b) {
// do sth
};
3.使用Function构造器函数(不推荐)
如:var sum = new Function(num1,num2,"return num1 + num2")
1
2
3
4
5
6
7
8
9
10
11
12// CASE 1;localVal仍为局部变量
Function('var localVal = "local"; console.log(localVal);')();
console.log(typeof localVal);
// result: local, undefined
// CASE 2;local不可访问,全局变量global可以访问
var globalVal = 'global';
(function() {
var localVal = 'local';
Function('console.log(typeof localVal, typeof globalVal);')();
})();
// result: undefined, string
函数属性 & arguments
除了声明时定义的形式参数,每个函数接受两个附加的参数:this和arguments,参数this在面向对象编程中非常重要,它的值取决于调用的模式。
函数的arguments属性包含了调用函数时传入的所有参数,而不管函数的声明中是否定义了这些形参;arguments不是数组,只是一个“类似数组”的对象(在函数中运行arguments instanceof Array;返回false)。可以通过Array.prototype.slice.apply(arguments)
apply/call方法
1 | function foo(x, y) { |
bind方法
作用1: this执行bind()中的对象1
2
3
4
5
6
7
8
9
10this.x = 9;
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 81
var getX = module.getX;
getX(); // 9
var boundGetX = getX.bind(module);
boundGetX(); // 81
作用2:柯里化currying.bind(this,a):第一个参数为this指向,也可以不指明,用undefined/null,第二个为参数,即传入对象中的第一个参数。1
2
3
4
5
6
7
8
9
10
11
12
13
14function add(a, b, c) {
return a + b + c;
}
var func = add.bind(undefined, 100);
func(1, 2); // 103
var func2 = func.bind(undefined, 200);
func2(10); // 310
function getConfig(colors, size, otherOptions) {
console.log(colors, size, otherOptions);
}
var defaultConfig = getConfig.bind(null, "#CC0000", "1024 * 768");
defaultConfig("123"); // #CC0000 1024 * 768 123
defaultConfig("456"); // #CC0000 1024 * 768 456
bind与构造函数一起使用时,其this指向消失。1
2
3
4
5
6
7function foo() {
this.b = 100;
return this.a;
}
var func = foo.bind({a:1});
func(); // 1
new func(); // {b : 100}
bind方法模拟:浏览器兼容方案:
this
在一个函数中,this总是指向当前函数的所有者对象,this总是在运行时才能确定其具体的指向, 也才能知道它的调用对象。
全局的this(浏览器)
1 | console.log(this.document === document); // true |
一般函数的this(浏览器)
1 | function f1(){ |
作为对象方法的函数的this
1 | var o = { |
对象原型链上的this
1 | var o = {f:function(){ return this.a + this.b; }}; |
get/set方法与this
1 | function modulus(){ |
构造器中的this
1 | function MyClass(){ |
call/apply方法与this
1 | function add(c, d){ |
bind方法与this
1 | function f(){ |
闭包
闭包是指有权访问另一个函数作用域中的变量的函数。
创建闭包的常见的方式,就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量。
闭包其实就是一个函数;如果一个函数访问了它的外部变量,那么它就是一个闭包。从技术上来讲,在Javascript中,每个function都是闭包,因为它总是能访问在它外部定义的变量。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16function outer() {
var localVal = 30;
return function() {
return localVal;
}
}
var func = outer();
func(); // 30
!function() {
var localData = "localData here";
document.addEventListener('click',
function(){
console.log(localData);
});
}();
闭包优缺点
优点:
- 灵活和方便
- 封装
缺点: - 空间浪费
- 内存泄漏
- 性能消耗
闭包常见错误——循环闭包
1 | document.body.innerHTML = "<div id=div1>aaa</div>" |
作用域
包括:全局作用域、函数作用域、eval作用域
作用域链
执行上下文
执行上下文(Execution Context,缩写EC)
变量对象
变量对象(Variable Object, 缩写为VO)是一个抽象
概念中的“对象”,它用于存储执行上下文中的:
- 变量
- 函数声明
- 函数参数
变量初始化阶段,VO按照如下顺序填充:
- 函数参数 (若未传⼊,初始化该参数值为undefined)
- 函数声明 (若发生命名冲突,会覆盖)
- 变量声明 (初始化变量值为undefined,若发生命名冲突,会忽略。 )
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
28function foo(x,y,z){
function func(){};
var func;
console.log(func);
}
foo(100); // function func(){}
function foo(x,y,z){
function func(){};
var func=1;
console.log(func);
}
foo(100); // 1
//测试
alert(x); // function
var x = 10;
alert(x); // 10
x = 20;
function x() {}
alert(x); // 20
if (true) {
var a = 1;
} else {
var b = true;
}
alert(a); // 1
alert(b); // undefined