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

V少JS基础班之第五弹

文章目录

    • 一、 前言
    • 二、本节涉及知识点
    • 三、重点内容
      • 1- 函数的定义
      • 2- 函数的构成
        • 1.函数参数详解
            • 1) 参数个数不固定
            • 2) 默认参数
            • 3) arguments 对象(类数组)
            • 4)剩余参数(Rest 参数)
            • 5)函数参数是按值传递的
            • 6)解构参数传递
            • 7)参数校验技巧(JavaScript 没有类型限制,需要手动校验)
        • 2.函数返回值详解
      • 3- 函数的分类
          • 1- 函数声明式:
          • 2- 函数表达式:
          • 3- 箭头函数:
          • 4- 构造函数:
          • 5- IIFE:
          • 6- 生成器函数:
          • 7- 异步函数:
          • 8- 类方法:
          • 9- 匿名函数:
      • 4- 函数的调用
          • 0- 函数的调用一般有一下几种方式
          • 1- 变量提升:
          • 2- 构造函数和函数声明式的区别:

一、 前言

第五弹内容是函数。 函数是JavaScript语言中不可或缺的一部分。它没有显得那么难,但是其重要程度无可忽视。js代码中哪哪都有它的身影, 属于那种必学的知识点。那我们就开始今天的函数学习。

来了来了他来了, 终于第五弹函数部分更新了。前段时间确实在处理一个网站没有精力去写文章,如果和市面文章一样拼凑函数的知识点也很快。但是我始终觉得,市面上的函数讲解都不太适合爬虫,要么就是简单的认识函数,要么就是太过偏向js开发。所以一直迟迟未发布第五弹(当然也有自己偷懒的成分,大家自行忽略)。本系列预计是6个月。中间出了点小插曲,专栏完成周期需要适当往后延一延,但是后续更新也会更加紧凑,为了赶上进度,会不定期的多发一弹,缩短更新时间,还望大家多多谅解。

本系列为一周一更,计划历时6个月左右。从JS最基础【变量与作用域】到【异步编程,密码学与混淆】。希望自己能坚持下来, 也希望给准备入行JS逆向的朋友一些帮助, 脸皮厚了一点,明目张胆的要点赞,评论和收藏。也是希望如果本专栏真的对大家有帮助可以点个赞,有建议或者疑惑可以在下方随时问。

先预告一下【V少JS基础班】的全部内容。看着很少,其实,正儿八经细分下来其实挺多的,第一个月的东西也一点不少。
第一个月【变量作用域BOMDOM数据类型操作符
第二个月【函数、闭包、原型链、this】
第三个月【面向对象编程、异步编程、nodejs】
第四个月【密码学、各类加密函数】
第五个月【jsdom、vm2、express】
第六个月【基本请求库、前端知识对接】

==========================================================

二、本节涉及知识点

 函数:基础、 构成、 分类、调用、this、 作用域、 特殊函数本章内容:函数的定义(函数的本质是什么)函数的构成(参数,函数体,函数返回)函数的分类(各种函数)函数的调用(函数的调用放假)

==========================================================

三、重点内容

1- 函数的定义

什么是函数: 在 JavaScript 中,函数(Function)是一种可复用的代码块,它用于执行特定任务或计算返回值。
函数可以接收输入(称为“参数”),经过处理后返回一个结果(称为“返回值”)。
口语解释: 函数就是一个固定结构的代码块。构建它以实现指定功能,它提供一个引用方式供项目使用。
最常规的函数:

function demo(){console.log('hello wold')
}

为什么要使用函数?

代码复用:可以将重复的逻辑封装成函数,避免冗余代码。
提高可读性与模块化:函数让程序结构更清晰,有利于分工协作和后期维护。
便于测试与调试:将逻辑封装为函数后,可以单独测试每个函数是否正确。
可以作为参数传递:JavaScript 是函数式语言的一部分,函数可以像数据一样被传来传去。

好!到这里我们先停一停。 我再问一嘴,js中的函数是什么

ok,这时候大家可能说是一个特殊的代码块。

是的, 但是这不是函数的本质。 在js中,函数的本质是对象。请记住这句话, 所有函数在 JavaScript 中都是对象。并且它们都是可调用的对象。
当然对象是什么后面会讲到。好,那我们现在继续

==========================================================

2- 函数的构成

在编程中,一个函数通常由以下几个部分构成:函数名(用于标识函数)、参数(用于接收输入)、函数体(包含具体的执行语句)以及返回值(用于输出结果)。

function demo(){console.log('hello wold')
}
1.函数参数详解

我们以普通函数为例, 来讲解参数

1) 参数个数不固定

JavaScript 是动态语言,传参个数不匹配不会报错:

