javaScript手写专题——实现instanceof/call/apply/bind/new的过程/继承方式

news2025/1/10 11:07:40

目录

原型链相关

手写instanceof

实现一个_instance方法,判断对象obj是否是target的实例

 测试

 手写new的过程

实现一个myNew方法,接收一个构造函数以及构造函数的参数,返回构造函数创建的实例对象

测试myNew方法

手写类的继承

ES6:class+extends实现继承

组合继承:调用两次父类构造函数

Object.create原型式继承

寄生组合式继承:调用一次父类构造函数+通过object()复制原型

手写call/apply/bind方法

手写call方法

普通版call方法

 使用Symbol标识属性

处理非object类型绑定

测试call方法

手写apply方法

手写bind方法

 bind使用场景

普通版bind方法

用作构造函数boundFn处理

考虑构造函数继承

 测试bind方法


原型链相关

原型链(prototype chain)是 JavaScript 中面向对象编程的一个重要概念,用于实现对象的继承和共享属性。每个函数(构造函数)都有一个 prototype 属性,指向一个对象,这个对象称为原型对象。这个原型对象包含了所有实例共享的属性和方法。

当我们使用构造函数创建一个新对象时,新对象一个隐式的原型属性(__proto__,指向构造函数的原型对象。这样,新对象就可以通过原型链访问到构造函数原型对象上的属性和方法。

如果我们继续查找 __proto__ 属性,可以找到一个叫做 Object.prototype 的对象,它是所有对象的原型。如果继续查找 __proto__ 属性,会找到 null,表示原型链的结束。

这就形成了一个原型链的连接,从新对象的 __proto__ 属性可以一直向上查找到 Object.prototype,然后再查找到 null。这种连接方式让所有对象都可以继承 Object.prototype 的属性和方法,并且可以通过原型链实现对象的继承和共享属性。

手写instanceof

实现一个_instance方法,判断对象obj是否是target的实例

思路:instanceof的原理是基于原型链的概念,通过遍历对象的原型链,检查原型链中的某个原型是否等于目标构造函数的 prototype 属性。如果找到匹配,则返回 true,否则返回 false

instanceof使用场景:我们通常用typeof判断基本类型、symbol、function。对于对象的具体类型,通常用instanceof来判断,比如判断Map、Set、Array、Date类型。 

function _instanceof(obj, target) {
  //instanceof只检测对象
  if (typeof obj != "object" || obj == null) {
    return false;
  }
  let proto = Object.getPrototypeOf(obj); //拿到对象的原型
  //   let proto = obj.__proto__;
  while (proto) {
    if (proto == target.prototype) {
      //原型上找到了target
      return true;
    }
    proto = Object.getPrototypeOf(proto);
    // proto = proto.__proto__;
  }
  return false;
}

 可以通Object.getPrototypeOf()方法拿到对象/构造函数原型的原型

也可以使用__proto__两个下划线proto拿到原型

 测试

对Array、Set、Map类型进行测试

console.log(_instanceof(null, Array));
console.log(_instanceof([], Array)); //判断数组
console.log(_instanceof({}, Array));
console.log(_instanceof({}, Object)); //普通对象
const set = new Set();
console.log(_instanceof(set, Set)); //判断Set
const map = new Map();
console.log(_instanceof(map, Map)); //判断Map
const date = new Date();
console.log(_instanceof(date, Date)); //判断Date

 手写new的过程

实现一个myNew方法,接收一个构造函数以及构造函数的参数,返回构造函数创建的实例对象

思路:

  1. 创建一个对象obj,使用 constructor.prototype 作为其原型。
  2. 使用 apply 方法改变构造函数 constructorthis 指向为新对象 obj,并将 args 传递给 constructor
  3. 如果 constructor 返回一个对象,则返回 constructor 返回的对象;否则返回 newObj
//第一个参数构造函数,第二个通过...拿到的args参数
function myNew(constructor, ...args) {
  const obj = Object.create(constructor.prototype);
  let res = constructor.apply(obj, args); //使用apply绑定this,传args类数组对象,执行constructor构造函数方法
  //   let res = constructor.call(obj, ...args);
  return typeof res === "object" ? res : obj; //构造函数如果没有返回值,返回obj;如果有返回值,返回res
}

在myNew方法中调用constructor方法,需要显示通过call或apply改变this。让方法找调用的对象obj。

测试myNew方法

测试myNew方法,打印person实例,调用person方法

// 测试
function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.say = function () {
  console.log(this.age);
};
let person = myNew(Person, "三月的一天", 18);
console.log(person);
person.say();

手写类的继承

在过去,JavaScript 中实现类的继承有多种方式,包括原型链继承、构造函数继承、组合继承、原型式继承等。这些方式各有优缺点,需要开发人员根据具体情况进行选择和使用。

但是,随着 ES6 的普及和使用,开发人员可以更多地关注使用 classextends 实现继承,而不必过多地关注其他继承方式的优缺点。这也是为什么说类的继承在 ES6 普及后越来越不重要的原因之一。

ES6:class+extends实现继承

class Parent {
  constructor(name) {
    this.name = name;
  }
  sayName() {
    console.log("parent", this.name);
  }
}
class Child extends Parent {
  constructor(name, age) {
    super(name); //继承父类构造函数
    this.age = age;
  }
  sayAge() {
    console.log("Child", this.age);
  }
}
const child = new Child("三月的一天", 18);
child.sayName(); //继承父类的fangfa
child.sayAge(); //自己的方法

 手写继承就是不用Class和extends通过原型+构造函数实现继承。在红宝书里写到,虽然ES6类表面实现了面向对象编程,但是背包使用的仍然是原型和构造函数的概念。

组合继承:调用两次父类构造函数

组合继承:原型链+盗用父类构造函数

即通过call方法,将父类的构造函数在子类中执行一遍,使得子类也获取同样属性。将子类的prototype指向父类构造函数的实例,实现子类和父类的原型链继承。

 组合继承有两点注意:一是在子类的构造函数里,通过call方法,手动让父类的构造函数在子类里执行一遍。其次,必须先将子类的prototype更改为Parent的实例,在去给子类添加方法。否则子类的方法会被Parent覆盖掉。

function Parent(name) {
  this.name = name;
}
Parent.prototype.sayName = function () {
  console.log("parent", this.name);
};
function Child(name, age) {
  Parent.call(this, name); //通过call方法,手动让父类构造函数执行一遍
  this.age = age; //子类自己的属性
}
//先改变Child的原型
Child.prototype = new Parent(); //必须先将Child原型改成Parent,否则Child的方法会被Parent覆盖
//定义Child自己的原型方法
Child.prototype.sayAge = function () {
  console.log("Child", this.age);
};
const child = new Child("三月的一天", 18);
child.sayName();
child.sayAge();

Object.create原型式继承

Object.create() 是 JavaScript 中用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。Object.create() 方法接受一个对象,然后以此对象为原型,返回一个新的对象。Object.create() 的第二个参数是一个可选的属性描述对象,用于定制创建的对象。这个参数与 Object.defineProperties() 方法的第二个参数格式相同。

使用Object.create方法继承适合不需要单独创建构造函数,但仍然需要再对象之间共享信息的场合。但是属性中包含的引用类型的值始终会在对象间共享,跟使用原型模式是一样的。Object.create方法的实现了下面的过程

function object(o){
    function F(){}
    F.prototype =o;
    return new F();
}
let person = {
  name: "三月的一天",
  age: 18,
};
let anotherPerson = Object.create(person);

console.log(anotherPerson.name);//输出'三月的一天'
console.log(anotherPerson.age);

通过Objec.create实现继承。通过Object的第二个参数 修改name,增加sex属性。

let person = {
  name: "三月的一天",
  age: 18,
};
let anotherPerson = Object.create(person, {
  name: {
    value: "新的名字",
  },
  sex: {
    value: "female",
  },
});

console.log(anotherPerson.name);//输出'新的名字'
console.log(anotherPerson.age);//18
console.log(anotherPerson.sex);//female

寄生组合式继承:调用一次父类构造函数+通过object()复制原型

在红宝书中描述了,最好的方式是寄生组合式继承。因为组合式继承调用了父类两次构造函数,子类的实例和子类的原型上都实例化了两次属性。为了解决这个问题,在原型链构建上,不通过父类构造函数给子类原型赋值,而是复制一份父类原型的副本。

function Parent(name) {
  this.name = name;
}
Parent.prototype.sayName = function () {
  console.log(this.name);
};
function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}

