作用域
作用域是指程序源代码中定义的范围,分为全局作用域和局部作用域也叫函数作用域
作用域规定了如何设置变量,也就足确定当前执行代码对变量的访间权限
函数作用域采用词法作用域,也就是静态作用域
- 所谓词法作用域就是在函数定义的时候,就已经确定了
var value = 1;
// 定义的时候就确定了作用域
function foo() {
console.log(va1ue);
}
function bar() {
var value = 2;
foo();
}
bar(); // 1
- 变量对象
变量对象是当前代码段中,所有的变量(变量函数形参arguments)组成的一个对象
变量对象是在执行上下文中被激活的,只有变量对象被激活了, 在这段代码中才能使用所有的变量
变量对象分为全局变量对象和局部变量对象
全局简称为Viriable Object VO 函数由于执行才被激活称为Active Object AO
var value = 1;
function foo() {
console.log(va1ue);
}
function bar() {
var value = 2;
foo();
}
bar(); // 1
作用域链
在js中,函数存在-个隐式属性[[scopes]],这个属性用来保存当前函数的执行上下文环境,由子在数据结构上是链式的,因此也被称作是作用域链,我们可以把它理解为一个数组
可以理解为是一系列的AO对象所组成的一个链式结构
scopes 存的就是VO的集合
function a() {}
consale.dir(a) // 打印结构
- 当函数被调用后
function a() {
cosole.log(a)
}
a()
因此我们可以得出一个结论: [[scops]]属性在函数声明时产生,在函数调用时更新
即:在函数被调用时,将该图数的A0对象压入到[[scopes]]中
作用域链的作用
作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的
最直观的表现就是:
- 函数内部可以访问到函数外部声明的变量
var a=100
function fn(){
console.log(a)
}
fn() // 100
- 函数外部访问不到函数内部的变量
function fn() {
var a=100
}
fn()
console.1og(a) // a is not defined
画出下面代码执行的作用域链
var global
function a() {
var aa = 123
function b() {
var bb = 234
}
b()
}
a()
- 第一步:a函数生成
- 第二步:a函数调用
- 第三步:b函数生成(a函数调用导致b函数生成)
- 第四步:b函数调用
函数执行完就销毁, 所谓销毁就是断掉图中的线
思考a函数调用和b函数生成是不是同一个作用域链
- 是的, 下面代码验证
var global
function a() {
//到这个里面来了
var aa = 123
// b函数定义
function b() {
//到这里面来了
var bb = 234
aa = 0
}
// b执行
b()
console.log(aa);
}
// a执行
a()
//打印aa的结果,如果是00,说明是同一个没有分离
- 那么就有了下面的图解
看看闭包
var global
function a() {
//到这个里面来了
var aa = 123
// b函数定义
function b() {
console.log(aa)
}
return b
}
// a执行
var res = a()
res()
- 底层原理结合图9讲解.
a执行完 - 销毁 - 断掉a的线,但b定义的时候,其的作用域链还在!!!
a()执行完,把b函数保存到了外面。
- 造成内存泄漏的前提是:内部的函数只有被return出来,保存到外部去执行的时候才会造成内存泄漏。
经过上述的讲解那么谈谈闭包的定义
- 就是能够读取其他函数内部变量的函数
闭包的实战应用
- 回调函数
function add(num1, num2, callback) {
var sum = num1 + num2;
if (typeof callback === 'function') {//类型判断,确认是一个函数
callback(sum);
}
}
add(1, 2, function (sum) {
console. 1og(sum);
})
- 手写js的方法-手写bind方法
let foo={
name: 'jill'
}
function getName() {
console.1og(this.name)
}
Function.prototype.myBind = function(obj) {
//将当前函数的this指向目标对象
let_ self = this
return function() {
return _self.call(obj)|
}
}
let getFooName = getName.myBind(foo)
getFooName()
- 防抖节流函数
- 单例模式
- 定时器传参
function fn(a) {
return function() {
console.log(a)
}
}
setTimeout(fn(123),1000)
6.利用闭包判断数据类型
function isType(type) {
return function (target) {
return `[object ${type}]` === object.prototype.tostring.ca1l(target)
}
}
const isArray = isType('Array')
console.log(isArray([1, 2, 3])); // true
console.log(isArray({}));
- 封装私有变量和函数
function createPerson(name) {
var age = 0;
return {
getName: function() {
return name;
},
getAge: function() {
return age;
},
setAge: function(newAge) {
age = newage;
}
};
}
var person = createPerson("John");
console.log(person.getName(); // "John"
console.log(person.getAge()); // 0
person.setAge(30);
console.log(person. getAge()); // 30
- 高阶函数
- 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
- 实现对一个数组的求和
function sum(arr) {
return arr.reduce(function (x, y) {
return x + y;
});
}
sum([1, 2, 3, 4, 5]); // 15
- 但是,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办? 可以不返回求和的结果,而是返回求和的函数!
function 1azy_sum(arr) {
var sum = function () {
return arr.reduce(function (x, y) {
return x + y;
});
return sum;
}
var f = 1azy_sum([1, 2,3, 4, 5]);
- 迭代器(执行一次函数往下取一个值)
var arr = ['aa','bb','cc'];
function incre(arr){
var i=0;
return function(){
//这个函数每次被执行都返回数组arr中i下标对应的元素
return arr[i++] || ' 数组值已经遍历完';
}
}
var next = incre(arr);
console.1og(next()); //aa
console.1og(next()); //bb
console.log(next()); //cc
console.log(()); //数组值已经遍历完
- 缓存
比如求和操作,如果没有缓存,每次调用都要重复计算,采用缓存已经执行过的去查找,查找到了就直接返回,不需要重新计算
var fn = (function(){
var cache = {};//缓存对象
var calc = function(arr){//计算函数
var sum = 0;
// 求和
for(var i=0;i<arr.1ength;i++){
sum+=arr[i];
}
return sum;
}
return function(){
var args = Array.prototype.slice.call(arguments ,0) ;//arguments转换成数组
var key-args.join(",");//将args用逗号连接成字符串
var result,tsum = cache[key];
if(tsum){//如果缓存有
console.1og('从缓存中取: ',cache)//打印方便查看
result = tsum;
}else{
//重新计算,并存入绥存同时赋值给result
result = cache[key]=calc(args);
console.1og('存入缓存: ' ,cache)//打印方便查看
}
return result;
}
})();
fn(1,2,3,4,5);
fn(1,2,3,4,5);
fn(1,2,3,4,5,6);
fn(1,2,3,4,5,8);
fn(1,2,3,4,5,6);