第2集丨JavaScript 中原型链(prototype chain)与继承

news2024/12/24 3:09:50

目录

  • 一、一些基础概念
    • 1.1 ECMAScript 标准
    • 1.2 prototype和 __proto__
    • 1.3 constructor属性
    • 1.4 函数名
  • 二、原型链的维护
    • 2.1 内部原型链和构造器原型链
    • 2.2 从实例回溯原型链
    • 2.3 修正原型指向
  • 三、基于原型链的继承
    • 3.1 继承属性
    • 3.2 继承“方法”
  • 四、构造函数
    • 4.1 案例
      • 一个简单的实现
      • 手动创建__proto__
      • 构造函数方式
    • 4.2 类语法
    • 4.3 修改prototype
    • 4.4 字面量的隐式构造函数
  • 五. 构建更长的继承链
    • 5.1 典型的原型链
    • 5.2 Object.setPrototypeOf()
    • 5.3 Object.create()
  • 六、实例对象原型链
  • 七、性能问题
    • 7.1 hasOwnProperty

对于使用过基于类的语言(如 JavaC++)的开发者来说,JavaScript 实在是有些令人困惑——JavaScript 是动态的且没有静态类型。一切都是对象(实例)或函数(构造函数),甚至函数本身也是 Function 构造函数的实例。即使是语法结构中的“类”也只是运行时的构造函数。

当谈到继承时,JavaScript 只有一种结构:对象。每个对象(object)都有一个私有属性指向另一个名为原型(prototype)的对象。原型对象也有一个自己的原型,层层向上直到一个对象的原型为 null。根据定义,null 没有原型,并作为这个原型链(prototype chain)中的最后一个环节。可以改变原型链中的任何成员,甚至可以在运行时换出原型,因此 JavaScript 中不存在静态分派的概念。

尽管这种混杂通常被认为是 JavaScript 的弱点之一,但是原型继承模型本身实际上比类式模型更强大。例如,在原型模型的基础上构建类式模型(即类的实现方式)相当简单。

尽管类现在被广泛采用并成为 JavaScript 中新的范式,但类并没有带来新的继承模式。虽然类为大部分原型的机制提供了抽象,但了解原型在底层是如何工作的仍然十分有用。

一、一些基础概念

1.1 ECMAScript 标准

遵循 ECMAScript 标准,符号 someObject.[[Prototype]] 用于标识 someObject 的原型。

  • [[Prototype]] 可以通过 Object.getPrototypeOf() Object.setPrototypeOf() 函数来访问。这个等同于 JavaScript非标准但被许多 JavaScript 引擎实现的属性 __proto__ 访问器。
  • 为在保持简洁的同时避免混淆,在我们的符号中会避免使用 obj.__proto__,而是使用 obj.[[Prototype]] 作为代替。其对应于 Object.getPrototypeOf(obj)

它不应与函数的 func.prototype 属性混淆,后者指定在给定函数被用作构造函数时分配给所有对象实例的 [[Prototype]]

有几种可以指定对象的 [[Prototype]] 的方法。值得注意的是,{ __proto__: ... } 语法与 obj.__proto__ 访问器不同:前者是标准且未被弃用的

1.2 prototype和 proto

每个函数都具有prototype属性,一般来说构造函数的prototype属性才具有实际意义;而实例(对象)是不具有prototype属性的,但是其有一个内部的__proto__属性 。

  • Object是构造器;我们定义的函数也是构造器,所以他们具有prototype 属性

  • JavaScript 中的所有构造函数都有一个被称为 prototype 的特殊属性,它与 new 运算符一起使用。对原型对象的引用被复制到新实例的内部属性[[Prototype]]中。

  • 箭头函数没有默认的原型属性

function doSomething() {}
console.log(doSomething.prototype);
// 你如何声明函数并不重要;
// JavaScript 中的函数总有一个默认的原型属性——有一个例外:
// 箭头函数没有默认的原型属性:
const doSomethingFromArrowFunction = () => {};
// undefined
console.log(doSomethingFromArrowFunction.prototype); 

