js 深入学习各种继承方法使用场景

news2024/12/26 0:15:24

前言

问题: JS 如何实现继承呢?主要有几种继承方式及分别适用于哪些场景呢?

  我们学习高级语言,就必须学习面向对象,想要成为高手,就必须学习别人不会的,就比如JS中的继承,好多前端人员都不怎么能够熟练使用。其实能够灵活的实现继承,将是前端程序员甩开其他人的一把利剑。学习继承,必须深入理解 原型,可以看上一篇《深入理解原型prototype和创建对象》。

1. 原型链

1.1 原型链的底层原理

  ECMA-262 把原型链定义为 ECMAScript 的主要继承方式。其基本思想就是通过原型继承多个引用类型的属性和方法。重温一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型。如果原型是另一个类型的实例呢?那就意味着这个原型本身有一个内部指针指向另一个原型,相应地另一个原型也有一个指针指向另一个构造函数。这样就在实例和原型之间构造了一条原型链这就是原型链的基本构想。
  实现原型链涉及如下代码模式:


/*父类型的构造函数,只包含一个属性 property*/
function SuperType() {
	this.property = true;
}
/*原型对象中定义一个函数*/
SuperType.prototype.getSuperValue = function() {
	return this.property;
};

/*子类型的构造函数*/
function SubType() {
	this.subproperty = false;
}

// 继承 SuperType
SubType.prototype = new SuperType(); 				//这一行就是关键点,这就形成了一个原型链。
SubType.prototype.getSubValue = function () { //SubType的原型对象(其实就是SuperType的实例)添加了一个方法
	return this.subproperty;
};
let instance = new SubType();
console.log(instance.getSuperValue()); // true

  以上代码定义了两个类型: SuperType 和 SubType 。这两个类型分别定义了一个属性和一个方法。这两个类型的主要区别是 SubType 通过创建 SuperType 的实例并将其赋值给自己的原型 SubType.prototype 实现了对 SuperType 的继承。这个赋值重写了 SubType 最初的原型,将其替换为SuperType 的实例。这意味着SuperType 实例可以访问的所有属性和方法也会存在于 SubType.
prototype
。这样实现继承之后,代码紧接着又给 SubType.prototype ,也就是这个 SuperType 的实例添加了一个新方法。最后又创建了 SubType 的实例并调用了它继承的 getSuperValue() 方法。下图展示了子类的实例与两个构造函数及其对应的原型之间的关系。
在这里插入图片描述
  这个例子中实现继承的关键,是 SubType 没有使用默认原型,而是将其替换成了一个新的对象。这个新的对象恰好是 SuperType 的实例。这样一来, SubType 的实例不仅能从 SuperType 的实例中继承属性和方法,而且还与 SuperType 的原型挂上了钩。于是 instance (通过内部的 [[Prototype]] )指向SubType.prototype ,而 SubType.prototype (作为 SuperType 的实例又通过内部的[[Prototype]] )指向 SuperType.prototype 。注意, getSuperValue() 方法还在 SuperType.prototype 对象上,而 property 属性则在SubType.prototype 上。这是因为 getSuperValue() 是一个原型方法,而 property 是一个实例属性。 SubType.prototype 现在是 SuperType 的一个实例,因此 property才会存储在它上面。还要注意,由于 SubType.prototype 的 constructor 属性被重写为指向 SuperType ,所以 instance.constructor 也指向 SuperType
  原型链扩展了前面描述的原型搜索机制。我们知道,在读取实例上的属性时,首先会在实例上搜索这个属性。如果没找到,则会继承搜索实例的原型。在通过原型链实现继承之后,搜索就可以继承向上,搜索原型的原型。对前面的例子而言,调用instance.getSuperValue() 经过了 3 步搜索: instance 、SubType.prototype 和 SuperType.prototype ,最后一步才找到这个方法。对属性和方法的搜索会一直持续到原型链的末端。

