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

JavaScript的函数

文章目录

    • 函数类型
    • arguments对象
    • 构造函数
    • 作用域
    • 闭包

函数,其实也是一种对象,每一个函数都是Function类型的实例,可以定义不同类型的属性和方法。

函数类型

函数分为函数声明、函数表达式和构造函数

  1. 函数声明,是直接使用function关键字接一个函数名,
// 函数声明式
function sum(param1, param2) {
   return param1 + param2;
}
  1. 函数表达式,类似于一个普通变量的初始化,
let sum = function(param1, param2) {
   return param1 + param2;
}
  1. Function构造函数,通过new操作符,调用Function构造函数。
const  add  = new Function('a','b','return a + b')
console.log(add(1,2));

Function构造函数用得比较少,因为Function构造函数的每一次执行,都会解析函数主体,创建一个新的函数对象,如果是一个频繁调用的函数,这样效率非常低。

另一个原因是Function创建的函数一直作为顶级函数来执行,如果我们在一个函数A中调用Function构造函数,其中的函数体不能访问函数A的局部变量,如下代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        var y = 'global';  // 全局环境定义的y值
        function constructFunction() {
            var y = 'local';  // 局部环境定义的y值
            return new Function('return y');  // 无法获取局部环境定义的值 
        }
        console.log(constructFunction()()); // 输出'global'
    </script>
</body>

</html>

arguments对象

arguments对象是所有函数都具有的一个内置局部变量,表示是函数实际接收的参数,arguments是一个类数组的结构。arguments对象只能在函数体内使用,如下代码:

(function (a, b) {
    console.log(arguments); // [Arguments] { '0': 2 }
})(2)

arguments是表示实际参数的,所以就由实际参数决定,如上代码中,定义的形参有两个,实参却只有一个,那么arguments对象的值也只有一个。

构造函数

构造函数和和普通函数的区别:

  1. 构造函数的函数名的首字母是大写;
  2. 构造函数体的this关键字,表示是实例对象,构造函数一般不会返回任何值,默认是返回this;比如:
function Do(){
    this.name = 'play'
}
const aa = new Do()

console.log(aa.name); // play
  1. 构造函数调用的时候,需要与new操作符配合使用

作用域

一个变量的定义和调用都是在一个范围内,这个范围就是作用域。
作用域分为全局作用域、函数作用域和块级作用域

全局作用域和块级作用域比较简单,这里仅仅总结一下函数作用域。

在函数内部使用var关键字声明变量的时候,函数中会存在变量提升的问题,如下代码:

(function(){
    console.log(a); // undefined
    var a =908;
})()

在变量a声明之前打印,输出结果为undefined。这段代码等同于:

(function(){
    var a ;
    console.log(a); // undefined
})()

这就是变量提升,将变量的声明提升到函数的顶部位置,但是变量的赋值并没有提升。
只有通过var关键字声明的变量才会出现提升的问题

使用函数声明方式来定义一个函数,也会出现函数提升问题,通过函数表达式声明函数不会出现函数提升问题。

add(1, 2); // 3
function add(a, c) {
    console.log(a + c)
}

闭包

闭包是一个拥有多个变量和绑定这些变量执行上下文环境的表达式,一般都是一个函数。

函数拥有外部变量的引用,在函数执行完成后,该变量没有被销毁,处于活跃状态;
当闭包作为一个函数返回的时候,它的执行上下文环境没有被销毁;

这样闭包就导致大量消耗内存,闭包使用越多,内存消耗就越大。

但是闭包也有一些有点:

  1. 结果缓存,比如有一个很耗时的函数对象,在每次调用都消耗很长时间。这时候我们可以将函数的处理结果在内存中缓存,这样在执行代码的时候,如果内存中存有结果,就直接返回,否则再执行一次函数并且更新缓存结果。比如以下代码:
var cachedBox = (function () {
    // 缓存的容器
    var cache = {};
    return {
        searchBox: function (id) {
            // 如果在内存中,则直接返回
            if (id in cache) {
                return '查找的结果为:' + cache[id];
            }
            // 经过一段很耗时的dealFn()函数处理
            var result = dealFn(id);
            // 更新缓存的结果
            cache[id] = result;
            // 返回计算的结果
            return '查找的结果为:' + result;
        }
    };
})();
// 处理很耗时的函数
function dealFn(id) {
    console.log('这是一段很耗时的操作');
    return id;
}
// 两次调用searchBox()函数
console.log(cachedBox.searchBox(1));
console.log(cachedBox.searchBox(1));  
  1. 封装,在进行代码模块化封装的时候,需要把一些具有一定特征的属性封装在一起,只需要对外暴露对应的函数就好,不用担心内部逻辑的实现。比如数据结构中的栈:
let stack=(
    function() {
        let items = [];
        return {
            push:function (element) {
                items.push(element)
            },
            pop:function () {
                return items.pop();
            },
            size:function () {
                return items.length;
            }
        }
    }
)()

stack.push(90);
stack.push(100);

console.log(stack.size());// 2

总结闭包的优点:

  1. 保护函数内变量的安全,实现封装,防止变量流入其他环境发生命名冲突
  2. 在适当的时候,可以在内存中维护变量缓存,提升执行效率

闭包缺点:

  1. 内存消耗,一般情况下函数的活动会随着执行上下文环境一起被销毁,但是闭包引用的是外部的函数活动对象,在闭包函数执行完后,这个活动对象没有销毁,导致闭包比一般的函数更消耗内存;
  2. 内存泄漏,如果闭包中的作用域链中有DOM对象,那么这个DOM对象无法销毁,造成内存泄漏。

相关文章:

  • java基于springboot+vue基本微信小程序的乒乓球课程管理系统 uniapp小程序
  • 安装数据库中间件——Mycat
  • 爬虫之Scrapy框架
  • 哈工大李治军老师操作系统笔记【23】:内存换出(Learning OS Concepts By Coding Them !)
  • Ubuntu 20.04 设置开机自启脚本
  • Vue2封装评论组件详细讲解
  • java-php-python-springboot校园新闻趣事计算机毕业设计
  • 使用Docker Compose搭建WordPress博客
  • 【Linux篇】第十一篇——动静态库(动静态库的介绍+动静态库的打包与使用)
  • 多任务学习(MTL)--学习笔记
  • 前端性能优化方法与实战01 体系总览:性能优化体系及关键指标设定
  • 小米面试——C++开发岗位
  • 【训练方法】OHEM
  • java毕业设计汽车出租平台源码+lw文档+mybatis+系统+mysql数据库+调试
  • C#教程 - 其他(Other)
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • 4. 路由到控制器 - Laravel从零开始教程
  • Angular Elements 及其运作原理
  • canvas绘制圆角头像
  • gitlab-ci配置详解(一)
  • mysql 数据库四种事务隔离级别
  • PHP变量
  • python docx文档转html页面
  • rc-form之最单纯情况
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • 阿里云购买磁盘后挂载
  • 第三十一到第三十三天:我是精明的小卖家(一)
  • 短视频宝贝=慢?阿里巴巴工程师这样秒开短视频
  • 基于webpack 的 vue 多页架构
  • 来,膜拜下android roadmap,强大的执行力
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 算法---两个栈实现一个队列
  • MiKTeX could not find the script engine ‘perl.exe‘ which is required to execute ‘latexmk‘.
  • 小白应该如何快速入门阿里云服务器,新手使用ECS的方法 ...
  • ​iOS安全加固方法及实现
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • "无招胜有招"nbsp;史上最全的互…
  • (4) PIVOT 和 UPIVOT 的使用
  • (libusb) usb口自动刷新
  • (Redis使用系列) SpringBoot 中对应2.0.x版本的Redis配置 一
  • (蓝桥杯每日一题)平方末尾及补充(常用的字符串函数功能)
  • (五)MySQL的备份及恢复
  • (原創) 人會胖會瘦,都是自我要求的結果 (日記)
  • ./configure,make,make install的作用
  • .Net CF下精确的计时器
  • .NET Reactor简单使用教程
  • .NET 的静态构造函数是否线程安全?答案是肯定的!
  • .NET 线程 Thread 进程 Process、线程池 pool、Invoke、begininvoke、异步回调
  • .NET企业级应用架构设计系列之结尾篇
  • .php文件都打不开,打不开php文件怎么办
  • @serverendpoint注解_SpringBoot 使用WebSocket打造在线聊天室(基于注解)
  • [ CTF ] WriteUp-2022年春秋杯网络安全联赛-冬季赛
  • [ IOS ] iOS-控制器View的创建和生命周期
  • [ NOI 2001 ] 食物链