面試官問:能否模擬實現JS的call和apply方法

寫于2018年11月30日,發布在掘金上閱讀量近一萬,現在發布到微信公眾號申明原創。相對比較基礎的知識,雖然日常開發可能用得比較少,各種源碼中有很多callapply,需要掌握。

前言

這是面試官問系列的第三篇,旨在幫助讀者提升JS基礎知識,包含new、call、apply、this、繼承相關知識。
面試官問系列文章如下:感興趣的讀者可以點擊閱讀。
1.面試官問:能否模擬實現JS的new操作符
2.面試官問:能否模擬實現JS的bind方法
3.面試官問:能否模擬實現JS的call和apply方法
4.面試官問:JS的this指向
5.面試官問:JS的繼承

之前寫過兩篇《面試官問:能否模擬實現JSnew操作符》和《面試官問:能否模擬實現JSbind方法》

其中模擬bind方法時是使用的callapply修改this指向。但面試官可能問:能否不用callapply來實現呢。意思也就是需要模擬實現callapply的了。

附上之前寫文章寫過的一段話:已經有很多模擬實現callapply的文章,為什么自己還要寫一遍呢。學習就好比是座大山,人們沿著不同的路登山,分享著自己看到的風景。你不一定能看到別人看到的風景,體會到別人的心情。只有自己去登山,才能看到不一樣的風景,體會才更加深刻。

先通過MDN認識下callapply

MDN 文檔:Function.prototype.call()
語法

fun.call(thisArg, arg1, arg2, ...)

thisArg
fun函數運行時指定的this值。需要注意的是,指定的this值并不一定是該函數執行時真正的this值,如果這個函數處于非嚴格模式下,則指定為nullundefinedthis值會自動指向全局對象(瀏覽器中就是window對象),同時值為原始值(數字,字符串,布爾值)的this會指向該原始值的自動包裝對象。
arg1, arg2, ...
指定的參數列表
返回值
返回值是你調用的方法的返回值,若該方法沒有返回值,則返回undefined

MDN 文檔:Function.prototype.apply()

func.apply(thisArg, [argsArray])

thisArg
可選的。在 func 函數運行時使用的 this 值。請注意,this可能不是該方法看到的實際值:如果這個函數處于非嚴格模式下,則指定為 nullundefined 時會自動替換為指向全局對象,原始值會被包裝。
argsArray
可選的。一個數組或者類數組對象,其中的數組元素將作為單獨的參數傳給 func 函數。如果該參數的值為 nullundefined,則表示不需要傳入任何參數。從ECMAScript 5 開始可以使用類數組對象。
返回值
調用有指定this值和參數的函數的結果。直接先看例子1

callapply 的異同

相同點:
1、callapply的第一個參數thisArg,都是func運行時指定的this。而且,this可能不是該方法看到的實際值:如果這個函數處于非嚴格模式下,則指定為 nullundefined 時會自動替換為指向全局對象,原始值會被包裝。
2、都可以只傳遞一個參數。
不同點:apply只接收兩個參數,第二個參數可以是數組也可以是類數組,其實也可以是對象,后續的參數忽略不計。call接收第二個及以后一系列的參數。
看兩個簡單例子1和2**:

// 例子1:瀏覽器環境 非嚴格模式下var doSth = function(a, b){console.log(this);console.log([a, b]);
}
doSth.apply(null, [1, 2]); // this是window  // [1, 2]
doSth.apply(0, [1, 2]); // this 是 Number(0) // [1, 2]
doSth.apply(true); // this 是 Boolean(true) // [undefined, undefined]
doSth.call(undefined, 1, 2); // this 是 window // [1, 2]
doSth.call('0', 1, {a: 1}); // this 是 String('0') // [1, {a: 1}]
// 例子2:瀏覽器環境 嚴格模式下'use strict';
var doSth2 = function(a, b){console.log(this);console.log([a, b]);
}
doSth2.call(0, 1, 2); // this 是 0 // [1, 2]
doSth2.apply('1'); // this 是 '1' // [undefined, undefined]
doSth2.apply(null, [1, 2]); // this 是 null // [1, 2]

