对象,类与面向对象编程 上

news2024/12/25 22:08:48

目录

前言

理解对象

 属性的类型

数据属性【Data Properties】

访问器属性【Accessor Properties】

 合并对象

 对象标识及相等判定

增强的对象语法

1. 属性值简写

2.可计算属性

3.简写方法名

4.对象解构

5.嵌套解构

创建对象 

工厂模式

 构造函数模式

原型模式

原型是如何工作的

 原型层级 

原型和in操作符 

属性枚举顺序

对象迭代

 另一种原型语法

 原型的动态性

原生对象原型 

 原型的问题


前言

ECMA-262 将对象定义为一组属性的无序集合。严格来说,这意味着对象就是一组没有特定顺序的值。对象的每个属性或方法都由一个名称来标识,这个名称映射到一个值。正因为如此(以及其他还未讨论的原因),可以把ECMAScript 的对象想象成一张散列表,其中的内容就是一组名/值对,值可以是数据或者函数。

理解对象


创建自定义对象的通常方式是创建Object 的一个新实例,然后再给它添加属性和方法,如下例所示: sayName()方法会显示this.name 的值,这个属性会解析为person.name

let person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";
person.sayName = function() {
	console.log(this.name);
};

后来对象字面量的方式越来越受欢迎

let person = {
 name: "Nicholas",
 age: 29,
 job: "Software Engineer",
 sayName() {
  console.log(this.name);
 }
};

 属性的类型

ECMA-262 标准定义了一些只内部使用的属性,这些属性分为两类:数据属性和访问器属性。为了表明这些属性是内部使用,它们被双中括号包裹着。

数据属性【Data Properties】

  • [[Configurable]] 表明属性是否可以通过 delete 重新定义,默认为true
  • [[Enumerable]] 表明属性是否可以通过 for-in 循环返回,默认为true
  • [[Writable]] 表明是否可以更改属性值,默认为true
  • [[Value]] 包含属性的真正值,默认值是 undefined

当用对象字面量定义一个属性值时,[[Configurable]],[[Enumerable]], 和[[Writable]]都被设为 true 同时[[Value]]属性被设为给定的值,

修改属性的默认特性,就必须使用Object.defineProperty()方法。这个方法接收3 个参数:

  • 要给其添加属性的对象
  • 属性的名称
  • 一个描述符对象
    let person = {};
    Object.defineProperty(person, "name", {
        configurable: false,
        writable: false,

        value: "Nicholas"
    });
    console.log(person.name); // "Nicholas"
    person.name = "Greg";
    console.log(person.name); // "Nicholas" 
    delete person.name;
    console.log(person.name); // "Nicholas"
    // 无法重新定义属性:name
    Object.defineProperty(person, "name", {
        configurable: true,
        value: "Nicholas"
    });

设置 writable 为false之后企图改变属性的值没有成功,严格模式下则会抛错

设置 configurable 为false之后企图删除属性没有成功。

设置了 configurable 为false之后,不能再设置为 true,企图设置为 true一律报错

访问器属性【Accessor Properties】

  • [[Get]] 获取一个属性值,默认值是 undefined
  • [[Set]] 设置一个属性值,默认值是 undefined
    let book = {};
    Object.defineProperties(book, {
        year_: {
            value: 2017
        },

        edition: {
            value: 1
        },

        year: {
            get() {
                return this.year_;
            },

            set(newValue) {
                if (newValue > 2017) {
                    this.year_ = newValue;
                    this.edition += newValue - 2017;
                }
            }
        }
    });
book.year = 2018;
console.log(book.edition); // 2

读取属性值

Object.getOwnPropertyDescriptor() 方法接收俩个参数:对象本身和属性名

 合并对象

ECMAScript 6 专门为合并对象提供了Object.assign()方法。这个方法接收一个目标对象和一个或多个源对象作为参数,

然后将每个源对象中可枚举(Object.propertyIsEnumerable()返回true)和自有(Object.hasOwnProperty()返回true)

属性复制到目标对象。以字符串和符号为键的属性会被复制。对每个符合条件的属性,这个方法会使用源对象上的[[Get]]取得属性的值,然后使用目标对象上的[[Set]]设置属性的值。

