【JavaScript】原型链/作用域/this指针/闭包

1.原型链

参考资料:Annotated ES5

ECMAScript起初并不支持如C++、Smalltalk 或 Java 中“类”的形式创建对象,而是通过字面量表示法或者构造函数创建对象。每个构造函数都是一个具有名为“prototype”的属性的函数,该属性用于实现基于原型的继承和共享属性。通过在new表达式中使用构造函数来创建对象;例如,new Date(2009,11) 创建一个新的 Date 对象。

继承:指的是将特征从父级传递给子级,以便新的代码段可以重用并基于现有代码的特性进行构建。

原型对象:由构造函数创建的每个对象都对其构造函数的“prototype”属性的值有一个隐式引用(称为该对象的原型对象),即在下列代码中,称“Animal.prototype”为“animal的原型对象”,“Animal.prototype”本身也是个对象。

/*构造函数*/
function Animal(name,weight){this.name = namethis.weight = weight
}
/*在new表达式中使用构造函数创建的对象*/
const animal = new Animal('米粒',10)console.log('animal的原型:',Animal.prototype)

原型链:原型对象本身也有自己的原型,以此类推,直到到达一个原型为 null 的对象(null 没有原型而作为引用链的终点)。我们称这一条完整的隐式引用链为原型链(prototype chain)。原型链的作用是为了实现继承和共享属性。

__proto__:对象的内置属性(非标准的属性,但已经被绝大部分Javascript引擎实现,对应于ECMAScript中的[[prototype]],ECMAScript标准的获取对象原型的API是Object.getPrototypeOf() ),用于指向该对象的原型对象prototype,即:

/*构造函数*/
function Animal(name,weight){this.name = namethis.weight = weight
}
/*在new表达式中使用构造函数创建的对象*/
const animal = new Animal('米粒',10)console.log('animal的原型:',Animal.prototype)
console.log('animal的原型:',animal.__proto__)
console.log('animal.__proto__===Animal.prototype:',animal.__proto__===Animal.prototype)
/*通过Object.getPrototypeOf()规范地获取animal的原型*/
console.log('animal的原型:',Object.getPrototypeOf(animal))

constructor: 原型对象prototype上都有个预定义的constructor属性,用来引用它的函数对象。这是种循环引用的目的是允许人们从任何实例访问原始构造函数。

/*构造函数*/
function Animal(name,weight){this.name = namethis.weight = weight
}
/*在new表达式中使用构造函数创建的对象*/
const animal = new Animal('米粒',10)
// Animal.prototype.constructor === Animal: true
console.log('Animal.prototype.constructor === Animal:',Animal.prototype.constructor === Animal)

注意要点:
(1)构造函数是一个函数对象,所以其原型是Function.prototype。
(2)原型对象是一个对象,所以其原型是Object.prototype。
(3)Object.prototype.__proto__ 指向 null,null是原型链的终点。
(4)尝试访问对象的属性时,不仅要在对象上查找该属性,还要在对象的原型prototype、原型的原型等上查找该属性,直到找到具有匹配名称的属性或到达原型链的末尾。

完整原型链示例图:(其中Animal、Object、Function是构造函数)

2.作用域

参考文档:ECMA-262-5 in detail. Chapter 3.1. Lexical environments: Common Theory. – Dmitry Soshnikov

作用域(Scope):一个封闭的上下文,用于管理程序不同部分的变量的可见性和可访问性。我们也可以说,作用域是一个逻辑边界,其中的变量(或表达式)有其独特的含义。例如,全局变量、局部变量等,通常反映变量生存期(或范围)的逻辑范围。

作用域属性:嵌套、变量解析方式(静态作用域和动态作用域)。

作用域链:由于作用域可以逐层嵌套,所以各层作用域组成了一个逻辑链,我们称其为作用域链。当访问某个变量时,js引擎会从当前作用域开始在包裹当前作用域的父级作用域中查找,若未找到,则再向上查找父级作用域,以此类推。从执行上下文角度看,作用域链是由环境记录(EnvironmentRecord)中的外部环境引用属性(OuterEnv)串联而成。