如上所示,doSomething() 有一个默认的 prototype 属性(正如控制台所示)。运行这段代码后,控制台应该显示一个类似于下面的对象。

{
  constructor: ƒ doSomething(),
  [[Prototype]]: {
    constructor: ƒ Object(),
    hasOwnProperty: ƒ hasOwnProperty(),
    isPrototypeOf: ƒ isPrototypeOf(),
    propertyIsEnumerable: ƒ propertyIsEnumerable(),
    toLocaleString: ƒ toLocaleString(),
    toString: ƒ toString(),
    valueOf: ƒ valueOf()
  }
}

1.3 constructor属性

  • 实例具有constructor 属性,而且其值和类的prototypeconstructor 属性是等价。即:
    Object.prototype.constructor === new Object().constructor (也就是说:实例的构造器和)

  • 构造函数也有constructor属性(因为函数本身也是一个对象),并且其值为Funciton,但是其值和(构造函数).prototype.constructor 是
    不等价的,即:(构造函数).prototype.constructor !== (构造函数).constructor //这两者是不等价的

function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        return this.name;
    }
}
var p1 = new Person("张三",20);
console.log(p1.constructor);    //Person(name, age)
console.log(p1.constructor === Person.prototype.constructor);   //true
console.log(Person.constructor === Person.prototype.constructor)    //false
console.log(Person.constructor === Function)    //true 即:ƒ Function() { [native code] }

1.4 函数名

函数名:就是指函数的本身的引用

  • (函数名).prototype.constructor == (函数名)

二、原型链的维护

2.1 内部原型链和构造器原型链

  • 内部原型链 和 构造器原型链:
    下图说明了构造器通过了显示的prototype属性构建了一个原型链,而对象实例也通过内部的__proto__构建了一个内部原型链。
    在这里插入图片描述

2.2 从实例回溯原型链

注意不要混淆:obj.constructor.prototype == 构造器.prototype构造器.prototype.constructor
通过 obj.constructor.prototype.__proto__.__proto__. . . === Object.prototype 为止

请注意:左边是prototype 属性,右边是__proto__ 属性,他们都指向原型对象,并且
原型对象是一个实例,所以原型对象本身也有原型即有__proto__ 属性。这样我们可以
一层一层的找到他们的父类,直到顶级类的Object.prototype 为止。

在这里插入图片描述

2.3 修正原型指向

MyObjectEx.prototype = new MyObject();
//MyObjectEx.prototype.constructor =  MyObjectEx;	//加上这句话,修正构造器指向自身

但是这有个问题:由于丢弃掉了原型的constructor属性,因此事实上也就切断了与原型父类的关系,如下图,这个时候,通过constructor属性就不知道其父类是谁了。但是为什么他依然可以继承父类,是因为还有内部的__proto__,他依然记得。

因此,可靠的原型的回溯必须要通过__proto__属性,因为根据prototypeconstructor来是不可靠的,我们必须要维护正确的原型链才行,而实际的过程中,我们随时可以修改prototype.constructor的值。

简单的来说,内部原型链是JavaScript的原型继承机制所需。而通过prototypeconstructor所维护的构造器原型链,则是用户代码需要回溯时才需要的。如果用户无需回溯,那么不维护这个“原型链”,也没有关系。

在这里插入图片描述

三、基于原型链的继承

3.1 继承属性

JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

例如,当你执行 const a1 = new A() 时,JavaScript(在内存中创建对象之后,为其定义 this 并执行 A() 之前)设置 a1.[[Prototype]] = A.prototype。然后,当你访问实例的属性时,JavaScript 首先检查它们是否直接存在于该对象上,如果不存在,则在 [[Prototype]] 中查找。会递归查询 [[Prototype]],即 a1.doSomethingObject.getPrototypeOf(a1).doSomethingObject.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething,以此类推,直至找到或 Object.getPrototypeOf 返回 null。这意味着在 prototype 上定义的所有属性实际上都由所有实例共享,并且甚至可以更改 prototype 的部分内容,使得更改被应用到所有现有的实例中。

下面以具体代码来进行说明

  • 注意:自有属性属性遮蔽 这两个概念
