1. 函数
1.1. 定义
- 函数声明
function theFirstName(){ } - 函数表达式
// 命名函数表达式
let test = function abc(){
console.log('a')
}
// 匿名函数表达式 ==> 函数表达式
let demo = function(){}
console.log(test.name); // abc
console.log(demo.name); //demo
1.2. 组成形式
- 函数名称
// 命名函数表达式
let test = function abc(){
console.log('a')
}
// 匿名函数表达式 ==> 函数表达式
let demo = function(){}
console.log(test.name); // abc
console.log(demo.name); //demo
- 参数
- 形参
- 实参
// 形式参数 -- 形参
function sum(a) {
// arguments-- [11, 2, 3] 实参列表
}
// 实际参数 -- 实参
sum(11, 2, 3)
* 判读
// 形式参数 -- 形参
function sum(a,b) {
console.log(sum.length); // 形参长度 2
console.log(arguments.length); // 实参参长度 3
}
// 实际参数 -- 实参
sum(11, 2, 3)
- 返回值
- return 终止, 有值返回值
1.3. 函数使用
- 高内聚,低耦合
- 函数复用
function test(){
document.write('a');
document.write('b');
document.write('c');
}
if(1>0){
test();
}
if(2>0){
test();
}
if(3>0){
test();
}
- 形参和实参不一致好处:求取无穷数相加
function sum(){
var result = 0;
for (let index = 0; index < arguments.length; index++) {
result += arguments[index]
}
console.log(result);
}
sum(2,3,4,555,5,222,6,111,7)
- 映射
// 映射机制,不论实参值修改,还是实参列表修改值,都会改变相应的值
function sum(a,b){
var a = 2;
// arguments[0] = 10;
console.log(a);
}
sum(1,2)
- 实参列表arguments长度固定
1.4. 函数练习题
- 函数:告知你所选定的小动物的叫声
function scream(animal) {
switch (animal) {
case "cat":
console.log('miao!');
return;
case "dog":
console.log('wang!');
return;
case "fish":
console.log('o~o~o!');
return;
}
}
scream('cat');
- 一组函数:输入数字,逆转并输出汉字形式
// 逆转: reverse 汉字: transfer
function reverse() {
var num = window.prompt('input');
var str = "";
for (var i = num.length - 1; i >= 0; i--) {
// 原理:类型转换 number + string = String(number) + string
str += transfer(num[i]);
}
document.write(str);
}
function transfer(target) {
switch (target) {
case "1":
return "壹";
case "2":
return "俩";
case "3":
return "仨";
default:
return ">仨"
}
}
reverse();
- 函数实现n的阶乘
// // 规律: n * (n - 1)!
function mul(n){
// // n的阶乘 方法1: for循环赋值运算
// let num = 1;
// for (let index = 0; index < n; index++) {
// num *= index
// }
// // n的阶乘 方法2: 递归
if(n == 1){
return 1;
}
return n * mul(n - 1);
}
console.log(mul(1));
// mul(5);
// mul(5); ==> 5 * mul(4);
// mul(4); ==> 4 * mul(3);
// mul(3); ==> 3 * mul(1);
// mul(2); ==> 2 * 1
- 递归
- 方法: 找规律, 找出口
- 递归特别慢 空间复杂度巨高
- 第n位等于什么, 有什么规律, 如: 以1结束, 以n开始, *=, += ...
- 函数实现斐波那契数列
// 斐波那契额数列
// 找规律
// 1 1 2 3 5 8
// 8 = 5 + 3
// n = (n - 1) + (n - 2)
// fbnq(n) = fbnq(n - 1) + fbnq(n - 2)
// 找出口
// 第一个1和第二个1,终止
function fbnq(n) {
if (n == 1 || n == 2) {
return 1;
}
return fbnq(n - 2) + fbnq(n - 1);
}
console.log(fbnq(5));
// fbnq(5)
// fbnq(5) ==> fbnq(4) + fbnq(3)
// fbnq(4) ==> fbnq(3) + fbnq(2)
// fbnq(3) ==> fbnq(2) + fbnq(1)
// fbnq(2) = 1
// fbnq(1) = 1
2. 作用域精解
- 运行上下文:当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。
- 查找变量:从作用域链的顶端依次向下查找。
- 作用域定义:变量(变量作用域又称上下文)和函数生效(能被访问)的区域
- 全局、局部变量
- 作用域的访问顺序
2.1. js运行顺序
- 语法分析(判断语法中有无汉字符号..., 为低级错误,一旦出错,代码不执行并报错)
- 预编译(定义变量声明,函数声明)
- 解释执行(执行代码阶段)
2.2. 预编译阶段
- 简单说法:
- 函数声明整体提升, 变量 声明提升
- 预编译前奏:
- imply global 暗示全局变量: 即任何变量, 如果变量未经声明就赋值,此变量就为全局变量所有。
- eg:
a = 123; - eg:
var a = b = 123;在全局环境或局部环境下,b都为全局变量,未声明变量都属于window;
- eg:
- 一切声明的全局变量, 全是
window属性
- eg:
var a = 123; ===> window.a = 123;// 全局访问a 就是访问window.a - window 就是全局的域
- 预编译发生在函数执行的前一刻
- 预编译步骤
- 创建AO对象
- 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
- 将实参值和形参统一
- 在函数体里面找函数声明,值赋予函数体
- 预编译+函数执行实例
function fn(a) {
console.log(a); // ƒ a() { }
var a = 123;
console.log(a); // 123
function a() { };
console.log(a); // 123
var b = function () { };
console.log(b); // ƒ () { }
function d() { }
}
fn(1);
// 预编译发生在函数执行的前一刻
// 1. 创建AO对象(Activation Object)(执行期上下文)
// 2. AO对象变化过程
// 2.1. 变量和形参名作为属性名
// AO{
// a: undefined,
// b: undefined
// }
// 2.2. 实参值和形参统一
// AO{
// a: 1,
// b: undefined
// }
// 2.3. 函数体中找函数声明,函数表达式为赋值操作,在代码执行阶段完成
// AO{
// a: function a(){},
// b: undefined,
// d: function d(){}
// }
// 2.4. 预编译阶段完成
// 3. 代码逐行执行,作用域为AO
// 3.1. 第一行 console.log(a); 从作用域可以找到 ƒ a() { };
// 3.2. 第二行 var a = 123; var阶段已在预编译阶段完成,只需进行赋值操作,此时a变为123
// 3.3. 第三行 console.log(a); 此时a=123,所以打印值为"1";
// 3.4. 第四行 function a() { }; 此代码为函数声明,已在预编译阶段完成,略过。
// 3.5. 第五行 console.log(a); 此时a=123,所以打印值为"1";
// 3.6. 第六行 var b = function () { }; 此代码为函数表达式,var已在预编译阶段完成,对b进行赋值操作,此时b为function () { }
// 3.7. 第七行 console.log(b); 此时b为function () { },打印 function () { }
// 3.8. 第八行 function d() { }; 此代码为函数声明,已在预编译阶段完成,略过。
- 函数执行
- AO对象就是作用域
- 预编译在函数和全局一样使用
- 全局GO对象(Global Object) 局部AO对象(Activation Object)
- GO对象就是window
- 局部环境下的未声明变量暗示在GO对象上
- 只有表达式才能被执行符号执行,正负号可以隐式转换函数声明,
GO { 变量a: ... } AO { 变量a: ... }
3. 作用域链
- 变量寻找:根据已层嵌套关系,使用变量,自己所在作用域有此变量,使用自己的,没有的话向上寻找, 找到了使用,找不到报错;
- 作用域链 GO和AO的关系
- 从作用域链顶端的AO到底端GO寻找变量

