js原型与原型链详解(万文总结,一文搞懂原型链!)

       

目录

一,原型

    1, 对象

    2,原型(原型对象)

二,隐式原型__proto__

    1,__proto__

    2,考你一下

三,原型链

    1,Object.prototype

    2,链

四,练习一下

五,进阶

    1,普通对象与函数对象

    2,原型链机制 

    3,Function的原型

六,练习一下

七,总结


         作为一个初入前端的小白,原型链的概念太多,一直觉得难以理解,对整个原型链的了解非常模糊。理解原型链是深入学习js的一小步,在参考诸多大佬文章后,整理笔记如下:        

一,原型

        原型与原型链,首先我们要知道什么是原型。在开始了解原型之前,卖个关子先认识下js中的对象

    1, 对象

        对象(Object)是一种复合数据类型,它是一种无序的键值对集合。对象用于存储和传递多个值,每个值都有一个键(key)与之关联。

        对象的键是字符串类型,值可以为任意数据类型(数字,字符串,布尔)和其他对象。总之就是我们经常用到的 键值对啦  {key: value}    (对象又分为函数对象与普通对象,此处不赘述,以免绕晕,放到【 第五点 进阶 】下面)

        对象的创建方式  以下是几种常见的对象创建方式:

第一种: 对象字面量方式
var obj1 = {name: "Jack",age: 26,
}第二种: Object构造函数模式
var obj2 = new Object()
obj2.name = "Jack"
obj2.age = 26 第三种: 构造函数模式
function Test(name, age){this.name = namethis.age = agethis.say = function(){console.log('我能说话')}
}
var obj3 = new Test('Jack', 26)
var obj4 = new Test('Rose', 25)

        日常中我们最常用的应该是字面量方式,那为什么会出现第三种构造函数方式呢?

        想想看我们需要多个obj1,obj2的时候,字面量方式我们需要重复代码去创建对象;而使用构造函数的方式只需要写一遍属性和方法,我们就可以通过new关键字,new出多个不同的对象。

        试试在控制台打印如下obj3.say === obj4.say,false  看起来调用的是同一个函数方法,实际并不相等。因为他们的内存地址是不同的,每个new出来的obj3 和 obj4 都包含一份独立的属性和方法(可能导致浪费内存)

function Test(){this.name = 'rose'this.say = function(){console.log('我能说话')}
}
var obj3 = new Test()
var obj4 = new Test()obj3.say === obj4.say // false
obj3.name === obj4.name // true

        你可能会问为什么obj3.name和obj4.name是相等的呢?刚才不是说的内存不同独立的属性和方法吗? 要理解这个行为,可以大致参考下面这种情况:

const a = { value: 10 };
const b = { value: 10 };console.log(a.value === b.value);  // 输出 true,因为属性值相同
console.log(a === b);              // 输出 false,因为是不同的对象

        同样的逻辑,对比obj3.name的时候比较的是name这个属性值,而obj3.name输出值为rose,所以rose比较值obj4.name也是rose,是相同的

        现在我们来说说构造函数方式,上面例子中的 obj3 和 obj4 都是 Test的实例(也叫实例对象),而Test是 obj3 和 obj4 的构造函数。 实例都有一个构造函数属性(constructor)指向构造函数,通过构造函数创建的对象拥有构造函数内部定义的属性和方法

function Test(name, age){this.name = namethis.age = age
}Test.prototype.say = function(){console.log('我能说话')
}
var obj3 = new Test('Jack', 26)
var obj4 = new Test('Rose', 25)// constructor属性指向构造函数
console.log(obj3.constructor === Test)  // true
console.log(obj4.constructor === Test)  // true
console.log(obj4.constructor === obj3.constructor) // true

        记住以下两个概念:

        (1)Test是构造函数

        (2)obj3 和 obj4 是构造函数Test的 实例,实例的属性constructor指向构造函数Test

    2,原型(原型对象)

        上面的例子中, obj3 和 obj4 都需要调用 Test 中的say()方法,我们有没有办法将公共方法放到一个公共的地方呢? 这时候有请公共的原型(prototype)登场

        在js中,每一个对象(函数也是对象)都有一个特殊的属性叫做原型(prototype),它指向另一个对象,这个对象(Test.prototype)被称为原型对象, 原型对象是用来共享属性和方法的

        Test.prototype 就叫原型对象

        

        打印Test.prototype可以看到上图中原型对象存在一个constructor属性,指向Test

