关于 js:2. 对象与原型链

一、对象

对象是:

  • 键值对集合

  • 所有非原始类型(number、string、boolean、null、undefined、symbol、bigint)都是对象

  • 支持动态增删属性

  • 每个对象都继承自 Object.prototype,具备原型链结构

1. 对象的创建方式

字面量方式(最常见)

const user = {name: 'Tom',age: 30
};

使用 new Object()

const user = new Object();
user.name = 'Tom';

使用构造函数

function Person(name) {this.name = name;
}
const p = new Person('Tom');

使用 Object.create()

const proto = { sayHi() { console.log('hi'); } };
const obj = Object.create(proto);

2. 对象属性的类型

属性可以分为两种:

类型描述
数据属性value 值,比如 obj.name = 'Tom'
访问器属性getset 函数,比如下面代码
const obj = {get age() {return 18;}
};

3. 对象与原型链关系

每个对象都有一个隐藏属性 [[Prototype]],即 __proto__,它指向另一个对象,从而形成“原型链”:

const obj = {};
console.log(obj.__proto__ === Object.prototype); // true

4. 总结

对象的用途

  • 存储和组织数据

  • 构造复杂结构(如 DOM、组件、Vue/React 对象)

  • 原型链和继承的核心单位

  • JavaScript 中一切皆对象(函数、数组、日期等)

对象就是一个动态的、键值对的集合,它可以存储数据、函数、还可以通过原型链实现继承,是 JavaScript 编程的核心结构。


二、对象属性

1. 枚举性(enumerable)

什么是“可枚举”?

在 JavaScript 中,对象的属性有一个 enumerable 属性,它表示这个属性是否可以通过枚举方式被遍历出来,如:

  • for...in

  • Object.keys()

默认创建的属性(如 obj.a = 1)是 可枚举的

const obj = {a: 1,b: 2
};for (let key in obj) {console.log(key);  // 输出 a 和 b
}

不可枚举属性

可以使用 Object.defineProperty 创建不可枚举属性:

/*
* Object.defineProperty:这是 JavaScript 提供的精细控制属性行为的方式。
* hidden:为 obj 创建的属性
* enumerable: false  不可枚举(非 enumerable)
* */const obj = {};
Object.defineProperty(obj, 'hidden', {value: 42,enumerable: false
});console.log(Object.keys(obj)); // []
console.log('hidden' in obj);  // true 仍然存在,只是不参与遍历

判断枚举性的方法

/* 输出:
{value: 42,writable: false,enumerable: false,configurable: false
}
*/
Object.getOwnPropertyDescriptor(obj, 'hidden');Object.propertyIsEnumerable.call(obj, 'hidden'); // false

2. in 操作符

in 是 JavaScript 中用来检查某个 属性是否存在于对象中 的运算符,包括对象自身属性和原型链上的属性

对象自身属性

const obj = { name: 'Alice' };
console.log('name' in obj);  // true
console.log('age' in obj);   // false

原型链属性

const arr = [];
console.log('push' in arr);      // true,来自 Array.prototype
console.log('toString' in arr);  // true,来自 Object.prototype

不可枚举属性也能检测

const obj = {};
Object.defineProperty(obj, 'hidden', {value: 42,enumerable: false
});
console.log('hidden' in obj); // true

in 不关心属性是否可枚举,只要属性在对象本身或其原型链上,就返回 true

in 与其他方式的对比

检查方式是否包含原型链属性是否受 enumerable 影响
'prop' in obj 是 否
obj.hasOwnProperty('prop') 否(只查自身) 否
Object.keys(obj).includes() 否(只查自身) 是(仅枚举属性)

例子:

const obj = Object.create({ a: 1 });
obj.b = 2;console.log('a' in obj);               //  true,来自原型
console.log(obj.hasOwnProperty('a'));  //  false

在逆向中的典型使用

判断反调试钩子是否注入

if ('debugger' in window) {// 表明 window 上定义了 debugger 属性(哪怕不可见)
}

检测对象是否被注入属性(例如:hook、proxy)

if ('__hooked__' in window) {alert('被 hook 了');
}

3. getter 和 setter(访问器属性)

在 JavaScript 中,对象的属性分为两类:

类型行为方式
数据属性储存具体的值
访问器属性通过函数获取或设置值(getter/setter)

访问器属性不直接保存值,而是通过函数动态返回或设置值

基本语法:定义 getter 和 setter