// Child.prototype = new Parent(); //组合继承
Child.prototype = Object.create(Parent.prototype);//寄生组合式继承
Child.prototype.constructor = Child;//手动修复Child.prototype的constructor

Child.prototype.sayAge = function () {
  console.log("Child", this.age);
};
const child = new Child("三月的一天", 18);
child.sayName(); //继承父类的fangfa
child.sayAge(); //自己的方法
console.log(child.__proto__ == Child.prototype);
console.log(Child.prototype.__proto__ == Parent.prototype);

原型链断裂通常是由于重写原型对象而导致的。在JavaScript中,每个函数都有一个prototype属性,用于指向原型对象,而原型对象中又有一个constructor属性,指向该函数本身。

当我们重写一个函数的prototype时,实际上是创建了一个新的对象,这个新对象的constructor属性会指向新对象的构造函数,而不再指向原来的函数。

 上面通过Child.prototype = Object.create(Parent.prototype);这行代码,实际上是将Child.prototype指向了一个新的对象prototype,而这个新对象的constructor属性指向的是Parent构造函数,而不是Child构造函数。这就导致了原型链的断裂,因为Child.prototype.constructor不再指向Child构造函数。

为了避免原型链断裂,我们可以手动修复constructor属性,确保它指向正确的构造函数,如您之前提到的Child.prototype.constructor = Child;这行代码所做的操作。