typeof7種類型(undefined number string boolean symbol object function),筆者都驗證了一遍:更加驗證了相同點第一點,嚴格模式下,函數的this值就是callapply的第一個參數thisArg,非嚴格模式下,thisArg值被指定為 nullundefinedthis值會自動替換為指向全局對象,原始值則會被自動包裝,也就是new Object()

重新認識了callapply會發現:它們作用都是一樣的,改變函數里的this指向為第一個參數thisArg,如果明確有多少參數,那可以用call,不明確則可以使用apply。也就是說完全可以不使用call,而使用apply代替。
也就是說,我們只需要模擬實現applycall可以根據參數個數都放在一個數組中,給到apply即可。

模擬實現 apply

既然準備模擬實現apply,那先得看看ES5規范。ES5規范 英文版ES5規范 中文版apply的規范下一個就是call的規范,可以點擊打開新標簽頁去查看,這里摘抄一部分。

Function.prototype.apply (thisArg, argArray)
當以 thisArgargArray 為參數在一個 func 對象上調用 apply 方法,采用如下步驟:

1.如果 IsCallable(func)false, 則拋出一個 TypeError 異常。
2.如果 argArraynullundefined, 則返回提供 thisArg 作為 this 值并以空參數列表調用 func[[Call]] 內部方法的結果。
3.返回提供 thisArg 作為 this 值并以空參數列表調用 func[[Call]] 內部方法的結果。
4.如果 Type(argArray) 不是 Object, 則拋出一個 TypeError 異常。
5~8 略
9.提供 thisArg 作為 this 值并以 argList 作為參數列表,調用 func[[Call]] 內部方法,返回結果。
apply 方法的 length 屬性是 2

在外面傳入的 thisArg 值會修改并成為 this 值。thisArgundefinednull 時它會被替換成全局對象,所有其他值會被應用 ToObject 并將結果作為 this 值,這是第三版引入的更改。

結合上文和規范,如何將函數里的this指向第一個參數thisArg呢,這是一個問題。這時候請出例子3

// 瀏覽器環境 非嚴格模式下var doSth = function(a, b){console.log(this);console.log(this.name);console.log([a, b]);
}
var student = {name: '若川',doSth: doSth,
};
student.doSth(1, 2); // this === student // true // '若川' // [1, 2]
doSth.apply(student, [1, 2]); // this === student // true // '若川' // [1, 2]

可以得出結論1:在對象student上加一個函數doSth,再執行這個函數,這個函數里的this就指向了這個對象。那也就是可以在thisArg上新增調用函數,執行后刪除這個函數即可。知道這些后,我們試著容易實現第一版本:

// 瀏覽器環境 非嚴格模式function getGlobalObject(){returnthis;
}
Function.prototype.applyFn = function apply(thisArg, argsArray){ // `apply` 方法的 `length` 屬性是 `2`。// 1.如果 `IsCallable(func)` 是 `false`, 則拋出一個 `TypeError` 異常。if(typeofthis !== 'function'){thrownewTypeError(this + ' is not a function');}// 2.如果 argArray 是 null 或 undefined, 則// 返回提供 thisArg 作為 this 值并以空參數列表調用 func 的 [[Call]] 內部方法的結果。if(typeof argsArray === 'undefined' || argsArray === null){argsArray = [];}// 3.如果 Type(argArray) 不是 Object, 則拋出一個 TypeError 異常 .if(argsArray !== newObject(argsArray)){thrownewTypeError('CreateListFromArrayLike called on non-object');}if(typeof thisArg === 'undefined' || thisArg === null){// 在外面傳入的 thisArg 值會修改并成為 this 值。// ES3: thisArg 是 undefined 或 null 時它會被替換成全局對象 瀏覽器里是windowthisArg = getGlobalObject();}// ES3: 所有其他值會被應用 ToObject 并將結果作為 this 值,這是第三版引入的更改。thisArg = newObject(thisArg);var __fn = '__fn';thisArg[__fn] = this;// 9.提供 thisArg 作為 this 值并以 argList 作為參數列表,調用 func 的 [[Call]] 內部方法,返回結果var result = thisArg[__fn](...argsArray);delete thisArg[__fn];return result;
};