const o = {
  a: 1,
  b: 2,
  // __proto__ 设置了 [[Prototype]]。它在这里被指定为另一个对象字面量。
  __proto__: {
    b: 3,
    c: 4,
  },
};

// o.[[Prototype]] 具有属性 b 和 c。
// o.[[Prototype]].[[Prototype]] 是 Object.prototype(我们会在下文解释其含义)。
// 最后,o.[[Prototype]].[[Prototype]].[[Prototype]] 是 null。
// 这是原型链的末尾,值为 null,
// 根据定义,其没有 [[Prototype]]。
// 因此,完整的原型链看起来像这样:
// { a: 1, b: 2 } ---> { b: 3, c: 4 } ---> Object.prototype ---> null

console.log(o.a); // 1
// o 上有自有属性“a”吗?有,且其值为 1。

console.log(o.b); // 2
// o 上有自有属性“b”吗?有,且其值为 2。
// 原型也有“b”属性,但其没有被访问。
// 这被称为属性遮蔽(Property Shadowing)

console.log(o.c); // 4
// o 上有自有属性“c”吗?没有,检查其原型。
// o.[[Prototype]] 上有自有属性“c”吗?有,其值为 4。

console.log(o.d); // undefined
// o 上有自有属性“d”吗?没有,检查其原型。
// o.[[Prototype]] 上有自有属性“d”吗?没有,检查其原型。
// o.[[Prototype]].[[Prototype]] 是 Object.prototype 且
// 其默认没有“d”属性,检查其原型。
// o.[[Prototype]].[[Prototype]].[[Prototype]] 为 null,停止搜索,
// 未找到该属性,返回 undefined。

3.2 继承“方法”

JavaScript 并没有其他基于类的语言所定义的“方法”。在 JavaScript 中,任何函数都被可以添加到对象上作为其属性。函数的继承与其他属性的继承没有差别,包括上面的“属性遮蔽”(这种情况相当于其他语言的方法重写)。

当继承的函数被调用时,this 值指向的是当前继承的对象,而不是拥有该函数属性的原型对象。

const parent = {
  value: 2,
  method() {
    return this.value + 1;
  },
};

console.log(parent.method()); // 3
// 当调用 parent.method 时,“this”指向了 parent

// child 是一个继承了 parent 的对象
const child = {
  __proto__: parent,
};
console.log(child.method()); // 3
// 调用 child.method 时,“this”指向了 child。
// 又因为 child 继承的是 parent 的方法,
// 首先在 child 上寻找“value”属性。但由于 child 本身
// 没有名为“value”的自有属性,该属性会在
// [[Prototype]] 上被找到,即 parent.value。

child.value = 4; // 在 child,将“value”属性赋值为 4。
// 这会遮蔽 parent 上的“value”属性。
// child 对象现在看起来是这样的:
// { value: 4, __proto__: { value: 2, method: [Function] } }
console.log(child.method()); // 5
// 因为 child 现在拥有“value”属性,“this.value”现在表示
// child.value

四、构造函数

原型的强大之处在于,如果一组属性应该出现在每一个实例上,那我们就可以重用它们——尤其是对于方法。

4.1 案例

:假设我们要创建多个盒子,其中每一个盒子都是一个对象,包含一个可以通过 getValue 函数访问的值。

一个简单的实现

const boxes = [
  { value: 1, getValue() { return this.value; } },
  { value: 2, getValue() { return this.value; } },
  { value: 3, getValue() { return this.value; } },
];

这是不够好的,因为每一个实例都有自己的,做相同事情的函数属性,这是冗余且不必要的

手动创建__proto__

相反,我们可以将 getValue 移动到所有盒子的 [[Prototype]] 上:

const boxPrototype = {
  getValue() {
    return this.value;
  },
};

const boxes = [
  { value: 1, __proto__: boxPrototype },
  { value: 2, __proto__: boxPrototype },
  { value: 3, __proto__: boxPrototype },
];

这样,所有盒子的 getValue 方法都会引用相同的函数,降低了内存使用率。但是,手动绑定每个对象创建的 proto 仍旧非常不方便

构造函数方式