块级作用域: ES6 之前, ECMAScript 不支持块级作用域:

var x = 10;if (true) {var x = 20;console.log(x); // 20
}console.log(x); // 20

可以通过立即调用函数表达式IIFE实现:

var x = 10;if (true) {(function (x) {console.log(x); // 20})(20);
}console.log(x); // 10

通过函数作用域隔离变量,实现了个性化块级作用域效果,这也是ES6之前js模块化开发的依据。直到ES6 中标准化了 let 关键字,创建块范围的变量就变得方便多了:

let x = 10;if (true) {let x = 20;console.log(x); // 20
}console.log(x); // 10

静态作用域:亦称词法作用域。在静态作用域中,标识符指向其最近的词法环境,变量的作用域在代码编写时就确定的,并且在程序执行期间不会改变。具有以下特点:

  1. 定义位置决定作用域:变量的可访问范围由其定义的位置决定。
  2. 函数内部的变量:只能在该函数内部访问和使用。
  3. 块级作用域:可能存在块级别的作用域,限制变量的可见性。
  4. 稳定性:在运行时作用域不会改变。

例如,下列例子中,变量 x 在全局作用域中进行了词法定义——这意味着在运行时它也在全局作用域中进行解析,即解析为10。而对于 y 这个变量,我们定义了两次。但正如前面所说,总是考虑包含该变量的最近的词法作用域。自身作用域具有最高优先级并被首先考虑。因此,在 bar 函数的情况下,y 变量被解析为 30。bar 函数的本地变量 y 被称为遮蔽了全局作用域中同名的变量 y。然而,在 foo 函数的情况下,同名的 y 被解析为 20——即使它是在包含另一个 y 的 bar 函数内部被调用的。也就是说,标识符的解析独立于调用者的环境(在这种情况下,bar 是 foo 的调用者,而 foo 是被调用者)。同样,这是因为在定义 foo 函数时,具有 y 名称的最近的词法上下文——是全局上下文。

var x = 10;
var y = 20;function foo() {console.log(x, y);
}foo(); // 10, 20function bar() {var y = 30;console.log(x, y); // 10, 30foo(); // 10, 20
}bar();

动态作用域:在程序运行时才能确定变量的作用域。在该作用域中,变量不是在词法环境中解析,而是在动态形成的全局变量堆栈环境中解析。每次遇到变量声明,只是将变量的名称放在堆栈上。变量的作用域(生命周期)结束后,变量将从堆栈中移除(弹出)。这意味着,对于单个函数,我们可能有相同变量名的无限解析方式——这取决于调用该函数的上下文。

Javascript使用的是静态作用域。虽然ECMAScript中的with指令和eval指令具有动态效果,但不像标准动态作用域定义中那样涉及全局变量堆栈,这两个指令对静态作用域起到的效果可以称之为:“Runtime scope augmentation”(运行时作用域增强)

可执行代码:在ECMAScript 中有三种可执行代码:
(1)全局代码global code:是被视为 ECMAScript 程序的源文本,在全局作用域中执行的代码。
(2)Eval 代码eval code:通过 eval 函数动态执行的字符串形式的代码。提供给内置 eval 函数的源文本。更准确地说,如果内置 eval 函数的参数是 String,则将其视为 ECMAScript 程序。特定调用的 eval 代码 eval 是该程序的全局代码部分。
(3)函数代码function code:作为 FunctionBody 的一部分进行分析的源文本,包含在函数定义内部的代码。

全局代码在程序的任何地方都可以访问和执行。函数体代码只有在函数被调用时才会执行。eval 函数可以动态地解析和执行字符串中的代码,但由于其安全性和性能方面的考虑,在实际开发中应谨慎使用。

每种可执行代码在执行前,会经js引擎编译并创建对应的执行上下文。全局代码在全局上下文中执行,函数代码在函数上下文中执行,Eval代码在Eval上下文中执行。每一个js文件只有一个全局上下文。

执行上下文:包含跟踪其关联代码的执行进度所需的任何状态。每个执行上下文均包含三个状态组件:
(1)词法环境(LexicalEnvironment):用于实现作用域。包含两个组成部分:

  • 环境记录(EnvironmentRecord):将变量名映射到变量值(类似于Map)。作用域变量的实际存储空间。记录中的「名称-值」条目称为绑定。
  • 对外部环境的引用(OuterEnv):当前可以访问的外部词法环境,是一个抽象类,有3个具体的子类:
    A.声明式环境记录:又派生出函数环境记录和module环境记录。
    B.对象环境记录;
    C.全局环境记录。

    因此,可以认为存在三种作用域:
    A.声明式作用域:可以通过 var/const/let/class/module/import/function生成。ES6块级作用域是函数作用域的子级。
    B.对象作用域:使用with关键字指定代码块的作用域范围:
    function test(){let ob = { name:'javascript' };// 利用with扩展了作用域with(ob){console.log(`Hello ${name}`)}
    }test(); // Hello javascript

    C.全局作用域:最外层的作用域。Outer Env为null。通过声明式环境记录(使用内部对象存储变量)和对象环境记录(将变量存储在全局对象中)来管理变量。

(2)变量环境(VariableEnvironment):本质是一个对象,记录此执行上下文中由 VariableStatements 和 FunctionDeclarations 定义的变量和函数(即var和function声明的标识符)。
(3)this绑定(ThisBinding):与此执行上下文关联的 ECMAScript 代码中 this 的关键字关联的值。

ES6中执行到代码块时,如果代码块中有 let 或者 const 声明的变量,针对变量的查询路径为: 1. 词法环境 2. 变量环境 3. OuterEnv对象(上一层作用域继续先1后2)

工作过程:当控制转移到 ECMAScript 可执行代码时,控制正在进入执行上下文。活动执行上下文在逻辑上形成一个堆栈。此逻辑堆栈上的顶级执行上下文是正在运行的执行上下文。每当将控制从与当前正在运行的执行上下文关联的可执行代码传输到与该执行上下文无关的可执行代码时,都会创建一个新的执行上下文。新创建的执行上下文被推送到堆栈上,并成为正在运行的执行上下文。

作用域和执行上下文的关系:作用域是执行上下文有权访问的一组有限的变量或对象,同一个执行上下文上可能存在多个作用域,每个执行上下文均有自己的作用域。

执行上下文堆栈(ECS:Execution context Stack)ECS:ECMAScript 用来管理函数执行的调用堆栈模型称为执行上下文堆栈。工作过程如下:

  • 首先创建全局执行上下文, 压入栈底
  • 每当调用一个函数时,创建函数的函数执行上下文。并且压入栈顶
  • 当函数执行完成后,会从执行上下文栈中弹出,js引擎继续执栈顶的函数。

变量提升:js代码在执行前还有一个编译的过程,在编译过程中,var变量和function函数部分会被js引擎放入到对应上下文的变量环境中,并且变量会被默认设置为undefined。在执行阶段,js引擎才会在变量环境中查找到声明的变量和函数。在程序员角度看来,就出现了变量在声明前就可以访问,并且函数在定义之前便可调用的现象。我们称该现象为变量提升。
需要注意的是,var变量只有创建和初始化被提升,赋值并没有被提升;而function的创建、初始化和赋值均会被提升。所以在变量的声明之前访问,该变量的值是undefined,而函数则可以在声明之前正常调用。

暂时性死区:ES6中引入了let、const关键字,解决了变量提升问题,使得js支持块级作用域。严格意义上,letconst没有解决变量提升问题,当js代码被编译时,letconst变量代码会被存放在词法环境中。此时letconst变量已经被提升到了作用域顶部,但是只是声明被提升,初始化和赋值并没有被提升,如果在赋值代码行之前去读写该变量,便会报错。我们将这种从作用域开始到赋值变量代码之前暂时无法读写对应变量的区域称为“暂时性死区”

头等函数( first-class functions ):可以以变量使用的函数,如:将函数作为另一个函数的参数,将函数赋值给变量,将函数作为另一个函数的返回值。Javascript就是具有头等函数的语言。

高阶函数:满足两个条件之一的函数:接受函数作为参数传递的函数;返回一个另一个函数的函数。

回调函数:被当做参数传递给另一个函数的函数。

3.this指针

        this是一个的关键字,用于指向一段代码(如函数的主体)应该运行的上下文。JavaScript 中“this”的值取决于函数是如何被调用的(即运行时绑定),而不是它是如何被定义的。
        当一个普通函数作为对象的方法被调用(obj.method())时,“this”指向该对象。
        当作为一个独立的函数被调用(没有附着在任何一个对象上:func())时,“this”通常指的是全局对象(在非严格模式下)或undefined(在严格模式下)。Function.prototype.bind()方法可以创建一个“this”绑定不会改变的函数,并且方法 apply()和 call()也可以为特定的调用设置“this”值。
        箭头函数在处理 this 时有所不同:它们在定义时从父作用域继承 this。这种行为使箭头函数对于回调和保留上下文特别有用。然而,箭头函数没有自己的 this 绑定。因此,它们的 this 值不能通过 bind()、apply() 或 call() 方法进行设置。

在非严格模式下, this 始终指向一个对象。在严格模式下,可以指向任何值。this的取值由其出现的上下文环境决定,具体情况如下:

(1)函数上下文

在函数内部,this 的值取决于函数如何被调用。可以将 this 看作是函数的一个隐藏参数(就像函数定义中声明的普通参数一样),this 是在函数体被执行时创建的绑定。

对于典型的函数,this 的值是函数被访问的对象。换句话说,如果函数调用的形式是 obj.f(),那么 this 就指向 obj

严格模式下:
如果函数在没有被任何东西访问的情况下被调用,this 将是 undefined。

非严格模式下:
如果一个函数被调用时 this 被设置为 undefined 或 null,则this 会被替换为globalThis;
如果函数被调用时 this 被设置为一个原始值,this 会被替换为原始值的包装对象。

function bar() {console.log(Object.prototype.toString.call(this));
}bar.call(7); // [object Number]
bar.call("foo"); // [object String]
bar.call(undefined); // [object Window]

(2)全局上下文

在脚本的顶层,无论是否在严格模式下,this 会指向globalThis。如果源代码放在 HTML 的<script>元素内并作为脚本执行,则this === window

如果源代码作为模块加载(对于 HTML,这意味着在 <script> 标签中添加 type="module"),在顶层,this 总是 undefined

如果源代码使用eval()执行,this 与直接调用eval() 的闭合上下文相同,或者与间接调用 eval 的 globalThis(就像它在单独的全局脚本中运行一样)相同。

bind():

调用f.bind(someObj)会创建一个新函数,这个新函数具有与 f 相同的函数体和作用域,但 this 的值永久绑定(一旦绑定,后续再次绑定到其他对象的操作将会被忽略,绑定不生效但入参生效)到 bind 的第一个参数,无论函数如何被调用。调用绑定函数通常会执行其所包装的函数,也称为目标函数(target function)。绑定函数将绑定时传入的参数(包括 this 的值和前几个参数)提前存储为其内部状态,而不是等到实际调用时才传入。

"use strict"; // 防止 `this` 被封装到到包装对象中function log(...args) {console.log(this, ...args);
}
const boundLog = log.bind("this value", 1, 2);
const boundLog2 = boundLog.bind("new this value", 3, 4);
boundLog2(5, 6); // "this value", 1, 2, 3, 4, 5, 6