function add(a, b) {console.log(a + b);
}add(1);        // NaN(b 是 undefined)
add(1, 2, 3);  // 3(多余的参数会被忽略)
2) 默认参数

从 ES6 开始,支持为参数设置默认值:

function greet(name = "Guest") {console.log("Hello, " + name);
}greet();        // Hello, Guest
greet("Tom");   // Hello, Tom
3) arguments 对象(类数组)

函数内部可以使用 arguments 获取所有传入的实参,不论是否定义了形参:

function showArguments() {console.log(arguments);
}showArguments(1, 2, 3); // [1, 2, 3],是一个类数组
4)剩余参数(Rest 参数)

用 … 收集多余参数为一个数组,更现代:

function sum(...numbers) {return numbers.reduce((a, b) => a + b, 0);
}console.log(sum(1, 2, 3)); // 6
5)函数参数是按值传递的

原始类型(如 Number, String)是值传递
引用类型(如 Object, Array)是对象引用的地址传递(但本质还是按值)

function change(x) {x = 10;
}
let a = 5;
change(a);
console.log(a); // 5,没变function modify(obj) {obj.name = "Changed";
}
let user = { name: "Tom" };
modify(user);
console.log(user.name); // Changed
6)解构参数传递

你可以在函数参数中直接使用解构:

function display({ name, age }) {console.log(name + " is " + age);
}display({ name: "Alice", age: 30 }); // Alice is 30
7)参数校验技巧(JavaScript 没有类型限制,需要手动校验)

1. 检查参数是否传入

function greet(name) {if (name === undefined) {throw new Error("name is required");}console.log("Hello, " + name);
}

2. 类型判断

function setAge(age) {if (typeof age !== 'number') {throw new TypeError("age must be a number");}
}

3. 使用默认值+校验结合

function register(name = "", age = 0) {if (!name) throw new Error("Name cannot be empty");
}
2.函数返回值详解

在这里我们用普通函数和构造函数做一个比较, 来观察这个返回值。 我们首先看下普通函数。

function normalFunc() {return { name: "普通函数返回值" };
}const result = normalFunc();
console.log(result); // 👉 { name: "普通函数返回值" }

说明: 普通函数中,return 语句决定了返回值。如果没有 return,则返回 undefined。

我们再来看下构造函数:

function Person() {this.name = "构造函数默认返回值";
}const p = new Person();
console.log(p); // 👉 { name: "构造函数默认返回值" }

构造函数中,默认返回的是 this 指向的对象,即新创建的实例对象。
特殊情况对比: 构造函数中主动 return

function Person() {this.name = "被忽略";return { name: "手动返回对象" };
}const p = new Person();
console.log(p); // 👉 { name: "手动返回对象" }

构造函数中,如果返回的是对象,则这个对象会 覆盖默认的 this 返回值。

function Person() {this.name = "正常 this";return 123;
}const p = new Person();
console.log(p); // 👉 { name: "正常 this" }

如果返回的是非对象类型(如数字、字符串、布尔等),则会 忽略 return,仍然返回 this。

这里大家可能会有点疑惑构造函数是什么。 没关系,我们看完函数的分类就会明白。

==========================================================

3- 函数的分类

函数声明:标准函数定义方式,支持提升,常用于基础函数调用和回调。
函数表达式:函数作为变量的值,无法提升,常用于基础函数调用和回调。
箭头函数:简洁的语法,继承 this,适合简化代码,回调处理。
构造函数:用于创建对象实例,用于对象实例化。
IIFE:立即执行的匿名函数,常用于创建私有作用域,用于私有作用域。
生成器函数:带有 yield 的函数,用于生成多个值。常用于控制流程、异步操作。
异步函数:返回 Promise,使异步代码更简洁,常用于简化异步操作。
类方法:类中的方法,作用于类实例,用于面向对象设计。
1- 函数声明式:

函数声明语法或函数声明式,这是最常见的一种定义函数的方式。

function greet() {console.log("Hello, world!");
}

特点:

函数名必须指定。
可以在函数声明之前调用(提升)。
函数声明会被提升到当前作用域的顶部。函数声明会被提升到代码的顶部,可以在定义之前调用。
2- 函数表达式:

函数表达式将函数作为一个值赋给变量。与函数声明式不同,函数表达式在被定义后才能使用。

const myFunction = function() {console.log("Hello");
};

特点:

不提升:函数表达式不会被提升,在定义之前不能调用它。
myFunction();  // 错误:myFunction is not a functionconst myFunction = function() {console.log("Hello");
};
3- 箭头函数:

箭头函数是 ES6 引入的一种更简洁的函数定义方式,它采用了不同的语法,并且与传统函数有一些关键的区别,特别是在 this 的处理上。