使用对象字面量

const person = {firstName: 'John',lastName: 'Doe',get fullName() {return this.firstName + ' ' + this.lastName;},set fullName(value) {const parts = value.split(' ');this.firstName = parts[0];this.lastName = parts[1];}
};console.log(person.fullName);     // John Doe(调用 getter)
person.fullName = 'Jane Smith';   // 调用 setter
console.log(person.firstName);    // Jane

使用 Object.defineProperty

const obj = {};
Object.defineProperty(obj, 'secret', {get() {return 'Hidden value';},set(val) {console.log('Attempted to set secret to:', val);},enumerable: true
});console.log(obj.secret);      // "Hidden value"
obj.secret = '123';           // 打印: Attempted to set secret to: 123

识别访问器属性

Object.getOwnPropertyDescriptor(obj, 'secret');

输出:

{get: ƒ,set: ƒ,enumerable: true,configurable: true
}

二、构造函数

构造函数就是用来批量创建对象的函数模板,约定俗成首字母大写。

function Person(name, age) {this.name = name;this.age = age;this.sayHi = function() {console.log('Hi, I am ' + this.name);};
}const p1 = new Person('Tom', 18);
const p2 = new Person('Jerry', 20);

每次 new Person()

  • 创建一个新对象 {}

  • 将这个对象的 __proto__ 指向 Person.prototype

  • 把构造函数中的 this 绑定到新对象;

  • 自动返回这个对象(如果没手动 return 其他对象)。

1. 构造函数的工作原理

function Foo(x) {this.x = x;
}
let obj = new Foo(10);

等价于底层执行过程:

let obj = {};                             // 创建一个新对象
obj.__proto__ = Foo.prototype;           // 原型链接
Foo.call(obj, 10);                       // 执行构造函数
return obj;                              // 返回新对象

2. 构造函数 vs 普通函数

特性构造函数普通函数
调用方式new Constructor()func()
this 指向新对象全局 / 调用者 / 严格模式
返回值默认是什么?新建的对象undefined(或函数返回)
是否连接原型链 是,obj.__proto__ = Foo.prototype 否

3. 构造函数与原型的关系

构造函数本身有一个默认属性 prototype,用于存储共享给所有实例的方法和属性。

function Person(name) {this.name = name;
}
Person.prototype.sayHi = function() {console.log('Hi, I am ' + this.name);
};const p = new Person('Alice');
p.sayHi();  // 来自原型

构造函数与实例的关系图:

Person ——prototype——→ { sayHi }↑                      ↑|                      |new                   __proto__|                      |+——→ p(实例对象) ←——+

核心关系:

p.__proto__ === Person.prototype       
Person.prototype.constructor === Person 

4. 构造函数中的共享陷阱

如果在构造函数中定义引用类型(如数组/对象),每个实例都会拥有独立的副本。

function Dog() {this.favorites = [];
}
let d1 = new Dog();
let d2 = new Dog();
d1.favorites.push('bone');
console.log(d2.favorites); // [],互不影响

但如果把数组定义在 prototype 上,就会被所有实例共享:

function Cat() {}
Cat.prototype.favorites = [];
let c1 = new Cat();
let c2 = new Cat();
c1.favorites.push('fish');
console.log(c2.favorites); // ['fish'] 被共享了

5. 构造函数返回值规则

function A() {this.name = 'A';return { name: 'B' }; // 返回的是对象
}
console.log(new A().name); // 'B'function B() {this.name = 'B';return 123;  // 原始值被忽略
}
console.log(new B().name); // 'B'

结论: 构造函数显式返回一个“对象”时会覆盖默认返回值;返回原始值时会被忽略。

6.总结

概念说明
构造函数本质使用 function 定义的模板函数,用于 new 创建对象
this指向新创建的对象
prototype构造函数用于定义所有实例共享属性的方法位置
__proto__实例对象的隐藏属性,链接到构造函数的 prototype
关键关系obj.__proto__ === Constructor.prototype
返回值特例返回对象会替换新建对象,返回原始值无效

三、原型对象

每一个通过 构造函数 创建的函数(非箭头函数)都有一个默认属性:prototype,它指向一个对象,这个对象用于为所有实例对象共享方法和属性

function Person(name) {this.name = name;
}Person.prototype.sayHi = function() {console.log('Hi, I am ' + this.name);
};const p1 = new Person('Tom');
p1.sayHi(); // 来自原型对象