let dest, src, result;
/**
* 简单复制
*/
dest = {};
src = { id: 'src' };
result = Object.assign(dest, src);
// Object.assign 修改目标对象
// 也会返回修改后的目标对象
console.log(dest === result); // true
console.log(dest !== src); // true
console.log(result); // { id: src }
console.log(dest); // { id: src }


因此,Object.assign()实际上对每个源对象执行的是浅复制。如果多个源对象都有相同的属性,则使用最后一个复制的值。

 对象标识及相等判定

在 ECMAScript6 之前,全等 === 符号不能判断某些情形

console.log(true === 1);  // false
console.log({} === {});   // false
console.log("2" === 2);   // false

// These have different representations in the JS engine and yet are treated as equal
console.log(+0 === -0);  // true
console.log(+0 === 0);   // true
console.log(-0 === 0);   // true

// To determine NaN equivalence, the profoundly annoying isNaN() is required
console.log(NaN === NaN); // false
console.log(isNaN(NaN));  // true

ES6 新增了 Object.is() 修复这些问题,如果要判断的对象大于2,则使用递归 

function recursivelyCheckEqual(x, …rest) {
 return Object.is(x, rest[0]) && 
     (rest.length < 2 || recursivelyCheckEqual(…rest));
}

增强的对象语法

1. 属性值简写

当属性名与变量名一样时:

let name = 'Matt';
let person = {
	name
};
console.log(person); // { name: 'Matt' }

2.可计算属性

有了可计算属性,就可以在对象字面量中完成动态属性赋值。中括号包围的对象属性键告诉运行时将其作为JavaScript 表达式而不是字符串来求值:

const nameKey = 'name';
const ageKey = 'age';
const jobKey = 'job';
let uniqueToken = 0;

function getUniqueKey(key) {
	return `${key}_${uniqueToken++}`;
}

let person = {
	[getUniqueKey(nameKey)]: 'Matt',
	[getUniqueKey(ageKey)]: 27,
	[getUniqueKey(jobKey)]: 'Software engineer'
};

console.log(person); // { name_0: 'Matt', age_1: 27, job_2: 'Software engineer' }

3.简写方法名

在给对象定义方法时,通常都要写一个方法名、冒号,然后再引用一个匿名函数表达式,如下所示:

let person = {
	sayName: function(name) {
		console.log(`My name is ${name}`);
	}
};
person.sayName('Matt'); // My name is Matt

let person = {
	sayName(name) {
		console.log(`My name is ${name}`);
	}
};
person.sayName('Matt'); // My name is Matt

4.对象解构

可以在一条语句中使用嵌套数据实现一个或多个赋值操作。简单地说,对象解构就是使用与对象匹配的结构来实现对象属性赋值。通过匹配来获取值

// 使用对象解构
let person = {
	name: 'Matt',
	age: 27
};
let { name: personName, age: personAge } = person;
console.log(personName); // Matt
console.log(personAge); // 27
  • 解构赋值不一定与对象的属性匹配,当引用的属性不存在时,会赋值undefined:
  • 可以在解构赋值的同时定义默认值,若匹配成功会覆盖默认值:
  • 当解构值的数量与对象的属性数量不等时,若值的数量小于属性数量会赋值前面的属性,若大于则会报错。
  • 解构并不要求变量必须在解构表达式中声明。

解构在内部使用函数ToObject()(不能在运行时环境中直接访问)把源数据结构转换为对象。
即会把上例中的person视为一个对象进行处理
这意味着在对象解构的上下文中,原始值会被当成对象。这也意味着(根据ToObject()的定义),null和undefined 不能被解构,否则会抛出错误(这两个类型不能被当成对象进行处理)。
 

let { _ } = null; // TypeError
let { _ } = undefined; // TypeError

5.嵌套解构

解构对于引用嵌套的属性或赋值目标没有限制。为此,可以通过解构来复制对象属性:

let person = {
	name: 'Matt',
	age: 27,
	job: {
		title: 'Software engineer'
	}
};
let personCopy = {};

({
	name: personCopy.name,
	age: personCopy.age,
	job: personCopy.job
} = person);

// 因为一个对象的引用被赋值给personCopy,所以修改
// person.job 对象的属性也会影响personCopy
person.job.title = 'Hacker'