用法:其中arg1,arg2......可选,

bind(thisArg)
bind(thisArg, arg1)
bind(thisArg, arg1, arg2)
bind(thisArg, arg1, arg2, /* …, */ argN)

手写实现

Function.prototype.myBind = function () {const _this= thisconst args = Array.prototype.slice.call(arguments)const newThis = args.shift()return function () {return _this.apply(newThis, args)}
}

apply():

        通常情况下,在调用函数时,函数内部的this的值是访问该函数的对象。使用 apply(),你可以在调用现有函数时将任意值分配给 this,而无需先将函数作为属性附加到对象上。
        可以使用任何类数组对象作为第二个参数。实际上,这意味着它需要具有 length 属性,并且整数(“索引”)属性的范围在 (0..length - 1) 之间。像 { 'length': 2, '0': 'eat', '1': 'bananas' }这样的对象或['a','b']这样的数组甚至直接使用arguments,当然还可以使用剩余参数或者展开运算符。
        一般而言,fn.apply(null, args) 等同于使用参数展开语法的 fn(...args),只是在前者的情况下,args 期望是类数组对象,而在后者的情况下,args 期望是可迭代对象。

用法如下:

apply(thisArg)
apply(thisArg, argsArray)

手写实现

Function.prototype.myApply = function (context, args) {context = context || windowargs = [...args]const fn = Symbol()context[fn] = thisconst result = context[fn](...args)delete context[fn]return result
}