构造函数、实例和原型对象的关系

结构图:

Person ——prototype——→ { sayHi }↑                      ↑|                      |new                   __proto__|                      |+——→ p1(实例对象) ←——+

对比关键点:

名称指向 / 作用
Person.prototype构造函数的原型对象
p1.__proto__实例的原型对象,等于 Person.prototype
sayHi 方法定义在 Person.prototype 上,被所有实例共享

几个关键关系

p1.__proto__ === Person.prototype           //  实例指向构造函数原型
Person.prototype.constructor === Person     //  原型对象的 constructor 回指
Object.getPrototypeOf(p1) === Person.prototype //  等价写法

原型对象上的属性会被共享

原型上的方法和属性不是复制到每个实例中,而是通过原型链访问:

function Animal() {}
Animal.prototype.legs = 4;const dog = new Animal();
console.log(dog.legs); // 4,来自原型dog.legs = 2; // 给实例添加同名属性(不会修改原型)
console.log(dog.legs); // 2
console.log(Animal.prototype.legs); // 4

1. 使用原型的场景

1)为实例添加方法(推荐)

User.prototype.login = function() {};

如果在构造函数中定义方法,那每个实例都一份,占内存:

function User() {this.login = function() {}; //  每次 new 都新建一个函数
}

2)动态修改原型对象

可以随时给原型对象加方法:

Person.prototype.walk = function() {};

实例会立即感知变更,因为访问属性是“沿原型链查找”的。

2. 手动设置或获取原型对象

获取原型对象:

Object.getPrototypeOf(obj) === obj.__proto__;

设置原型对象:

Object.setPrototypeOf(obj, newProto);

不推荐频繁使用 __proto__,它虽然广泛兼容但不是标准推荐方式。

3. 构造函数 vs 原型对象 vs 实例的对比表

名称示例作用
构造函数function A() {}用来 new 实例
原型对象A.prototype定义共享方法,实例的 __proto__ 指向它
实例对象new A()拥有自身属性和访问原型属性
实例原型instance.__proto__A.prototype
原型构造函数A.prototype.constructor回指构造函数

4. 在逆向分析中的应用

在混淆代码中,很多功能方法被挂在原型对象上,而实例调用时并未直接暴露名字。分析这些结构时常见技巧:

// 发现某个对象 obj
let proto = Object.getPrototypeOf(obj);// 看它继承自谁
console.log(proto.constructor.name);// 查看原型链所有方法
console.log(Object.getOwnPropertyNames(proto));// 拦截其原型方法进行 hook
proto.targetFunc = function() { debugger; };

5. 构造函数没有 prototype 的例外

有两个函数没有 prototype 属性:

  • 箭头函数

  • class 中的 static 静态方法

const arrow = () => {};
console.log(arrow.prototype); // undefined 

6. 总结

原型对象作用说明
共享方法所有实例共享原型上的方法节省内存
连接构造函数与实例constructor__proto__ 建立关系
继承链条的基础是原型链中向上查找的中间节点
调试混淆代码的切入点找到对象原型能看到隐藏方法或防护逻辑

四、继承机制(__proto__、prototype)

核心理念:原型链继承

每一个对象都有一个内部属性 [[Prototype]](表现为 __proto__),它指向其“父对象”的原型,这就形成了一条原型链。JS 会在这条链上查找属性或方法

1. prototype vs proto 的区别

名称类型属于谁?用途
prototype对象函数(构造函数)创建实例时作为原型
__proto__对象实例对象指向构造函数的 prototype

举例说明:

function Animal() {}
let a = new Animal();console.log(Animal.prototype);   // 原型对象
console.log(a.__proto__);        // 指向 Animal.prototype console.log(a.__proto__ === Animal.prototype); // true 

2. 原型链继承机制图示

function Parent() {this.name = 'Parent';
}
Parent.prototype.sayHi = function() {console.log('Hi from Parent');
};function Child() {this.age = 10;
}
Child.prototype = new Parent();   // 继承核心:原型链继承
Child.prototype.constructor = Child;let c = new Child();

结构图如下:

c
│
├── __proto__ → Child.prototype
│                     │
│                     ├── __proto__ → Parent.prototype
│                                             │
│                                             ├── sayHi
│                                             └── __proto__ → Object.prototype → null

3. 手动实现继承的几种方式

原型链继承(早期写法)

Child.prototype = new Parent();