console.log(person);
// { name: 'Matt', age: 27, job: { title: 'Hacker' } }

console.log(personCopy);
// { name: 'Matt', age: 27, job: { title: 'Hacker' } }

需要注意的是,涉及多个属性的解构赋值是一个输出无关的顺序化操作。如果一个解构表达式涉及多个赋值,开始的赋值成功而后面的赋值出错,则整个解构赋值只会完成一部分

在函数参数列表中也可以进行解构赋值。对参数的解构赋值不会影响arguments对象

创建对象 

虽然使用 Object 构造函数或者对象字面量来创建单一对象非常方便,但是不方便创建多个对象。在ES6 之前,没有正式地支持面向对象构造函数----比如类或者继承。不过接下来,你会看到这一过程如何逐渐成功演变。

  • 工厂模式 【factory pattern】
  • 构造函数模式 【constructor pattern】
  • 原型模式 【prototype pattern】

构造函数名称的首字母都是要大写的,非构造函数则以小写字母开头。这是从面向对象编程语言那里借鉴的,有助于在ECMAScript 中区分构造函数和普通函数。

工厂模式

工厂模式在软件设计中非常流行,只要有返回新对象的函数,它不是构造函数或者类,那么就是工厂模式。

unction createPerson(name, age, job){
  let o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function(){
    console.log(this.name);
  };
  return o;
}

let person1 = createPerson("Nicholas", 29, "Software Engineer");
let person2 = createPerson("Greg", 27, "Doctor");

工厂模式解决了批量创建对象的问题,但是没有解决对象识别问题,所有对象原型都是 Object, 无法识别 Array Data 等类型。

 构造函数模式

ECMAScript 里面有原生的构造函数比如 ObjectArray ,同时也可以自定义构造函数,比如上面工厂函数的例子可以写成

function Person(name, age, job){
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = function(){
    console.log(this.name);
  };
}

let person1 = new Person("Nicholas", 29, "Software Engineer");
let person2 = new Person("Greg", 27, "Doctor");

console.log(person1 instanceof Person);    // true,解决了工厂模式中不能识别的问

构造出来的对象的constructor属性会指向原函数:通过这种方式创造出的既是Object的实例又是Person的实例,因此可以体现出对象之间的差异,将对象标识为特定的一种类型。

 构造函数也是函数

构造函数与普通函数唯一的区别就是调用方式不同。除此之外,构造函数也是函数。任何函数只要使用new 操作符调用就是构造函数,而不使用new 操作符调用的函数就是普通函数。

若直接作为函数调用,会将这些属性添加到window对象

new操作符

使用 new 操作符来新建对象实例,调用构造函数会执行如下操作:

在内存中创建一个新对象。
这个新对象内部的[[Prototype]]特性被赋值为构造函数的prototype 属性。
构造函数内部的this 被赋值为这个新对象(即this 指向新对象)。
执行构造函数内部的代码(给新对象添加属性)。
如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。

构造函数的问题 ❤❤❤❤❤❤❤

构造函数的缺点是新建每个实例方法都被创建了一次,比如上面的 person1 和 person2 都有一个方法名叫 sayName(),但是这两个方法不是同一个 Function 构造函数的实例。

逻辑上来说,上面的构造函数长这样

function Person(name, age, job){
 this.name = name;
 this.age = age;
 this.job = job;
 this.sayName = new Function("console.log(this.name)"); // logical equivalent
}

 也就是说,每一个Person 实例的sayName 方法都有一个自己的 Function 构造函数,这种重复是没有必要的,

原型模式

每一个新建的函数都有 prototype 属性,这个 prototype 对象包含了通过同一构造函数创建的所有实例共享的属性和方法。同样的原型模式可以使用函数声明和函数表达式两种形式。

function Person() {}
      
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
 console.log(this.name);
};
      
let person1 = new Person();
person1.sayName();  // "Nicholas"
      
let person2 = new Person();
person2.sayName();  // "Nicholas"
      
console.log(person1.sayName == person2.sayName); // true

在这里,sayName() 方法是挂载在原型对象上,构造函数为空。和2.2构造函数模型不一样的是,原型模式的实例共享所有的属性和方法