call():

功能与apply一致,只不过传参不同,函数的参数以列表的形式逐个传递给 call()的。

function greet() {console.log(this.animal, "的睡眠时间一般在", this.sleepDuration, "之间");
}const obj = {animal: "猫",sleepDuration: "12 到 16 小时",
};greet.call(obj); // 猫 的睡眠时间一般在 12 到 16 小时 之间

用法:

call(thisArg)
call(thisArg, arg1)
call(thisArg, arg1, arg2)
call(thisArg, arg1, arg2, /* …, */ argN)

 如果省略第一个 thisArg 参数,则默认为 undefined。在非严格模式下,this 值将被替换为全局对象globalThis。

 手写实现

Function.prototype.myCall = function (context) {context = context || windowconst arg = [...arguments].slice(1)const fn = Symbol()context[fn] = thisconst result = context[fn](...arg)delete context[fn]return result
}

bind()、apply()、bind()三者的异同

(1)相同点 
        bind、call、apply都能为函数内部的this分配指定值。 第一个参数均为this的目标值。均可以利用后续参数向目标函数传参。
(2)不同点
        call和bind传参相同,均是以此传入多个参数。apply只有两个参数,第二个参数为数组或类数组。call和apply均是直接调用目标函数,而bind方法不会立即调用函数,而是返回一个修改this后的新函数。