缺点:

  • 父类构造函数会执行两次(继承和实例化)

  • 子类不能向父类传参

  • 所有实例共享父类引用属性(如数组)

经典组合继承(构造继承 + 原型继承)

function Parent(name) {this.name = name;
}
Parent.prototype.sayHi = function() {console.log('Hi from Parent');
};function Child(name, age) {Parent.call(this, name); // 第一次调用父构造函数this.age = age;
}Child.prototype = Object.create(Parent.prototype); // 原型继承
Child.prototype.constructor = Child;

优点:

  • 不共享引用类型

  • 支持向父类传参

  • 保持原型链关系

ES6 class 语法糖继承(推荐)

class Parent {constructor(name) {this.name = name;}sayHi() {console.log('Hi from Parent');}
}class Child extends Parent {constructor(name, age) {super(name);  // 调用父类构造函数this.age = age;}
}

等价于组合继承,只是语法更清晰。

4. 属性/方法的查找过程(原型链的运行机制)

const obj = new Child();
obj.sayHi(); // 怎么找到这个方法的?// 查找顺序:
1. 先查 obj 自身有没有 sayHi
2. 再查 obj.__proto__(即 Child.prototype)
3. 再查 Child.prototype.__proto__(即 Parent.prototype)
4. 再查 Object.prototype(比如 toString)
5. 到 null 停止(找不到就报错)

判断继承关系的方法

// 检查 obj 的原型链上是否 有一个原型等于 Parent.prototype
obj instanceof Parent             // true
// 检查 Parent.prototype 是否存在于 obj 的原型链中
Parent.prototype.isPrototypeOf(obj) // true

在逆向分析中的应用

很多混淆代码通过手动设置原型来实现多级继承或结构伪装:

Object.setPrototypeOf(obj, SomeHiddenPrototype);

分析时可以:

  • Object.getPrototypeOf(obj) 逐层查看原型链

  • 遍历所有继承的方法和属性

  • 判断某个对象是哪个类的实例(用 constructor.name 或 instanceof)

原型链的终点

所有对象最终都会继承自:

Object.prototype

并且:

Object.prototype.__proto__ === null // 原型链终点

小结

概念说明
prototype构造函数专属,用于实例共享方法
__proto__实例专属,指向构造函数的 prototype
继承的关键机制是设置子类 prototype 的 __proto__ 指向父类 prototype
原型链查找顺序自身 → 构造函数 prototype → Object.prototype

五、class 语法与继承语法糖

1. 什么是 class

class 是 ES6 引入的语法糖,本质上还是基于 原型链 的封装,只不过写法更接近传统面向对象语言(如 Java、C++)。

class Person {constructor(name) {this.name = name;}sayHi() {console.log(`Hi, I'm ${this.name}`);}
}const p = new Person('Tom');
p.sayHi(); // Hi, I'm Tom

等价于(底层写法):

function Person(name) {this.name = name;
}
Person.prototype.sayHi = function () {console.log(`Hi, I'm ${this.name}`);
};

2. class 的基本组成

关键词用法说明
constructor()类的构造方法初始化实例
方法名()定义原型方法所有实例共享
static 方法名()定义静态方法(不在原型上)类调用,不可实例调用
get/set定义访问器属性(属性拦截)控制读取或设置某属性行为

3. 继承语法糖:extendssuper

class Parent {constructor(name) {this.name = name;}sayHi() {console.log(`Hi from ${this.name}`);}
}class Child extends Parent {constructor(name, age) {super(name); // 调用父类构造函数this.age = age;}sayAge() {console.log(`I am ${this.age} years old.`);}
}const c = new Child('Alice', 20);
c.sayHi();   // 来自父类
c.sayAge();  // 子类自己的方法

对比底层写法(等价):

function Parent(name) {this.name = name;
}
Parent.prototype.sayHi = function () { ... };function Child(name, age) {Parent.call(this, name); // 继承构造函数this.age = age;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.sayAge = function () { ... };

4. class 的本质仍然是函数 + 原型

typeof Parent; // 'function'
console.log(Object.getPrototypeOf(Child)); // Parent
console.log(Child.prototype.__proto__ === Parent.prototype); // true

class 的继承背后做了两件事:

  • 设置 Child.prototype.__proto__ = Parent.prototype

  • 设置 Child.__proto__ = Parent(静态继承)

5. class 的静态方法不会被实例继承

class A {static sayHello() {console.log("Hello");}
}const a = new A();
a.sayHello();       //  报错
A.sayHello();       //  正确调用

但静态方法可以被子类继承:

class B extends A {}
B.sayHello();       //  从 A 继承了静态方法

6. 访问器(getter / setter)

class Rectangle {constructor(width, height) {this._w = width;this._h = height;}get area() {return this._w * this._h;}set width(value) {this._w = value;}
}const r = new Rectangle(2, 3);
console.log(r.area);  // 6
r.width = 5;
console.log(r.area);  // 15

7. 在逆向分析中的典型特征(混淆代码也用 class)

会经常看到如下结构:

class t {constructor(e) {this._v = e;}get d() {return this._v.split('').reverse().join('');}static f(x) {return new t(x);}
}

分析技巧:

  • 先找构造函数构造了什么字段(如 this._v)

  • 再看原型方法是对字段如何处理的

  • 静态方法大多是工厂函数/入口

  • 利用 Object.getOwnPropertyNames(Object.getPrototypeOf(obj)) 找所有原型方法

8. 总结:class 与继承的原理

特性class 语法糖底层原理说明
类定义class A {}function A() {}
方法定义say() {}A.prototype.say = function() {}
继承父类extends BA.prototype = Object.create(B.prototype)
构造函数继承super()B.call(this)
静态方法static f()A.f = function() {}
静态继承自动继承父类静态方法Object.setPrototypeOf(A, B)
属性访问器get/setObject.defineProperty

六、原型链调试技巧(逆向中常看到混淆的对象结构)

在混淆代码中,经常会遇到这些情况:

  • 对象被构造函数动态生成,不直接明示类名

  • 方法被挂载在 prototype 或 __proto__

  • 属性被 getter 包装,或动态通过 Object.defineProperty 定义

  • 某些类或函数名被压缩为 t, e, n,难以识别

  • Object.setPrototypeOf__proto__ 被手动修改,原型链人为构造

调试时必须:

  •  看清对象的真实构造函数是谁
  •  弄明白方法/属性从哪里继承来的
  •  把被混淆命名的对象关系“还原成人话”

1. 常用调试工具和方法

1)console.dir(obj) —— 看原型链结构树

这个方法比 console.log 更适合看“展开结构”。

console.dir(obj)

输出可以看到:

  • [[Prototype]](即 __proto__)指向谁

  • 构造函数 constructor 是哪个函数

  • 是否有 getter/setter

  • 继承来的方法在哪里

2)Object.getPrototypeOf(obj)

// 返回对象的内部原型,即 obj.__proto__
let proto = Object.getPrototypeOf(obj);

逐层向上找:

while (proto) {console.log(proto.constructor.name, proto);proto = Object.getPrototypeOf(proto);
}

能清晰地打印出原型链结构,即使构造函数被压缩了名,也能知道它的层级关系。

3)Object.getOwnPropertyNames(obj)