这时,我们就可以使用构造函数,它会自动为每个构造的对象设置 [[Prototype]]。构造函数是使用 new 调用的函数。

// 一个构造函数
function Box(value) {
  this.value = value;
}

// 使用 Box() 构造函数创建的所有盒子都将具有的属性
Box.prototype.getValue = function () {
  return this.value;
};

const boxes = [new Box(1), new Box(2), new Box(3)];

  • 我们说 new Box(1) 是通过 Box 构造函数创建的一个实例。Box.prototype 与我们之前创建的 boxPrototype 并无太大区别——它只是一个普通的对象。

  • 通过构造函数创建的每一个实例都会自动将构造函数的 prototype 属性作为其 [[Prototype]]。即,Object.getPrototypeOf(new Box()) === Box.prototype

  • Constructor.prototype 默认具有一个自有属性:constructor,它引用了构造函数本身。即,Box.prototype.constructor === Box。这允许我们在任何实例中访问原始构造函数

4.2 类语法

class Box {
  constructor(value) {
    this.value = value;
  }

  // 在 Box.prototype 上创建方法
  getValue() {
    return this.value;
  }
}

4.3 修改prototype

因为 Box.prototype 引用了(作为所有实例的 [[Prototype]] 的)相同的对象,所以我们可以通过改变 Box.prototype 来改变所有实例的行为。

function Box(value) {
    this.value = value;
}
Box.prototype.getValue = function () {
return this.value;
};
const box = new Box(1);
console.log(box.getValue()); // 1
// 在创建实例后修改 Box.prototype
Box.prototype.getValue = function () {
return this.value + 1;
};
console.log(box.getValue()); // 2

推论:重新赋值 Constructor.prototype(Constructor.prototype = ...)是一个不好的主意,原因有两点:

  • 在重新赋值之前创建的实例的 [[Prototype]] 现在引用的是与重新赋值之后创建的实例的 [[Prototype]] 不同的对象——改变一个的 [[Prototype]] 不再改变另一个的 [[Prototype]]
  • 除非你手动重新设置 constructor 属性,否则无法再通过 instance.constructor 追踪到构造函数,这可能会破坏用户期望的行为。一些内置操作也会读取 constructor 属性,如果没有设置,它们可能无法按预期工作。

Constructor.prototype 仅在构造实例时有用。它与 Constructor.[[Prototype]] 无关,后者是构造函数的自有原型,即 Function.prototype。也就是说,Object.getPrototypeOf(Constructor) === Function.prototype

4.4 字面量的隐式构造函数

JavaScript 中的一些字面量语法会创建隐式设置 [[Prototype]] 的实例。例如:

// 对象字面量(没有 `__proto__` 键)自动将
// `Object.prototype` 作为它们的 `[[Prototype]]`
const object = { a: 1 };
Object.getPrototypeOf(object) === Object.prototype; // true

// 数组字面量自动将 `Array.prototype` 作为它们的 `[[Prototype]]`
const array = [1, 2, 3];
Object.getPrototypeOf(array) === Array.prototype; // true

// 正则表达式字面量自动将 `RegExp.prototype` 作为它们的 `[[Prototype]]`
const regexp = /abc/;
Object.getPrototypeOf(regexp) === RegExp.prototype; // true

五. 构建更长的继承链

Constructor.prototype 属性将成为构造函数实例的 [[Prototype]]

默认情况下,Constructor.prototype 是一个普通对象——即 Object.getPrototypeOf(Constructor.prototype) === Object.prototype

唯一的例外是 Object.prototype 本身,其 [[Prototype]] null——即 Object.getPrototypeOf(Object.prototype) === null

5.1 典型的原型链

因此,一个典型的构造函数将构建以下原型链:

function Constructor() {}

const obj = new Constructor();
// obj ---> Constructor.prototype ---> Object.prototype ---> null

5.2 Object.setPrototypeOf()

要构建更长的原型链,我们可用通过 Object.setPrototypeOf() 函数设置 Constructor.prototype[[Prototype]]