1.2 默认原型

  实际上,原型链中还有一环。默认情况下,>所有引用类型都继承自 Object ,这也是通过原型链实现的。任何函数的默认原型都是一个 Object 的实例,这意味着这个实例有一个内部指针指向 Object.prototype 。这也是为什么自定义类型能够继承包括 toString() 、 valueOf() 在内的所有默认方法的原因。因此前面的例子还有额外一层继承关系。下图 展示了完整的原型链。
在这里插入图片描述
  SubType 继承 SuperType ,而 SuperType 继承 Object 。在调用 instance.toString() 时,实际上调用的是保存在 Object.prototype 上的方法。

1.3 原型与继承关系

  原型与实例的关系可以通过两种方式来确定。第一种方式是使用 instanceof 操作符,如果一个实例的原型链中出现过相应的构造函数,则 instanceof 返回 true 。如下例所示:

console.log(instance instanceof Object); // true
console.log(instance instanceof SuperType); // true
console.log(instance instanceof SubType); // true

  从技术上讲, instance 是 Object 、 SuperType 和 SubType 的实例,因为 instance 的原型链中包含这些构造函数的原型。结果就是 instanceof 对所有这些构造函数都返回 true 。
  确定这种关系的第二种方式是使用 isPrototypeOf() 方法。原型链中的每个原型都可以调用这个方法,如下例所示,只要原型链中包含这个原型,这个方法就返回 true :

console.log(Object.prototype.isPrototypeOf(instance)); // true
console.log(SuperType.prototype.isPrototypeOf(instance)); // true
console.log(SubType.prototype.isPrototypeOf(instance)); // true

1.4 子类型的原型新添方法

  子类有时候需要覆盖父类的方法,或者增加父类没有的方法。为此,这些方法必须在原型赋值之后再添加到原型上。来看下面的例子:

function SuperType() {
	this.property = true;
}
SuperType.prototype.getSuperValue = function() {
	return this.property;
};
function SubType() {
	this.subproperty = false;
}

// 继承 SuperType
SubType.prototype = new SuperType();

//  原型对象上新添新方法
SubType.prototype.getSubValue = function () {
	return this.subproperty;
};
//  覆盖已有的方法
SubType.prototype.getSuperValue = function () {
	return false;
};
let instance = new SubType();
console.log(instance.getSuperValue()); // false

  在上面的代码中,第15到21行部分涉及两个方法。第一个方法 getSubValue() 是 SubType 的新方法,而第二个方法 getSuperValue() 是原型链上已经存在但在这里被遮蔽的方法。后面在 SubType 实例上调用 getSuperValue() 时调用的是这个方法。而 SuperType 的实例仍然会调用最初的方法。重点在于上述两个方法都是在把原型赋值为 SuperType 的实例之后定义的。另一个要理解的重点是,以对象字面量方式创建原型方法会破坏之前的原型链,因为这相当于重写了原型链。下面是一个例子:

function SuperType() {
	this.property = true;
}
SuperType.prototype.getSuperValue = function() {
	return this.property;
};
function SubType() {
	this.subproperty = false;
}

// 继承 SuperType
SubType.prototype = new SuperType();

//  通过对象字面量添加新方法,这会导致上一行无效
SubType.prototype = {
	getSubValue() {
		return this.subproperty;
	},
	someOtherMethod() {
		return false;
	}
};
let instance = new SubType();
console.log(instance.getSuperValue()); //  出错!

  在这段代码中,子类的原型在被赋值为 SuperType 的实例后,又被一个对象字面量覆盖了。覆盖后的原型是一个 Object 的实例,而不再是 SuperType 的实例。因此之前的原型链就断了。 SubType和 SuperType 之间也没有关系了。

1.5 原型链的问题

  原型链继承的问题:

  • 修改一个子类型原型的引用值,所有子类型的引用值都改变了。
  • 子类型在实例化时不能给父类型的构造函数传参。

  前面在谈到原型的问题时也提到过,原型中包含的引用值会在所有实例间共享,这也是为什么属性通常会在构造函数中定义而不会定义在原型上的原因。在使用原型实现继承时,原型实际上变成了另一个类型的实例。这意味着原先的实例属性摇身一变成为了原型属性。下面的例子揭示了这个问题:

function SuperType() {
	this.colors = ["red", "blue", "green"];
}
function SubType() {}

// 继承 SuperType
SubType.prototype = new SuperType();
let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
let instance2 = new SubType();
console.log(instance2.colors); // "red,blue,green,black"

  在这个例子中, SuperType 构造函数定义了一个 colors 属性,其中包含一个数组(引用值)。每个 SuperType 的实例都会有自己的 colors 属性,包含自己的数组。但是,当 SubType 通过原型继承SuperType 后, SubType.prototype 变成了 SuperType 的一个实例,因而也获得了自己的 colors属性。这类似于创建了 SubType.prototype.colors 属性。最终结果是, SubType 的所有实例都会共享这个 colors 属性。这一点通过 instance1.colors 上的修改也能反映到 instance2.colors上就可以看出来。
  原型链的第二个问题是,子类型在实例化时不能给父类型的构造函数传参。事实上,我们无法在不影响所有对象实例的情况下把参数传进父类的构造函数。再加上之前提到的原型中包含引用值的问题,就导致原型链基本不会被单独使用。

2. 盗用构造函数 (constructor stealing)

  为了解决原型包含引用值导致的继承问题,一种叫作“盗用构造函数”(constructor stealing)的技术在开发社区流行起来(这种技术有时也称作“对象伪装”或“经典继承”)。

2.1 基本思路和实现

基本思路很简单:在子类构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简单对象,所以可以使用apply() 和 call() 方法以新创建的对象为上下文执行构造函数。来看下面的例子:

function SuperType() {
	this.colors = ["red", "blue", "green"];
}
function SubType() {
	//  继承 SuperType,这里直接使用SubType的对象 this 调用SuperType()函数
	SuperType.call(this); 
}
let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
let instance2 = new SubType();
console.log(instance2.colors); // "red,blue,gr

  示例中加粗的代码展示了盗用构造函数的调用。通过使用 call() (或 apply() )方法, SuperType构造函数在为 SubType 的实例创建的新对象的上下文中执行了。这相当于新的 SubType 对象上运行了SuperType() 函数中的所有初始化代码。结果就是每个实例都会有自己的 colors 属性。这个特别像其他高级语言中,子类在构造函数中,调用父类的构造函数

2.2 传递参数

  相比于使用原型链,盗用构造函数的一个优点就是可以在子类构造函数中向父类构造函数传参。来看下面的例子:

function SuperType(name){
	this.name = name;
}
function SubType() {
	// 继承 SuperType 并传参
	SuperType.call(this, "Nicholas");
	// 实例属性
	this.age = 29;
}
let instance = new SubType();
console.log(instance.name); // "Nicholas";
console.log(instance.age); // 29

  在这个例子中, SuperType 构造函数接收一个参数 name ,然后将它赋值给一个属性。在 SubType构造函数中调用 SuperType 构造函数时传入这个参数,实际上会在 SubType 的实例上定义 name 属性。如果自定义的属性和父类的属性名一致,自定义属性在调用构造函数的时候会被后者覆盖。为确保 SuperType 构造函数不会覆盖 SubType 定义的属性可以在调用父类构造函数之后再给子类实例添加额外的属性

2.3 盗用构造函数的问题

  盗用构造函数的主要缺点,也是使用构造函数模式自定义类型的问题:

  • 必须在构造函数中定义方法,因此函数不能重用。每定义一个对象,都要创建一次方法
  • 子类也不能访问父类原型上定义的方法,因此所有类型只能使用构造函数模式。

由于存在这些问题,盗用构造函数基本上也不能单独使用

3. 组合继承

  组合继承(有时候也叫伪经典继承)综合了原型链和盗用构造函数,将两者的优点集中了起来。基本的思路是使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。来看下面的例子:

/*父类型构造函数*/
function SuperType(name){
	this.name = name;
	this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
	console.log(this.name);
};

/*子类型构造函数*/
function SubType(name, age){
	// 继承属性
	SuperType.call(this, name); //继承了name和colors两个属性
	this.age = age;
}
// 继承方法
SubType.prototype = new SuperType(); 
SubType.prototype.sayAge = function() {
	console.log(this.age);
};

let instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
instance1.sayName(); // "Nicholas";			//调用父类型原型的方法,返回了从父类型继承来的 name
instance1.sayAge(); // 29							//调用子类型原型的方法,返回示例属性 age 
let instance2 = new SubType("Greg", 27);  
console.log(instance2.colors); // "red,blue,green"
instance2.sayName(); // "Greg";
instance2.sayAge(); // 27

  在这个例子中, SuperType 构造函数定义了两个属性, name 和 colors ,而它的原型上也定义了一个方法叫 sayName() 。 SubType 构造函数调用了 SuperType 构造函数,传入了 name 参数,然后又定义了自己的属性 age 。此外, SubType.prototype 也被赋值为 SuperType 的实例。原型赋值之后,又在这个原型上添加了新方法 sayAge() 。这样,就可以创建两个 SubType 实例,让这两个实例都有自己的属性,包括 colors ,同时还共享相同的方法。组合继承弥补了原型链和盗用构造函数的不足,是 JavaScript 中 使用最多的继承模式 。而且组合继承也保留了 instanceof 操作符和 isPrototypeOf() 方法识别合成对象的能力。

4. 原型式继承 (Prototypal Inheritance)

  2006 年,Douglas Crockford (这位可是js的大宗师)写了一篇文章:《JavaScript 中的原型式继承》(“Prototypal Inheritance in JavaScript”)。这篇文章介绍了一种不涉及严格意义上构造函数的继承方法。他的出发点是即使不自定义类型也可以通过原型实现对象之间的信息共享。文章最终给出了一个函数:

function object(o) {
	function F() {} 			//临时构造函数
	F.prototype = o;			//临时构造函数的原型为 o 
	return new F();			  //返回这个临时变量的实例
}

  这个 object() 函数会创建一个临时构造函数,将传入的对象赋值给这个构造函数的原型,然后返回这个临时类型的一个实例。本质上, object() 是对传入的对象 o 执行了一次浅复制。来看下面的例子:

let person = {
	name: "Nicholas",
	friends: ["Shelby", "Court", "Van"]
};
let anotherPerson = object(person);  //创建一个实例
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");		
let yetAnotherPerson = object(person);  //创建另一个实例
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie" ,发现两个实例的数组操作,都对person产生了影响

  Crockford 推荐的原型式继承适用于这种情况:你有一个对象,想在它的基础上再创建一个新对象。你需要把这个对象先传给 object() ,然后再对返回的对象进行适当修改
  在这个例子中, person 对象定义了另一个对象也应该共享的信息,把它传给 object() 之后会返回一个新对象。这个新对象的原型是 person ,意味着它的原型上既有原始值属性又有引用值属性。这也意味着 person.friends 不仅是person 的属性,也会跟 anotherPerson 和 yetAnotherPerson 共享。这里实际上克隆了两个 person 。ECMAScript 5 通过增加 Object.create() 方法(这个方法类似于C++中的clone() )将原型式继承的概念规范化了。这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。在只有一个参数时,Object.create() 与这里的 object() 方法效果相同:

let person = {
	name: "Nicholas",
	friends: ["Shelby", "Court", "Van"]
};
let anotherPerson = Object.create(person); //等价于 object(person)
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
let yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"

  Object.create() 的第二个参数与 Object.defineProperties() 的第二个参数一样:每个新增属性都通过各自的描述符来描述。以这种方式添加的属性会遮蔽原型对象上的同名属性。比如:

let person = {
	name: "Nicholas",
	friends: ["Shelby", "Court", "Van"]
};
let anotherPerson = Object.create(person, {
	name: {
		value: "Greg"
	}
});
console.log(anotherPerson.name); // "Greg"

  原型式继承非常适合不需要单独创建构造函数,但仍然需要在对象间共享信息的场合。但要记住,属性中包含的引用值始终会在相关对象间共享,跟使用原型模式是一样的。

