實現雙向綁定Proxy比defineproperty優劣如何?

来源:https://www.cnblogs.com/duxinyi/archive/2019/10/15/11676902.html
-Advertisement-
Play Games

前言 雙向綁定 其實已經是一個老掉牙的問題了,只要涉及到MVVM框架就不得不談的知識點,但它畢竟是Vue的三要素之一. Vue三要素 響應式: 例如如何監聽數據變化,其中的實現方法就是我們提到的雙向綁定 模板引擎: 如何解析模板 渲染: Vue如何將監聽到的數據變化和解析後的HTML進行渲染 可以實 ...


前言

雙向綁定其實已經是一個老掉牙的問題了,只要涉及到MVVM框架就不得不談的知識點,但它畢竟是Vue的三要素之一.

Vue三要素

  • 響應式: 例如如何監聽數據變化,其中的實現方法就是我們提到的雙向綁定
  • 模板引擎: 如何解析模板
  • 渲染: Vue如何將監聽到的數據變化和解析後的HTML進行渲染

可以實現雙向綁定的方法有很多,KnockoutJS基於觀察者模式的雙向綁定,Ember基於數據模型的雙向綁定,Angular基於臟檢查的雙向綁定,本篇文章我們重點講面試中常見的基於數據劫持的雙向綁定。

常見的基於數據劫持的雙向綁定有兩種實現,一個是目前Vue在用的Object.defineProperty,另一個是ES2015中新增的Proxy,而Vue的作者宣稱將在Vue3.0版本後加入Proxy從而代替Object.defineProperty,通過本文你也可以知道為什麼Vue未來會選擇Proxy

嚴格來講Proxy應該被稱為『代理』而非『劫持』,不過由於作用有很多相似之處,我們在下文中就不再做區分,統一叫『劫持』。

我們可以通過下圖清楚看到以上兩種方法在雙向綁定體系中的關係.

基於數據劫持的當然還有已經涼透的Object.observe方法,已被廢棄。

提前聲明: 我們沒有對傳入的參數進行及時判斷而規避錯誤,僅僅對核心方法進行了實現.


文章目錄

  1. 基於數據劫持實現的雙向綁定的特點
  2. 基於Object.defineProperty雙向綁定的特點
  3. 基於Proxy雙向綁定的特點

1.基於數據劫持實現的雙向綁定的特點

1.1 什麼是數據劫持

數據劫持比較好理解,通常我們利用Object.defineProperty劫持對象的訪問器,在屬性值發生變化時我們可以獲取變化,從而進行進一步操作。

// 這是將要被劫持的對象
const data = {
  name: '',
};

function say(name) {
  if (name === '古天樂') {
    console.log('給大家推薦一款超好玩的游戲');
  } else if (name === '渣渣輝') {
    console.log('戲我演過很多,可游戲我只玩貪玩懶月');
  } else {
    console.log('來做我的兄弟');
  }
}

// 遍歷對象,對其屬性值進行劫持
Object.keys(data).forEach(function(key) {
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      console.log('get');
    },
    set: function(newVal) {
      // 當屬性值發生變化時我們可以進行額外操作
      console.log(`大家好,我系${newVal}`);
      say(newVal);
    },
  });
});

data.name = '渣渣輝';
//大家好,我系渣渣輝
//戲我演過很多,可游戲我只玩貪玩懶月

1.2 數據劫持的優勢

目前業界分為兩個大的流派,一個是以React為首的單向數據綁定,另一個是以Angular、Vue為主的雙向數據綁定。

其實三大框架都是既可以雙向綁定也可以單向綁定,比如React可以手動綁定onChange和value實現雙向綁定,也可以調用一些雙向綁定庫,Vue也加入了props這種單向流的api,不過都並非主流賣點。

單向或者雙向的優劣不在我們的討論範圍,我們需要討論一下對比其他雙向綁定的實現方法,數據劫持的優勢所在。

  1. 無需顯示調用: 例如Vue運用數據劫持+發佈訂閱,直接可以通知變化並驅動視圖,上面的例子也是比較簡單的實現data.name = '渣渣輝'後直接觸發變更,而比如Angular的臟檢測則需要顯示調用markForCheck(可以用zone.js避免顯示調用,不展開),react需要顯示調用setState
  2. 可精確得知變化數據:還是上面的小例子,我們劫持了屬性的setter,當屬性值改變,我們可以精確獲知變化的內容newVal,因此在這部分不需要額外的diff操作,否則我們只知道數據發生了變化而不知道具體哪些數據變化了,這個時候需要大量diff來找出變化值,這是額外性能損耗。

