目标:
* 如何定义和使用函数
* 如何向函数传递参数
* 了解预定义函数(内建函数)
* 了解变量作用域 ⭐
* “函数也是数据”
* 匿名函数的调用
* 回调函数
* 自调函数 ⭐
* 内嵌函数
* 以函数为返回值的函数 ⭐
* 能重定义自身的函数
* 闭包 ⭐
*****
# 函数
实质为代码分组形式,便于重用;
组成:`function + 函数名 + 参数 + 函数体(代码块) + return (undefined)`
调用/请求:函数名 + ([参数])
传递的参数,少了,参数设为undefined,多了,忽略。
函数内建有**arguments**数组(类似数组的对象),可返回所接收的全部参数。
```
function args(){return arguments;}
args();
// Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ]
args(1,2,3,4,true);
// Arguments(5) [1, 2, 3, 4, true, callee: ƒ, Symbol(Symbol.iterator): ƒ]
```
# 内建函数
* parseInt()
* parseFloat()
* isNaN()
* isFinite()
* encodeURI()
* decodeURI()
* encodeURIComponent()
* decodeURIComponent()
* eval()
*****
## parseInt(a1,radix)
将a1转换成整数类型,失败返回NaN;
最好指明radix进制。
```
parseInt('abc123')
// NaN
parseInt('123abc')
// 123 在遇到第一个异常字符时会放弃转换 ⭐
parseInt(NaN);
// NaN
parseInt(1e1)
// 10
parseInt('1e1');
// 1
```
`radix`一般默认为10,即10进制,例外情况:
首参数字符串是0x开头,radix=16;
首参数字符串是0开头,radix=8;
## parseFloat(a)
只支持转换为10进制数,多余参数会忽略
可转换指数数据,parseInt不支持 ⭐
```
parseFloat('123e2');
// 12300
parseInt('123e2', 10);
// 123
parseFloat('1e1');
// 10
```
## isNaN()
用于判断输入值是否是非数值,`true-非数,false-数值`,即返回false才可作算术运算
```
isNaN('a');
// true
isNaN('123');
// false
isNaN(parseInt(NaN));
// true
```
## isFinite()
检查输入是否一个既非infinity也非NaN数字,是否有限数值,true-有限
```
isFinite(Infinity);
// false
isFinite(12);
// true
```
## URI编码与解码
`encodeURI()`——域名部分不编码
`encodeURIComponent()`——全部编码
`decodeURI()`——域名部分不编码
`decodeURIComponent()`——全部编码
## eval()
欺骗函数,类似Function()
将传入的string作为代码执行,动态代码,
* 性能差;
* 安全性差;
## alert()
# 变量的作用域 ⭐
全局变量:声明在所有函数之外的变量;
局部变量:声明在某个函数内部的变量,在该函数外是不可见的。
> 声明一个变量如果没有使用var语句,该变量会被默认为全局变量!
> 减少全局变量的数量
函数域优先于全局域
```
var a= 123;
function f() {
alert(a);
var a = 1; // 局部变量a声明被提升至函数域顶部,只声明被提升!
alert(a);
}
f(); // undefined 1
```
# 函数也是数据
函数标识记法,像变量一样使用函数
函数名不能以数字开头;
```
// function f(){return;}
var f = function(){return;}
typeof f; // "function"
```
函数特性:
* 它们所包含的是代码;
* 它们是可执行的;
## 匿名函数
两种用法:
* 将匿名函数作为参数传递给其他函数;
* 定义匿名函数来执行某些一次性任务;
## 回调函数 Callback()
将函数A传递给函数B,由B来执行A,A就是一个(匿名)回调函数
优势:
* 匿名传递函数,节省全局变量;
* 将调用A的操作委托给B,节省代码量;
* 提升性能;
## 自调函数 IIFE
`(function(参数){函数体})('入参')`
## 内部(私有)函数
好处:
* 确保全局名字空间的纯洁性;
* 私有性,暴露该暴露的,封装其余的;
## 返回函数的函数
`return function(){...};`
## 能重写自己的函数
利用返回函数覆盖旧函数;
执行一次性初始化工作;
* 外部重写;
* 内部重写;
```
function a(){
alert('A!');
a = function(){
alert('B!');
}
}
a(); // A!
a(); // B!
```
"浏览器特性探测"技术实现原理
# 闭包
## 作用域链
在函数中可访问的变量,既可以来自自身作用域,也可以来自其父级作用域。这就是一条作用域链(scope chain)。
## 词法作用域
每个函数在被**定义时**都会创建一个属于自己的环境(作用域)
```
function f1(){var a = 1; f2();}
function f2(){return a;}
f1();
// Uncaught ReferenceError: a is not defined
```
我们可以在函数中对变量执行添加、移除和更新等操作,但函数只会看到该变量的最终状态。因此我们可以重写函数执行体,依旧能正常工作。
## 利用闭包突破作用域链
将函数移动到词法作用域以外,函数仍记得其所定义作用域的路径,保持着对原定义作用域的引用,依旧可以访问原作用域的变量。
### 闭包1
```
function f(){
var b = 'b';
return function(){
return b;
}
}
b;
// Uncaught ReferenceError: b is not defined
var n = f(); // n 为全局变量,可以访问f()私有空间
n;
// ƒ (){ return b; }
n();
// "b"
```
### 闭包2
```
var n;
function f(){
var b = "b";
n = function(){
return b;
}
}
f();
n();
// "b"
```
### 闭包3
函数绑定的是作用域本身,而非变量或返回值
```
function f(arg){
var n = function(){
return arg;
}
arg++;
return n;
}
var m = f(123);
m();
124
```
## Getter & Setter
通过匿名自调函数,供全局函数间接访问局部变量secret
```
var getValue, setValue;
(function(){
var secret = 0;
getValue = function(){
return secret;
};
setValue = function(v){
secret = v;
};
})()
getValue(); // 0
setValue(123);
getValue(); // 123
```