實現第一版后,很容易找出兩個問題:

  • [ ] 1.__fn 同名覆蓋問題,thisArg對象上有__fn,那就被覆蓋了然后被刪除了。

針對問題1 解決方案一:采用ES6 Sybmol() 獨一無二的。可以本來就是模擬ES3的方法。如果面試官不允許用呢。解決方案二:自己用Math.random()模擬實現獨一無二的key。面試時可以直接用生成時間戳即可。

// 生成UUID 通用唯一識別碼// 大概生成 這樣一串 '18efca2d-6e25-42bf-a636-30b8f9f2de09'function generateUUID(){var i, random;var uuid = '';for (i = 0; i < 32; i++) {random = Math.random() * 16 | 0;if (i === 8 || i === 12 || i === 16 || i === 20) {uuid += '-';}uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)).toString(16);}return uuid;
}
// 簡單實現// '__' + new Date().getTime();

如果這個key萬一這對象中還是有,為了保險起見,可以做一次緩存操作。比如如下代碼:

var student = {name: '若川',doSth: 'doSth',
};
var originalVal = student.doSth;
var hasOriginalVal = student.hasOwnProperty('doSth');
student.doSth = function(){};
delete student.doSth;
// 如果沒有,`originalVal`則為undefined,直接賦值新增了一個undefined,這是不對的,所以需判斷一下。if(hasOriginalVal){student.doSth = originalVal;
}
console.log('student:', student); // { name: '若川', doSth: 'doSth' }
  • [ ] 2.使用了ES6擴展符...
    解決方案一:采用eval來執行函數。

eval把字符串解析成代碼執行。
MDN 文檔:eval
語法

eval(string)

參數
string
表示JavaScript表達式,語句或一系列語句的字符串。表達式可以包含變量以及已存在對象的屬性。
返回值
執行指定代碼之后的返回值。如果返回值為空,返回undefined
解決方案二:但萬一面試官不允許用eval呢,畢竟eval是魔鬼。可以采用new Function()來生成執行函數。MDN 文檔:Function
語法

newFunction ([arg1[, arg2[, ...argN]],] functionBody)

參數
arg1, arg2, ... argN
被函數使用的參數的名稱必須是合法命名的。參數名稱是一個有效的JavaScript標識符的字符串,或者一個用逗號分隔的有效字符串的列表;例如“×”“theValue”,或“A,B”
functionBody
一個含有包括函數定義的JavaScript語句的字符串。
接下來看兩個例子:

簡單例子:
var sum = newFunction('a', 'b', 'return a + b');
console.log(sum(2, 6));
// 稍微復雜點的例子:var student = {name: '若川',doSth: function(argsArray){console.log(argsArray);console.log(this.name);}
};
// var result = student.doSth(['若川i', 18]);// 用new Function()生成函數并執行返回結果var result = newFunction('return arguments[0][arguments[1]](arguments[2][0], arguments[2][1])')(student, 'doSth', ['若川i', 18]);
// 個數不定// 所以可以寫一個函數生成函數代碼:function generateFunctionCode(argsArrayLength){var code = 'return arguments[0][arguments[1]](';for(var i = 0; i < argsArrayLength; i++){if(i > 0){code += ',';}code += 'arguments[2][' + i + ']';}code += ')';// return arguments[0][arguments[1]](arg1, arg2, arg3...)return code;
}

你可能不知道在ES3、ES5undefined 是能修改的

可能大部分人不知道。ES5中雖然在全局作用域下不能修改,但在局部作用域中也是能修改的,不信可以復制以下測試代碼在控制臺執行下。雖然一般情況下是不會的去修改它。

function test(){varundefined = 3;console.log(undefined); // chrome下也是 3
}
test();