const myFunction = () => {console.log("Hello");
};

并且

特性普通函数箭头函数
this 绑定动态,取决于调用者静态,取决于定义时上下文
arguments有自己的 arguments没有自己的 arguments
使用 new可以作为构造函数无法作为构造函数,不能使用 new
语法较为繁琐,需要 function 关键字语法简洁,常用于回调和匿名函数
原型(prototype)有自己的 prototype没有 prototype,不能作为构造函数
yield(生成器)支持不支持
4- 构造函数:

构造函数用于创建对象实例,使用 function 声明并通过 new 关键字调用。

function Person(name) {this.name = name;
}const john = new Person("John");
console.log(john.name); // "John"

特点:

可使用 new 实例化对象。
内部的 this 指向新创建的对象。
构造函数名称通常首字母大写。
5- IIFE:

IIFE 是立即执行的函数表达式,定义后立即执行,常用于创建私有作用域。

(function() {console.log("IIFE executed");
})();

特点:

定义后立即执行。
不会污染全局命名空间。
常用于模块化或封装代码。
外层括号将其变为表达式,否则语法会被当成函数声明。
6- 生成器函数:

生成器函数可以在执行过程中多次暂停和恢复,使用 function* 定义,使用 yield 暂停。

function* counter() {yield 1;yield 2;yield 3;
}const gen = counter();
console.log(gen.next()); // { value: 1, done: false }

特点:

使用 function* 定义。
使用 yield 暂停执行,配合 .next() 控制流程。
适合用于惰性迭代、异步控制流等场景。
返回一个实现了迭代器协议的对象。
7- 异步函数:

异步函数用于处理异步操作,返回一个 Promise,使用 await 等待异步结果。

async function fetchData() {const data = await fetch("https://example.com");return data;
}

特点:

使用 async 声明。
函数总是返回一个 Promise。
使用 await 可以等待 Promise 结果,避免回调地狱。
可结合 try/catch 捕获异步错误。
8- 类方法:

类方法是定义在类中的函数,用于描述对象的行为。

class Animal {speak() {console.log("Animal speaks");}
}const dog = new Animal();
dog.speak(); // "Animal speaks"

特点:
使用 class 语法定义。

定义在类体中,方法之间不需要逗号。
类方法不能当作构造函数(不能用 new 调用)。
有 prototype 绑定行为,适合面向对象编程。
9- 匿名函数:

匿名函数,就是没有名的函数,一般出现就直接调用。

1. (function(o) {console.log(o);})('hello world');
2. ~(function(o) {console.log(o);return 10})('hello world');
3. +function(o) {console.log(o);}('hello world');
4. -function(o) {console.log(o);}('hello world');
5. !function(o) {console.log(o);}('hello world');
6. void function(o) {console.log(o);}('hello world');
7. (0, function(o) {console.log(o);})('hello world')
8. ((a,b)=>{return a+b})(1, 2)

==========================================================

4- 函数的调用

0- 函数的调用一般有一下几种方式
1- 函数名()
2- 函数名. call(thisArg, param1, param2, ...)
3- 函数名.apply(thisArg, [param1,param2,...])
4- 匿名自执行
5- 对象.方法
调用方式关键语法this 指向
普通调用fn()全局对象 or undefined(严格模式)
对象方法调用obj.fn()obj
构造函数调用new Fn()新建的实例对象
显式调用fn.call/apply/bind(...)显式传入的对象
箭头函数() => {}定义时所在作用域
自执行函数(function(){})()同普通函数调用
1- 变量提升:

大家应该对函数有了一个初步的了解了。 但是还是没有细致了解。 我们说函数声明式和函数表达式的区别在于函数的提升。 是什么意思。 首先函数声明式是这样的:

function greet() {console.log("Hello, world!");
}

什么是变量提升
我们先了解变量提升。
JavaScript在执行代码前,它会先进行一个编译阶段(预处理阶段)。在这个阶段,JS 引擎会扫描整个作用域,提前把所有的变量声明(var)、函数声明(function)提到当前作用域的顶部,这就形成了“变量提升”
变量提升发生在 JavaScript 引擎在执行代码前的编译阶段,会先分析标识符、生成抽象语法树(AST)、创建作用域,并在内存中提前分配变量声明和函数声明的空间。

所以在代码执行之前, 作用域就已经生成了【这里也是个很重要的知识点, JavaScript 中的作用域是在代码编译阶段就被创建的,而不是等到函数执行时】。
ok, 到这里大家就应该明白了, 前面说的。 函数声明式,可以变量提升。 因为在编译时 greet() 就被编译成了function,存储在了作用域中
而在函数表达式中:

const myFunction = function() {console.log("Hello");
};