原型是如何工作的

 无论何时,只要创建一个函数,就会按照特定的规则为这个函数创建一个prototype 属性(指向原型对象)。原型对象只会默认获得一个名为constructor 的属性,指回与之关联的构造函数(相等于指向构造函数Person),并会默认继承来自于Object的方法(会继承一个属性和多个方法)

正常的原型链都会终止于Object 的原型对象 Object 原型的原型是null 

使用Object.getPrototypeOf()可以方便地取得一个对象的原型,返回参数的内部特性[[Prototype]]的值

setPrototypeOf()方法,可以向实例的私有特性[[Prototype]]写入一个新值(影响性能)

可以通过Object.create()来创建一个新对象,同时为其指定原型:

 原型层级 

每次在访问属性时,总是先在实例上搜索该属性是否存在,不存在则继续在原型上搜索。如果实例和原型同时都有该属性,实例的优先级更高,也就是说在实例上找到了该属性就不再往原型上去找了,即便是该属性值为null。

但是如果使用 delete 操作符移除了该属性,那么搜索会继续走向原型。

function Person() {}
      
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
 console.log(this.name);
};
      
let person1 = new Person();
let person2 = new Person();
      
person1.name = "Greg";
console.log(person1.name); // "Greg" - 来自实例 instance
console.log(person2.name); // "Nicholas" - 来自原型 prototype

hasOwnProperty 方法可以判断属性是存在实例上(返回true)还是原型上(返回false)。

在被遮蔽时,实例上调用hasPrototypeProperty()返回false。不过,使用delete 操作符可以完全删除实例上的这个属性,从而让标识符解析过程能够继续搜索原型对象

ECMAScript 的Object.hasOwnPropertyDescriptor()方法只对实例属性有效。

要取得原型属性的描述符,就必须直接在原型对象上调用Object.getOwnPropertyDescriptor()。如果指定的属性存在于对象上,则返回其属性描述符对象(property descriptor),否则返回 undefined。

原型和in操作符 

主要有两种方式使用 in 操作符:直接在对象上操作和在 for-in 循环中操作。

直接在对象上使用,会同时检测实例上和原型上是否存在该属性。

function Person(){}

Person.prototype.name = "God";

let abby = new Person();

abby.hasOwnProperty("name");   // false 只检测实例上
console.log("name" in abby);   // true  会同时检测实例上和原型上是否存在该属性

使用这个特性还可以检测只存在原型上的属性

function hasPrototypeProperty(object, name){
  return !object.hasOwnProperty(name) && (name in object);
}  

使用 for-in 循环时,会返回所有可以枚举的对象属性,包括实例上的和原型上的。

Object.keys

Object.keys()方法会返回一个由一给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致

function Person() {}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
 console.log(this.name);
};

let keys = Object.keys(Person.prototype);
console.log(keys); // ["name", "age","job", "sayName"]
let p1 = new Person();
p1.name = "Rob";
p1.age = 31;
let p1keys = Object.keys(p1);
console.log(p1keys); // ["name", "age"]

Object.getOwnPropertyNames

如果想得到包括不可枚举的属性,使用 Object.getOwnPropertyNames() 方法,注意 constructor 是不可枚举属性

let keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys); // ["constructor", "name","age","job", "sayName"]

Object.keys()Object.getOwnPropertyNames() 都是 for-in 循环很好的替代品

 Object.getOwnPropertySymbols()

针对 ES6 新增的 Symbols 类型,Object.getOwnPropertyNames() 方法不再奏效,因为Symbols 没有属性名一说,所以引入了 Object.getOwnPropertySymbols() 

let k1 = Symbol('k1'),
    k2 = Symbol('k2');

let o = {
 [k1]: 'k1',
 [k2]: 'k2'
};

console.log(Object.getOwnPropertySymbols(o));
// [Symbol(k1), Symbol(k2)]

属性枚举顺序

for-in循环,Object.keys(),Object.getOwnPropertyNames/Symbols(), 和Object.assign() 在枚举顺序上有很大的不同。

前两个在枚举顺序上是随机的,这是由JavaScript 引擎和浏览器决定的。