所以判斷一個變量a是不是undefined,更嚴謹的方案是typeof a === 'undefined'或者a === void 0; 這里面用的是voidvoid的作用是計算表達式,始終返回undefined,也可以這樣寫void(0)。更多可以查看韓子遲的這篇文章:為什么用「void 0」代替「undefined」 解決了這幾個問題,比較容易實現如下代碼。

使用 new Function() 模擬實現的apply

// 瀏覽器環境 非嚴格模式function getGlobalObject(){returnthis;
}
function generateFunctionCode(argsArrayLength){var code = 'return arguments[0][arguments[1]](';for(var i = 0; i < argsArrayLength; i++){if(i > 0){code += ',';}code += 'arguments[2][' + i + ']';}code += ')';// return arguments[0][arguments[1]](arg1, arg2, arg3...)return code;
}
Function.prototype.applyFn = function apply(thisArg, argsArray){ // `apply` 方法的 `length` 屬性是 `2`。// 1.如果 `IsCallable(func)` 是 `false`, 則拋出一個 `TypeError` 異常。if(typeofthis !== 'function'){thrownewTypeError(this + ' is not a function');}// 2.如果 argArray 是 null 或 undefined, 則// 返回提供 thisArg 作為 this 值并以空參數列表調用 func 的 [[Call]] 內部方法的結果。if(typeof argsArray === 'undefined' || argsArray === null){argsArray = [];}// 3.如果 Type(argArray) 不是 Object, 則拋出一個 TypeError 異常 .if(argsArray !== newObject(argsArray)){thrownewTypeError('CreateListFromArrayLike called on non-object');}if(typeof thisArg === 'undefined' || thisArg === null){// 在外面傳入的 thisArg 值會修改并成為 this 值。// ES3: thisArg 是 undefined 或 null 時它會被替換成全局對象 瀏覽器里是windowthisArg = getGlobalObject();}// ES3: 所有其他值會被應用 ToObject 并將結果作為 this 值,這是第三版引入的更改。thisArg = newObject(thisArg);var __fn = '__' + newDate().getTime();// 萬一還是有 先存儲一份,刪除后,再恢復該值var originalVal = thisArg[__fn];// 是否有原始值var hasOriginalVal = thisArg.hasOwnProperty(__fn);thisArg[__fn] = this;// 9.提供 `thisArg` 作為 `this` 值并以 `argList` 作為參數列表,調用 `func` 的 `[[Call]]` 內部方法,返回結果。// ES6版// var result = thisArg[__fn](...args);var code = generateFunctionCode(argsArray.length);var result = (newFunction(code))(thisArg, __fn, argsArray);delete thisArg[__fn];if(hasOriginalVal){thisArg[__fn] = originalVal;}return result;
};

利用模擬實現的apply模擬實現call

Function.prototype.callFn = function call(thisArg){var argsArray = [];var argumentsLength = arguments.length;for(var i = 0; i < argumentsLength - 1; i++){// argsArray.push(arguments[i + 1]);argsArray[i] = arguments[i + 1];}console.log('argsArray:', argsArray);returnthis.applyFn(thisArg, argsArray);
}
// 測試例子var doSth = function (name, age){var type = Object.prototype.toString.call(this);console.log(typeof doSth);console.log(this === firstArg);console.log('type:', type);console.log('this:', this);console.log('args:', [name, age], arguments);return'this--';
};var name = 'window';var student = {name: '若川',age: 18,doSth: 'doSth',__fn: 'doSth',
};
var firstArg = student;
var result = doSth.applyFn(firstArg, [1, {name: '若川i'}]);
var result2 = doSth.callFn(firstArg, 1, {name: '若川i'});
console.log('result:', result);
console.log('result2:', result2);

細心的你會發現注釋了這一句argsArray.push(arguments[i + 1]);,事實上push方法,內部也有一層循環。所以理論上不使用push性能會更好些。面試官也可能根據這點來問時間復雜度和空間復雜度的問題。

// 看看V8引擎中的具體實現:function ArrayPush() {var n = TO_UINT32( this.length );    // 被push的對象的lengthvar m = %_ArgumentsLength();     // push的參數個數for (var i = 0; i < m; i++) {this[ i + n ] = %_Arguments( i );   // 復制元素     (1)}this.length = n + m;      // 修正length屬性的值    (2)returnthis.length;
};

