關于 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
英文地址,請注明出處:http://en.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適配器等)的唯一標識。每個網絡設…