1.3 基於數據劫持雙向綁定的實現思路

數據劫持是雙向綁定各種方案中比較流行的一種,最著名的實現就是Vue。

基於數據劫持的雙向綁定離不開ProxyObject.defineProperty等方法對對象/對象屬性的"劫持",我們要實現一個完整的雙向綁定需要以下幾個要點。

  1. 利用ProxyObject.defineProperty生成的Observer針對對象/對象的屬性進行"劫持",在屬性發生變化後通知訂閱者
  2. 解析器Compile解析模板中的Directive(指令),收集指令所依賴的方法和數據,等待數據變化然後進行渲染
  3. Watcher屬於Observer和Compile橋梁,它將接收到的Observer產生的數據變化,並根據Compile提供的指令進行視圖渲染,使得數據變化促使視圖變化

我們看到,雖然Vue運用了數據劫持,但是依然離不開發佈訂閱的模式,之所以在系列2做了Event Bus的實現,就是因為我們不管在學習一些框架的原理還是一些流行庫(例如Redux、Vuex),基本上都離不開發佈訂閱模式,而Event模塊則是此模式的經典實現,所以如果不熟悉發佈訂閱模式,建議讀一下系列2的文章。


2.基於Object.defineProperty雙向綁定的特點

關於Object.defineProperty的文章在網路上已經汗牛充棟,我們不想花過多時間在Object.defineProperty上面,本節我們主要講解Object.defineProperty的特點,方便接下來與Proxy進行對比。

Object.defineProperty還不瞭解的請閱讀文檔

兩年前就有人寫過基於Object.defineProperty實現的文章,想深入理解Object.defineProperty實現的推薦閱讀,本文也做了相關參考。

上面我們推薦的文章為比較完整的實現(400行代碼),我們在本節只提供一個極簡版(20行)和一個簡化版(150行)的實現,讀者可以循序漸進地閱讀。

2.1 極簡版的雙向綁定

我們都知道,Object.defineProperty的作用就是劫持一個對象的屬性,通常我們對屬性的gettersetter方法進行劫持,在對象的屬性發生變化時進行特定的操作。

我們就對對象objtext屬性進行劫持,在獲取此屬性的值時列印'get val',在更改屬性值的時候對DOM進行操作,這就是一個極簡的雙向綁定。

const obj = {};
Object.defineProperty(obj, 'text', {
  get: function() {
    console.log('get val'); 
  },
  set: function(newVal) {
    console.log('set val:' + newVal);
    document.getElementById('input').value = newVal;
    document.getElementById('span').innerHTML = newVal;
  }
});

const input = document.getElementById('input');
input.addEventListener('keyup', function(e){
  obj.text = e.target.value;
})

線上示例 極簡版雙向綁定 by Iwobi (@xiaomuzhu) on CodePen.

2.2 升級改造

我們很快會發現,這個所謂的雙向綁定貌似並沒有什麼亂用。。。

原因如下:

  1. 我們只監聽了一個屬性,一個對象不可能只有一個屬性,我們需要對對象每個屬性進行監聽。
  2. 違反開放封閉原則,我們如果瞭解開放封閉原則的話,上述代碼是明顯違反此原則,我們每次修改都需要進入方法內部,這是需要堅決杜絕的。
  3. 代碼耦合嚴重,我們的數據、方法和DOM都是耦合在一起的,就是傳說中的麵條代碼。

那麼如何解決上述問題?

Vue的操作就是加入了發佈訂閱模式,結合Object.defineProperty的劫持能力,實現了可用性很高的雙向綁定。

首先,我們以發佈訂閱的角度看我們第一部分寫的那一坨代碼,會發現它的監聽發佈訂閱都是寫在一起的,我們首先要做的就是解耦。