5. 寄生式继承(parasitic inheritance)

  与原型式继承比较接近的一种继承方式是寄生式继承(parasitic inheritance),也是 Crockford 首倡的一种模式。寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。基本的寄生继承模式如下:

function createAnother(original){
	let clone = object(original); // 通过调用函数创建一个新对象 ,这里也可以是使用 Object.create(original)
	clone.sayHi = function() { 	// 以某种方式增强这个对象,即可以添加其他功能
		console.log("hi");
	};
	return clone; // 返回这个对象
}

  在这段代码中, createAnother() 函数接收一个参数,就是新对象的基准对象。这个对象 original会被传给 object() 函数,然后将返回的新对象赋值给 clone 。接着给 clone 对象添加一个新方法 sayHi() 。最后返回这个对象。可以像下面这样使用 createAnother() 函数:

let person = {
	name: "Nicholas",
	friends: ["Shelby", "Court", "Van"]
};
let anotherPerson = createAnother(person);
anotherPerson.sayHi(); // "hi"

  这个例子基于 person 对象返回了一个新对象。新返回的 anotherPerson 对象具有 person 的所有属性和方法,还有一个新方法叫 sayHi() 。寄生式继承同样适合主要关注对象,而不在乎类型和构造函数的场景。 object() 函数不是寄生式继承所必需的,任何返回新对象的函数都可以在这里使用

注意 通过寄生式继承给对象添加函数会导致函数难以重用,与构造函数模式类似。

6. 寄生式组合继承

  第3节中的组合继承其实也存在效率问题。最主要的效率问题就是父类构造函数始终会被调用两次(相当于实例化属性被继承了两次,这不浪费嘛):一次在是创建子类原型时调用,另一次是在子类构造函数中调用。本质上, 子类原型最终是要包含父类对象的所有实例属性,子类构造函数只要在执行时重写自己的原型就行了。再来看一看这个组合继承的例子:

function SuperType(name) {
	this.name = name;
	this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
	console.log(this.name);
};
function SubType(name, age){
	SuperType.call(this, name); //  第二次调用 SuperType()
	this.age = age;
}
SubType.prototype = new SuperType(); //  第一次调用 SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
	console.log(this.age);
};

  代码中第9、12行是调用 SuperType 构造函数的地方。在上面的代码执行后, SubType.prototype上会有两个属性: name 和 colors 。它们都是 SuperType 的实例属性,但现在成为了 SubType 的原型属性。在调用 SubType 构造函数时,也会调用 SuperType 构造函数,这一次会在新对象上创建实例属性 name 和 colors 。这两个实例属性会遮蔽原型上同名的属性。下图 展示了这个过程。
在这里插入图片描述
  上图中有两组 name 和 colors 属性:一组在实例上,另一组在 SubType 的原型上。这是调用两次 SuperType 构造函数的结果。好在有办法解决这个问题。
  寄生式组合继承通过盗用构造函数继承属性,但使用混合式原型链继承方法。基本思路是不通过调用父类构造函数给子类原型赋值,而是取得父类原型的一个副本。说到底就是使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型。寄生式组合继承的基本模式如下所示:

function inheritPrototype(subType, superType) {
	let prototype = object(superType.prototype); // 创建对象
	prototype.constructor = subType; // 增强对象
	subType.prototype = prototype; // 赋值对象
}

  这个 inheritPrototype() 函数实现了寄生式组合继承的核心逻辑。这个函数接收两个参数:子类构造函数和父类构造函数。在这个函数内部,第一步是创建父类原型的一个副本。然后,给返回的prototype 对象设置 constructor 属性,解决由于重写原型导致默认 constructor 丢失的问题。最后将新创建的对象赋值给子类型的原型(这个新创建的对象示例中没有父类的示例属性)。如下例所示,调用 inheritPrototype() 就可以实现前面例子中的子类型原型赋值:

/* 父类的构造函数定义属性,*/
function SuperType(name) { 
	this.name = name;
	this.colors = ["red", "blue", "green"];
}
/* 父类的原型定义方法,可以重用 */
SuperType.prototype.sayName = function() {
	console.log(this.name);
};
function SubType(name, age) {
	SuperType.call(this, name);  //子类可以继承父类构造函数的属性,不会共享
	this.age = age;
}
inheritPrototype(SubType, SuperType);  //子类会继承父类的prototype中的sayName()方法,多个示例对象可以共享这个方法。
SubType.prototype.sayAge = function() {
	console.log(this.age);
};

  这里只调用了一次 SuperType 构造函数,避免了 SubType.prototype 上不必要也用不到的属性,因此可以说这个例子的效率更高。而且,原型链仍然保持不变,因此 instanceof 操作符和isPrototypeOf() 方法正常有效。寄生式组合继承可以算是引用类型继承的最佳模式。

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

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

相关文章

ASP.NET在线交流论坛管理系统

ASP.NET在线交流论坛管理系统 说明文档 运行前附加数据库.mdf(或sql生成数据库) 主要技术: 基于asp.net架构和sql server数据库 用户功能有个人信息管理 帖了信息管理 意见反馈信息管理 点赞管理 收藏管理 后台管理员可以进行用户管理 …

部署及使用seata

目录 1.下载seata1.7.0.zip 2.上传至云服务器,使用unar工具解压 3.配置application.yml,主要配置如下参数 4.为seata执行mysql脚本 5.配置nacos配置中心 *6.启动seata服务器 问题:“cause:can not register RM,err:can not connect to…

国外项目管理软件最佳实践:选型与应用

国内外主流的10款国外项目管理软件对比:PingCode、Worktile、Asana、Trello、Monday.com、ClickUp、Wrike、ProofHub、Zoho Projects、Hive。 在寻找适合的国外项目管理软件时,你是否感到不知从何选择?市场上琳琅满目的选项往往令人眼花缭乱&…

微软披露Office最新零日漏洞,可能导致数据泄露

近日,微软披露了 Office 中一个未修补的零日漏洞,如果被成功利用,可能导致敏感信息在未经授权的情况下泄露给恶意行为者。 该漏洞被追踪为 CVE-2024-38200(CVSS 得分:7.5),被描述为一个欺骗漏洞…

关于xilinx的FFTIP的使用和仿真

工具:vivado2018.3,modelsim10.6d 场景:在进行数据进行频谱分析的时候,使用FPGA来完成FFT的计算可以加快数据的计算速度。 下面使用仿真完成DDS产生的数据的FFT以及IFFT。原始数据使用DDSIP产生,通过IP产生的波形数据…

旧手机NAS方案

这里写目录标题 1、参考2、alpine-term-v16.0-release.apk下载安装3、电脑端ssh连接3、安装docker3.1 网络配置3.2 配置APK源 1、参考 【Docker与Termux】闲置旧安卓手机上的NAS无缝部署方案 https://blog.csdn.net/AnRanGeSi/article/details/138717589 【Alpine Term】Andr…

线程与进程(5)

