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

js 深入理解函数(一):函数的本质

目录

  • 概述
  • 1. 箭头函数
  • 2. 函数名 :指向函数的指针
  • 3. 理解参数
    • 3.1 arguments 对象的作用
    • 3.2 arguments 的注意点
    • 3.3 箭头函数中的参数
  • 4. 没有重载
  • 5. 默认参数值
    • 5.1 ES 6 支持显示定义默认参数
    • 5.2 传 undefined 等于没有传值
    • 5.3 arguments 不反映参数默认值
    • 5.4 默认值可以是函数返回值
    • 5.4 箭头函数也可以使用默认参数值
    • 5.5 默认参数作用域
    • 5.6 暂时性死区

函数一共分为三篇文章分别为

  • 《js 深入理解函数(一):函数的本质》
  • 《js 深入理解函数(二):扩展操作符、函数的内部对象、属性和方法》
  • 《js 深入理解函数(三):巧妙使用》

概述

  js 中的函数实际上是对象。每个函数都是 Function类型的实例,而 Function 也有属性和方法,跟其他引用类型一样。因为函数是对象,所以函数名就是指向函数对象的指针,而且不一定与函数本身紧密绑定。函数通常以函数声明的方式定义,比如:

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

  注意函数定义最后没有加分号。
  另一种定义函数的语法是函数表达式。函数表达式与函数声明几乎是等价的:

let sum = function(num1, num2) {return num1 + num2;
};

  这里,代码定义了一个变量 sum 并将其初始化为一个函数。注意 function 关键字后面没有名称,因为不需要。这个函数可以通过变量 sum 来引用。
  注意这里的函数末尾是有分号的,与任何变量初始化语句一样。
  还有一种定义函数的方式与函数表达式很像,叫作“箭头函数”(arrow function),如下所示:

let sum = (num1, num2) => {return num1 + num2;
};

  最后一种定义函数的方式是使用 Function 构造函数。这个构造函数接收任意多个字符串参数,最后一个参数始终会被当成函数体,而之前的参数都是新函数的参数。来看下面的例子:

let sum = new Function("num1", "num2", "return num1 + num2"); // 不推荐

  我们不推荐使用这种语法来定义函数,因为这段代码会被解释两次:第一次是将它当作常规 ECMAScript 代码,第二次是解释传给构造函数的字符串。这显然会影响性能。不过,把函数想象为对象,把函数名想象为指针是很重要的。而上面这种语法很好地诠释了这些概念。

这几种实例化函数对象的方式之间存在微妙但重要的差别,本章后面会讨论。无论如何,通过其中任何一种方式都可以创建函数。

1. 箭头函数

  ECMAScript 6 新增了使用箭头( => )语法定义函数表达式的能力。很大程度上,箭头函数实例化的函数对象与正式的函数表达式创建的函数对象行为是相同的。任何可以使用函数表达式的地方,都可以使用箭头函数:

let arrowSum = (a, b) => {return a + b;
};
let functionExpressionSum = function(a, b) {return a + b;
};
console.log(arrowSum(5, 8)); // 13
console.log(functionExpressionSum(5, 8)); // 13

  箭头函数简洁的语法非常适合嵌入函数的场景:

let ints = [1, 2, 3];
console.log(ints.map(function(i) { return i + 1; })); // [2, 3, 4]
console.log(ints.map((i) => { return i + 1 })); // [2, 3, 4]

  如果只有一个参数,那也可以不用括号。只有没有参数,或者多个参数的情况下,才需要使用括号:

// 以下两种写法都有效
let double = (x) => { return 2 * x; };
let triple = x => { return 3 * x; };
// 没有参数需要括号
let getRandom = () => { return Math.random(); };
// 多个参数需要括号
let sum = (a, b) => { return a + b; };
// 无效的写法:
let multiply = a, b => { return a * b; };

  箭头函数也可以不用大括号,但这样会改变函数的行为。使用大括号就说明包含“函数体”,可以在一个函数中包含多条语句,跟常规的函数一样。如果不使用大括号,那么箭头后面就只能有一行代码,比如一个赋值操作,或者一个表达式。而且,省略大括号会隐式返回这行代码的值:

// 以下两种写法都有效,而且返回相应的值
let double = (x) => { return 2 * x; };
let triple = (x) => 3 * x;
// 可以赋值
let value = {};
let setName = (x) => x.name = "Matt";
setName(value);
console.log(value.name); // "Matt"
// 无效的写法:
let multiply = (a, b) => return a * b;

  箭头函数虽然语法简洁,但也有很多场合不适用。箭头函数不能使用 arguments 、 super 和 new.target ,也不能用作构造函数。此外,箭头函数也没有 prototype 属性。

2. 函数名 :指向函数的指针

  因为函数名就是指向函数的指针,所以它们跟其他包含对象指针的变量具有相同的行为。这意味着一个函数可以有多个名称,如下所示:

function sum(num1, num2) {return num1 + num2;
}
console.log(sum(10, 10)); // 20
let anotherSum = sum;
console.log(anotherSum(10, 10)); // 20
sum = null;	//切断了sum与函数之间的关联
console.log(anotherSum(10, 10)); // 20
console.log(sum(10, 10)); //报错, Uncaught TypeError: sum is not a function

  以上代码定义了一个名为 sum() 的函数,用于求两个数之和。然后又声明了一个变量 anotherSum ,并将它的值设置为等于 sum 。注意,使用不带括号的函数名会访问函数指针,而不会执行函数。此时,anotherSum 和 sum 都指向同一个函数。调用 anotherSum() 也可以返回结果。把 sum 设置为 null之后,就切断了它与函数之间的关联。而 anotherSum() 还是可以照常调用,没有问题。
  ECMAScript 6 的所有函数对象都会暴露一个只读的 name 属性,其中包含关于函数的信息。多数情况下,这个属性中保存的就是一个函数标识符,或者说是一个字符串化的变量名。即使函数没有名称,也会如实显示成空字符串。如果它是使用 Function 构造函数创建的,则会标识成 “anonymous” :

function foo() {}
let bar = function() {};
let baz = () => {};
console.log(foo.name); // foo
console.log(bar.name); // bar
console.log(baz.name); // baz
console.log((() => {}).name); //(空字符串)
console.log((new Function()).name); // anonymous

  如果函数是一个获取函数、设置函数,或者使用 bind() 实例化,那么标识符前面会加上一个前缀:

function foo() {}
console.log(foo.bind(null).name); // bound foo    ,这里带了一个 前缀 bound//bind() 用于将函数内的this指向目标对象(bind的第一个参数),这里将目标对象置null了
let dog = {years: 1,get age() {return this.years;},set age(newAge) {this.years = newAge;}
}
let propertyDescriptor = Object.getOwnPropertyDescriptor(dog, 'age');
console.log(propertyDescriptor.get.name); // get age  ,带了前缀 get
console.log(propertyDescriptor.set.name); // set age  ,带了前缀 set

3. 理解参数

3.1 arguments 对象的作用

  ECMAScript 函数的参数跟大多数其他语言不同。ECMAScript 函数既不关心传入的参数个数,也不关心这些参数的数据类型。定义函数时要接收两个参数,并不意味着调用时就传两个参数。你可以传一个、三个,甚至一个也不传,解释器都不会报错。
  之所以会这样,主要是因为 ECMAScript 函数的参数在内部表现为一个数组。函数被调用时总会接收一个数组,但函数并不关心这个数组中包含什么。如果数组中什么也没有,那没问题;如果数组的元素超出了要求,那也没问题。事实上,在使用 function 关键字定义(非箭头)函数时,可以在函数内部访问 arguments 对象,从中取得传进来的每个参数值。
   arguments 对象是一个类数组对象(但不是 Array 的实例),因此可以使用中括号语法访问其中的元素(第一个参数是 arguments[0] ,第二个参数是 arguments[1] )。而要确定传进来多少个参数,可以访问 arguments.length 属性。
  在下面的例子中, sayHi() 函数的第一个参数叫 name :

function sayHi(name, message) {console.log("Hello " + name + ", " + message);
}

  可以通过 arguments[0] 取得相同的参数值。因此,把函数重写成不声明参数也可以:

function sayHi() {console.log("Hello " + arguments[0] + ", " + arguments[1]);
}

  在重写后的代码中,没有命名参数。 name 和 message 参数都不见了,但函数照样可以调用。这就表明,ECMAScript 函数的参数只是为了方便才写出来的,并不是必须写出来的。与其他语言不同,在 ECMAScript 中的命名参数不会创建让之后的调用必须匹配的函数签名。这是因为根本不存在验证命名参数的机制。
  也可以通过 arguments 对象的 length 属性检查传入的参数个数。下面的例子展示了在每调用一个函数时,都会打印出传入的参数个数:

function howManyArgs() {console.log(arguments.length);
}
howManyArgs("string", 45); // 2
howManyArgs(); // 0
howManyArgs(12); // 1

  这个例子分别打印出 2、0 和 1(按顺序)。既然如此,那么开发者可以想传多少参数就传多少参数。比如:

function doAdd() {if (arguments.length === 1) {console.log(arguments[0] + 10);} else if (arguments.length === 2) {console.log(arguments[0] + arguments[1]);}
}
doAdd(10); // 20
doAdd(30, 20); // 50

  这个函数 doAdd() 在只传一个参数时会加 10,在传两个参数时会将它们相加,然后返回。因此 doAdd(10) 返回 20,而 doAdd(30,20) 返回 50。虽然不像真正的函数重载那么明确,但这已经足以弥补 ECMAScript 在这方面的缺失了。

3.2 arguments 的注意点

  还有一个必须理解的重要方面,那就是 arguments 对象可以跟命名参数一起使用,比如:

function doAdd(num1, num2) {if (arguments.length === 1) {console.log(num1 + 10);} else if (arguments.length === 2) {console.log(arguments[0] + num2);}
}

  在这个 doAdd() 函数中,同时使用了两个命名参数和 arguments 对象。命名参数 num1 保存着与arugments[0] 一样的值,因此使用谁都无所谓。(同样, num2 也保存着跟 arguments[1] 一样的值。)
  arguments 对象的另一个有意思的地方就是,它的值始终会与对应的命名参数同步。来看下面的例子:

function doAdd(num1, num2) {arguments[1] = 10;console.log(arguments[0] + num2);
}
doAdd(100,100);     //110
doAdd(100); 		//NaN ,第2个参数没有赋值,即使arguments[1]赋值了,num2也没有获取到值,因为此时num2并不存在

  这个 doAdd() 函数把第二个参数的值重写为 10。因为 arguments 对象的值会自动同步到对应的命名参数,所以修改 arguments[1] 也会修改 num2 的值,因此两者的值都是 10。但这并不意味着它们都访问同一个内存地址,它们在内存中还是分开的,只不过会保持同步而已。另外还要记住一点:如果只传了一个参数,然后把 arguments[1] 设置为某个值,那么这个值并不会反映到第二个命名参数(上例中第6行就是这种情况)。这是因为 arguments 对象的长度是根据传入的参数个数,而非定义函数时给出的命名参数个数确定的。
  对于命名参数而言,如果调用函数时没有传这个参数,那么它的值就是 undefined 。这就类似于定义了变量而没有初始化。比如,如果只给 doAdd() 传了一个参数,那么 num2 的值就是 undefined 。
  严格模式下, arguments 会有一些变化。首先,像前面那样给 arguments[1] 赋值不会再影响 num2的值。就算把 arguments[1] 设置为 10, num2 的值仍然还是传入的值。其次,在函数中尝试重写arguments 对象会导致语法错误。(代码也不会执行。)

3.3 箭头函数中的参数

  如果函数是使用箭头语法定义的,那么传给函数的参数将不能使用 arguments 关键字访问,而只能通过定义的命名参数访问。

function foo() {console.log(arguments[0]);
}
foo(5); // 5
let bar = () => {console.log(arguments[0]);
};
bar(5); // ReferenceError: arguments is not defined

  虽然箭头函数中没有 arguments 对象,但可以在包装函数中把它提供给箭头函数:

function foo() {let bar = () => {console.log(arguments[0]); // 5};bar();
}
foo(5);

注意 ECMAScript 中的所有参数都按值传递的。不可能按引用传递参数。如果把对象作为参数传递,那么传递的值就是这个对象的引用。

4. 没有重载

  ECMAScript 函数不能像传统编程那样重载。在其他语言比如 Java 中,一个函数可以有两个定义,只要签名(接收参数的类型和数量)不同就行。如前所述,ECMAScript 函数没有签名,因为参数是由包含零个或多个值的数组表示的。没有函数签名,自然也就没有重载。
  如果在 ECMAScript 中定义了两个同名函数,则后定义的会覆盖先定义的。来看下面的例子:

function addSomeNumber(num) {return num + 100;
}
function addSomeNumber(num) {return num + 200;
}
let result = addSomeNumber(100); // 300

  这里,函数 addSomeNumber() 被定义了两次。第一个版本给参数加 100,第二个版本加 200。最后一行调用这个函数时,返回了 300,因为第二个定义覆盖了第一个定义。
  前面也提到过,可以通过检查参数的类型和数量,然后分别执行不同的逻辑来模拟函数重载。
  把函数名当成指针也有助于理解为什么 ECMAScript 没有函数重载。在前面的例子中,定义两个同名的函数显然会导致后定义的重写先定义的。而那个例子几乎跟下面这个是一样的:

let addSomeNumber = function(num) {return num + 100;
};
addSomeNumber = function(num) {return num + 200;
};
let result = addSomeNumber(100); // 300

  看这段代码应该更容易理解发生了什么。在创建第二个函数时,变量 addSomeNumber 被重写成保存第二个函数对象了。

5. 默认参数值

5.1 ES 6 支持显示定义默认参数

  在 ECMAScript 5.1 及以前,实现默认参数的一种常用方式就是检测某个参数是否等于 undefined ,如果是则意味着没有传这个参数,那就给它赋一个值:

function makeKing(name) {name = (typeof name !== 'undefined') ? name : 'Henry';  // 没有赋值,则给一个默认值return `King ${name} VIII`;
}
console.log(makeKing()); // 'King Henry VIII'
console.log(makeKing('Louis')); // 'King Louis VIII'

  ECMAScript 6 之后就不用这么麻烦了,因为它支持显式定义默认参数了。下面就是与前面代码等价的 ES6 写法,只要在函数定义中的参数后面用 = 就可以为参数赋一个默认值:

function makeKing(name = 'Henry') {return `King ${name} VIII`;
}
console.log(makeKing('Louis')); // 'King Louis VIII'
console.log(makeKing()); // 'King Henry VIII'

5.2 传 undefined 等于没有传值

  给参数传 undefined 相当于没有传值,不过这样可以利用多个独立的默认值:

function makeKing(name = 'Henry', numerals = 'VIII') {return `King ${name} ${numerals}`;
}
console.log(makeKing()); // 'King Henry VIII'
console.log(makeKing('Louis')); // 'King Louis VIII'
console.log(makeKing(undefined, 'VI')); // 'King Henry VI'

5.3 arguments 不反映参数默认值

  在使用默认参数时, arguments 对象的值不反映参数的默认值,只反映传给函数的参数。当然,跟 ES5 严格模式一样,修改命名参数也不会影响 arguments 对象,它始终以调用函数时传入的值为准:

function makeKing(name = 'Henry') {name = 'Louis';return `King ${arguments[0]}`;
}
console.log(makeKing()); // 'King undefined'
console.log(makeKing('Louis')); // 'King Louis'

5.4 默认值可以是函数返回值

  默认参数值并不限于原始值或对象类型,也可以使用调用函数返回的值:

let romanNumerals = ['I', 'II', 'III', 'IV', 'V', 'VI'];
let ordinality = 0;
function getNumerals() {// 每次调用后递增return romanNumerals[ordinality++];
}
function makeKing(name = 'Henry', numerals = getNumerals()) {return `King ${name} ${numerals}`;
}
console.log(makeKing()); // 'King Henry I'
console.log(makeKing('Louis', 'XVI')); // 'King Louis XVI'
console.log(makeKing()); // 'King Henry II'
console.log(makeKing()); // 'King Henry III'

  函数的默认参数只有在函数被调用时才会求值,不会在函数定义时求值。而且,计算默认值的函数只有在调用函数但未传相应参数时才会被调用。

5.4 箭头函数也可以使用默认参数值

  箭头函数同样也可以这样使用默认参数,只不过在只有一个参数时,就必须使用括号而不能省略了:

let makeKing = (name = 'Henry') => `King ${name}`;
console.log(makeKing()); // King Henry

5.5 默认参数作用域

  因为在求值默认参数时可以定义对象,也可以动态调用函数,所以函数参数肯定是在某个作用域中求值的。
  给多个参数定义默认值实际上跟使用 let 关键字顺序声明变量一样。来看下面的例子:

function makeKing(name = 'Henry', numerals = 'VIII') {return `King ${name} ${numerals}`;
}
console.log(makeKing()); // King Henry VIII

  这里的默认参数会按照定义它们的顺序依次被初始化。可以依照如下示例想象一下这个过程:

function makeKing() {let name = 'Henry';let numerals = 'VIII';return `King ${name} ${numerals}`;
}

5.6 暂时性死区

  因为参数是按顺序初始化的,所以后定义默认值的参数可以引用先定义的参数。看下面这个例子:

function makeKing(name = 'Henry', numerals = name) {return `King ${name} ${numerals}`;
}
console.log(makeKing()); // King Henry Henry

  参数初始化顺序遵循“暂时性死区”规则,即前面定义的参数不能引用后面定义的。像这样就会抛出错误:

// 调用时不传第一个参数会报错
function makeKing(name = numerals, numerals = 'VIII') {return `King ${name} ${numerals}`;
}

  参数也存在于自己的作用域中,它们不能引用函数体的作用域:

// 调用时不传第二个参数会报错
function makeKing(name = 'Henry', numerals = defaultNumeral) {let defaultNumeral = 'VIII';return `King ${name} ${numerals}`;
}

相关文章:

js 深入理解函数(一):函数的本质

目录 概述1. 箭头函数2. 函数名 :指向函数的指针3. 理解参数3.1 arguments 对象的作用3.2 arguments 的注意点3.3 箭头函数中的参数 4. 没有重载5. 默认参数值5.1 ES 6 支持显示定义默认参数5.2 传 undefined 等于没有传值5.3 arguments 不反映参数默认值5.4 默认值…...

MySql表结构设计

创建 create table 表名(字段1 字段类型 [约束] [comment 字段1注释],...) [comment 表注释];约束是作用于表中字段上的规则,用于限制存储在表中的数据。它的目的是保证数据库中数据的正确性、有效性和完整性。 约束描述关键字非空约束限制该字段不能为nullnot nu…...

java:pdfbox 3.0 去除扫描版PDF中文本水印

官网下载 https://pdfbox.apache.org/download.html下载 pdfbox-app-3.0.3.jar cd D:\pdfbox 运行 java -jar pdfbox-app-3.0.3.jar java -jar pdfbox-app-3.0.3.jar Usage: pdfbox [COMMAND] [OPTIONS] Commands:debug Analyzes and inspects the internal structu…...

python知识点100篇系列(17)-替换requests的python库httpx

Requests 是使用 Apache2 Licensed 许可证的 基于Python开发的HTTP 库,其在Python内置模块的基础上进行了高度的封装,使用Requests可以轻而易举的完成浏览器可有的任何操作。 但是在python3.6之后,出现了一个requests的替代选项; httpx httpx是Python新一代的网络请求库…...

python 实现graph list图列算法

graph list图列算法介绍 图列(Graph List)算法通常指的是在图的表示中,使用列表(List)或更具体地说,邻接表(Adjacency List)来表示图的一种算法。邻接表是图的一种常见表示方法&…...

LFU算法 初始频率 动态频率

LFU(Least Frequently Used)算法是一种缓存淘汰策略,其核心思想是根据数据的访问频率来决定淘汰哪些数据。具体来说,     LFU算法认为如果一个数据在过去一段时间内被访问的次数很少,那么它在未来被再次访问的概率也…...

Spring Boot 进阶-详解SpringBoot的复杂数据校验规则

在之前的文章中,我们介绍了SpringBoot整合JSR-303规则来完成数据校验操作。接下来我们来聊一聊关于数据校验的具体用法。 之前的文章中举过一个简单的例子通过学生信息提交的例子来介绍了关于数据校验如何去做。那么接下来这篇文章,我们就来看看对于一些复杂的数据校验如何完…...

wsl环境下安装Ubuntu,并下载MySQL5.7

安装操作需root权限,切换root用户有两种方式: 1-通过 sudo su - ,切换到root用户(登录后长期有效)。 2-在每一个命令前加上sudo,临时提升权限(仅对一条命令有效)。 1、下载apt仓库…...

倪师学习笔记-天纪-01

一、概要 介绍课程内容,介绍部分概念 二、具体内容 1、天纪内容 天机道:看象,使用斗数等工具人间道:看卦,使用易经地脉道:看风水地理 2、神 神与形对应,形是神的实例,神是形的…...

深入理解缓存穿透、缓存击穿和缓存雪崩

在现代分布式系统中,缓存是提升系统性能和减轻数据库负载的重要组件。然而,在实际应用中,我们可能会遇到一些缓存问题,如缓存穿透、缓存击穿和缓存雪崩。本文将详细探讨这三种缓存问题的原理、影响以及解决方案。 一,…...

【玩转动态规划专题】70. 爬楼梯【简单】

【玩转动态规划专题】70. 爬楼梯【简单】 1、力扣链接 https://leetcode.cn/problems/climbing-stairs/description/ 2、题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 示例 1&…...

前端开发设计模式——组合模式

目录 一、组合模式的定义和特点 1.定义 2.特点: 二、组合模式的实现方式 1.定义抽象组件类 2.创建叶节点类 3.创建组合类: 三、组合模式的应用场景 1.界面布局管理 2.菜单系统构建 3.组件库开发 四、组合模式的优点 1.简化客户端代码 2.增…...

初探OceanBase 4.x单机环境下如何进行主备架构搭建

本文来自OceanBase 用户的体验分享 (以下简称 OB),已经开源了3年左右,其间从3.x版本演进至4.x版本,发生了许多变化。对一个DBer而言,最为关切的是如何高效运用OB,以及是否能实现如同应用MySQL般…...

python 实现Edmonds-Karp算法

Edmonds-Karp算法介绍 Edmonds-Karp算法是一种用于解决最大流问题的算法,在计算机科学中广泛应用。以下是关于Edmonds-Karp算法的详细解释: 算法概述 Edmonds-Karp算法是基于Ford-Fulkerson方法的改进,它通过广度优先搜索(BFS&…...

【牛客刷题实战】BC120 争夺前五名

大家好,我是小卡皮巴拉 文章目录 目录 牛客题目: BC120 争夺前五名 题目描述 输入描述: 输出描述: 示例1 示例2 解题思路: 具体思路: 题目要点: 完整代码: 兄弟们共…...

WMS 智慧仓储管理系统的可视化管理_SunWMS

【大家好,我是唐Sun,唐Sun的唐,唐Sun的Sun。一站式数智工厂解决方案服务商】 WMS 智慧仓储管理系统的可视化管理主要表现在以下几个方面: 首先是库存可视化。通过系统,仓库管理人员能够以直观的图表、图形等形式清晰地…...

动态代理代码示例

理解动态代理 动态代理的核心在于代理对象的创建和方法调用是在运行时动态发生的,而不是在编译时就已经确定的性能监控、事务管理、日志记录通常需要使用代理对象对目标对象的功能进行增强为什么JDK动态代理只能代理有接口的类? 因为Proxy.newProxyIns…...

SpringBoot+Activiti7工作流使用进阶实例-高亮显示BPMN流程图( SpringBoot+Activiti+mybatis+shiro实现)

文章目录 说明绘制流程图排他网关设置任务节点设置创建工程修改 pom.xml 文件准备数据库的表和测试数据修改 application.yml 文件配置静态资源Shiro 相关配置ShiroConfiguration.javaMyShiroRealm.java流程控制器添加静态的资源和模板页面运行结果截图源码地址说明 使用 Spri…...

C#使用Lazy<T>提高性能

以下是一些适合使用Lazy<T>的场景&#xff1a; 单例模式 在实现单例模式时&#xff0c;Lazy<T>是非常有用的。如前面提到的示例&#xff0c;它可以确保单例对象在首次被访问时才进行创建&#xff0c;同时在多线程环境下也能保证正确的行为。这种方式比传统的双重检…...

创建读取比特币1P类型地址

创建读取比特币1P类型地址 比特币的地址类型有多种&#xff0c;其中 P2TR&#xff08;Pay-to-Taproot&#xff09;地址是基于最近的升级&#xff08;Taproot&#xff09;引入的一个新类型。本文将介绍如何创建和读取比特币的 1P 类型地址&#xff0c;主要通过 JavaScript 和相…...

从零开始Hadoop集群环境搭建

目录 1. Centos7.5硬件配置1.1 创建虚拟机1.2 虚拟机系统设置 2. IP地址和主机名称配置3. 软件配置3.1 安装 epel-release3.2 卸载虚拟机自带的JDK3.3 克隆虚拟机3.4 修改克隆虚拟机的IP3.5 JDK安装3.6 Hadoop安装 4. Hadoop目录结构 1. Centos7.5硬件配置 1.1 创建虚拟机 1.2…...

Copley耐环境伺服驱动器 极端环境下高精度控制解决方案

全球工业环境的日益复杂多变&#xff0c;对伺服驱动器的要求不再局限于基本的性能参数&#xff0c;而是在极端环境下的稳定性与可靠性。Copley耐环境伺服驱动器以卓越的性能和出色的环境适应性&#xff0c;为工业自动化领域的高精度控制提供了可靠的解决方案。 一、多样化的产…...

前端的全栈混合之路Meteor篇:分布式数据协议DDP深度剖析

本文属于进阶篇&#xff0c;并不是太适合新人阅读&#xff0c;但纯粹的学习还是可以的&#xff0c;因为后续会实现很多个ddp的版本用于web端、nodejs端、安卓端和ios端&#xff0c;提前预习和复习下。ddp协议是一个C/S架构的协议&#xff0c;但是客户端也同时可以是服务端。 什…...

基于Zynq SDIO WiFi移植一(支持2.4/5G)

基于SDIO接口的WIFI&#xff0c;在应用上&#xff0c;功耗低于USB接口&#xff0c;且无须USB Device支持&#xff0c;满足某些应用场景 1 硬件连接 2 Vivado工程配置 3 驱动编译 3.1 KERNRL CONFIG (build ENV) 修改 export KERNELPATH<path of kernel header>export T…...

数据结构与算法篇(刷题篇 - 链表)

目录 1. 反转链表&#xff08;简单&#xff09; 1.1. 题目描述 1.2. 解题思路 方法一&#xff1a;迭代&#xff08;推荐使用&#xff09; 方法二&#xff1a;递归&#xff08;扩展思路&#xff09; 方法三&#xff1a;使用栈解决 方法四&#xff1a;双链表求解 2. 链表内…...

TinyAgent: 从零开始构建最小化Agent系统

引言 随着大模型&#xff08;LLM&#xff09;的崛起&#xff0c;特别是ChatGPT等大模型的广泛应用&#xff0c;基于LLM的系统越来越受欢迎。然而&#xff0c;尽管大模型具备强大的生成能力和推理能力&#xff0c;它们在处理某些专有领域或实时问题时仍然存在局限性。因此&#…...

Android Studio New里面没有New Flutter Project

跟着Flutter中文网的配置教程&#xff0c;安装好了flutter,在Android studio里面也安装了dart和flutter的插件。重启后还是在FIle->New里面没有显示New Flutter Project。 反复卸载重装dart和flutter插件好几次&#xff0c;依然没有效果。 原来是没有把Android APK Suppor…...

linux信号 | 学习信号四步走 | 透析信号是如何被处理的?

前言&#xff1a;本节内容讲述linux信号的捕捉。 我们通过前面的学习&#xff0c; 已经学习了信号的概念&#xff0c; 信号的产生&#xff0c; 信号的保存。 只剩下信号的处理。 而信号的处理我们应该着重注意的是第三种处理方式——信号的捕捉。 也就是说&#xff0c; 这篇文章…...

mysql语句执行过程

具体流程如下: 1】当客户端的SOL发送到MySQL时&#xff0c;首先是到达服务器层的连接器&#xff0c;连接器会对你此次发起的连接进行权限校验&#xff0c;以此来获取你这个账号拥有的权限。当你的账号或密码不正确时&#xff0c;会报用户错误。连接成功如果后续没有任何操作&am…...

最新版本SkyWalking【10.1.0】部署

这里写目录标题 前言前置条件启动Skywalking下载解压启动说明 集成Skywalking Agent下载Agent在IDEA中添加agent启动应用并访问SpringBoot接口 说明 前言 基于当前最新版10.1.0搭建skywalking 前置条件 装有JDK11版本的环境了解SpringBoot相关知识 启动Skywalking 下载 地…...