行文至此,就基本結束了,你可能還發現就是寫的非嚴格模式下,thisArg原始值會包裝成對象,添加函數并執行,再刪除。而嚴格模式下還是原始值這個沒有實現,而且萬一這個對象是凍結對象呢,Object.freeze({}),是無法在這個對象上添加屬性的。所以這個方法只能算是非嚴格模式下的簡版實現。最后來總結一下。

總結

通過MDN認識callapply,閱讀ES5規范,到模擬實現apply,再實現call
就是使用在對象上添加調用apply的函數執行,這時的調用函數的this就指向了這個thisArg,再返回結果。引出了ES6 SymbolES6的擴展符...evalnew Function(),嚴格模式等。
事實上,現實業務場景不需要去模擬實現callapply,畢竟是ES3就提供的方法。但面試官可以通過這個面試題考察候選人很多基礎知識。如:callapply的使用。ES6 SymbolES6的擴展符...evalnew Function(),嚴格模式,甚至時間復雜度和空間復雜度等。
讀者發現有不妥或可改善之處,歡迎指出。另外覺得寫得不錯,可以點個贊,也是對筆者的一種支持。

// 最終版版 刪除注釋版,詳細注釋看文章// 瀏覽器環境 非嚴格模式function getGlobalObject(){returnthis;
}
function generateFunctionCode(argsArrayLength){var code = 'return arguments[0][arguments[1]](';for(var i = 0; i < argsArrayLength; i++){if(i > 0){code += ',';}code += 'arguments[2][' + i + ']';}code += ')';return code;
}
Function.prototype.applyFn = function apply(thisArg, argsArray){if(typeofthis !== 'function'){thrownewTypeError(this + ' is not a function');}if(typeof argsArray === 'undefined' || argsArray === null){argsArray = [];}if(argsArray !== newObject(argsArray)){thrownewTypeError('CreateListFromArrayLike called on non-object');}if(typeof thisArg === 'undefined' || thisArg === null){thisArg = getGlobalObject();}thisArg = newObject(thisArg);var __fn = '__' + newDate().getTime();var originalVal = thisArg[__fn];var hasOriginalVal = thisArg.hasOwnProperty(__fn);thisArg[__fn] = this;var code = generateFunctionCode(argsArray.length);var result = (newFunction(code))(thisArg, __fn, argsArray);delete thisArg[__fn];if(hasOriginalVal){thisArg[__fn] = originalVal;}return result;
};
Function.prototype.callFn = function call(thisArg){var argsArray = [];var argumentsLength = arguments.length;for(var i = 0; i < argumentsLength - 1; i++){argsArray[i] = arguments[i + 1];}returnthis.applyFn(thisArg, argsArray);
}

學習源碼整體架構系列

1.學習 jQuery 源碼整體架構,打造屬于自己的 js 類庫
2.學習 underscore 源碼整體架構,打造屬于自己的函數式編程類庫
3.學習 lodash 源碼整體架構,打造屬于自己的函數式編程類庫
4.學習 sentry 源碼整體架構,打造屬于自己的前端異常監控SDK
5.學習 vuex 源碼整體架構,打造屬于自己的狀態管理庫
6.學習 axios 源碼整體架構,打造屬于自己的請求庫

微信公眾號

作者:常以若川為名混跡于江湖。前端路上 | PPT 愛好者 | 所知甚少,唯善學。
博客:https://lxchuan12.cn/posts/,閱讀體驗可能更好些。

若川視野

主要發布前端 | PPT | 生活 | 效率相關的文章,長按掃碼關注。歡迎加我微信lxchuan12(注明來源,基本來者不拒),拉您進【前端視野交流群】,長期交流學習~

小提醒:若川視野公眾號原創文章合集在菜單欄中間【原創精選】按鈕,歡迎點擊閱讀