function Base() {}
function Derived() {}
// 将 `Derived.prototype` 的 `[[Prototype]]`
// 设置为 `Base.prototype`
Object.setPrototypeOf(Derived.prototype, Base.prototype);

const obj = new Derived();
// obj ---> Derived.prototype ---> Base.prototype ---> Object.prototype ---> null

在类的术语中,这等同于使用 extends 语法。

class Base {}
class Derived extends Base {}

const obj = new Derived();
// obj ---> Derived.prototype ---> Base.prototype ---> Object.prototype ---> null

5.3 Object.create()

你可能还会看到一些使用 Object.create() 来构建继承链的旧代码。然而,因为这会重新为 prototype 属性赋值并删除 constructor 属性,所以更容易出错,而且如果构造函数还没有创建任何实例,性能提升可能并不明显。所以尽量不要使用底下代码

function Base() {}
function Derived() {}
// 将 `Derived.prototype` 重新赋值为 `Base.prototype`,
// 以作为其 `[[Prototype]]` 的新对象
// 请不要这样做——使用 Object.setPrototypeOf 来修改它
Derived.prototype = Object.create(Base.prototype);  // 

// 修改之前是ƒ Derived() {},修改之后ƒ Base() {}
console.log(Derived.prototype.constructor)  //ƒ Base() {}

允许使用 Object.create(null) 创建没有原型的对象

六、实例对象原型链

function doSomething() {}
doSomething.prototype.foo = "bar"; // 向原型上添加一个属性
const doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // 向该对象添加一个属性
console.log(doSomeInstancing);

这会产生类似于下面的输出:

{
  prop: "some value",
  [[Prototype]]: {
    foo: "bar",
    constructor: ƒ doSomething(),
    [[Prototype]]: {
      constructor: ƒ Object(),
      hasOwnProperty: ƒ hasOwnProperty(),
      isPrototypeOf: ƒ isPrototypeOf(),
      propertyIsEnumerable: ƒ propertyIsEnumerable(),
      toLocaleString: ƒ toLocaleString(),
      toString: ƒ toString(),
      valueOf: ƒ valueOf()
    }
  }
}

如上所示,doSomeInstancing[[Prototype]]doSomething.prototype。但是,这是做什么的呢?当你访问 doSomeInstancing 的属性时,运行时首先会查找 doSomeInstancing 是否有该属性。

如果 doSomeInstancing 没有该属性,那么运行时会在 doSomeInstancing.[[Prototype]](也就是 doSomething.prototype)中查找该属性。如果 doSomeInstancing.[[Prototype]] 有该属性,那么就会使用 doSomeInstancing.[[Prototype]] 上的该属性。

否则,如果 doSomeInstancing.[[Prototype]] 没有该属性,那么就会在 doSomeInstancing.[[Prototype]].[[Prototype]] 中查找该属性。

默认情况下,任何函数的 prototype 属性的 [[Prototype]] 都是 Object.prototype。因此,然后会在 doSomeInstancing.[[Prototype]].[[Prototype]](也就是 doSomething.prototype.[[Prototype]](也就是 Object.prototype))上查找该属性。

如果在 doSomeInstancing.[[Prototype]].[[Prototype]] 中没有找到该属性,那么就会在 doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]] 中查找该属性。但是,这里有一个问题:doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]] 不存在,因为 Object.prototype.[[Prototype]]null 。然后,只有在查找完整个 [[Prototype]] 链之后,运行时才会断言该属性不存在,并得出该属性的值为 undefined

七、性能问题

原型链上较深层的属性的查找时间可能会对性能产生负面影响,这在性能至关重要的代码中可能会非常明显。此外,尝试访问不存在的属性始终会遍历整个原型链

7.1 hasOwnProperty

此外,在遍历对象的属性时,原型链中的每个可枚举属性都将被枚举。要检查对象是否具有在其自身上定义的属性,而不是在其原型链上的某个地方,则有必要使用 hasOwnPropertyObject.hasOwn 方法。除 [[Prototype]] null 的对象外,所有对象都从 Object.prototype 继承 hasOwnProperty——除非它已经在原型链的更深处被覆盖。我们将使用上面的图示例代码来说明它,具体如下:

function Graph() {
  this.vertices = [];
  this.edges = [];
}

Graph.prototype.addVertex = function (v) {
  this.vertices.push(v);
};

const g = new Graph();
// g ---> Graph.prototype ---> Object.prototype ---> null

g.hasOwnProperty("vertices"); // true
Object.hasOwn(g, "vertices"); // true

g.hasOwnProperty("nope"); // false
Object.hasOwn(g, "nope"); // false

g.hasOwnProperty("addVertex"); // false
Object.hasOwn(g, "addVertex"); // false

Object.getPrototypeOf(g).hasOwnProperty("addVertex"); // true

注意:仅检查属性是否为 undefined 是不够的。该属性很可能存在,但其值恰好设置为 undefined

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

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

相关文章

SAP S4 Hana 财务凭证存储表变化

一、统一日记账的表 1. ACDOCA里有的,BSEG里不一定有 以下的一些凭证行项目,都是只在ACDOCA表里面存在,而在BSEG表里不存在的(你可以通过BKPF表的BSTAT字段的凭证状态U来识别): 资产折旧过账 特定账套&am…

VMware Workstation虚拟机如何连接usb网卡

小伙伴不知道怎么将网卡链接到VMware虚拟机系统里面,因此今天我们就做一个简单的教程,教大家如何连接虚拟机使用: 1.插上usb网卡,然后如下图所示操作: 2.点击连接网卡到虚拟机之后,查询一下网卡是否识别到…

【JAVA程序员学C++】第二节、引用与指针,类型转换,结构体

一、指针与引用 1.1 指针 先说指针,由于java有jvm,所以对于java程序员,对于内存这一块关注就毕竟少了。但是C不同,C里面所有的堆内存,都需要程序员自己把控,把控不好,泄露了也是常有的事情。 …

Leetcode刷题笔记--Hot11-20