我們先實現一個訂閱發佈中心,即消息管理員(Dep),它負責儲存訂閱者和消息的分發,不管是訂閱者還是發佈者都需要依賴於它。

  let uid = 0;
  // 用於儲存訂閱者併發布消息
  class Dep {
    constructor() {
      // 設置id,用於區分新Watcher和只改變屬性值後新產生的Watcher
      this.id = uid++;
      // 儲存訂閱者的數組
      this.subs = [];
    }
    // 觸發target上的Watcher中的addDep方法,參數為dep的實例本身
    depend() {
      Dep.target.addDep(this);
    }
    // 添加訂閱者
    addSub(sub) {
      this.subs.push(sub);
    }
    notify() {
      // 通知所有的訂閱者(Watcher),觸發訂閱者的相應邏輯處理
      this.subs.forEach(sub => sub.update());
    }
  }
  // 為Dep類設置一個靜態屬性,預設為null,工作時指向當前的Watcher
  Dep.target = null;

現在我們需要實現監聽者(Observer),用於監聽屬性值的變化。

// 監聽者,監聽對象屬性值的變化
  class Observer {
    constructor(value) {
      this.value = value;
      this.walk(value);
    }
    // 遍歷屬性值並監聽
    walk(value) {
      Object.keys(value).forEach(key => this.convert(key, value[key]));
    }
    // 執行監聽的具體方法
    convert(key, val) {
      defineReactive(this.value, key, val);
    }
  }

  function defineReactive(obj, key, val) {
    const dep = new Dep();
    // 給當前屬性的值添加監聽
    let chlidOb = observe(val);
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get: () => {
        // 如果Dep類存在target屬性,將其添加到dep實例的subs數組中
        // target指向一個Watcher實例,每個Watcher都是一個訂閱者
        // Watcher實例在實例化過程中,會讀取data中的某個屬性,從而觸發當前get方法
        if (Dep.target) {
          dep.depend();
        }
        return val;
      },
      set: newVal => {
        if (val === newVal) return;
        val = newVal;
        // 對新值進行監聽
        chlidOb = observe(newVal);
        // 通知所有訂閱者,數值被改變了
        dep.notify();
      },
    });
  }

  function observe(value) {
    // 當值不存在,或者不是複雜數據類型時,不再需要繼續深入監聽
    if (!value || typeof value !== 'object') {
      return;
    }
    return new Observer(value);
  }

那麼接下來就簡單了,我們需要實現一個訂閱者(Watcher)。

  class Watcher {
    constructor(vm, expOrFn, cb) {
      this.depIds = {}; // hash儲存訂閱者的id,避免重覆的訂閱者
      this.vm = vm; // 被訂閱的數據一定來自於當前Vue實例
      this.cb = cb; // 當數據更新時想要做的事情
      this.expOrFn = expOrFn; // 被訂閱的數據
      this.val = this.get(); // 維護更新之前的數據
    }
    // 對外暴露的介面,用於在訂閱的數據被更新時,由訂閱者管理員(Dep)調用
    update() {
      this.run();
    }
    addDep(dep) {
      // 如果在depIds的hash中沒有當前的id,可以判斷是新Watcher,因此可以添加到dep的數組中儲存
      // 此判斷是避免同id的Watcher被多次儲存
      if (!this.depIds.hasOwnProperty(dep.id)) {
        dep.addSub(this);
        this.depIds[dep.id] = dep;
      }
    }
    run() {
      const val = this.get();
      console.log(val);
      if (val !== this.val) {
        this.val = val;
        this.cb.call(this.vm, val);
      }
    }
    get() {
      // 當前訂閱者(Watcher)讀取被訂閱數據的最新更新後的值時,通知訂閱者管理員收集當前訂閱者
      Dep.target = this;
      const val = this.vm._data[this.expOrFn];
      // 置空,用於下一個Watcher使用
      Dep.target = null;
      return val;
    }
  }

那麼我們最後完成Vue,將上述方法掛載在Vue上。

  class Vue {
    constructor(options = {}) {
      // 簡化了$options的處理
      this.$options = options;
      // 簡化了對data的處理
      let data = (this._data = this.$options.data);
      // 將所有data最外層屬性代理到Vue實例上
      Object.keys(data).forEach(key => this._proxy(key));
      // 監聽數據
      observe(data);
    }
    // 對外暴露調用訂閱者的介面,內部主要在指令中使用訂閱者
    $watch(expOrFn, cb) {
      new Watcher(this, expOrFn, cb);
    }
    _proxy(key) {
      Object.defineProperty(this, key, {
        configurable: true,
        enumerable: true,
        get: () => this._data[key],
        set: val => {
          this._data[key] = val;
        },
      });
    }
  }

看下效果:

線上示例 雙向綁定實現---無漏洞版 by Iwobi (@xiaomuzhu) on CodePen.