由于公眾號限制外鏈,點擊讀原文,或許閱讀體驗更佳,覺得文章不錯,可以點個在看呀^_^另外歡迎留言交流~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/276470.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/276470.shtml
英文地址,請注明出處:http://en.pswp.cn/news/276470.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HTML中小meta的大作用

轉載鏈接&#xff1a;http://www.pconline.com.cn/pcedu/sj/wz/html/0401/293106.html meta是用來在HTML文檔中模擬HTTP協議的響應頭報文。meta 標簽用于網頁的<head>與</head>中&#xff0c;meta 標簽的用處很多。meta 的屬性有兩種&#xff1a;name和http-equiv。…

prometheus 發送恢復 值_Prometheus基礎知識介紹

【編者的話】本文會讓你了解Prometheus是什么&#xff0c;并讓你理解它在監控領域的適用場景。Prometheus起源很久以前&#xff0c;加利福尼亞州山景城有一家名為Google的公司。他們推出了大量產品&#xff0c;其中最著名的是廣告系統和搜索引擎平臺。為了運行這些不同的產品&a…

FAT32格式和NTFS有什么區別

FAT32與NTFS的區別在推出FAT32文件系統之前&#xff0c;通常PC機使用的文件系統是FAT16。像基于MS-DOS&#xff0c;Win 95等系統都采用了FAT16文件系統。在Win 9X下&#xff0c;FAT16支持的分區最大為2GB。我們知道計算機將信息保存在硬盤上稱為“簇”的區域內。使用的簇越小&a…

修復系統COM+

有的時候由于安裝COM&#xff0b;過程中出錯&#xff0c;可能會造成不能再次展開COM&#xff0b;瀏覽器的〔我的電腦〕。解決辦法&#xff1a;1 刪除注冊表鍵HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\COM32 運行appwiz.cpl&#xff0c;打開系統的[添加\刪除]窗體&#xff0c;點擊…

面試官問:JS的this指向

寫于2018年12月25日&#xff0c;發布在掘金上閱讀量近一萬&#xff0c;現在發布到微信公眾號申明原創。前言這是面試官問系列的第四篇&#xff0c;旨在幫助讀者提升JS基礎知識&#xff0c;包含new、call、apply、this、繼承相關知識。面試官問系列文章如下&#xff1a;感興趣的…

CSS實現div懸浮框的代碼(兼容IE6)

轉載鏈接&#xff1a;http://hi.baidu.com/grayworm/item/b735c2061f4e33ea34990265 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"http://ww…

python數組替換_Python:替換數組中的值

我會按照以下思路做些事情&#xff1a;import numpy as npdef fill(arr, fwd_fill):out arr.copy()if fwd_fill:start, end, step 0, len(out), 1else:start, end, step len(out)-1, -1, -1cur out[start]for i in range(start, end, step):if np.isnan(out[i]):out[i] cu…

(原創) 如何在Ubuntu設定P7010的1280 x 768解析度? (OS) (Linux) (Ubuntu) (NB) (P7010)

AbstractFujitsu P7010唯一較特別的硬體規格就是1280 x 768的解析度&#xff0c;無論任何Linux distribution預設都只能抓到1024 x 768&#xff0c;該如何才能抓到最佳的1280 x 768呢?IntroductionP7010非常適合Linux&#xff0c;但唯一的遺憾就是1280 x 768解析度不被支援&am…

python的常量和變量_python變量和常量

變量什么是變量&#xff1f;變量&#xff0c;是用于在內存中存放程序數據的容器計算機的最核心功能就是“計算”&#xff0c; 計算需要數據源&#xff0c;數據源要存在內存里&#xff0c;比如我要把小明的姓名、身高、年齡信息存下來&#xff0c;后面程序會調用。怎樣定義一個變…

要做PPT,一直找不到資源?

寫于 2016年6月&#xff0c;工作后就很少做PPT了。但工作至今也有人問我如何做PPT有沒有模板之類的問題&#xff08;比如&#xff1a;大學室友做公司年度匯報時也找到我問有沒有模板&#xff0c;我發了這篇文章給他&#xff0c;他說不記得我寫了這篇文章呀&#xff09;&#xf…