这个方法可以获取一个对象自身的所有属性,包括不可枚举属性。

Object.getOwnPropertyNames(obj)

常用于识别 prototype 对象上挂了哪些方法,尤其是混淆时你可能看到:

class t {constructor(e) {this.a = e;}['\x73\x61\x79']() {// 混淆后的方法名 say}
}

getOwnPropertyNames(t.prototype) 能看出真实方法名。

4)判断对象归属哪个类

obj.constructor.name        // 类名(如果没被改)
obj instanceof 某类         // 是否继承
某类.prototype.isPrototypeOf(obj)  // 判断是否是子孙类

逆向时经常这样判断当前对象到底是哪个类实例。

5)看 getter/setter

使用:

// 获取 obj.prop 这个属性的完整定义信息(描述符),包括它是普通数据属性还是带有 getter/setter 的访问器属性。
let desc = Object.getOwnPropertyDescriptor(obj, 'prop');
console.log(desc);

输出如果有 get: functionset: function,就表示它是通过访问器定义的,可能在干某种加解密、编码等逻辑。

6)利用断点观察对象变化

在浏览器 DevTools 中:

  • 给构造函数打断点:在构造阶段观察 this 的属性变化

  • 给访问器打断点:右键属性 → Break on → Property access/change

  • 使用 debugger:手动断点进入混淆函数体,观察 this 和局部变量

7)逆向实战示例

发现:

Object.getOwnPropertyDescriptor(window, 'crypto');
// 输出:
{ get: ƒ() { return customDecryptor(); }, configurable: true }