Test.prototype.constructor === Test
// true

        原型对象:

        (1),原型对象有一个constructor属性指向构造函数本身(Test)。

        (2),原型对象是一个普通的对象,它包含属性和方法。

        (3),原型对象的属性和方法会被继承到所有通过原型链与它相连的对象。

        简单来说,原型对象是一个普通对象,属性和方法会被继承到其他对象,而每个对象都有一个原型(prototype),用来继承其他对象的属性和方法。

        此时,我们就可以把say方法放到这个原型对象上, obj3 和 obj4 就可以访问这个方法,再不用写到Test中去重复占用内存,所有new出来的实例都可以使用此方法

        我们再来打印 obj3.say === obj4.say,为true, 证明obj3和obj4调用的就是同一个方法

function Test(name, age){this.name = namethis.age = age
}Test.prototype.say = function(){console.log('我能说话')
}
var obj3 = new Test('Jack', 26)
var obj4 = new Test('Rose', 25)obj3.say()    // 我能说话
obj4.say()    // 我能说话
console.log(obj3.say === obj4.say)    // true

           构造函数和实例之间就初步构成了这样一个关系,如图:

  

二,隐式原型__proto__

    1,__proto__

        在js中,每个对象都有一个“ __proto__ ”属性(左右两边两个短下划线),这个__proto__就被称为隐式原型。(记住这点)

        实例对象当然也是对象,也存在__proto__属性


console.log(obj3.__proto__ === Test.prototype)
// true

        打印以上obj3.__proto__ === Test.prototype结果为true,所以:

        (1),每个js对象都有一个隐藏的原型对象属性__proto__,它指向创建它的构造函数的原型对象(Test.prototype)

        (2),__proto__存在的意义在于为原型链查找提供方向,原型链查找靠的是__proto__,而不是prototype(画重点,后面要考!!!)

        实例对象obj3通过__proto__指向了Test的原型对象(Test.prototype),如图:(Test.prototype.constructor :从Test先指向原型对象Test.prototype在.constructor指回Test,绕了一圈,图中就不列举)  

    2,考你一下

        前面提到的几个概念理解清楚,再来看看下面的列子是否清楚

function Test(name, age){this.name = namethis.age = age
}Test.prototype.say = function(){console.log('我能说话')
}
var obj3 = new Test('Jack', 26)1, 构造函数是? 实例是?
2, obj3.constructor === Test   true of false?
3, obj3.__proto__ === Test ?
4, Test.prototype === obj3.__proto__ ?
5, obj3.__proto__.constructor === Test ?// 1, Test  obj3  2,true  3,false  4,true  5,true

三,原型链

    1,Object.prototype

        在上面第二点中,每个js对象都有一个隐藏的原型对象属性__proto__

        那Test的原型对象Test.prototype会不会也有一个隐式原型__proto__呢? 控制台输出如下:

        Test.prototype当然也存在一个属性__proto__,而这个Test.prototype.__proto__到底是谁呢?

Test.prototype.__proto__ === Object.prototype
// true

          (1) Test.prototype的隐式原型(__proto__)就是Object.prototype

          (2) 所有的对象,包括构造函数的原型对象,最终都继承自 Object.prototype,这是js原型链的顶点

        Object.prototype是从哪里来的呢? 当然是由Object的属性prototype指向来的。Object.prototype同样也会存在属性 constructor指回Object(【目录 2,原型 原型对象】中提到)

        此时的关系图:

    2,链

        在控制台打印Object.prototype,会发现 Object.prototype也是一个对象

        既然它也是对象,它也存在隐式属性__proto__。想想看,如果Object.prototype.__proto__再去指向某个对象的原型(prototype),那整条线就显得无穷无尽,一直找下去

        js代码在创建时我们的开发者当然考虑到了,Object.prototype作为原型链的顶端,位于原型链的最末端因此,它不再有自己的原型,所以Object.prototype.__proto__ 指向null,表示原型链的终点

        原型链的终点是null

        Object.prototype.__proto__ === null

        这个时候终于到达了终点,形成了这样一个关系图(一整个链接在一起):

         每个对象都有一个原型(prototype),它指向另外一个对象,而指向的对象又存在属性(_proto_)指向另外一个对象。当我们访问对象(obj3)的属性时,会先在对象定义的属性中进行查找,没找到就会沿着__proto__一路向上查找,最终形成一个链式结构,这整个链式结构就叫做原型链

        如果在原型链中找到了这个属性,就返回找到的属性值;如果整个原型链都没找到这个属性值,则返回 undefined,没找到方法直接报错(not a function)

