当前位置: 首页 > news >正文

理解js函数(Ⅱ)

1、函数的尾调用优化是什么?

在 JavaScript 中,尾调用优化(Tail Call Optimization, TCO) 是一种优化技术,用于优化递归函数调用。当一个函数的最后一个操作是调用另一个函数(即尾调用)时,引擎可以重用当前的栈帧,而不是创建新的栈帧。这种优化可以防止因递归深度过大而导致的栈溢出(stack overflow)问题,并提高性能。

定义:

尾调用指的是一个函数在它的执行上下文中,最后一个执行的操作是调用另一个函数,并且调用返回的结果直接作为当前函数的返回值。例如:

function tailCallExample(x) {if (x === 0) return 1;return x * factorial(x - 1); // 这不是尾调用,因为结果需要计算 x * ...
}function factorial(x, acc = 1) { // 使用累加器参数实现尾调用if (x === 0) return acc;return factorial(x - 1, x * acc); // 这是尾调用
}

条件:

  • 尾位置:函数调用必须是函数体的最后一个操作。
  • 返回值直接传递:调用结果直接作为当前函数的返回值,没有其他计算。
  • 无副作用:调用后没有需要保留的上下文(如局部变量等)。

2、什么是闭包?

定义:

闭包是指那些引用了另一函数作用域中变量的函数。举例:

function test() {let result;return function(a,b){result = a + b;return result;}}let sum=test();

调用test()方法,返回匿名函数。匿名函数中引用了外层函数test中的result这个变量。因此,在test函数执行完毕后,他的执行上下文和作用域链会被销毁,但是他的活动对象并不会被销毁。这样会造成内存泄漏,除非你手动进行销毁:

sum=null

闭包中this的指向

1)箭头函数的this指向定义箭头函数的上下文对象

2)普通函数中的this指向调用函数的对象。

3)匿名函数中的this通常指向window。(非严格模式下)

this和arguments都是不等你直接在内部函数中访问的。通常需要将其引用先保存到闭包能访问到的另一个变量中:

let name = 789;let obj = {name: "123",test() {let result;console.log(this.name)let that = this;return function (a, b) {result = a + b;console.log(that.name);console.log(typeof this.name);};},};obj.test()();

这里注意let声明的name并不会挂到全局上下文的变量对象中,因此你在闭包函数中的this.name访问全局的name是空字符串,而不是789;

let name = 789;let obj = {name: "123",test() {let result;console.log(this.name)let that = this;return (a, b)=> {result = a + b;console.log(that.name);console.log(this.name);};},};obj.test()();

箭头函数中的this指向obj,因为箭头函数定义在test函数的上下文中,他的this就是箭头函数的this,test的this指向obj。因此箭头函数中的this时钟指向obj。

3、立即执行函数表达式

立即执行函数参数产生的目的是实现块级作用域效果的目的,锁住变量的值(类似let)。

举个例子:

let divs=document.querySelectorAll('div')for(var i=0;i<divs.length;i++){divs[i].onclick=(function(j){return function(){console.log(j)}})(i)}

有了let实现:

let divs = document.querySelectorAll("div");for (let i = 0; i < divs.length; i++) {divs[i].onclick = function () {console.log(i);};}

4、私有变量

因为函数拥有自己的作用域和上下文,而且在这个函数或块的外部无法访问内部的变量,你可以将其理解为私有变量。

实现私有变量有几种方式:

A)构造函数

function Person(){//私有变量和方法let name = 'haha'function getName(){return name}//特权方法this.publicMethod=function(){console.log(name);getName();}}

通过在构造函数中定义私有变量和方法,只有通过Person的实例调用特权方法才能访问到。

以上这种方式存在构造函数的缺点,特权方法在每个实例中都是独立的。

B)改进

(function(){let name='haha'function getName(){return name}Person=function(){}Person.ptototype.publicMethod=function(){console.log(name);getName();}})()

把特权方法放在函数的原型上,解决构造函数特权方法不重用的问题。

注意上面的细节:Person没有用关键字声明,会被添加到全局作用域中,因此立即执行函数表达式执行完毕后,在全局作用域中能都使用Person创建实例。

C)模块模式

let singleton=function(){let name='haha'function getName(){return name}return {publicMethod:function(){console.log(name);getName();}}}()

这种模式是在一个匿名函数内部返回一个对象并将他赋值给了外部变量singleton,因为将私有变量和方法定义在匿名函数中,而返回的对象中的方法或属性是能够访问私有变量和方法的。

D)模块增强模式

let singleton = (function () {let name = "haha";function getName() {return name;}let obj = new Person();obj.publicMethod = function () {console.log(name);getName();};return obj;})();

以上这种模式,相较于模块模式,匿名函数中返回的对象可以是指定的类型。

相关文章:

  • 嵌入式Linux驱动开发:LED实验
  • Spring Boot中自定义404异常处理问题学习笔记
  • Android学习总结之Room篇
  • 发送网络请求
  • 《无尽的尽头》今日开播 刘家祎大胆演绎林磊儿的“另一面”
  • RAG(检索增强生成)技术详解与应用实践:从原理到落地
  • 简单几步,开启 Intel VT-x 让电脑“解开CPU封印”
  • 蓝桥杯 20. 压缩变换
  • 数据分析之 商品价格分层之添加价格带
  • 欧姆龙NJ系列PLC通讯
  • vue3-springboot-mysql的docker部署
  • 怎么实现RAG检索相似文档排序:similarities
  • 云蝠智能大模型呼叫:AI驱动的通信服务革新与实践
  • 操作系统---进程同步与互斥
  • 【频谱分析仪与信号分析仪】异同比较
  • Unity后处理全解析:从入门到优化
  • 《Linux程序设计》实验8 线程程序设计
  • vulkanscenegraph显示倾斜模型(6)-帧循环
  • RTS 如何使用流控方式自动实现收发
  • 【每天一个知识点】熵(Entropy)
  • 四川公布一起影视盗版案例:1个网站2人团伙盗售30多万部
  • 央行上海总部答澎湃:上海辖内金融机构已审批通过股票回购增持贷款项目117个
  • 开发国内首个泌尿专科智能体,医生们将临床经验转变为知识图谱
  • 直播中抢镜“甲亢哥”的翁东华卸任了!此前任文和友小龙虾公司董事
  • 日方炒作中国社会治安形势不佳,外交部:政治操弄意图明显
  • 为青少年写新中国成立的故事,刘统遗著《火种》出版