1. 作用域
1.1 执行上下文
- 执行上下文,作用域链用定义时的
// 执行上下文
function outer() {
/*
executionContext = {
scopeChain: {},
variableObject: { // 初始化变量 函数 形参
age: undefined,
arguments:[]
},
this: {}
}
*/
var age = 18;
function demo(num) {
// 创建阶段,浏览器或者node会创建一个执行上下文 ,即变量提升,执行时赋值
/*
executionContext = {
scopeChain: {}, // 作用域链 scope chain
variableObject: { // 初始化函数中声明的 变量 函数 形参
name: undefined,
getData: undefined,
c: undefined,
num: undefined,
arguments:[]
},
this: {}
}
*/
/*
** 当前作用域variableObject找不到age 向上一层找, 上一层也没有,找全局,全局没有则undefined
*/
console.log('age::', age) // 18
console.log('before::', name, num, getData, c)
// before:: undefined 100 undefined function c() {}
var name = 'paul';
var getData = function getData() {
};
function c() {
};
console.log('after::', name, num, getData, c)
// after:: paul 100 function getData() {} function c() {}
}
demo(100);
}
outer();
- 作用域链,为,定义时而非运行时
// 作用域链为定义时,而非执行时
function demo(num){
var getData = function getData() {};
console.log('name::', name);
// name is not defined
// 向外找作用域是 定义时的, 而非执行时的作用域,定义时,demo外是window、global
function c() {};
}
function outer(){
var name = 'chris';
demo(100); // 执行
}
outer();
1.2 执行上下文栈
// 执行上下文栈
function demo (num) {
var name = 'chris paul';
var getData = function getData() {};
function c() {};
console.log('demo');
if (num >= 1000000){
return;
}
// 递归
demo(num + 1);
console.log('name::', name)
// Maximum call stack size exceeded
// 每次入栈,都存入外边的变量,存入上下文,直到执行结束才会释放,js调用栈爆
// 递归每次入栈,都需要保证上面的变量还可以访问,所以不能删,结束才能销毁
// 百万节点用栈模拟递归,尾递归优化(浏览器兼容性)
}
demo(1);
1.3 let(babel编译实例)
// let const
function demo (num) {
console.log('name::', name) // undefined,变量提升,可以访问到但是没赋值
console.log('name2::', name2) // name2 is not defined
// const let 没有变量提升 不会提升到 variableObject
// babel 会把let直接替换成var babel有时候实现的不好, 遇到bug,有值,不报错,往变量提升上想想
var name = 'chris paul';
let name2 = 'kobe';
}
demo(100);
// babel编译块级作用域假象
function demo(num) {
// 用var时i在这里
for (let i = 0; i < 100; i++) {
// 用let时i在这里
setTimeout(()=>{
console.log('i::', i)
},1)
}
}
demo(100);
// babel编译如下
function demo(num){
var _loop = function _loop(i){
setTimeout(()=>{
console.log('i::', i)
},1)
}
for (var i = 0; i < 100; i++){
_loop(i);
}
}
// babel 只是傻傻的把let变成var么
function demo(num){
{
let name = 'iverson';
console.log('name::', name)
}
console.log('name::', name) // name 访问不到
}
demo(100);
// babel编译后
function demo(num){
{
var _name = 'iverson';
console.log('name::', _name)
}
console.log('name::', name)
}
demo(100);
2. 闭包
2.1 定义
// 闭包:通过外层函数的执行返回里层函数
function outer() {
var top = 'backham'; // 定义时就有
function inner() {
console.log('top', top);
}
return inner;
}
var inner = outer();
inner();
// 因为作用域链的关系,里层函数执行时,向上找定义时候的top
// 在内部函数访问到外部函数的变量就叫闭包
function outer() {
var top = 'backham';
function inner() {
console.log('top', top);
}
inner(); // 有权访问另一个函数作用域重的变量的函数,就算是闭包,不用非得return
}
outer();
2.2 用法
- wife可以访问person的money
(function () {
var Person = function () {
this.money = 100000000;
}
Person.prototype.buybuybuy = function () {
this.money -= 100;
console.log('left money::', this.money);
}
let teacher = new Person();
teacher.buybuybuy();
function wifeSearch() {
teacher.money = 0;
}
wifeSearch();
teacher.buybuybuy();
// left money:: 99999900
// left money:: -100
// 对象上的属性,外部可以直接改,不希望外部可以直接访问,用闭包解决这个问题
})();
- 设置闭包私有,wife无法访问person的money
(function () {
var Person = (function (){
// 静态私有变量 共用了私有变量
var _money = 100000000;
function Person() {
// 公有变量放在这里
// 私有变量提到闭包上 _money
};
Person.prototype.buybuybuy = function () { //函数内部,访问外部作用域变量
_money -= 100;
console.log('left money::', _money);
};
return Person;
})();
let teacher = new Person();
teacher.buybuybuy();
function wifeSearch() {
teacher.money = 0;
}
wifeSearch(); // 改不了
teacher.buybuybuy();
})();
- 日常用法
- 用闭包做缓存(wx.getUserInfo()可以缓存)
(function() {
var axios = require('axios');
// 封装了一个数据接口 利用闭包特性 缓存
// 返回值永远类型相等 返回promise
function apiGenerator() {
let menuData = null;
return function getInitMenu() {
if(meunData) {
return promise.resolve(menuData);
}
return axios.get('menuapi')
.then(res => {
menuData = res;
return menuData;
})
}
}
let result = apiGenerator();
})();