至此,一個簡單的雙向綁定算是被我們實現了。

2.3 Object.defineProperty的缺陷

其實我們升級版的雙向綁定依然存在漏洞,比如我們將屬性值改為數組。

let demo = new Vue({
  data: {
    list: [1],
  },
});

const list = document.getElementById('list');
const btn = document.getElementById('btn');

btn.addEventListener('click', function() {
  demo.list.push(1);
});


const render = arr => {
  const fragment = document.createDocumentFragment();
  for (let i = 0; i < arr.length; i++) {
    const li = document.createElement('li');
    li.textContent = arr[i];
    fragment.appendChild(li);
  }
  list.appendChild(fragment);
};

// 監聽數組,每次數組變化則觸發渲染函數,然而...無法監聽
demo.$watch('list', list => render(list));

setTimeout(
  function() {
    alert(demo.list);
  },
  5000,
);

線上示例 雙向綁定-數組漏洞 by Iwobi (@xiaomuzhu) on CodePen.

是的,Object.defineProperty的第一個缺陷,無法監聽數組變化。
然而Vue的文檔提到了Vue是可以檢測到數組變化的,但是只有以下八種方法,vm.items[indexOfItem] = newValue這種是無法檢測的。

push()
pop()
shift()
unshift()
splice()
sort()
reverse()

其實作者在這裡用了一些奇技淫巧,把無法監聽數組的情況hack掉了,以下是方法示例。

const aryMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
const arrayAugmentations = [];

aryMethods.forEach((method)=> {

    // 這裡是原生Array的原型方法
    let original = Array.prototype[method];

   // 將push, pop等封裝好的方法定義在對象arrayAugmentations的屬性上
   // 註意:是屬性而非原型屬性
    arrayAugmentations[method] = function () {
        console.log('我被改變啦!');

        // 調用對應的原生方法並返回結果
        return original.apply(this, arguments);
    };

});

let list = ['a', 'b', 'c'];
// 將我們要監聽的數組的原型指針指向上面定義的空數組對象
// 別忘了這個空數組的屬性上定義了我們封裝好的push等方法
list.__proto__ = arrayAugmentations;
list.push('d');  // 我被改變啦! 4

// 這裡的list2沒有被重新定義原型指針,所以就正常輸出
let list2 = ['a', 'b', 'c'];
list2.push('d');  // 4

由於只針對了八種方法進行了hack,所以其他數組的屬性也是檢測不到的,其中的坑很多,可以閱讀上面提到的文檔。

我們應該註意到在上文中的實現里,我們多次用遍歷方法遍歷對象的屬性,這就引出了Object.defineProperty的第二個缺陷,只能劫持對象的屬性,因此我們需要對每個對象的每個屬性進行遍歷,如果屬性值也是對象那麼需要深度遍歷,顯然能劫持一個完整的對象是更好的選擇。

Object.keys(value).forEach(key => this.convert(key, value[key]));

3.Proxy實現的雙向綁定的特點

Proxy在ES2015規範中被正式發佈,它在目標對象之前架設一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫,我們可以這樣認為,Proxy是Object.defineProperty的全方位加強版,具體的文檔可以查看此處;

3.1 Proxy可以直接監聽對象而非屬性

我們還是以上文中用Object.defineProperty實現的極簡版雙向綁定為例,用Proxy進行改寫。

const input = document.getElementById('input');
const p = document.getElementById('p');
const obj = {};

const newObj = new Proxy(obj, {
  get: function(target, key, receiver) {
    console.log(`getting ${key}!`);
    return Reflect.get(target, key, receiver);
  },
  set: function(target, key, value, receiver) {
    console.log(target, key, value, receiver);
    if (key === 'text') {
      input.value = value;
      p.innerHTML = value;
    }
    return Reflect.set(target, key, value, receiver);
  },
});

input.addEventListener('keyup', function(e) {
  newObj.text = e.target.value;
});

線上示例 Proxy版 by Iwobi (@xiaomuzhu) on CodePen.

我們可以看到,Proxy直接可以劫持整個對象,並返回一個新對象,不管是操作便利程度還是底層功能上都遠強於Object.defineProperty

3.2 Proxy可以直接監聽數組的變化

當我們對數組進行操作(push、shift、splice等)時,會觸發對應的方法名稱和length的變化,我們可以藉此進行操作,以上文中Object.defineProperty無法生效的列表渲染為例。