说明:

访问 window.crypto 实际上是在执行一个函数 customDecryptor(),它可能返回了某个解密后的对象,或者某种伪造的数据。

2. 真实逆向场景举例(常见结构)

示例 1:混淆类结构还原

class t {constructor(e) {this._ = e;}get v() {return this._.split('').reverse().join('');}
}

如果看到实例:

let o = new t("abc123");
console.log(o.v); // "321cba"

调试方法:

  • console.dir(o) → 找到 [[Prototype]]t.prototype

  • Object.getOwnPropertyDescriptor(Object.getPrototypeOf(o), 'v') → 发现是 get

  • 把 t 改名为 DecodeStr → 增强可读性

示例 2:构造函数伪装结构

function Obf() {this.token = Math.random().toString(36).slice(2);
}
Object.setPrototypeOf(Obf.prototype, AnotherClass.prototype);

调试方法:

  • obj.constructor.nameObf

  • obj.__proto__.__proto__ === AnotherClass.prototype

  • 多层继承伪装 → Object.getPrototypeOf(Object.getPrototypeOf(obj))

示例 3:多个对象共用原型(对象池/策略模式)

const sharedProto = {doThing() { console.log('shared logic'); }
};const a = Object.create(sharedProto);
const b = Object.create(sharedProto);

调试方式:

  • Object.getPrototypeOf(a) === Object.getPrototypeOf(b)

  • 所有方法来自 sharedProto,便于还原公共逻辑

3. 总结

目标技术手段
找到构造函数是谁obj.constructor.name / Object.getPrototypeOf
理解对象属性来自哪里查看 __proto__、prototype、constructor
识别 getter/setter 是否干活Object.getOwnPropertyDescriptor()
解开类结构混淆重命名类、调试构造函数、查找类方法
判断多个对象是否共用原型Object.getPrototypeOf(obj) 对比

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

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

相关文章

AtCoder 第404场初级竞赛 A~E题解

A Not Found 【题目链接】 原题链接:A - Not Found 【考点】 枚举,数组计数法 【题目大意】 找到在26个字母中,未输出的一个字母,如果有多个,输出其中一个即可。 【解析】 遍历字符串,使用数组的记录对应字母的出现次数,最后遍历数组为0的下标,输出对应的字母即…

检测内存条好坏有工具,推荐几款内存检测工具

检测内存条的好坏其实很重要,这直接就关系到计算机是不是能够稳定的运行,也有一部分人就会关注内存检测的工具。你应该如何来选择的,不如看一下以下的这几个。 MemTest86是一个比较受到大家喜欢的内存检测工具,会支持各种类型&…

01Introduction

文本主题 关于协作式多智能体强化学习的简介 文章目录 文本主题一、MARL主要框架集中式训练与执行 (CTE)集中式训练分布式执行(CTDE)分布式训练与执行(DTE) 二、Dec-POMDPjoint policy V and Q 一、MARL主要框架 MARL当前主流的…

小程序问题(记录版)

1、样式不生效 在h5上生效 但是 小程序上没反应 解决办法:解除组件样式隔离 1、isolated 表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(一般情况下的默认值) 2、apply-shared 表示页面 wxs…

排列组合算法:解锁数据世界的魔法钥匙

在 C 算法的奇幻世界里,排列和组合算法就像是两把神奇的魔法钥匙,能够帮我们解锁数据世界中各种复杂问题的大门。今天,作为 C 算法小白的我,就带大家一起走进排列和组合算法的奇妙天地。 排列算法:创造所有可能的顺序…

深入探讨 UDP 协议与多线程 HTTP 服务器

深入探讨 UDP 协议与多线程 HTTP 服务器 一、UDP 协议:高效但“不羁”的传输使者 UDP 协议以其独特的特性在网络传输中占据一席之地,适用于对实时性要求高、能容忍少量数据丢失的场景。 1. UDP 的特点解析 无连接:无需提前建立连接&…

引用第三方自定义组件——微信小程序学习笔记

1. 使用 npm 安装第三方包 1.1 下载安装Node.js 工具 下载地址:Node.js — Download Node.js 1.2 安装 npm 包 在项目空白处右键弹出菜单,选择“在外部终端窗口打开”,打开命令行工具,输入以下指令: 1> 初始化:…

数字化转型是往哪转?怎么转?