四,练习一下

        到了这里应该对整个原型链有了自己的认知,其实只要记住以下几个概念,就可以试着自己画出整个关系图

1,在js中,每一个对象(函数也是对象)都有一个特殊的属性叫做原型(prototype),它指向另一个对象,这个对象被称为原型对象, 原型对象是用来共享属性和方法的2,对象有一个属性(__proto__)指向构造函数的原型对象,构造函数的原型对象也存在__proto__3,原型链的顶端是Object.prototype4,原型链的终点是null

         看看下面的题是否清楚:

function Test(name, age){this.name = namethis.age = age
}
Test.prototype.say = function(){console.log('我能说话')
}
var obj3 = new Test('Jack', 26)
var obj4 = new Test('Rose', 24)1, Test.prototype === ( ) ?
2, obj3.__proto__.__proto__ === ( ) ?
3, obj3.__proto__ === obj4.__proto__ ?
4, Test.prototype.__proto__ === ( ) ?
5, obj4.__proto__.constructor === ( ) ?
6, Object.prototype.__proto__ === ( ) ?
7, obj3.say === obj4.say ?// 1, obj3.__proto__ 或 obj4.__proto    2,Object.prototype    3, true (二者都由Test new出来,在原型链上都指向 Test.prototype)
// 4, Object.prototype    5, Test    6, null (终点)    7,true (同问题3)

        要是不清楚可以在结合关系图捋一捋

五,进阶

    1,普通对象与函数对象

        在 js 中,有两种主要类型的对象:普通对象函数对象。普通对象最常见,通过"{ }"创建的就是普通对象;通过new Function出来的就是函数对象(函数声明、函数表达式创建的为函数对象),我们可以用typeof来区分  (注意:这里函数声明式和表达式不要和对象字面量方式混淆)

        f1,f2,f3都是函数对象, Object 也是函数对象, b1,2,3为普通对象; 

        简单理解,普通对象就是我们最长见的 { } 键值对; 函数对象通常包含了一个function

function f1(){}var f2 = function(){}var f3 = new Function('name')var b1 = new f1()var b2 = {name: 'Rose'}var b3 = new Object()typeof f1    // 'function'
typeof f2    //'function'
typeof f3    //'function'
typeof b1    //'object'
typeof b2    //'object'
typeof b3    //'object'
typeof Object // 'Function'

        这是再来看我们上面的例子就很清晰了, obj3 为普通对象, Test为函数对象 (不信F12控制台打开 typeof试试)

function Test(name, age){this.name = namethis.age = age
}
var obj3 = new Test('Jack', 26)

        在上面【2 原型对象】我们提到过每一个对象(函数也是对象)都有一个特殊的属性叫做原型(prototype)   obj3是对象,但是它没有prototype的这个原型属性(不信控制台试试)

        所以这话不够完整,只有 函数对象才具有 prototype 这个原型属性

    2,原型链机制

        在【二,隐式原型】中,提到过: __proto__存在的意义在于为原型链查找提供方向。 看你是不是忘记了吧

        为什么会说提供查找方向呢,看看下面两个例子:

        左边在构造函数Test的原型对象(Test.prototype)上定义了sex, Test.sex为undefined ( 注意是Test.sex 不是 obj.sex); 右边在Object.prototype上定义sex ,Test.sex 能获取到值;  为什么在Test.prototype上定义,Tset.sex不能通过原型链到Test.prototype上去找到sex属性;而定义到顶点Object.prototype上,又能通过原型链找到了

        看了大佬的一些解答分析,定义到Test.prototype上的时候,Test.sex并没有通过原型链查找,而是检查Test自身是否定义该属性,没有所以是undefined。感觉解释会有点说不通,定义到Object.prototype,不也应该是Test自身检查,也会检查不到,但我们能输出值,证明的确是顺着原型链去查找到了Object.prototype上

                  

         why ? 

        来看看ai对此的回答

         

        我们在左边例子中,Test本身并没有直接定义'sex'属性,所以查找失败返回undefined, 如果要到原型对象上查找 正确的方式应该是 Test.prototype.sex   这结论很好没任何问题,但是为毛右边的例子能获取到呢 ?不要给我岔开话题啊喂....

        为了找到答案,我一度去翻遍全网,问遍ai,得到结论大概都是:" 原型链从实例对象开始查找,不是从构造函数开始查找,构造函数不具备相同的原型链机制"   这回答非常好,可是还是没解决我的问题啊!!!  不具备相同的原型链机制,为什么定义到Object.prototype上就能获取到了呢?  反复询问ai,就开始给我绕圈子了.......  费解

        终究还是得靠自己  先捋一下

        既然__proto__才是原型链查找的方向,同时对象都有__proto__这个属性,那构造函数Test是属于函数对象,函数对象也是对象 那是否Test也会存在__proto__这个属性呢? 在上面的原型链图中并没有指出这个属性  请往下看

    3,Function的原型

        在第二点【隐式原型__proto__】中,我们提到__proto__指向创建它的构造函数的原型对象。