手写call/apply/bind方法

通常,在调用函数时,函数内部的 this 值是访问该函数的对象。使用函数原型上的 call方法/apply方法/bind方法,你可以在调用现有函数时将任意值分配给 this,而无需首先将函数附加到对象上作为属性。这样可以将一个对象的方法用作通用的实用函数

使用场景:说人话就是,当你自己本身没有某个方法,想要借用别的对象上的方法时,可以使用callapply来改变this的指向,使得方法似乎是当前对象自己的方法。这种感觉就像是方法去找对象。所有call/apply/bind的左侧使用方是函数,函数.call(谁调用我,参数拿来)。

具体使用哪个方法,取决于你的具体需求: 

  • 使用call:当你知道函数的参数是哪些,并且想要按顺序传递它们时。
  • 使用apply:当你知道函数的参数是一个数组,并且想要以数组的形式传递这些参数时。
  • 使用bind:当你需要一个新函数,新函数的this指向和参数已经确定时,或者当你需要动态地传递参数时。

手写思路:

将函数(this)设为对象的属性;执行函数,处理入参;删除这个属性;

原生callapply传入的this如果是值类型,会被new Object(如fn.call('abc')

手写call方法

 举一个使用call方法的例子:

var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.call(foo); // 1

 可以看call 改变了 this 的指向,指向到 foo;然后bar 函数执行了。

一般来说,带有this的是构造函数,需要通过new构造函数,拿到构造函数的实例对象,然后通过对象访问方法。但是这里好像在执行foo.bar方法。可是foo又不是构造函数的实例,怎么foo.bar进行方法调用?换个思路,将bar当成是对象的某个属性呢?

那么模拟call的过程,是不是可以假设将bar函数当成是foo的一个属性,foo.属性,对象访问属性

var foo = {
  value: 1,
  fn: function bar() {
    console.log(this.value);
  },
};

foo.fn();

首先call方法思路:将函数设为对象的属性;通过对象.属性执行函数;删除函数,不能真正改变原对象的属性; 如果不传入参数时,设置默认指向window

普通版call方法

Function.prototype.myCall = function (context = window, ...args) {
  context.fn = this;
  let result = context.fn(...args);
  delete context.fn;
  return result;
};

这样我们得到了一个简单的myCall方法。通过给对象context添加一个fn属性,fn的值为函数。然后立刻执行函数。之后将context.fn删除,但是有个问题,如果context对象本身就有fn。这样不就破坏了原始对象的属性了吗?怎么给新增的属性起一个不冲突的名字呢?是不是可以用Symbol啊。

在JavaScript中,Symbol是一种新的原始数据类型,它的实例是唯一且不可变的。由于Symbol的值是唯一且不可变的,所以不能通过点语法直接访问一个Symbol属性。这是因为Symbol属性不会出现在对象的属性枚举中(例如使用for...inObject.keys()),这是符合语言设计的一部分。

为了访问一个Symbol属性,可以使用中括号语法,并将Symbol作为属性名。这是因为Symbol属性不会被覆盖或者更新,因此可以确保在访问时不会发生冲突。

 使用Symbol标识属性

Function.prototype.myCall = function (context = window, ...args) {
  let fn = Symbol(); //将fn属性名称定义为Symbol
  context[fn] = this; //通过[]访问Symbol类型变量,将this当前函数赋给value
  let result = context[fn](...args); //调用方法
  delete context[fn]; //删除属性fn
  return result;
};

这样是不是差不多了。这里假设context是对象,如果context是普通类型,第二行就会报错。而原始call方法是不会报错的。这个时候要对基本类型进行转换。

处理非object类型绑定

//添加函数原型myCall方法
Function.prototype.myCall = function (context = window, ...args) {
  if (typeof context != "object") {
    //非object类型的,手动转object
    context = new Object(context);
  }
  let fn = Symbol(); //将fn属性名称定义为Symbol
  context[fn] = this; //通过[]访问Symbol类型变量,将this当前函数赋给value
  let result = context[fn](...args); //调用方法
  delete context[fn]; //删除属性fn
  return result;
};

测试call方法

手写apply方法

apply和call的差异就是入参的区别

  • call()方法接收的是参数列表,即每个参数都需要单独传递。
  • apply()方法接收的是一个包含多个参数的数组,即所有参数都放在一个数组中传递。

 将call的手写入参改一下就好了

Function.prototype.myApply = function (context = Window, args) {
  //context myApply传入的对象
  //this 调用myApply的函数
  //args this需要的参数
  if (typeof context != "object") {
    context = new Object(context);
  }
  let fn = Symbol();
  context[fn] = this;
  let result = context[fn](...args); //实际函数的入参一定是全展开的
  delete context[fn];
  return result;
};

测试apply方法

传入数组args成功解析

 

手写bind方法

bind()是JavaScript中函数的一个内置方法,用于创建一个新的函数,该函数在调用时,this值会被绑定到传给bind()方法的值上,并可以传入其他参数。

 bind使用场景

函数柯里化,将多参函数转换为单参函数

function add(a, b, c) {
  return a + b + c;
}
const add5 = add.bind(null, 5);
console.log(add5(3, 4)); // 12

 在类的构造函数中使用 bind() 函数来创建一个新的构造函数。

class MyClass {
  constructor(name) {
    this.name = name;
  }
  sayName() {
    console.log(this.name);
  }
}
const obj = {
  name: 'obj'
};
const boundClass = MyClass.bind(obj);
const boundObj = new boundClass('boundObj');
boundObj.sayName(); // 'boundObj'

 将事件监听器函数绑定到某个对象上,并在事件触发时保持对象的上下文。

const obj = {
  name: 'obj',
  handleEvent: function(event) {
    console.log(this.name + ' handled event: ' + event);
  }
};
const button = document.getElementById('myButton');
button.addEventListener('click', obj.handleEvent.bind(obj));

普通版bind方法

根据bind的作用,调用的函数fn.bind(调用对象obj,...部分参数1)生成一个新的函数boundFn。然后对这个新的boundFn函数传参,调用它 boundFn(...剩余参数)。

我们得到下面的bind方法。

将当前this保存给fn。因为当前不执行fn函数,而是在boundFn函数内部执行。

myBind方法返回boundFn函数。boundFn这个函数内部还继续接收入参。将闭包内首次入参args与innerArgs合并,作为fn全部的入参。

//myBind方法的调用形式let boundFn =fn.myBind(obj,...args)
//fn是对象想要使用的函数
//fn是调用方法的对象,args是bind时候传入的函数入参的部分或全部参数
Function.prototype.myBind = function (context = window, ...args) {
  const fn = this; //这里的this是fn
  function boundFn(...innerArgs) {
    //创建一个新函数boundFn
    return fn.apply(context, args.concat(innerArgs)); //执行fn,fn从闭包中的this获取,所以this要提前存给fn;fn的参数要将两次入参拼起来
  }
  return boundFn; //返回新函数 新函数执行boundFn(...innerArgs)
};

定义一个内部函数 boundFn,它接收任意数量的剩余参数 ...innerArgs。boundFn 函数内部,使用 fn.apply() 方法调用原函数 fn,并将 fn 函数的执行上下文设置为指定的 context 对象,将原函数 fn 的参数 args 与新传入的参数 innerArgs 合并后作为入参传递给 fn这样即使调用bind分两次传参,底层还是将两个参数合并了。

返回 boundFn 函数,这个函数可以在调用时传入参数,并在指定的 context 对象上执行原函数 fn

 由于函数还包括了构造函数。如果原始fn是个构造函数,那么新的boundFn也是构造函数,构造函数要通过new得到实例。这时fn实际执行的context就是 new boundFn后的对象,也就是boundFn里的this。

用作构造函数boundFn处理

对boundFn函数内部执行fn方法的context做判断。如果boundFn用作构造函数,那么fn里的context就是this。

myBind里出现了两个this。第一个this是fn函数,实际执行的函数,在闭包中被fn保留下来。

第二个this是bind返回的函数调用方,也就是new后的对象。这两个this是不同的意思。

为什么用this instanceof boundFn?

因为newObj = new boundFn()得到,instanceof可以判断实例是否通过构造函数生成的

//myBind方法的调用形式let boundFn =fn.myBind(obj,...args)
//fn是对象想要使用的函数
//fn是调用方法的对象,args是bind时候传入的函数入参的部分或全部参数
Function.prototype.myBind = function (context = window, ...args) {
  const fn = this; //这里的this是fn
  function boundFn(...innerArgs) {
    //创建一个新函数boundFn
    context = this instanceof boundFn ? this : context;//如果boundFn被当做构造函数,执行fn的对象就是当前的this
    return fn.apply(context, args.concat(innerArgs)); //执行fn,fn从闭包中的this获取,所以this要提前存给fn;fn的参数要将两次入参拼起来
  }
  return boundFn; //返回新函数 新函数执行boundFn(...innerArgs)
};

这里构造函数还没考虑继承,如果fn也是一个构造函数呢?,boundFn是不是还要处理原型链关系。原型继承是不是要用“子类原型=Object.create(父类构造函数原型)’模板继承

考虑构造函数继承

创建新的函数跟new的过程差不多,都要处理继承。将boundFn与fn建立原型链。这样新返回的函数就能跟fn执行一样的方法了。

//myBind方法的调用形式let boundFn =fn.myBind(obj,...args)
//fn是对象想要使用的函数
//fn是调用方法的对象,args是bind时候传入的函数入参的部分或全部参数
Function.prototype.myBind = function (context = window, ...args) {
  const fn = this; //这里的this是fn
  function boundFn(...innerArgs) {
    //创建一个新函数boundFn
    context = this instanceof boundFn ? this : context; //如果boundFn被当做构造函数,执行fn的对象就是当前的this
    return fn.apply(context, args.concat(innerArgs)); //执行fn,fn从闭包中的this获取,所以this要提前存给fn;fn的参数要将两次入参拼起来
  }
  boundFn.prototype = Object.create(fn.prototype); //考虑fn也是构造函数,boundFn要继承
  return boundFn; //返回新函数 新函数执行boundFn(...innerArgs)
};

 测试bind方法

// 测试用例
function Person(age, job, gender) {
  console.log(this.name, age, job, gender);
}
var obj = {
  name: "三月的一天",
};
// let result = Person.myBind(obj, 22, "前端开发")("female");
let bindFn = Person.myBind(obj, 22);
bindFn("前端开发", "female");

 测试结果,bind后参数可以分开给,统一执行Person方法

 

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

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

相关文章

深入理解JVM后端优化技术-逃逸分析(Escape Analysis)

相关系统 深入理解jvm执行引擎-CSDN博客 深入理解JVM后端优化技术-方法内联-CSDN博客 定义 当一个对象在方法里面被定义后,它可能让外部方法所引用,作为调用参数传递到其它的方法中,这种称为方法逃逸;还有可能被外部线程访问到,赋值给可以在其它线程中访问的实例数量,这…

鼠标灵敏度怎么调,鼠标灵敏度怎么调最稳

鼠标和键盘是操作计算机过程中使用最频繁的设备之一,用电脑的时,我敢说你一定离不开鼠标。有些用户发现鼠标不太好用,尤其是在游戏时,总觉得鼠标移动太慢了。另外,如果你感觉鼠标按键失灵、鼠标单击变双击以及反应迟钝…

JVM的简单介绍

目录 一、JVM的简单介绍 JVM的执行流程 二、JVM中的内存区域划分 1、堆(只有一份) 2、栈(可能有N份) 3、程序计数器(可能有N份) 4、元数据区(只有一份) 经典笔试题 三、JVM…

C++类与对象中(个人笔记)

类与对象中 类的6个默认成员函数1.构造函数1.1特性 2.析构函数2.1特性 3.拷贝构造函数3.1特性 4.赋值运算符重载4.1特性 5.日期类的实现6.const成员6.1const成员的几个问题 7.取地址及const取地址操作符重载 类的6个默认成员函数 如果一个类中什么成员都没有,简称为…

k8s集群node节点状态为Not Ready

目录 一、Node节点Not Ready状态的可能原因 二、排查node节点状态为Not Ready的原因 一、Node节点Not Ready状态的可能原因 node节点状态为Not Ready可能的原因有: 1.网络插件出问题 有过安装经验的小伙伴应该很熟悉未安装网络插件的情况下node节点在集群中的状…

nacos分布式程序开发实例

1.通过windows docker desktop 完成 nacos 的安装/启动/配置 (1)先安装docker desktop docker-toolbox-windows-docker-for-windows-stable安装包下载_开源镜像站-阿里云 (2)配置docker 国内镜像源 Docker 镜像加速 | 菜鸟教程…

【相机方案】智能驾驶的域控采用的“串行器和解串器”方案的总结(持续更新),SerDes,GMSL

SerDes是Serializer/Deserializer的缩写,即串行器和解串器。由于同轴线的传输延迟几乎可以忽略不计(ns级别),相当于将原来只能短距离传输的高速并行信号(MIPI/I2C/CLK等)的传输距离延长,真正做到高带宽、低延迟、长距离…

【uniapp】开发微信小程序 — 注意事项

底部导航栏 (tabBar) 图标的正确做法: 1、图片的标准尺寸为 81px * 81px,该尺寸在官方的文档中有明确的说明,可以参考微信小程序全局配置文档中对 iconPath 属性的说明。 2、为了保持良好的间距,图片的内容区域设置 60px* 比较好&…

vue iview table实现全选

之前我们在文章《iview Table实现跨页勾选记忆功能以及利用ES6的Map数据结构实现根据id进行对象数组的去重》里实现过全选功能,不过那有一个弊端就是需要调接口一次性获取全部的数据,这会造成请求数据响应超时或报错,因为数据量大的话这样体验也不好,于是我们改了一下,因为…

全新4.0版本圈子社交论坛系统 ,可打包小程序,于TP6+uni-app 全开源 可打包小程序app uniapp前端+全开源+独立版

简述 首先 圈子系统的核心是基于共同的兴趣或爱好将用户聚集在一起,这种设计使得用户能够迅速找到与自己有共同话题和兴趣的人。 其次 圈子系统提供了丰富的社交功能,如发帖、建圈子、发活动等,并且支持小程序授权登录、H5和APP等多种形式…

【opencv】示例-cout_mat.cpp cout输出各种格式矩阵、向量

/** cvout_sample 只是演示了 cv::Mat 的序列化输出能力。* 也就是说&#xff0c;现在可以这样使用&#xff1a;cv::Mat M(...); cout << M;。*/#include "opencv2/core.hpp" // 包含OpenCV核心功能的头文件 #include <iostream> // 包含标准输入输出流的…

【opencv】示例-create_mask.cpp 通过鼠标交互产生掩膜图像(黑白图像)

/* * create_mask.cpp * * Author: * Siddharth Kherada <siddharthkherada27[at]gmail[dot]com> * * 这个教程演示了如何制作掩膜图像&#xff08;黑白图像&#xff09;。 * 该程序将输入图像作为源图像&#xff0c;并输出对应的掩膜图像。 */#include "opencv2/im…

Matlab的SimuLink对NXP的S32K3xx环境部署

利用官方发布的MBD搭载SimuLink对S32K3xx进行程序设计 优点&#xff1a; 1、免除C代码的系统配置&#xff0c;快速上手。 2、可利用SimuLink自带的算法库进行设计&#xff0c;免除算法设计的烦恼 3、图形化编程&#xff0c;逻辑清晰&#xff0c;便与维护和修改 等 方法&a…

SEO优化艺术:精细化技巧揭示与搜索引擎推广全面战略解读

SEO&#xff08;搜索引擎优化&#xff0c;Search Engine Optimization&#xff09;是一种网络营销策略&#xff0c;旨在通过改进网站内外的各项元素&#xff0c;提升网站在搜索引擎自然搜索结果中的排名&#xff0c;从而吸引更多目标用户访问网站&#xff0c;增加流量&#xff…

C++类与对象下(个人笔记)

类与对象下 1.构造函数补充1.1构造函数体赋值1.2初始化列表1.3explicit关键字 2.static成员2.1特性 3.友元3.1友元函数3.2友元类 4.内部类5.匿名对象6.拷贝对象的一些优化7.笔试题 1.构造函数补充 1.1构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xf…

掌握Linux虚拟网络设备:从基础到应用的全面指南

在现代计算环境中&#xff0c;尤其是云计算☁️、容器化&#x1f4e6;和微服务架构&#x1f3d7;️大行其道的时代&#xff0c;了解和掌握Linux虚拟网络设备变得极为重要。本文将深入探讨Linux虚拟网络设备的世界&#xff0c;带你了解它们是什么、包含哪些类型、为什么需要它们…

《深入浅出Spring Boot 3.x》正式出版了一周

各位&#xff0c;我编写的《深入浅出Spring Boot 3.x》已经正式发版了。 目前在京东已经开始销售了&#xff0c;希望有需要的朋友多多支持哦。 尽量采用Java 8后的语法编写&#xff0c;采用JDK 17去使用Jarkata EE 9。 相关内容如下&#xff1a;

(虚拟DOM)前端八股文修炼Day10

一 虚拟 DOM 是什么 虚拟 DOM (Virtual DOM) 本质上是真实 DOM 的一个轻量级的 JavaScript 表示形式。它是一个在内存中的抽象&#xff0c;用于描述真实 DOM 的结构和内容。虚拟 DOM 提供了一种机制&#xff0c;允许开发者通过操作 JavaScript 对象来间接更新页面&#xff0c;…

kafka(四)——生产者流程分析(c++)

前言 kafka生产者负责将数据发布到kafka集群的主题&#xff1b;kafka生产者消息发送方式有两种&#xff1a; 同步发送异步回调发送 流程 流程说明&#xff1a; Kafka Producer整体可看作是一个异步处理操作&#xff1b;消息发送过程中涉及两个线程&#xff1a;main线程和se…

一文读懂RISC-V与ARM

RISC-V和ARM是近年来备受关注的两种处理器架构。RISC-V是一种基于精简指令集计算(RISC)原理的开源指令集架构(ISA)&#xff0c;而ARM是一种专有ISA&#xff0c;由于其长期存在于嵌入式系统和移动设备中&#xff0c;已成为嵌入式系统和移动设备的主导选择。市场以及多年积累的信…