(3)使用场景 
        call函数的使用多用于类的继承。
        apply函数可配合Math.max()用于计算数组最大值等。
        bind函数可用于函数内部有定时器,改变定时器内部的this指向。

4.闭包

概念:在 JS 中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量。当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了。但是内部函数引用外部函数的变量依然保存在内存中,就把这些变量的集合称为闭包。简单而言,闭包是一个函数,其有权访问其词法作用域内部的变量即使该函数在词法作用域外部被调用。

柯里化

在JavaScript中,函数柯里化是一种将多个参数的函数转变为一系列接受单一参数的函数的过程。这种转变过程可通过闭包和递归的方式实现。利用闭包,可以形成一个不销毁的私有作用域,把预先处理的内容都存在这个不销毁的作用域里面。

function multiply(a) {return function executeMultiply(b) {return a * b;}
}
const double = multiply(2);
double(3); // => 6
double(5); // => 10
const triple = multiply(3);
triple(4); // => 12

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/798932.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java+Springboot】------ 通过JDBC+GetMapping方法进行数据select查询、多种方式传参、最简单的基本示例!

一、JDBC如何使用、PostGresql数据库 1、在pom.xml 先引用jdbc组件。 <!--jdbc--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency> 2、在pom.xml 再引用p…

嵌入式Linux驱动开发——汇编点灯