目录 信号量(线程的同步 ) 信号量的分类: 框架: (1)信号量的定义(semaphore): (2)信号量的初始化: (3)信号量的PV 操作 (4&#…

Ubuntu Linux安装Go语言

Golang是Google公司在2007年开始开发的一种静态强类型、编译型语言。Go语言被设计成一门简单、高效且可靠的编程语言,旨在解决大规模网络服务和分布式系统开发中的复杂性问题。Go语言结合了动态语言的开发速度和C/C等编译型语言的性能与安全性,提供了强大…

[DL]深度学习_针对图像恢复的高效扩散模型DiffIR

DiffIR: Efficient Diffusion Model for Image Restoration Abstract 扩散模型(DM)通过将图像合成过程建模为去噪网络的顺序应用,实现了SOTA的性能。然而,与图像合成不同的是,图像恢复(IR)对生成符合ground-truth的结果有很强的约束。因此&am…

家穷就去互联网

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247485367&idx1&sn837891059c360ad60db7e9ac980a3321&chksmc0e47eebf793f7fdb8fcd7eed8ce29160cf79ba303b59858ba3a6660c6dac536774afb2a6330#rd 《网安面试指南》http://mp.weixin.qq.com/s?…

AI编程工具合集(附使用地址)

在AI编程工具领域,随着技术的飞速发展,越来越多的工具正在改变编程的方式。以下是目前排名前十的AI编程工具合集,提供了丰富的功能来提升开发效率,并在多个编程场景中帮助开发者解决问题。 1. GitHub Copilot • 是什么: GitHub C…

打靶笔记--medium_socnet

medium_socnet 靶机地址:https://www.vulnhub.com/entry/boredhackerblog-social-network,454/ 内容简介: 这是本系列的第04次打靶,我选择了一个中等难度的靶机。在这次打靶过程中,我们将使用到以下攻击手段: 主机发现 端口扫…

javaEE WebServlet、SpringWebMVC、SpringBoot实现跨域访问的4种方式及优先级,nginx配置跨域

文章目录 1. 前置知识2. 原理和解决方案总结2.1. 跨域不通过原理流程图2.2. 实现原理:添加以下http响应头2.3. 四种跨域实现方式及优先级(从高到低) 3. 具体实现代码3.1. 跨域全局配置方式-Filter(全适用)3.2. 跨域全局配置方式-SpringMvc3.3…

鸿蒙内核源码分析(内存规则篇) | 内存管理到底在管什么

先说如果没有内存管理会怎样? 那就是个奴才们能把主子给活活踩死, 想想主奴不分,吃喝拉撒睡都在一起,称兄道弟的想干啥? 没规矩不成方圆嘛,这事业肯定搞不大,单片机时代就是这种情况. 裸机编程,指针可以…

【笔记】MSPM0G3507使用RT-Thread FinSH——MSPM0G3507与RT_Thread(四)

接上篇 KEIL 添加 FinSH 源码 添加自己的函数实现rt_hw_console_getchar 修改为: #include "C:\ti\mspm0_sdk_2_01_00_03\examples\nortos\LP_MSPM0G3507\driverlib\G3507_RTT\ti_msp_dl_config.h"//ti_msp_dl_config.h的绝对地址RT_WEAK char rt_hw_con…

Java知识点一——列表、表格与媒体元素

显示表格边框&#xff1a;<table border"1"></table> 因为初始的表格是没有边框的 collapse相邻的单元格共用同一条边框&#xff08;采用 collapsed-border 表格渲染模型&#xff09;。 separate默认值。每个单元格拥有独立的边框&#xff08;采用 sep…

Haskell爬虫中日志记录:监控HTTP请求与响应

在当今信息爆炸的时代&#xff0c;数据抓取成为了获取信息的重要手段。Haskell&#xff0c;以其强大的类型系统和函数式编程特性&#xff0c;成为了编写高效、可靠爬虫的理想选择。然而&#xff0c;随着爬虫的运行&#xff0c;监控其行为变得尤为重要。本文将探讨如何在Haskell…

ROS2从入门到精通2-4:Rviz2插件制作案例(以工具栏和多点导航插件为例)

目录 0 专栏介绍1 Rviz2插件2 项目配置3 案例一&#xff1a;工具栏插件4 案例二&#xff1a;多点导航插件5 综合演示5.1 添加插件5.2 多点巡航 0 专栏介绍 本专栏旨在通过对ROS2的系统学习&#xff0c;掌握ROS2底层基本分布式原理&#xff0c;并具有机器人建模和应用ROS2进行实…

企业图纸防泄密的最佳方案,10款好用的图纸加密软件排行榜

在数字化时代&#xff0c;企业的图纸文件往往包含了极其重要的知识产权和技术秘密&#xff0c;因此保护这些图纸的安全至关重要。有效的图纸加密不仅能防止未授权访问&#xff0c;还能确保图纸在内外部流转过程中的完整性与安全性。本文将介绍十款2024年市场上评价较高的图纸加…

基于Mysql的商业辅助决策系统的设计与实现

TOC springboot295基于Mysql的商业辅助决策系统的设计与实现 第1章 绪论 1.1 课题背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。在互联网诞生之前&#xff0c;地域位置往往是人们…