1--有效的括号(20) 主要思路: 利用栈,遍历字符串,遇到左括号则入栈,遇到右括号则出栈,并判断出栈元素是否与右括号匹配; 当字符串有效时,栈为空(所有左括号都…

python语法 数据结构-字典和集合

文章目录 1. 字典1. 1. 字典特征1. 2. 创建字典1. 3.字典常用方法1.3.1 get()1.3.2 clear()1.3.3 copy()1.3.4 copy()1.3.4 update(key value)1.3.5 keys()、 values()和items() 1. 4. 获取字典值1. 4.1 通过 Key1. 4.2 通过迭代 1. 5. 列表与运算符 2. 集合2. 1. 元组特征2. …

NSS [HNCTF 2022 WEEK2]easy_include

NSS [HNCTF 2022 WEEK2]easy_include 过滤好多&#xff0c;试试日志包含。 服务器是nginx ?file/var/log/nginx/access.log 修改UA头为<?php system(‘ls’); ?> 修改UA头为<?php system(‘ls /’); ?> 修改UA头为<?php system(tac /ffflllaaaggg); ?&g

word2vec工具实战(使用gensim)

最开始需要新建一个conda环境 conda create -n word2vec python3.8 conda activate word2vec然后安装一下所需要的库 pip install numpy pip install scipy pip install gensim pip install jieba首先下载一下数据集zhwiki-20230701-pages-articles.xml.bz2&#xff0c;为了方…

数据库如何建表

MySQL数据库建表过程 目录 创建并使用数据库 第一步&#xff1a;打开命令行 第二步&#xff1a;运行MySQL 第三步&#xff1a;建立数据库以及表数据 创建表时约束条件 约束类型 其他SQL语句 MySQL命令行导入导出数据库 创建并使用数据库 第一步&#xff1a;打开命令…

帧内帧间预测实验

帧内帧间预测实验 文章目录 帧内帧间预测实验帧间预测配置opencv 帧内预测解决jupyter notebook无法找到虚拟环境的问题 帧间预测 配置opencv .h文件是头文件&#xff0c;包含了类、函数、变量的声明&#xff0c;用于在源代码文件中引用和访问这些声明。头文件通常包含函数和…

【OpenCV • c++】图像几何变换 | 图像坐标映射

&#x1f680; 个人简介&#xff1a;CSDN「博客新星」TOP 10 &#xff0c; C/C 领域新星创作者&#x1f49f; 作 者&#xff1a;锡兰_CC ❣️&#x1f4dd; 专 栏&#xff1a;【OpenCV • c】计算机视觉&#x1f308; 若有帮助&#xff0c;还请关注➕点赞➕收藏&#xff…

基于PyQt5的桌面图像调试仿真平台开发(12)图像灰度显示

系列文章目录 基于PyQt5的桌面图像调试仿真平台开发(1)环境搭建 基于PyQt5的桌面图像调试仿真平台开发(2)UI设计和控件绑定 基于PyQt5的桌面图像调试仿真平台开发(3)黑电平处理 基于PyQt5的桌面图像调试仿真平台开发(4)白平衡处理 基于PyQt5的桌面图像调试仿真平台开发(5)…

BUU [网鼎杯 2020 青龙组]AreUSerialz

BUU [网鼎杯 2020 青龙组]AreUSerialz 先看题目&#xff0c;是个php反序列化。源码如下。 <?phpinclude("flag.php");highlight_file(__FILE__);class FileHandler {protected $op;protected $filename; protected $content;function __construct(…

Ceph:关于 Ceph 中 BlueStore 架构以及 OSD 创建的一些笔记

写在前面 准备考试&#xff0c;整理ceph 相关笔记内容涉及&#xff1a;Blue Store OSD 存储引擎介绍&#xff0c;对应 OSD 的不同创建方式理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有一个&#xff1a;找到自我。然后在心中坚守其一生&#xff0c;全心全意&…

FreeCAD 3D绘图教程

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D开发工具链 构建现实世界 FreeCAD主要用于为现实世界设计对象。您在FreeCAD中所做的一切都使用现实世界的单位&#xff0c;无论是微米&#xff0c;公里&#xff0c;英寸还是英尺&#xff0c;甚至是单位的任意组合。FreeCAD提供了…

FreeRTOS实时操作系统(十)信号量

系列文章目录 文章目录 系列文章目录信号量二值信号量二值信号量API函数创建二值信号量函数释放二值信号量函数获取二值信号量函数 实验测试 计数型信号量计数型信号量API函数动态创建函数信号量计数值获取函数 实验测试 优先级反翻转实验测试 互斥信号量API函数实验测试 信号量…

深兰科技与韩国EVERYBOT集团签署服务机器人出口订单

7月4日&#xff0c;在深兰科技集团上海总部&#xff0c;韩国EVERYBOT Inc.集团与深兰科技智胜(上海)科技有限公司签署了服务机器人出口订单及韩国市场战略合作协议。根据协议&#xff0c;EVERYBOT将从深兰科技订购首批服务机器人&#xff0c;同时成为深兰科技各类服务机器人产品…

Layui如何给lay-data插入按钮呢?如何通过按钮获取Id值呢?

&#x1f607;作者介绍&#xff1a;一个有梦想、有理想、有目标的&#xff0c;且渴望能够学有所成的追梦人。 &#x1f386;学习格言&#xff1a;不读书的人,思想就会停止。——狄德罗 ⛪️个人主页&#xff1a;进入博主主页 &#x1f5fc;推荐系列&#xff1a;点击进入 &#…

机器学习实战:Python基于NN神经网络进行分类(十一)

文章目录 1 前言1.1 神经网络的介绍1.2 神经网络的应用 2. Tensorflow实战演示2.1 导入函数2.2 导入数据2.3 数据预处理2.4 建立神经网络2.5 训练模型2.6 评估模型2.7 预测 3. 讨论 1 前言 神经网络&#xff08;Neural network&#xff0c;NN&#xff09;机器学习是一种基于人…

【动态规划算法】第五题:62.不同路径

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树 &#x1f389;作者宣言&#xff1a;认真写好每一篇博客 &#x1f38a;作者gitee:gitee &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作…

查看虚拟机主机IP

虚拟机主机ip 文章目录 ifconfigip addr图形化界面 ifconfig 失败了 ip addr 图形化界面