嵌入式Linux驱动开发——汇编点灯 本文章开始记录学习嵌入式Linux的过程&#xff0c;使用的开发板是正点原子的阿尔法&#xff0c;以及左老师的书籍和视频。然后这个系列不会介绍基础知识&#xff08;书上都有&#xff09;&#xff0c;主要是记录思考过程以及需要注意的点。 代…

Ceph学习 -3.存储简介

文章目录 1.存储简介1.1 存储类型1.1.1 储备知识1.1.2 三种存储1.1.3 块存储1.1.4 文件存储1.1.5 对象存储1.1.6 三种存储之间的关系1.1.7 总结 1.2 Ceph简介1.2.1 官方介绍1.2.2 软件特点1.2.3 基本结构1.2.4 应用场景 1.3 小结 1.存储简介 学习目标&#xff1a;这一节&#x…

抖音引流私域转化模式1.0现场视频,从抖音源源不断把人加到私域买单

抖音-引流私域转化模式1.0现场视频&#xff0c;从抖音源源不断把人加到私域&#xff0c;让加到私域的粉丝买单 课程内容&#xff1a;抖音引流私域转化模式1.0现场视频&#xff0c;从抖音源源不断把人加到私域买单 - 百创网-源码交易平台_网站源码_商城源码_小程序源码 01.第一…

Python | Leetcode Python题解之第16题最接近的三数之和

题目&#xff1a; 题解&#xff1a; class Solution:def threeSumClosest(self, nums: List[int], target: int) -> int:nums.sort()n len(nums)best 10**7# 根据差值的绝对值来更新答案def update(cur):nonlocal bestif abs(cur - target) < abs(best - target):best…

LDR6328助力Type-C普及,便捷充电,绿色生活更精彩

随着科技的进步和全球统一接口的需求&#xff0c;Type-C接口正日益受到青睐。越来越多的设备正选择采纳这一先进的接口设计&#xff0c;它的普及无疑在改善着我们的日常生活。 在过往&#xff0c;许多小功率设备如小风扇、蓝牙音箱、桌面台灯以及家用加湿器等&#xff0c;都普遍…

Node.js进阶——Express

文章目录 一、初识Express1、概念2、安装3、使用3、托管静态资源4、nodemon 二、Express路由1、概念2、使用1&#xff09;简单使用2&#xff09;模块化路由 三、Express中间件1、介绍2、语法1&#xff09;基本语法2&#xff09;next函数作用3&#xff09;定义中间件函数4&#…

K8S - Service简介和 1个简单NodePort例子

大纲图 流量方向 如上图&#xff0c; 当用户or 别的service 从k8s 集群外部访问 集群内的services 流量方向有两种 一种是垂直方向&#xff0c; 通过域名 -> Load Balancer -> gateway -> services , 在k8s 一般是通过ingress 来实现&#xff0c; 而ingress 不是本文…

基于JSP SSM的社区生活超市管理系统

目录 背景 技术简介 系统简介 界面预览 背景 随着时代步伐的加速&#xff0c;计算机技术已广泛而深刻地渗透到社会的各个层面。随着居民生活水平的持续提升&#xff0c;人们对社区生活超市的期望和管理要求也越来越高。随着社区生活超市数量的稳步增长&#xff0c;开发一个…

项目:自主实现Boost搜索引擎

文章目录 写在前面开源仓库和项目上线其他文档说明 项目背景项目的宏观原理技术栈与环境搜索引擎原理正排索引倒排索引 去标签和数据清洗模块html文件名路径保存函数html数据解析函数文件写入函数 建立索引模块检索和读取信息建立索引建立正排索引建立倒排索引jieba工具的使用倒…

mysql结构与sql执行流程

Mysql的大体结构 客户端&#xff1a;用于链接mysql的软件 连接池&#xff1a; sql接口&#xff1a; 查询解析器&#xff1a; MySQL连接层 连接层&#xff1a; 应用程序通过接口&#xff08;如odbc,jdbc&#xff09;来连接mysql&#xff0c;最先连接处理的是连接层。 连接层…

SpringCloud Alibaba Sentinel 创建流控规则