function Test(name, age){this.name = namethis.age = age
}
var obj = new Test('Jack', 26)Test.__proto__ === Function.prototype  // true

        (1)构造函数Test的隐式原型( __proto__)指向 Function.prototype, 函数对象的__proto__指向Function.prototype

        至于为什么,js在设计时决定了构造函数本身是函数,当然也可以通过指向 Function.prototype来访问原型链上的属性和方法,让构造函数也能参与到原型链中来(虽然不建议通过构造函数访问)

        Function会有原型(prototype),当然也有隐式原型(__proto__),打印这两个原型,会发现二者互相相等  Function.prototype === Function.__proto__  是不是很神奇? 看起来像是自己创造了自己

        针对 Function.prototype === Function.__proto__, 诸多大佬对此有各种不同的解释,以下抛出两个观点仅供参考:

         1,Function 也是对象, 由new Function创建,所以自己创造自己       

         2,Function作为一个内置对象,代码运行前就存在,并非自己创造自己,先有的Function,然后实现上把原型指向了Function.prototype  以此来保持和其他函数一致,表明一种关系 

         对象都拥有隐式原型(__proto__),Function.prototype当然也存在__proto__,打印出来看看

 

         好像有点眼熟? 是不是和Object.prototype打印的有点像  Function.prototype是函数对象,按照刚得出的结论,函数对象的__proto__应该指向Function.prototype(即Function.prototype.__proto === Function.prototype),但是自己指向自己并没有意义。 别忘记Object.prototype才是原型链的顶点,Function.prototype存在于原型链中必然会与Object.prototype存在关联,指向Object.prototype能保证原型链存在终点,所以Function.prototype.__proto__ === Object.prototype

         再来看原型链关系,这时候就成了这样:

        (2)如果在深究一点,Object也是函数对象,Object.__proto__ 也会指向Function.prototype

        (3)构造函数Test也有constructor属性,这个属性指向创建该函数的构造函数;如果自己没有定义构造函数,会指向到 Function (Test.constructor === Function)

         原型链关系就成了这样:

        针对上面 【2,原型链机制】 中的问题也有了答案,在构造函数Test上访问Object.prototype中的属性时,其实是顺着Test.__proto__这条路径从Function去访问    空口无凭,证据如下,看看会输出什么

function Test(name, age){this.name = namethis.age = age
}
var obj = new Test('Jack', 26)Object.prototype.price = 2000Function.prototype.price = 300Test.price

        Test在自身没有找到price,顺着Test的__proto__到Function.prototype上找到了price = 300,所以直接返回 300;若Function.prototype上没有price,才会进一步顺着__proto__找到Object.prototype

         在实际使用中,要获取定义到Test.prototype上的属性,还可以用原型对象Test.prototype.price访问;不过建议还是通过实例(obj)来访问具体的属性(obj.name),而不是构造函数Test.name访问,毕竟实例new出来的目的就是为了调用构造函数上的方法属性

        真相大白,再一次证明__proto__存在的意义在于为原型链查找提供方向,原型链查找靠的是__proto__,而不是prototype

       

六,练习一下

        1,

function Test(){}
var obj1 = new Test()1, console.log( obj1.__proto__ === Test.prototype )
2, obj1.__proto__.__proto__ === ()?
3, Object.prototype.__proto__ === ()?
4, console.log( Test.prototype === obj1.__proto__ )
5, Test.__proto__ === Function.prototype ?
6, Function.prototype.__proto__ === () ?
7, Funcion.prototype === Function.__proto__ ?// 1,true 2,Object.prototype 3,null 4,true 5,true 6,Object.prototype 7,true

        2,