写在前面 当下数字化转型的风还在吹,企业数字化转型过程中以数字化项目满足业务化需求,已有相关数字化平台的话,就搞大平台、大系统,解决数据孤岛。政府数字化转型亦是如此,某些省市发了系统优化整合的文,旨…

嵌入式学习--江协51单片机day2

今天学的不多,内容为:静态、动态数码管的控制,模块化编程和lcd1602调试工具 数码管的控制 由于内部电路的设计,数码管每次只能显示一个位置的一个数字,动态的实现是基于不同位置的闪烁频率高。 P2_4,P2_3,P2_2控制位…

《数据结构:二叉搜索树(Binary Search Tree)》

文章目录 :red_circle:一、二叉搜索树的概念:red_circle:二、二叉搜索树的性能分析:red_circle:三、二叉搜索树的操作(一)插入(二)查找(三)删除 :red_circle:四、二叉搜索树的实现代码(一&#…

【Linux相关】实时查看Nvidia-smi使用情况

【Linux相关】 实时查看Nvidia-smi使用情况 文章目录 实时查看Nvidia-smi使用情况 实时查看Nvidia-smi使用情况 在本地终端执行下述语句 watch -n 1 nvidia-smi每一秒都会更新,将 1 改为其他数字可以满足不同需求

Kotlin密封类优化Android状态管理

Kotlin 的密封类(Sealed Class)确实是 Android 开发中管理复杂 UI 状态的利器。它通过类型安全的层次结构,让状态管理代码更加清晰简洁。让我们从实际开发场景出发,深入探讨其应用: 一、密封类核心优势 受限的类继承…

JavaWeb:SpringBootWeb快速入门

介绍 Spring SpringBoot 入门程序 需求 步骤 修改端口 1.新建application.yml #设置端口 server:port: 8081入门程序-分析 为什么main方法能启动web应用-内嵌tomcat 为什么tomcat能定位HelloController程序 请求先到DisPatcherServlet,根据路径转发 小结 1.…

Unity学习笔记二

文章目录 3D数学公共计算结构体Mathf常用成员三角函数 向量Vector3基本成员点乘叉乘插值运算 四元数引出基本概念Quaternion结构体成员四元数运算 更多的Mono延迟函数协同程序多线程相关协程概念辨析协程本体协程调度器 Resources资源动态加载特殊文件夹Resources同步加载Resou…

为什么Transformer推理需要做KV缓存

一、我们先来回忆一下在transformer中KV在哪里出现过,都有什么作用? α的计算过程: 这里引入三个向量: 图中的q为Query,用来匹配key值 图中的k为key,用来被Query匹配 图中的Value,是用来被进行加权平均的 由…

【大模型面试】大模型(LLMs)高频面题全面整理(★2025年5月最新版★)

【大模型面试】大模型(LLMs)高频面题全面整理(★2025年5月最新版★) 🌟 嗨,你好,我是 青松 ! 🌈 自小刺头深草里,而今渐觉出蓬蒿。 本笔记适合大模型初学者和…

JAVA:使用 iTextPDF 处理 PDF 的技术详解

1、简述 iTextPDF 是一个功能强大的 Java PDF 库,可以用来创建、修改和处理 PDF 文档。通过它,我们可以完成如生成 PDF、读取 PDF 内容、添加水印、合并 PDF 等多种操作。本篇博客将详细介绍 iTextPDF 的使用方法,并提供一些实践样例,帮助开发者快速上手。 样例代码: htt…

模态与非模态窗口及使用时的数据交互

模态窗口使用exec()方法显示,会阻塞父窗口,直到对话框关闭; 非模态对话框允许同时操作主窗口和设置窗口,使用show()。 模态和非模态的主要区别在于用户能否与父窗口交互,非模态更适合需要频繁切换的场景。非模态窗口需…

Docker进入MySQL之后如何用sql文件初始化数据

关闭Docker-compose.yml里面所有容器 docker compose -f docker_compose.yml down后台形式开启Docker-compose.yml所有容器 docker compose -f docker_compose.yml up -d罗列出所有启动过的(包括退出过的)容器 docker ps -a进入指定容器ID内部 docke…

MAC 地址

MAC地址(Media Access Control Address)是指网络设备在数据链路层使用的唯一标识符,也称为硬件地址或物理地址。它用于标识设备之间的网络通信,是网络适配器(如网卡、Wi-Fi适配器等)的唯一标识。每个网络设…