一、前言 接下来是开展一系列的 SpringCloud 的学习之旅&#xff0c;从传统的模块之间调用&#xff0c;一步步的升级为 SpringCloud 模块之间的调用&#xff0c;此篇文章为第十四篇&#xff0c;即介绍 SpringCloud Alibaba Sentinel 创建流控规则。 二、基本介绍 我们在 senti…

最新高自定义化的AI翻译(沉浸式翻译),可翻译网页和PDF等文件或者文献(附翻译API总结,Deeplx的api,Deepl的api)

前序 常见问题&#xff1a; 1.有时候想翻译网页&#xff0c;又翻译文献怎么办&#xff1f;下两个软件&#xff1f; 2.什么软件可以翻译视频字幕&#xff1f; 3.什么软件可以翻译PDF文件&#xff1f; 沉浸式翻译介绍 可以翻译文献可以翻译视频字幕可以翻译PDF文件支持OpenAI翻译…

Linux中shell脚本的学习第一天,编写脚本的规范,脚本注释、变量,特殊变量的使用等,包含面试题

4月7日没参加体侧的我自学shell的第一天 Shebang 计算机程序中&#xff0c;shebang指的是出现在文本文件的第一行前两个字符 #&#xff01; 1)以#!/bin/sh 开头的文件&#xff0c;程序在执行的时候会调用/bin/sh, 也就是bash解释器 2)以#!/usr/bin/python 开头的文件&#…

uniapp极光推送、java服务端集成

一、准备工作 1、进入【服务中心】-【开发者平台】 2、【创建应用】&#xff0c;填写应用名称和图标&#xff08;填写项目名称&#xff0c;项目logo就行&#xff0c;也可填写其他的&#xff09; 3、选择【消息推送】服务&#xff0c;点击下一步 ​ ​ Demo测试 参照文档&…

论文阅读《Semantic Prompt for Few-Shot Image Recognition》

论文地址&#xff1a;https://arxiv.org/pdf/2303.14123.pdf 论文代码&#xff1a;https://github.com/WentaoChen0813/SemanticPrompt 目录 1、存在的问题2、算法简介3、算法细节3.1、预训练阶段3.2、微调阶段3.3、空间交互机制3.4、通道交互机制 4、实验4.1、对比实验4.2、组…

【C++】哈希思想的应用(位图、布隆过滤器)及海量数据处理方法

文章目录 前言位图什么是位图简单实现一个自己的位图位图的应用场景 布隆过滤器位图的缺陷及布隆过滤器的提出布隆过滤器的概念简单实现一个自己的布隆过滤器布隆过滤器的优缺点布隆过滤器的应用场景 海量数据处理 前言 哈希思想的在实际中的应用除了哈希表这个数据结构之外还…

【Redis 知识储备】读写分离/主从分离架构 -- 分布系统的演进(4)

读写分离/主从分离架构 简介出现原因架构工作原理技术案例架构优缺点 简介 将数据库读写操作分散到不同的节点上, 数据库服务器搭建主从集群, 一主一从, 一主多从都可以, 数据库主机负责写操作, 从机只负责读操作 出现原因 数据库成为瓶颈, 而互联网应用一般读多写少, 数据库…

C++的List类(一):List类的基本概念

目录 前言 List类的基本概念 List的构造函数 List类迭代器的使用 List的功能 List的元素访问 List与vector比较 前言 vector的insert和erase都会导致迭代器失效list的insert不会导致迭代器失效&#xff0c;erase会导致迭代器失效 insert导致失效的原因是开辟了新空间后…

Visual Studio 2022-C语言如何防止头文件多次引入

头文件的包含 本地⽂件包含 #include "filename" 查找策略&#xff1a;先在源⽂件所在⽬录下查找&#xff0c;如果该头⽂件未找到&#xff0c;编译器就像查找库函数头⽂件⼀样在 标准位置查找头⽂件。 如果找不到就提⽰编译错误。 Linux环境的标准头⽂件的路径&…