- 预编译生成GO对象 --> 代码执行 --> 链[GO]在GO里寻找变量 --> 函数执行前预编译生成AO对象 --> 函数执行 --> 链[AO,GO],从AO里开始找变量或函数,找不到在找GO里的变量或函数
- [[scope]]-上下文集合-作用域链-[……AO,GO]
function a(){
function b(){
function c(){
}
c();
}
b();
}
a();
a defined a.[[scope]] --> 0 : GO
a doing a.[[scope]] --> 0 : aAO
1 : GO
b defined b.[[scope]] --> 0 : aAO
1 : GO
b doing b.[[scope]] --> 0 : bAo
1 : aAO
2 : GO
c defined c.[[scope]] --> 0 : bAO
1 : aAO
2 : GO
c doing c.[[scope]] --> 0 : cAO
1 : bAO
2 : aAO
3 : GO
- 在全局环境生存着其中被定义的变量,在函数环境生存着其中被定义的变量
- 函数销毁前,作用域链中的AO不变,同一个AO不会改变;
4. 闭包
- 闭包:但凡内部的函数被保存到外部
- 闭包会导致多个执行函数共用一个公有变量,原有作用域链不释放
- 立即执行函数:
(function(){})()(function(){}())
- 闭包中返回的函数,其中的作用域链不销毁,共用一个AO;
4.1. 闭包的作用
- 实现公有变量: 函数累加器
function add(){
var count = 0;
function demo(){
count++;
console.log(count);
}
return demo;
}
var counter = add();
counter(); // 1
counter(); // 2
counter(); // 3
counter(); // 4
- 做缓存(存储结构): eater
function test(){
var food = 'apple';
var obj = {
eatFood: function(){
if(food != ''){
console.log("I am eating "+ food);
}else{
console.log("There is nothing! empty");
}
},
pushFood: function(myFood){
food = myFood;
}
}
return obj;
}
var person = test();
person.eatFood(); // I am eating apple
person.eatFood(); // I am eating apple
person.pushFood('banana');
person.eatFood(); // I am eating banana
-
封装,属性私有化: Person();
-
函数:求字符串的字节长度:charCodeAt
function retByteslen(target) {
var count = target.length;
for (var i = 0; i < target.length; i++) {
if (target.charCodeAt(i) > 255) {
count++;
}
}
console.log(count);
}
retByteslen("世界hello,world"); // 15