只有 myFunction 这个变量被声明了。 我们无法去确定他是个函数。 所以说我们不可以在代码执行到表达式之前先进行函数调用。 【这里说明一下是。 我们可以提前调用myFunction这个变量,而且他的返回值会是】

这里说明一下是。 我们可以提前调用myFunction这个变量,
而且他的返回值会是 undefined。 但是我们不可以调用myFunciton()
因为此时的myFunction是个变量,而不是一个函数。 
所以需要在赋值操作完成之后才能调用
2- 构造函数和函数声明式的区别:

我们上面看了函数声明式和函数表达式的区别, 主要在变量的提升上。 那我们现在看一看函数表达式和构造函数的区别
函数声明式:

function greet(name) {this.name = name;console.log(this.name);
}

构造函数:

function Person(name) {this.name = name;console.log(this.name);
}

可能上述我们举例,使用函数声明式时不会携带this, 但其实函数声明式在js代码中运用的非常广泛。 类似于这种:

const person = {name: "Alice",sayName: function showName() {console.log(this.name);}
};person.sayName(); // 输出 "Alice"

那除了函数名的命名规范,构造函数和函数声明式就没有区别了吗?其实不然, 他们的区别不在函数的构造如何。而是看怎么调用。 一个函数如果是用于new一个对象, 那他就是个构造函数。 而如果是被对象.调用的方式,那他就不是一个构造函数。

听起来可能会有点绕。 我口语化表达一下就是:“构造函数”不是一个函数的身份,而是一种使用方式。函数声明式是一个普通函数,如果它用于new一个新的对象,那他就能被当成构造函数使用如果调用函数时不是用于new一个对象,那它就不能称之为构造函数。

那构造函数和普通函数的使用差别在什么地方呢。 以下是构造函数和普通函数的区别:

差异点构造函数 (new Func())普通函数 (Func() 或 obj.Func())
调用方式必须通过 new 关键字调用直接调用或作为对象的方法调用
this 指向指向新创建的对象取决于调用方式(全局对象 / 严格模式下为 undefined / 调用者对象)
返回值默认返回 this 指向的对象,除非显式返回对象返回值取决于 return 语句
原型绑定自动绑定到 构造函数.prototype 上不会自动挂载到任何原型上
是否用作构造器设计用于创建实例对象用于执行某些逻辑,不一定用于创建对象

总结:

函数这章太难写了。 写少了没有内容。 写多了又会携带很多
this,上下文,原型链的知识。 在这个边界里反复横跳。
比如: 1- 在参数里我们可以说,除了常规变量我们还能看到call、apply显示传递this参数。 2- 返回值,如果将函数作为返回值,涉及到的闭包知识点3- 还有函数中的作用域&上下文
如果深讲感觉不太好,有点头重脚轻。改了好多次,始终觉得差点意思。
还是决定把高阶函数放在闭包和this章节中讲解。函数章节就先发布,后续再持续优化吧。 
http://www.xdnf.cn/news/572185.html

相关文章:

  • ElasticSearch导读
  • 【网络安全】日志采集、监控任务守护进程详细教程(附实战案例)
  • 打卡31天
  • Python学习Day1:安装
  • 谷歌2025年I/O开发者大会热点总结
  • shell脚本总结3
  • 【LLMs篇】12:Qwen3 技术报告翻译
  • 人工智能路径:技术演进下的职业发展导航
  • 20个关于Java编程语言的常见问题
  • 从微积分到集合论(1630-1910)(历史简介)——第2章——牛顿(Newton)和莱布尼兹(Neibniz)以及莱布尼兹传统(H.J.M.Bos)
  • 2025年人工智能新应用与新技术全景解析
  • Qt+线段拖曳示例代码
  • 【UE5】环形菜单教程
  • 现代计算机图形学Games101入门笔记(十九)
  • 汽车电子电气架构诊断功能开发全流程解析
  • Linux nbd 网络块设备(2)-内核实现
  • fork 和 写时拷贝
  • NV009NV010美光闪存颗粒NV011NV012
  • 【Elasticsearch】字段别名
  • el-radio-group 与 el-dropdown 一起使用时的注意事项
  • Pytorch基础操作
  • cookie跨域共享踩的坑
  • sqli-labs第十八关——POST-UA注入
  • 使用MATLAB输出1000以内所有完美数
  • MoManipVLA-北京邮电-2025.3.17-移动操控-未完全开源
  • UML 时序图 使用案例
  • PostGIS实现栅格数据导出PNG应用实践【ST_AsPNG 】
  • 乘“4”而上,进取不止|Aloudata 的变与不变
  • 【专四 | 2022年真题】LANGUAGE USAGE逐题总结
  • dedecms织梦全局变量调用方法总结