后两者则有确定的枚举顺序:数字类型按照增序,字符串和symbols 类型按照插入顺序,在对象字面量里面的会按照逗号分隔顺序

对象迭代

在JavaScript 很长的历史中,迭代对象属性都不太方便。 ECMAScript 2017 引入了两个静态方法 Object.values()Object.entries() 可以将对象内容转化为序列化的可迭代的形式。

onst o = {
 foo: 'bar',
 baz: 1,
 qux: {}
};

console.log(Object.values(o));
// ["bar", 1, {}]

console.log(Object.entries((o)));
// [["foo", "bar"], ["baz", 1], ["qux", {}]]

 另一种原型语法

注意到上面每次定义原型属性和方法都把 prototype 写了一遍,也可以简写为

function Person() {}
Person.prototype = {
	name: "Nicholas",
	age: 29,
	job: "Software Engineer",
	sayName() {
		console.log(this.name);
	}
};

但这种写法相当于为Person.prototype这个属性赋了新的值,即重写了默认的原型对象。之前的写法是直接修改属性,所以不会影响。

这种写法会将一个新对象赋给Person.prototype,而不再是随着构造函数一起生成的对象,所以这个对象的constructor属性不再是Person函数,而是Object函数

如上所示,使用构造函数生成一个对象实例friend,它的属性继承于原型对象,也包括constructor属性,因此值是Object函数。

但可以通过使用其他方法来定义constructor 属性为构造函数

function Person() {
}
      
Person.prototype = {
 constructor: Person,  // ⚠️ 
 name: "Nicholas",
 age: 29,
 job: "Software Engineer",
 sayName() {
  console.log(this.name);
 }
}; 

// 这种方式等同于
// restore the constructor
Object.defineProperty(Person.prototype, "constructor", {
 enumerable: false,
 value: Person
});

 原型的动态性

因为从原型上搜索值的过程是动态的,所以即使实例在修改原型之前已经存在,任何时候对原型对
象所做的修改也会在实例上反映出来。下面是一个例子:

let friend = new Person();
Person.prototype.sayHi = function() {
	console.log("hi");
};
friend.sayHi(); // "hi",没问题!

即使在实例定义之后修改原型对象也会在实例中反映出来

之所以会这样,主要原因是实例与原型之间松散的联系。在调用friend.sayHi()时,首先会从这个实例中搜索名为sayHi 的属性。在没有找到的情况下,运行时会继续搜索原型对象。因为实例和原型之间的链接就是简单的指针,而不是保存的副本,所以会在原型上找到sayHi 属性并返回这个属性保存的函数。

但是重写原型对象并不会反映在实例中,因为实例中指向原型对象的指针[[Prototype]]是在实例生成时自动赋值的,因为重写原型对象并不会影响指针,所以实例中的方法和属性还是旧的原型对象上的

function Person() {}
Person.prototype = {
	name: "Nicholas",
	age: 29,
	job: "Software Engineer",
	sayName() {
		console.log(this.name);
	}
};

let friend = new Person();

Person.prototype = {
	name: 'newName',
	sayName() {
    console.log(this.name);
	}
};

friend.sayName();  // Nicholas

原生对象原型 

所有原生引用类型的构造函数(包括Object、Array、String 等)都在原型上定义了实例方法。

通过原生对象的原型可以取得所有默认方法的引用,也可以给原生类型的实例定义新的方法。可以像修改自定义对象原型一样修改原生对象原型,因此随时可以添加方法。
比如,下面的代码就给String原始值包装类型的实例添加了一个startsWith()方法:

String.prototype.startsWith = function (text) {
	return this.indexOf(text) === 0;
};
let msg = "Hello world!";
console.log(msg.startsWith("Hello")); // true

 原型的问题

原型模式弱化了向构造函数传递初始化参数的能力,会导致所有实例默认都取得相同的属性值。

原型的最主要问题源自它的共享特性,当属性包含引用值时,在一个实例上进行修改会导致其他实例的该属性同样会被修改。

浅复制的问题

function Person() {}
Person.prototype = {
	constructor: Person,
	name: "Nicholas",
	age: 29,
	job: "Software Engineer",
	// 引用赋值
	friends: ["Shelby", "Court"],
	sayName() {
		console.log(this.name);
	}
};