const list = document.getElementById('list');
const btn = document.getElementById('btn');

// 渲染列表
const Render = {
  // 初始化
  init: function(arr) {
    const fragment = document.createDocumentFragment();
    for (let i = 0; i < arr.length; i++) {
      const li = document.createElement('li');
      li.textContent = arr[i];
      fragment.appendChild(li);
    }
    list.appendChild(fragment);
  },
  // 我們只考慮了增加的情況,僅作為示例
  change: function(val) {
    const li = document.createElement('li');
    li.textContent = val;
    list.appendChild(li);
  },
};

// 初始數組
const arr = [1, 2, 3, 4];

// 監聽數組
const newArr = new Proxy(arr, {
  get: function(target, key, receiver) {
    console.log(key);
    return Reflect.get(target, key, receiver);
  },
  set: function(target, key, value, receiver) {
    console.log(target, key, value, receiver);
    if (key !== 'length') {
      Render.change(value);
    }
    return Reflect.set(target, key, value, receiver);
  },
});

// 初始化
window.onload = function() {
    Render.init(arr);
}

// push數字
btn.addEventListener('click', function() {
  newArr.push(6);
});

線上示例 Proxy列表渲染 by Iwobi (@xiaomuzhu) on CodePen.

很顯然,Proxy不需要那麼多hack(即使hack也無法完美實現監聽)就可以無壓力監聽數組的變化,我們都知道,標準永遠優先於hack。

3.3 Proxy的其他優勢

Proxy有多達13種攔截方法,不限於apply、ownKeys、deleteProperty、has等等是Object.defineProperty不具備的。

Proxy返回的是一個新對象,我們可以只操作新的對象達到目的,而Object.defineProperty只能遍歷對象屬性直接修改。

Proxy作為新標准將受到瀏覽器廠商重點持續的性能優化,也就是傳說中的新標準的性能紅利。

當然,Proxy的劣勢就是相容性問題,而且無法用polyfill磨平,因此Vue的作者才聲明需要等到下個大版本(3.0)才能用Proxy重寫。


公眾號

想要實時關註筆者最新的文章和最新的文檔更新請關註公眾號程式員面試官,後續的文章會優先在公眾號更新.

簡歷模板: 關註公眾號回覆「模板」獲取

《前端面試手冊》: 配套於本指南的突擊手冊,關註公眾號回覆「fed」獲取

2019-08-12-03-18-41

本文由博客一文多發平臺 OpenWrite 發佈!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 不知道你們有沒有發現,null和undefined竟然有這樣的關係~ ...
  • Vue.js是一套構建用戶界面的漸進式框架。與其他重量級框架不同的是,Vue 採用自底向上增量開發的設計。Vue 的核心庫只關註視圖層,並且非常容易學習,非常容易與其它庫或已有項目整合。另一方面,Vue 完全有能力驅動採用單文件組件和Vue生態系統支持的庫開發的複雜單頁應用。 那麼在windows系 ...
  • 一、回調函數 優點:簡單,方便,易用 缺點:易造成回調函數地獄,回調函數中嵌套多個回調函數,因為多個非同步操作造成強耦合,代碼亂做一團,無法管理。 二、事件監聽 優點:與回調函數相比,事件監聽實現了代碼的解耦,方便代碼管理 缺點:使用不方便,每次都要手動地綁定和觸發事件 三、Promise 優點:將回 ...
  • 背景 現在手上在做的 React 項目因為年代久遠,用的 "Redux" ,寫代碼的體驗不太好,所以想升級一下引入 dva。以往使用 dva 都是使用 "dva cli" 直接生成 dva 項目,或者在使用 "ant design pro" 的時候使用 umi 直接生成 react + antd + ...
  • 理解javascript事件執行機制 眾所周知,js是一個單線程的語言,這意味著同一時間只能做一件事,但是我們又說js是非同步的。首先,單線程並不是沒有優點。作為瀏覽器腳本語言,JavaScript 的主要用途是與用戶互動,以及操作 DOM。這決定了它只能是單線程,否則會帶來很複雜的同步問題。比如,假 ...
  • 簡單的描述了淺拷貝和深拷貝的區別後,分別進行實現且所有方法都已進行試驗。 ...
  • CSS樣式 CSS概述 CSS Cascading Style Shees層疊樣式表 HTML定義網頁的內容,CSS定義內容的樣式。 內容和樣式相互分離,便於修改樣式。 CSS語法 註意:1.最後一條聲明可以沒有分號,但是為了以後修改方便,一般也加上分號。 2.為了使用樣式更加容易閱讀,可以將每條代 ...
  • 前言 設計前端組件是最能考驗開發者基本功的測試之一,因為調用Material design、Antd、iView 等現成組件庫的 API 每個人都可以做到,但是很多人並不知道很多常用組件的設計原理。 能否設計出通用前端組件也是區分前端工程師和前端api調用師的標準之一,那麼應該如何設計出一個通用組件 ...