var Test = function(){}
Test.prototype.name = 'Rose'
var obj1 = new Test()
Test.prototype = {name: 'Rose', age: 26}
var obj2 = new Test()obj1.name
obj1.age
obj2.name
obj2.age// 'Rose'  undefined  'Rose'  26
// Test.prototype =  {} 将Test.prototype.name 覆盖

        3,

function Test(){}Object.prototype.a = function(){console.log(1)
}Function.prototype.b = function(){console.log(2)
}var obj = new Test()obj.a()
obj.b()
Test.a()
Test.b()//  1  (not a function)  1  2
// 不太清楚再去看看Function的原型

七,总结

        1,每个对象均存在隐式原型(__proto__),函数对象才有prototype属性

        2,__proto__存在的意义在于为原型链查找提供方向,原型链查找靠的是__proto__,而不是prototype

        3,函数对象的__proto__都指向Function.prototype

        4,每个对象都有一个隐式原型属性(__proto__),多个原型通过__proto__链接在一起形成的链式结构就是原型链

        文中若有错误或描述不当的地方,烦请评论私信指正,万分感谢 😃

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

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

相关文章

C++第四十七弹---深入理解异常机制:try, catch, throw全面解析

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】 目录 1.C语言传统的处理错误的方式 2.C异常概念 3. 异常的使用 3.1 异常的抛出和捕获 3.2 异常的重新抛出 3.3 异常安全 3.4 异常规范 4.自定义…

OpenCV结构分析与形状描述符(8)点集凸包计算函数convexHull()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 查找一个点集的凸包。 函数 cv::convexHull 使用斯克拉斯基算法(Sklansky’s algorithm)来查找一个二维点集的凸包&#…

java基础-IO(1)

1、从计算机的存储说起 电脑电脑,有电才能发挥出“脑”的作用,没电就是破铜烂铁一堆,根据电的特性(高低电平)巧妙的使用0、1组合,记住了万事万物,才得以让"破铜烂铁"发挥出神奇的功效…

为什么我选择这款PR剪辑软件?打工人亲测好用!

现在大家都爱看短视频和Vlog,要是你会剪辑视频,那可就牛了。不管是出去玩拍的视频,还是工作需要,都能派上用场。我就是个爱旅行、爱剪辑的发烧友,今天给你们推荐三款特别好用的视频剪辑软件,尤其是PR剪辑&a…

C语言 | Leetcode C语言题解之第386题字典序排数

题目&#xff1a; 题解&#xff1a; int* lexicalOrder(int n, int* returnSize){int *ret (int *)malloc(sizeof(int) * n);int number 1;for (int i 0; i < n; i) {ret[i] number;if (number * 10 < n) {number * 10;} else {while (number % 10 9 || number 1 …

解读三国历史中的配角们:探索未被书写的故事 - 《三国配角演义》读后感

在传统的三国叙事中&#xff0c;英雄主角们的事迹往往被无限放大&#xff0c;而那些默默无闻的小人物则被忽视。然而&#xff0c;《三国配角演义》通过挖掘历史细节&#xff0c;赋予这些小角色新的生命。书中用微小的史料合理推断&#xff0c;构建了他们不为人知的精彩故事。 …

C语言进阶版第8课—指针(2)

文章目录 1. 数组名的理解2. 指针访问数组3. 一维数组传参本质4. 冒泡排序5. 二级指针6. 指针数组7. 指针数组模拟二维数组 1. 数组名的理解 sizeof&#xff08;数组名&#xff09;— 这里的数组名代表整个数组&#xff0c;计算的也是整个数组的大小&数组名 — 这里的数组名…

使用 ELK Stack 进行云原生日志记录和监控:AWS 中的开发运营方法

使用 ELK Stack 进行云原生日志记录和监控 欢迎来到雲闪世界。在当今的云原生世界中&#xff0c;日志记录和监控是强大的 DevOps 策略的重要组成部分。监控应用程序性能、跟踪错误和分析日志对于确保无缝操作和主动识别潜在问题至关重要。在本文中&#xff0c;我们将指导您使用…

Vue——Diff算法

目录 什么是Diff算法&#xff1f; 比较方式 1. 同层比较 2. 双端比较 双端比较的步骤&#xff1a; 3. 通过 key 来优化比较 原理分析 1. 虚拟 DOM 和真实 DOM 2. Diff 算法的基本原理 3. 双端比较优化 4. 通过 key 进行优化 5. 具体操作 6. 原理总结 声明&#xf…

AWS SES服务 Golang接入教程(排坑版)