let person1 = new Person();
let person2 = new Person();

person1.friends.push("Van");
// 同时被修改
console.log(person1.friends); // "Shelby,Court,Van"
console.log(person2.friends); // "Shelby,Court,Van"
// 指向的是同一个值
console.log(person1.friends === person2.friends); // true

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

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

相关文章

【敲敲云】零代码平台快速入门指南—上篇

本文通过快速搭建一个《客户管理》应用&#xff0c;帮您快速掌握搭建敲敲云应用的基本思路和操作。 一、敲敲云简介 敲敲云是一个APaaS平台,零代码的应用搭建平台,无需编程简单易学,可以帮助业务人员在不写代码的情况下搭建个性化的CRM、ERP、OA等。敲敲云支持自动化工作流还可…

GIOU 附图说明

在目标检测中&#xff0c;常用IOU评价检测框和ground truth框的相似程度&#xff0c; IOU intersection / union. IOU的取值范围是[0,1]. 但是看下面这两种情况&#xff0c;两个目标框的距离是不一样的&#xff0c;这时它们的IOU都是0&#xff0c;反映不出来。 这时就可以用到…

【JavaGuide面试总结】Linux篇

【JavaGuide面试总结】Linux篇1.介绍一下inode2.说一下Linux系统的目录结构3.说一下Linux系统的文件类型1.介绍一下inode 硬盘的最小存储单位是扇区(Sector)&#xff0c;块(block)由多个扇区组成。文件数据存储在块中。块的最常见的大小是 4kb&#xff0c;约为 8 个连续的扇区…

Linux系统编程——共享内存映射(进程间通信)

目录mmap函数1.函数原型2.建立映射区3.注意事项4.父子进程间mmap通信4.无血缘关系进程间mmap通信写进程&#xff1a;读进程&#xff1a;附&#xff1a;文件用于进程间通信mmap函数 1.函数原型 void *mmap(void *addr&#xff0c;size_t length, int prot,int flags&#xff0…

代码随想录训练营第五十六天

1.两个字符串的删除操作 题583 ①dp数组含义 由于有两个数组比较&#xff0c;所以用二维数组dp。dp[i] [j]表示以i-1为结尾的word1和以j-1为结尾的word2达到相等需要删除的元素的个数。 ②递推公式 两种情况&#xff0c;当word[i-1] word2[j-1]时&#xff0c;有没有i-1为结…

【Linux】gcc编译器的使用(程序的翻译过程)

目  录1 程序的翻译1.1预处理&#xff08;进行宏替换&#xff09;1.2 编译&#xff08;生成汇编代码&#xff09;1.3 汇编&#xff08;生成机器可识别代码&#xff09;1.4 链接&#xff08;生成可执行文件或者库文件&#xff09;1.5 gcc常用选项总结程序的翻译过程包括&#…

Diffie-Hellman密钥协商算法探究

作者 | 魔王赵二狗 导读 隐私计算&#xff08;Privacy-preserving computation&#xff09;是指在保证数据提供方不泄露原始数据的前提下&#xff0c;对数据进行分析计算的一系列信息技术&#xff0c;保障数据在流通与融合过程中的可用不可见。而Diffie–Hellman密钥协商是一种…

2023-01-10 mysql列存储引擎-聚合多线程并行扫表-VCPackGuardian策略LOCK_ALL-概要设计

摘要: 当前的pack淘汰策略为LOCK_ONE, 在多线程切换时导致pack地址丢失。 新设计LOCK_ALL策略以保证多线程聚合正常工作。 设计思想: 多线程聚合运算期间, 对持有的pack不做淘汰业务中对pack的读取和释放保持原有逻辑架构设计: 静态结构: 动态结构: 上层业务通过VirtualCol…

【学习笔记之Linux】工具之vim基本介绍

vim基本认识 vim是一种多模式的编辑器&#xff0c;它是vi的升级版本&#xff0c;它兼容vi所有的指令并加入了一些新的特性在里面。vi是一个老式的文本编辑器&#xff0c;功能相当齐全&#xff0c;vim则是在vi之上更进了一步&#xff0c;拥有代码补全、编译及错误跳转等功能&…

pytorch OutOfMemoryError

