JS面试题--闭包--closure

闭包

让人迷惑的闭包

image-20230108110355826

JS中函数是一等公民

image-20230108111521429

01_函数作为参数的使用

// function foo(arg) {
    
    
//   console.log("foo", arg)
// }

// foo(123)

// 1. 将函数作为另外一个函数的参数
// function foo(aaaaa) {
    
    
//   aaaaa()
// }

// function bar() {
    
    
//   console.log("bar")
// }

// foo(bar)

// 案例
function calc(num1, num2, calcFn) {
    
    
  // 切记这里面才是关键 这里面是 calcFn(num1, num2)也就等于mul(num1, num2) 传过来的参数 参数要一致
  // 这里其实也是调用函数 这里的函数十分灵活,类似于一个格式接受传过来的函数 注函数的格式和参数需要一致
  console.log(calcFn(num1, num2));
}

function add(num1, num2) {
    
    
  return num1 + num2;
}

function sub(num1, num2) {
    
    
  return num1 - num2;
}

function mul(num1, num2) {
    
    
  return num1 * num2;
}

var m = 20;
var n = 30;

calc(m, n, mul);

02_函数作为返回值的使用

// js语法允许函数内部再定义函数
// function foo() {
//   function bar() {
//     console.log("bar")
//   }
// 函数作为返回值不要写小括号() 这样就变成调用 而不是返回
//   return bar
// }

// var fn = foo()
// fn()

function makeAdder(count) {
  function add(num) {
    return count + num;
  }

  return add;
}
// 通过传入 不同的参数来定制一些函数

var add5 = makeAdder(5);
// 这里就是count 5 + num 6 结果为11
console.log(add5(6));
// 这里就是count 5 + num 10 结果为15
console.log(add5(10));
// 这里就是count 10 可以再定义num    + num 10 结果为20
var add10 = makeAdder(10);
console.log(add10(10));
var add100 = makeAdder(100);
// 这里就是count 100 可以再定义num    + num 100 结果为200
console.log(add100(100));

// 高阶函数: 把一个函数如果接受另外一个函数作为参数,或者该函数会返回另外一个函数作为返回值的函数, 那么这个函数就称之为是一个高阶函数

03_数组中的函数使用

var nums = [10, 5, 11, 100, 55];

// var newNums = []
// for (var i = 0; i < nums.length; i++) {
    
    
//   var num = nums[i]
//   if (num % 2 === 0) {
    
    
//     newNums.push(num)
//   }
// }
// console.log(newNums)

// 函数和方法的区别:
// 函数function: 独立的function, 那么称之为是一个函数 foo() {}是函数
function foo() {
    
    }
// 方法method: 当我们的一个函数属于某一个对象时, 我们成这个函数是这个对象的方法 };// obj.foo();是方法
var obj = {
    
    
  foo: function () {
    
    },
};
obj.foo();

// 高阶函数: 把一个函数如果接受另外一个函数作为参数,或者该函数会返回另外一个函数作为返回值的函数, 那么这个函数就称之为是一个高阶函数

// 1.filter: 过滤 是属性nums对象里面的方法  filter:是数组内置的方法
// [10, 5, 11, 100, 55]
// 10 => true => 不会被放到newNums
// 5 => false => 不会被放到newNums
// 11 => false => 不会被放到newNums
// 100 => true => 不会被放到newNums
// 55 => false => 不会被放到newNums
// nums.filter(function(item,index,arr){})这个回调函数会传入三个参数  并且返回结果是布尔值
// item是每一项,数组里面有几个值回调几次,key是下标,arr是整个数组的引用,如果只用item其他不用写
// var newNums = nums.filter(function(item) {
    
    
//   return item % 2 === 0 // 偶数
// })
// console.log(newNums)

// 2.map: 映射
// [10, 5, 11, 100, 55]
// var newNums2 = nums.map(function(item) {
    
    
//   return item * 10
// })
// console.log(newNums2)

// 3.forEach: 迭代 forEach函数是没有返回值的
// nums.forEach(function(item) {
    
    
// 打印数组中的每一项
//   console.log(item)
// })

// 4.find/findIndex
// es6~12
// 找到第一个return返回为true的值
// var item = nums.find(function(item) {
    
    
//   return item === 11
// })
// console.log(item)
// var friends = [
//   {name: "why", age: 18},
//   {name: "kobe", age: 40},
//   {name: "james", age: 35},
//   {name: "curry", age: 30},
// ]
// 找到对应对象的每一项
// var findFriend = friends.find(function(item) {
    
    
//   return item.name === 'james'
// })
// console.log(findFriend)
// 找到对应对象的索引值.findIndex
var friendIndex = friends.findIndex(function (item) {
    
    
  return item.name === "james";
});
// console.log(friendIndex)