一周排行
    -Advertisement-
    Play Games
  • 一:背景 1. 講故事 這一期程式故障除了做原理分析,還順帶吐槽一下,熟悉我的朋友都知道我分析dump是免費的,但免費不代表可以濫用我的寶貴時間,我不知道有些人故意惡搞卡死是想幹嘛,不得而知,希望後面類似的事情越來越少吧!廢話不多說,我們來看看是如何被惡搞的。 二:WinDbg 分析 1. 程式是如 ...
  • TCP(Transmission Control Protocol): 特點:面向連接、可靠傳輸、按序交付、流量控制、擁塞控制。 用途:適用於需要高可靠性的數據傳輸,如網頁瀏覽、電子郵件、文件傳輸等。 優勢:數據包順序和完整性有保障,適合需要準確無誤傳輸數據的場景。 舉例:線上購物網站的交易數據傳輸 ...
  • 前面兩篇隨筆介紹了EAV模型(實體-屬性-值)的設計思路和Winform前端對於通用查詢的處理,本篇隨筆繼續深入EAV模型(實體-屬性-值)設計的探討,介紹實體屬性的定義,以及根據不同屬性的定義構建不同的輸入控制項處理,以及列表界面的展示。旨在結合關係型資料庫的熟練使用、性能優勢和MongoDB資料庫... ...
  • IEC60870-5-104 是一種電力自動化系統中常用的通信協議,使用 TCP/IP 協議作為底層通信協議,用於監視和控制電力系統中的各種設備,如變電站、發電機、開關等。 ...
  • 前言:最近幾天有好幾個小伙伴玩WPF,遇到不同頁面,不知道要怎麼傳遞消息。於是,我今天就來演示一個事件聚合器的玩法,採用prism框架來實現。作為福利,內容附帶了主頁面打開對話框時候直接通過參數傳遞消息的一個小例子,具體請自行圍觀。 以下內容,創建wpf項目以及引用prism和實現依賴註入等細節,可 ...
  • 在這篇文章中,我們介紹瞭如何利用大型語言模型為情人節營造難忘的氛圍。通過上傳圖片併進行風格轉化,我們可以為對方呈現一幅獨特的作品,增添浪漫的色彩。同時,藉助搜索功能,我們能夠輕鬆獲取與情人節相關的信息,為策劃活動提供更多靈感和建議。 ...
  • 正文 晚上跳舞回來,在便利店照例買根冰淇淋吃。看到店裡的老闆娘在訓她孩子。言辭依稀可以聽見考上好初中之類。 當時一個臨時起意,打算買兩根冰淇淋,塞一根到他手上,說一句:“我小時候也老被罵,沒什麼。” 然後跑掉。但是在冰櫃里翻了半天,都沒找到自己想吃的那種。與此同時,聽到他媽媽聲色俱厲地說:“你知道我小時 ...
  • strcpy和memcpy 目錄strcpy和memcpy 複製內容: strcpy:專門用於複製字元串,它會一直複製直到遇到源字元串中的'\0'結束符。這意味著如果源字元串長度超過了目標緩衝區的大小(不包括'\0'),就會發生緩衝區溢出,這是一個常見的安全隱患。 memcpy:可以複製任意內容,如 ...
  • 本文介紹在Visual Studio中,通過屬性表,使得一個新建解決方案中的項目可以快速配置已有解決方案的項目中各類已編譯好的C++第三方庫的方法~ ...
  • 將多個第三方包封裝成一個項目後,如果你的目的是讓其他開發人員可以直接引用這些依賴,一般來說有兩種常見的方式: 打成JAR包:將封裝好的項目編譯打包成JAR文件,其他開發人員可以將這個JAR文件添加到他們的項目中,併在項目的構建工具(比如Maven)中配置該JAR作為依賴。這樣做的好處是簡單直接,其他 ...