torch.cuda.OutOfMemoryError before: self.memory deque(maxlen50000) after: self.memory deque(maxlen500) ok.... pytorch模型提示超出内存cuda runtime error(2): out of memory - pytorch中文网 看到这个提示&#xff0c;表示您的GPU内存不足。由于我们经常在PyTo…

做外贸有没有好的软件?

在外贸电商行业中&#xff0c;邮件营销是非常重要的一种营销方式之一。除了性价比高&#xff0c;他还能对目标客户进行精准营销。但是&#xff0c;对于刚开始做的公司来讲&#xff0c;不注意方法和细节也难收获到理想的营销效果。 一、问题 1&#xff09;不管理邮箱联系人 只…

13.Isaac教程--模型制作

模型制作 ISAAC教程合集地址: https://blog.csdn.net/kunhe0512/category_12163211.html 该软件包演示了具有软件定义装配工作流程的工厂场景。 在模拟工厂环境中&#xff0c;多个 AMR 在装配站之间运输材料&#xff0c;而每个装配站的机械臂拾取所需材料并将其放置在对接的 …

自定义el-pagination分页

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; vue项目使用el-ui库&#xff0c;由于原本的el-pagination显示字段和样式无法满足其他项目的设计要求&#xff0c;需要进行改动 el-ui官网&#xff1a; 改动后&#xff1a; 解决方案&#xff1a; 1…

golang字符串常见功能

文章目录1. 获取字符串长度2. 是否一xx开头3. 是否以xx结尾4. 是否包含5. 变大写6. 变小写7. 去两边8. 替换9. 分割10. 拼接11. string转换为int12. int转换为string13. 字符串和字节切片14. 字符串和rune切片15. string和字符1. 获取字符串长度 2. 是否一xx开头 3. 是否以xx结…

抖音seo优化排名

武汉微驱动科技有限公司 你有没有想过&#xff0c;同样是运营抖音&#xff0c;为什么别人的视频总是排在你的前面&#xff1f;你死磕创意&#xff0c;拍摄、剪辑&#xff0c;甚至比同行更投入&#xff0c;为什么他的收益总是高于你&#xff1f; 当下抖音搜索引擎的用户数量已经…

Nginx与LUA(1)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e;HTTP服务器是相对于HTTP客户端来说的——HTTP客户端就是各种常用的「浏览器」&#xff0c;如IE、chrome、微信浏览器。当浏览器通过URL地址栏访问一个Web页面时&a…

【C++】STL六大组件之一——适配器(adapters)

目录1. 前言2. 初始适配器2.1 适配器的概念2.2 适配器的分类3. 容器适配器&#xff08;container adapters&#xff09;3.1 认识deque3.1.1 逻辑结构3.1.2 物理结构3.1.3 deque的迭代器3.1.4 选择deque做stack/queue底层容器的原因3.2 stack3.3 queue3.4 另一种容器适配器 ——…

阿里云计算巢 x GBase GCDW:自动化部署云原生数据仓库

近日&#xff0c;阿里云计算巢与天津南大通用数据技术股份有限公司&#xff08;以下简称&#xff1a;GBASE&#xff09;合作&#xff0c;双方融合各自技术优势&#xff0c;助力企业用户实现云上数据仓库的自动化部署&#xff0c;让用户在云端获取数据仓库服务“更简单”&#x…

【ESP32+freeRTOS学习笔记-(六)软件定时器】

目录1、软件定时器概念2、软件定时器的运行机制2.1 组成2.2 创建2.3 运行3、软件定时器的属性和状态3.1 定时器的周期3.2 定时器的类型3.3 定时器的状态4、软件定时器的回调函数原型5、定时器的使用5.1 创建定时器xTimeCreate()5.2 启动定时器xTimerStart()5.3 终止定时器xTime…

IPC进程间通信-system V 共享内存

&#x1f9f8;&#x1f9f8;&#x1f9f8;各位大佬大家好&#xff0c;我是猪皮兄弟&#x1f9f8;&#x1f9f8;&#x1f9f8; 文章目录一、共享内存原理二、共享内存的建立原理三、共享内存的创建四、共享内存的删除五、共享内存挂接到自己的地址空间六、从进程地址空间去掉与…