基础笔记——函数

函数

本节记录了
* 变量需要注意的问题: 变量的提升,全局作用域,块级作用域以及常量
* 方法定义, 利用函数本身apply()方法,控制this指向以及装饰器

变量

变量的提升

JavaScript函数有一个特点它会先扫描整个函数体的语句,把所有申明的变量“提升到函数的顶部”,注:JavaScript虽然自动提升了变量的声明,但是不会提升变量的赋值
例:
1
2
3
4
5
6
function foo() {
var x = 'Hello, ' + y;
alert(x);
var y = 'Bob';
}
foo();
相当于
1
2
3
4
5
6
function foo() {
var y; // 提升变量y的申明
var x = 'Hello, ' + y;
alert(x);
y = 'Bob';
}

全局作用域

不在任何函数内定义的变量就具有全局的作用域.实际上,JavaScript默认有一个全局变量window,全局作用域的变量实际上被绑定到window的一个属性.其中以变量方式var foo = function(){}定义的函数实际上也是一个全局变量,并绑定到window对象中

其他

全局变量命名问题

因为全局变量会绑定到window对象上,不同JavaScript文件如果使用了同的变量,或者命名了相同的名字的函数,就会造成命名冲突,解决冲突的方法就是把自己所有的变量和函数全部绑定到唯一一个全局变量中
1
2
3
4
5
6
7
8
9
10
// 唯一的全局变量MYAPP:
var MYAPP = {};
// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;

// 其他函数:
MYAPP.foo = function () {
return 'foo';
};

局部变量解决块级作用域问题

由于JavaScript的变量作用域实际上是函数内部,我们在for循环等语句块中是无法定义具有局部作用域的变量的

1
2
3
4
5
function foo() {
for (var i=0; i<100; i++) {
}
i += 100; // 仍然可以引用变量i
}

为了解决块级作用域,ES6引入了新的关键字 let, let 可以申明一个块级作用域的变量

1
2
3
4
5
6
7
	function foo() {
var sum = 0;
for (let i=0; i<100; i++) {
sum += i;
}
i += 1; // SyntaxError
}

常量

由于var和let申明的是变量,如果要申明一个常量,在ES6之前是不行的,我们通常用全部大写的变量来表示“这是一个常量,不要修改它的值”
ES6标准引入了新的关键字const来定义常量,const与let都具有块级作用域:

方法

绑定到对象上的函数称为方法,和普通函数没有什么区别,但是多了一个 this 关键字

thi关键字类似Java中的this关键字,this指向哪个对象要视情况而定

apply控制this指向

要指定函数this的指向哪个对象,可以用函数本身apply方法,它接受两个参数,第一个参数是需要绑定的this变量,第二个参数是array,标示函数本身的参数.用apply修复:
修复前:
1
2
3
4
5
6
7
8
9
10
11
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 25, 正常结果
getAge(); // NaN
修复后:
1
2
3
4
5
6
7
8
9
10
11
12
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空
}
另一个与apply()类似的方法是call(),唯一区别是:
* apply()把参数打包成Array后传入
* call() 把参数按顺序传入

装饰器

利用apply(),我们可以动态改变函数的行为.javascript的所有对象都是动态的,即使是内置函数,我们也可以指向新的函数.案例:

现在假定我们想统计一下代码一共调用了多少次parseInt(),可以把所有的调用都找出来,然后手动加上count += 1,不过这样做太傻了。最佳方案是用我们自己的函数替换掉默认的parseInt():
1
2
3
4
5
6
7
8
9
10
11
12
13
{
var count = 0;
var oldParseInt = parseInt; // 保存原函数
window.parseInt = function () {
count += 1;
return oldParseInt.apply(null, arguments); // 调用原函数
};
// 测试:
parseInt('10');
parseInt('20');
parseInt('30');
count; // 3
}

本笔记根据 廖雪峰老师的JavaScript 教程记录