// 5.reduce:
// nums.reduce
// [10, 5, 11, 100, 55]
// 最基础的累加方法
// var total = 0
// for (var i = 0; i < nums.length; i++) {
    
    
//   total += nums[i]
// }
// console.log(total)

// 高阶函数的写法 累加
// prevValue: 0, item: 10
// prevValue: 10, item: 5
// prevValue: 15, item: 11
// 函数的参数prevValue是返回上一次的值  item是这次的值
var total = nums.reduce(function (prevValue, item) {
    
    
  return prevValue + item;
  // 这个函数接受两个参数 第一个参数是函数,第二个参数可以传入初始化值,这里设置默认初始化的值为0
}, 0);
// 返回最终的总和结果
console.log(total);

JS中闭包的定义

image-20230108120716458

image-20230108120858476

04_高阶函数执行过程

function foo() {
    
    
  function bar() {
    
    
    console.log("bar")
  }

  return bar
}

var fn = foo()
fn()

05_闭包到底是什么

image-20230108121829740

function foo() {
    
    
  // AO: 销毁
  var name = "foo"
  function bar() {
    
    
    console.log("bar", name)
  }

  return bar
}

var fn = foo()
fn()


var name = "why"
function demo() {
    
    
  console.log(name)
}


// 可以访问: test就是闭包
// 有访问到: test就是不是闭包
function test() {
    
    
  // 1
  // 10000
}

06_函数的执行过程的内存

image-20230108130554662

image-20230108130820843

function foo() {
    
    
  var name = "foo"
  var age = 18
}

function test() {
    
    
  console.log("test")
}

foo()
test()

闭包的内存泄露

image-20230108133703150

image-20230108134148599

07_函数的执行过程的内存

image-20230108131722711

image-20230108133239324

扫描二维码关注公众号,回复: 16866415 查看本文章
function foo() {
    
    
  var name = "foo"
  var age = 18

  function bar() {
    
    
    console.log(name)
    console.log(age)
  }
  return bar
}

var fn = foo()
fn()

fn = null  
foo = null

// fn()
// fn()
// fn()
// fn()
// fn()
 

闭包的内存泄漏测试

image-20230109112620274

01_js闭包内存泄露案例

function createFnArray() {
    
    
  // var arr = [1, 1, 1, 1, 1, 1, 1, 1,1, 1,1, 1,1 ]
  // 一个整数占据四个字节 所以一共占据 1024*1024*4 个字节(byte) 1024字节(byte)=1kb 1024kb =1m
  // 这个数组的占据的空间是4M x 100 + 其他的内存 = 400M+
  // 1 -> number -> 8byte -> 8M
  // js: 10 3.14 -> number -> 8byte ? js引擎
  // 8byte => 2的64次方 => 4byte
  // 小的数字类型, 在v8中成为Sim, 小数字 2的32次方 只占据4个字节
  // 总结 + —2的32次方的数字一般情况下占据8byte 但是通过V8引擎的优化只占据4个字节byte
  // new Array(1024 * 1024)创建数组长度为1024 *1024的数组
  // fill()对数组的内容进行填充 填充什么值就在参数中写入什么 。
  var arr = new Array(1024 * 1024).fill(1); //创建一个长度为1024 *1024的数组,并每个位置都填入数字1
  return function () {
    
    
    console.log(arr.length);
  };
}

// 我们在这里调用 createFnArray()函数相当于调用function() { console.log(arr.length)} 这个函数,这个函数引用这上面newArray这个对象,所以不会销毁
// var arrayFn = createFnArray()
// arrayFn = null

// 100 * 100 = 10000 = 10s
var arrayFns = [];
for (var i = 0; i < 100; i++) {
    
    
  setTimeout(() => {
    
    
    arrayFns.push(createFnArray());
  }, i * 100);
}

// arrayFns = null 定时器 确定上面的代码执行完毕在清空数组  ,销毁并不是一点点销毁回收,而是等到一定的时间根据GC自己的算法我的优化 来回来在进行销毁
setTimeout(() => {
    
    
  for (var i = 0; i < 10; i++) {
    
    
    setTimeout(() => {
    
    
      arrayFns.pop();
    }, 100 * i);
  }
}, 10000);

02_js闭包引用的自由变量销毁

function foo() {
    
    
  var name = "why";
  var age = 18;

  function bar() {
    
    
    debugger; //用debugger在控制台打印测试--发现被浏览器引擎销毁优化 
    console.log(name);
    // age并没有使用 按照ECMA的规范 整个AO对象中的name和age都不会销毁--应为会被函数调用,但是js引擎V8引擎为了优化性能,后面用不到age,应该销毁age
    // console.log(age)
  }

  return bar;
}

var fn = foo();
fn();

猜你喜欢

转载自blog.csdn.net/SOLar7SysteM/article/details/128612168