因为刚来看的时候 也迷迷糊糊的 所以 先讲概念 再上代码 一 基础设置 这里需要完成两个最基础的设置任务 1 是验证至少一个收件电子邮箱 2 【很关键】是验证发送域。即身份里的域类型的身份。&#xff08;可以理解为配置你的域名邮箱服务器&#xff08;SMPT&#xff09;为亚马…

Flink问题记录

尚硅谷Flink1.17问题记录 上手pom依赖循环递归WordCountStreamDemo中readTextFile是deprecated&#xff08;强烈反对的&#xff09;Flink本地模式开启WebUI 上手 pom依赖循环递归 pom依赖中&#xff1a; <dependency><groupId>org.apache.flink</groupId>&…

前端开发中遇到的小问题以及解决方案记录2

1、H5中适配屏幕的工具-postcss-px-to-viewport postcss-px-to-viewport。因为设计稿一般给的都是375px宽度的&#xff0c;所以假如一个字体是16px&#xff0c;那么在开发中不能直接写死为16px&#xff0c;因为各个厂商的手机屏幕大小是不同的&#xff0c;所以要根据屏幕大小去…

【人工智能学习笔记】1_人工智能基础

本系列是个人学习《阿里云人工智能工程师ACA认证免费课程&#xff08;2023版&#xff09;》的笔记&#xff0c;仅为个人学习记录&#xff0c;欢迎交流&#xff0c;感谢批评指正 人工智能概述 智能的三大能力&#xff1a;感知、记忆与思维、学习与适应能力人工智能的定义 明斯基…

正规表达式例题

解析&#xff1a;从题意可知&#xff0c;a可以有零个或多个&#xff0c;b有1个或多个 选项A&#xff1a;这里a至少有1个&#xff0c;不符合题意 选项B&#xff1a;a^*bb^*&#xff0c;a是0个或多个&#xff0c;b可以是1个或多个&#xff0c;符合题意 选项C和选项D&#xff0…

Camunda调用子流程案例

调用子流程 调用子流程是指子流程在主流程的外面。子流程一般是多个流程可重用的流程&#xff0c;也可以独立调用子流程。 可以对比编程中的方法抽取。子流程运行时&#xff0c;主流程也是等待状态。子流程结束&#xff0c;主流程继续。 立即体验&#xff0c;请访问JeecgFlow …

AWTK HTML View 控件更新

AWTK HTML View 控件基于 Lite HTML 实现&#xff0c;从最初的版本开始&#xff0c;3 年多过去了&#xff0c;Lite HTML 做了大量的更新&#xff0c;最近抽空将 AWTK HTML View 控件适配到最新版本的 Lite HTML&#xff0c;欢迎大家使用。 AWTK HTML View 控件。HTML View 控件…

玩转Python Turtle库,实现满屏飘字的魔法!

前言 本文将教你如何使用Python的Turtle库&#xff0c;通过简单的编程实现满屏飘字的炫酷效果。无需复杂的编程知识&#xff0c;跟着我们的步骤&#xff0c;你也可以成为编程小达人&#xff01; 效果展示 开发过程 一、准备工作 首先&#xff0c;确保你的电脑上已经安装了Py…

12. GIS地图制图工程师岗位职责、技术要求和常见面试题

本系列文章目录&#xff1a; 1. GIS开发工程师岗位职责、技术要求和常见面试题 2. GIS数据工程师岗位职责、技术要求和常见面试题 3. GIS后端工程师岗位职责、技术要求和常见面试题 4. GIS前端工程师岗位职责、技术要求和常见面试题 5. GIS工程师岗位职责、技术要求和常见面试…

2. GIS数据工程师岗位职责、技术要求和常见面试题

本系列文章目录&#xff1a; 1. GIS开发工程师岗位职责、技术要求和常见面试题 2. GIS数据工程师岗位职责、技术要求和常见面试题 3. GIS后端工程师岗位职责、技术要求和常见面试题 4. GIS前端工程师岗位职责、技术要求和常见面试题 5. GIS工程师岗位职责、技术要求和常见面试…

COD论文笔记 BiRefNet

本质还是一个 U 型编码器解码器结构的分割模型。 我可以考虑将©和(d)结合&#xff0c;即对解码器的输入不进行 patchify,同时在各个阶段引入梯度参考信息 最近的相关工作&#xff0c;中间监督、额外先验(频率&#xff0c;梯度&#xff0c;边缘等)取得不错效果 作者观察到…