Linux系統安裝Appach 2.4.6

轉載鏈接&#xff1a;http://www.cnblogs.com/kerrycode/p/3261101.html Apache簡介 Apache HTTP Server&#xff08;簡稱Apache&#xff09;是Apache軟件基金會的一個開放源碼的網頁服務器&#xff0c;可以在大多數計算機操作系統中運行&#xff0c;由于其多平臺和安全性被廣…

[網摘].NET 程序員十種必備工具-概述

從 MSDN 中看到這篇文章&#xff0c;覺得不錯轉了過來&#xff0c;對于我這樣的.NET新手來說每個工具都是提高效率的好開始。將這十款軟件的介紹重新格式化一下&#xff0c;方便以后查閱。以 “.NET 程序員十種必備工具”開頭的10篇文章都是從這篇文章中分離出來&#xff0c;每…

完整asp.net圖形驗證碼程序

1、測試頁面&#xff1a;Default.aspx <% Page Language"C#" AutoEventWireup"true" CodeFile"Default.aspx.cs" Inherits"_Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &q…

學習 redux 源碼整體架構,深入理解 redux 及其中間件原理

如果覺得內容不錯&#xff0c;可以設為星標置頂我的公眾號1. 前言你好&#xff0c;我是若川。這是學習源碼整體架構系列第八篇。整體架構這詞語好像有點大&#xff0c;姑且就算是源碼整體結構吧&#xff0c;主要就是學習是代碼整體結構&#xff0c;不深究其他不是主線的具體函數…

pdf安裝包_有么有pdf控件,不需要用戶安裝任何安裝包直接打印的?

如果開發一個軟件&#xff0c;需要用到PDF功能&#xff0c;您的選擇是基于Adobe PDF嗎&#xff1f; 如果是基于Adobe PDF&#xff0c;需要用戶安裝一個幾十M的Adobe的安裝包&#xff0c;這顯然是不友好的。即使目前也有了一些其它的閱讀器&#xff0c;大小也還好。但是&#xf…

Centos編譯安裝Apache 2.4.6筆記 配置

轉載鏈接&#xff1a;http://www.onepx.com/centos-apache-246.html 之前服務器 Apache 版本一直是 2.2.x&#xff0c;鑒于 Centos 更新軟件的惰性&#xff0c;我看直到 2014 年結束&#xff0c;apache 2.4 都不一定會出現在 Centos 中&#xff0c;我是不打算等了&#xff0c;…

[轉] C#異步操作

Title 通過委托實現異步調用中BeginInvoke及回調函數的使用 通過委托實現異步調用的步驟&#xff1a; 1.定義委托。 2.將要進行異步調用的方法“實例化”到定義的委托。 3.在委托上調用BeginInvoke方法。其中&#xff0c;BeginInvoke的參數由三個部分構成。第一部分&#xff1…

HTTP Server Error 500 內部服務器錯誤

問題&#xff1a;HTTP500錯誤 或 Server Application Error ------------------------------------Server Application ErrorThe server has encountered an error while loading an application during the processing of your request. Please refer to the event log for mo…

使用 ohmyzsh 打造 windows、ubuntu、mac 系統高效終端命令行工具

如果覺得內容不錯&#xff0c;可以設為星標置頂我的公眾號原標題名&#xff1a;oh my zsh 和 windows git bash 設置別名提高效率寫于2018年06月03日在我的微信交流群中聽聞很多前端開發比較貧窮&#xff0c;沒有買mac電腦&#xff08;比如我&#xff09;&#xff0c;也沒有用過…

request獲取mac地址_【Go】獲取用戶真實的ip地址

原文鏈接&#xff1a;https://blog.thinkeridea.com/201903/go/get_client_ip.html用戶請求到達提供服務的服務器中間有很多的環節&#xff0c;導致服務獲取用戶真實的 ip 非常困難&#xff0c;大多數的框架及工具庫都會封裝各種獲取用戶真實 ip 的方法&#xff0c;在 exnet 包…