新聞中心
JavaScript
跨域的方式有哪些,為什么需要跨域,同源策略攔截客戶端請求還是服務(wù)器響應(yīng)。

之所以需要跨域,是因為瀏覽器同源策略的約束,面對不同源的請求,我們無法完成,這時候就需要用到跨域。同源策略攔截的是跨源請求,原因:CORS缺少。
Access-Control-Allow-Origin頭。
跨域的方式主要有:JSONP、proxy代理、CORS、XDR。
JSONP的原理是什么,缺點是什么,瀏覽器端如何做
JSONP主要是因為script標簽的src屬性不被同源策略所約束,同時在沒有阻塞的情況下資源加載到頁面后會立即執(zhí)行。
缺點:
- JSONP只支持get請求而不支持post請求,如果想傳給后臺一個json格式的數(shù)據(jù),此時問題就來了,瀏覽器會報一個http狀態(tài)碼415錯誤,請求格式不正確。
- JSONP本質(zhì)是一種代碼注入,存在安全問題。
JSONP服務(wù)器端如何處理
實際項目中JSONP通常用來獲取json格式的數(shù)據(jù),這時候前后端通常約定一個參數(shù)callback,該參數(shù)的值,就是處理返回數(shù)據(jù)的函數(shù)名稱。
var f = function(data){
alert(data.name)
}
var _script = document.createElement('script');
_script.type = 'text/javascript'
_script.src = 'http://localhost:8888/jsonp?callback=f'
document.head.appendCild(_script);//node處理
var query = _scr.query;
var params = qs.parse(query);
var f = '';
f = params.callback;
res.writeHead(200,{'Content-Type','text/javascript'})
res.write(f + "({name:'hello world'})")
res.end
//php處理(注意輸出格式)
$data = array(
'rand' => $_GET['random'],
'msg' => 'Success'
);
echo $_GET['callback'].'('.json_encode($data).')';
補充:cors是一種現(xiàn)代瀏覽器支持跨域資源請求的一種方式,它允許瀏覽器向跨源服務(wù)器,發(fā)出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。
//node處理
if(req.headers.origin){
res.wirteHead(200,{
'Content-type':'text/html;charset=UTF-8',
'Access-Control-Allow-Origin':'*', // *通配,或者安全考慮替換為指定域名
});
res.write('cors');
res.end();
}
列表無限滾動曾經(jīng)有遇到過嘛
簡單列表滾動加載是監(jiān)聽滾動條在滿足條件的時候觸發(fā)回調(diào),然后通過把新的元素加入到頁面頁尾的方法完成,但是如果用戶加載過多列表數(shù)據(jù)(比如我這一個列表頁有一萬條數(shù)據(jù)需要展示),那么用戶不斷加載,頁面不斷增加新的元素,很容易就導(dǎo)致頁面元素過多而造成卡頓,所以就提出的列表的無限滾動加載,主要是在刪除原有元素并且維持高度的基礎(chǔ)上,生成并加載新的數(shù)據(jù)。
如果滾動過快怎么辦,高頻率觸發(fā)事件解決方案-防抖和節(jié)流
節(jié)流:在一段時間內(nèi)不管觸發(fā)了多少次都只認為觸發(fā)了一次,等計時結(jié)束進行響應(yīng)(假設(shè)設(shè)置的時間為2000ms,再觸發(fā)了事件的2000ms之內(nèi),你在多少觸發(fā)該事件,都不會有任何作用,它只為第一個事件等待2000ms。時間一到,它就會執(zhí)行了。 )
//時間戳方式
function throttle(fn,delay){
let pre = Date.now();
return function(){
let context = this;
let args = arguments;
let now = Date.now();
if(now - pre > delay){
fn.apply(context,args);
pre = Date.now();
}
}
}
//定時器方式
function throttle(fn,delay){
let timer = null;
return function(){
let context = this;
let args = arguments;
if(!timer){
timer = setTimeout(function(){
fn.apply(context,args);
timer = null;
},delay)
}
}
}
防抖:在某段時間內(nèi),不管你觸發(fā)了多少次事件,都只認最后一次(假設(shè)設(shè)置時間為2000ms,如果觸發(fā)事件的2000ms之內(nèi),你再次觸發(fā)該事件,就會給新的事件添加新的計時器,之前的事件統(tǒng)統(tǒng)作廢。只執(zhí)行最后一次觸發(fā)的事件。)
//定時器方式
function debounce(fn,delay){
let timer = null;
return function(){
let context = this;
let args = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(context,args);
},delay)
}
}
介紹ES6你熟悉的幾個新特性
- let,const聲明變量。
- 解構(gòu)賦值。
- 箭頭函數(shù)。
- 擴展運算符。
- 數(shù)組的新方法 -- map,reduce,filter。
- promise。
說說var,let,const的區(qū)別
首先var相對let/const,后者可以使用塊級作用域,var聲明變量則存在函數(shù)作用域內(nèi)(該域內(nèi)存在變量提升)。let/const有一個暫時性死區(qū)的概念,即進入作用域創(chuàng)建變量到變量可以開始訪問的一段時間。
其次let,const主要的區(qū)別在于:const一旦被賦值就不再"改變"。
const name = 'Jack';
name = 'Tom' //TypeError:Assignment to constant
但需要理解的是:這并不意味著聲明的變量本身不可變,只是說它不可再次被賦值了(const定義引用類型時,只要它的引用地址不發(fā)生改變,仍然可以改變它的屬性)。
const person = {
name : 'Jack‘
}
person.name = 'Tom'解釋一下變量提升
JavaScript引擎會先進行預(yù)解析,獲取所有變量的聲明復(fù)制undefined,然后逐行執(zhí)行,這導(dǎo)致所有被聲明的變量,都會被提升到代碼的頭部(被提升的只有變量,值不會被提升),也就是變量提升(hoisting)。
console.log(a) // undefined
var a = 1
function b(){
console.log(a)
}
b() // 1
預(yù)解析階段會先獲的變量a賦值undefined,并將var a = undefined放在最頂端,此時a = 1還在原來的位置,實際就是:
var a = undefined
console.log(a) // undefined
a = 1
function b(){
console.log(a)
}
b() // 1
然后會是執(zhí)行階段,逐行執(zhí)行造成了打印出a是undefined。
數(shù)據(jù)類型有哪些,對symbol有了解嘛
基本數(shù)據(jù)類型:string、number、Boolean、undefined、null。
復(fù)雜數(shù)據(jù)類型:Object。
ES6新增數(shù)據(jù)類型:symbol、Map、Set。
其中symbol是基本數(shù)據(jù)類型,每一個symbol都是一個全局唯一的字符串// 在一個塊級作用域里使用symbol,創(chuàng)造一個隱藏屬性 { let a = Symbol(); let object = { name : 'okaychen', age : 3, [a] : '隱藏屬性' } window.object = object }set是object里面的一種,set里無論原始值還是引用類型的值,重復(fù)的都只會保留一個Map可以允許任何類型作為對象的鍵,彌補了object只能使用字符串作為鍵(注意Map是ES6中的一種數(shù)據(jù)結(jié)構(gòu),區(qū)別于數(shù)組方法map)。
值類型和引用類型有哪些區(qū)別
- 類型: 字符串string,數(shù)值number,布爾值boolean,null, undefined。
- 引用類型: 對象 Object,數(shù)組Array,函數(shù)Function。
值類型:
- 占用空間固定,保存在棧中:當一個方法執(zhí)行時,每個方法都會建立自己的內(nèi)存棧,也就是所謂的函數(shù)作用域,基礎(chǔ)變量的值是存儲在棧中的,而引用類型變量存儲在棧中的是指向堆中的數(shù)組或值者對象的"地址"。
- 保存與復(fù)制的是值本身。
- 可以用**typeof**檢測值類型。
- 基本數(shù)據(jù)類型是值類型。
引用類型:
- 占用空間不固定,保存在堆中:由于對象的創(chuàng)建成本比較大,在程序中創(chuàng)建一個對象時,這個對象將被保存到運行時數(shù)據(jù)區(qū)中,以便反復(fù)利用,這個運行時數(shù)據(jù)區(qū)就是堆內(nèi)存。
- 保存與復(fù)制的是指向?qū)ο蟮囊粋€指針。
- 使用**instanceof()**檢測數(shù)據(jù)類型。
- 使用 new() 方法構(gòu)造出的對象是引用型。
分別說一下數(shù)組中常用的方法
數(shù)組也是一種數(shù)據(jù)類型,類比數(shù)據(jù)類型的學(xué)習(xí)我們可以從其特性,增刪改查,其他方法,支持的運算符七個方面來學(xué)習(xí),可以明顯提高效率。
數(shù)組的方法我們除了作用以外,我們還比較關(guān)心的就是該數(shù)組方法是否改變原數(shù)組,下面就按照這個規(guī)則來分類:
// 改變原數(shù)組的方法:
pop() // 刪除數(shù)組中的最后一個元素,把數(shù)組長度減 1,并且返回它刪除的元素的值
push() // 該方法可把它的參數(shù)順序添加到數(shù)組的尾部。它直接修改數(shù)組,返回后修改數(shù)組的長度
reverse() // 將數(shù)組元素倒序,改變原數(shù)組
unshift() // 可以向數(shù)組開頭增加一個或多個元素,并返回新的長度
shift() // 數(shù)組的第一個元素從其中刪除,并返回第一個元素的值,減少數(shù)組的長度
sort() // 在原數(shù)組上進行排序,不生成副本
splice(start,刪除的個數(shù),插入的元素) //可刪除從index處開始的零個或多個元素,并且用參數(shù)列表中聲明的一個或多個值來替換那些被刪除的元素
// 不改變原數(shù)組:
concat() // 用于連接兩個或多個數(shù)組,不改變原數(shù)組,返回一個新的數(shù)組
join() // 將數(shù)組中的所有元素都轉(zhuǎn)化為字符串并拼接在一起,默認使用逗號,返回最終生成的字符串
map() // 對數(shù)組的每一項運行給定函數(shù),返回每次函數(shù)調(diào)用的結(jié)果組成的數(shù)組,不改變原數(shù)組
indexof // 返回指定位置的元素值或字符串,通過搜索值與下標尋找
every() // 如果每一項都為true,則返回true
some() // 某一項返回true,則返回true
forEach() // 對數(shù)組的每一項運行給定函數(shù),沒有返回值
對數(shù)組方法map和reduce方法的理解,區(qū)別在哪里
map方法的調(diào)用者一般是數(shù)組,參數(shù)是一個callback函數(shù),返回值是一個由原數(shù)組中每個元素執(zhí)行callback函數(shù)得到的返回值組成的新數(shù)組。
舉個栗子:
/ 接口數(shù)據(jù)映射
let arr = res.map(item=>{
return {
name:item.name,
age:item.age,
sex:item.sex === 1? '男':item.sex === 0?'女':'保密',
avatar: item.img
}
})
reduce方法調(diào)用者也一般為數(shù)組,參數(shù)是callback和一個可選的initialValue,為數(shù)組中每個元素執(zhí)行callback函數(shù),返回一個具體的結(jié)果,如果給定initialValue可以作為第一次調(diào)用callback的第一個參數(shù),可以控制返回值的格式。
舉個栗子:
// 求一個字符串中每個字母出現(xiàn)的次數(shù)
let str = 'abcabdaac'
str.split('').reduce((res,cur)=>{
res[cur] ? res[cur]++ : res[cur] = 1
return res
},{})
// {a: 4, b: 2, c: 2, d: 1}
對map和reduce實現(xiàn)的理解,能手寫一下嘛
數(shù)組方法map模擬實現(xiàn):
Array.prototype._map = fn => {
let newArr = [];
let me = this;
for(let i = 0;i newArr.push(fn(me[i]));
}
return newArr;
} 數(shù)組方法reduce模擬實現(xiàn):方法類似核心是數(shù)組的遍歷,因為reduce有第二個可選參數(shù)initialValue做起始值,所以要判斷是否有可選參數(shù)作為遍歷的起始值,并將得到的參數(shù)傳入回調(diào)函數(shù)中執(zhí)行。
Array.prototype._reduce = (callback, initialValue) => {
let arr = this;
let base = typeof initialValue == 'undefined' ? arr[0] : initialValue;
let startPoint = typeof initialValue === 'undefined' ? 1 : 0;
arr.slice(startPoint).forEach((val, index) => {
base = callback(base, val, index + startPoint, arr)
})
return base
}Ajax的概念,手寫一下原生實現(xiàn)的思路
首先需要知道的是Ajax主要是通過XMLHttpRequest對象向服務(wù)器提出請求并處理響應(yīng),進行頁面的局部更新,XMLHttpRequest對象常用的三大屬性:onreadystatechange,readyState,status。
//Ajax原生簡單實現(xiàn)
let xhr = XMLHttpRequest;
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status === 200){
console.log(xhr.responseText);
}else{
console.error(xhr.statusText);
}
}
}
xhr.onerror = e => {
console.error(xhr.statusText);
}
xhr.open('GET','/EndPonint',true);
xhr.send(null);
有了解過fetch嘛,和XMLHttpRequest的區(qū)別在哪
XMLHttpRequest歷史悠久,因為其API設(shè)計其實并不是很好,輸入,輸出,狀態(tài)都在同一個接口管理,容易寫出非常非常混亂的代碼,F(xiàn)etch API采取了一種新規(guī)范,用來取代XMLHttpReques,F(xiàn)etch更現(xiàn)代化,更接近于未來,內(nèi)部使用了Promise,用起來也更加簡潔。
fetch('./api/demo.json')
.then((response) => {
response.json().then((data) => {
...
});
});
.catch((err) => {...});Promise的了解,手撕Promise(Promise.all或者Promise.race)
Promise是一種異步編程的解決方法,相比容易陷入回調(diào)地獄的回調(diào)函數(shù),采用鏈式調(diào)用的方式更合理也更方便,Promise有三種狀態(tài):pending(進行中)、fulfilled(已成功)和rejected(已失敗),接受一個作為函數(shù)作為參數(shù),該函數(shù)有兩個參數(shù),分別是resolve和reject兩個函數(shù)。
// Promise的模擬實現(xiàn)
class _Promise {
constructor(fn) {
let _this = this;
this._queue = [];
this._success = null;
this._error = null;
this.status = '';
fn((...arg) => {
// resolve
if (_this.status != 'error') {
_this.status = 'success';
_this._success = arg;
_this._queue.forEach(json => {
json.fn1(...arg)
})
}
}, (...arg) => {
// reject
if (_this.status != 'success') {
_this.status = 'error';
_this._error = arg;
_this._queue.forEach(json => {
json.fn2(...arg)
})
}
})
}
then(fn1, fn2) {
let _this = this;
return new _Promise((resolve, reject) => {
if (_this.status == 'success') {
resolve(fn1(..._this._success))
} else if (_this.status == 'error') {
fn2(..._this._error)
} else {
_this._queue.push({fn1,fn2});
}
})
}
}
Promise.all和Promise.race在實際應(yīng)用中的比較,比如從接口中獲取數(shù)據(jù),等待所有數(shù)據(jù)到達后執(zhí)行某些操作可以用前者,如果從幾個接口中獲取相同的數(shù)據(jù)哪個接口先到就用哪個可以使用后者。
//Promise.all的模擬實現(xiàn)(race的實現(xiàn)類似)
Promise.prototype._all = interable => {
let results = [];
let promiseCount = 0;
return new Promise(function (resolve, reject) {
for (let val of iterable) {
Promise.resolve(val).then(res => {
promiseCount++;
results[i] = res;
if (promiseCount === interable.length) {
return resolve(results);
}
}, err => {
return reject(err);
})
}
})
}
js為什么0.1+0.2不等于0.3
主要是因為JavaScript同樣采用IEEE754標準,在64位中存儲一個數(shù)字的有效數(shù)字形式。
第0位表示符號位,0表示整數(shù)1表示負數(shù),第111位存儲指數(shù)部分,第1263位存小數(shù)部分。
由于二進制的有效數(shù)字總是表示為1.xxx...這樣的形式,尾數(shù)部分在規(guī)約形式下的第一位默認為1,故存儲時第一位省略不寫,尾數(shù)部分存儲有效數(shù)字小數(shù)點后的xxx...,最長52位,因此,JavaScript提供的有效數(shù)字最長為53個二進制位(尾數(shù)部分52位+被省略的1位)。
由于需要對求和結(jié)果規(guī)格化(用有效數(shù)字表示),右規(guī)導(dǎo)致低位丟失,此時需對丟失的低位進行舍入操作,遵循IEEE754舍入規(guī)則,會有精度損失。
如何正確判斷與使用this,箭頭函數(shù)有沒有自己的this指針
this有四種綁定規(guī)則,默認綁定、隱式綁定、顯示綁定、new 綁定,優(yōu)先級由低到高。
在ECMA內(nèi),this 會調(diào)用原生方法 ResolveThisBinding() 原生方法,該方法使用正在運行的執(zhí)行上下文的LexicalEnvironment確定關(guān)鍵字this的綁定。
可以簡單總結(jié)為:誰直接調(diào)用產(chǎn)生這個this指針的函數(shù),this就指向誰。
- this在一般模式下指向全局對象;嚴格模式下 this 默認為undefined。
- 箭頭函數(shù)沒有自己的this指針,它的this綁定取決于外層(函數(shù)或全局)作用域)。
- call,apply,bind在非箭頭函數(shù)下修改this值(箭頭函數(shù)下只傳遞參數(shù)),不管call , bind, apply多少次,函數(shù)的this永遠由第一次的決定。
對閉包的了解及其應(yīng)用場景
閉包是指有權(quán)訪問另外一個函數(shù)作用域中的變量的函數(shù).可以理解為(能夠讀取其他函數(shù)內(nèi)部變量的函數(shù))。
閉包的作用: 正常函數(shù)執(zhí)行完畢后,里面聲明的變量被垃圾回收處理掉,但是閉包可以讓作用域里的變量,在函數(shù)執(zhí)行完之后依舊保持沒有被垃圾回收處理掉。
具體有以下幾個應(yīng)用場景:
/經(jīng)典案例:斐波那契數(shù)列 :1, 1, 2, 3, 5, 8, 13, …
let count = 0;
const fib = (()=>{
let arr = [1,1]
return function(n){
count++;
let res = arr[n];
if(res){
return res;
}else{
arr[n] = fib(n-1) + fib(n-2);
return arr[n]
}
}
})();
//通過閉包特性,模擬私有變量
const book = (function () {
var page = 100;
return function () {
this.auther = 'okaychen';
this._page = function () {
console.log(page);
}
}
})();
var a = new book();
a.auther // "okaychen"
a._page() // 100
a.page // undefined
for (var i=1; i<=5; i++) {
setTimeout( function timer() {
console.log(i);
}, i*1000 );
}
// 經(jīng)典老問題,輸出結(jié)果:6 6 6 6 6
// js執(zhí)行的時候首先會先執(zhí)行主線程,異步相關(guān)的(setTimeout)會存到異步隊列里,
// 當主線程執(zhí)行完畢開始執(zhí)行異步隊列,主線程執(zhí)行完畢后,此時 i 的值為 6,
// 所以在執(zhí)行異步隊列的時候,打印出來的都是 6
// 利用閉包來取正確值
for (var i=1; i<=5; i++) {
setTimeout( (function(i) {
return function() {
console.log(i);
}
})(i), i*1000 );
}
// 方案二:或者使用ES6的let,這里let本質(zhì)也是形成了一個閉包
for (let i=1; i<=5; i++) {
setTimeout( function timer() {
console.log(i);
}, i*1000 );
}對eventloop事件循環(huán)機制的了解
首先,JavaScript一大特點就是單線程,這樣的設(shè)計讓它在同一時間只做一件事;作為瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操作DOM,避免了復(fù)雜性,比如假設(shè)JavaScript有兩個線程,那么在同一時間進行添加刪除節(jié)點操作,為瀏覽器分辨以哪個線程為準帶來困難,所以單線程是它作為瀏覽器腳本語言的優(yōu)勢,也是它的核心特征。
注:雖然為了利用多核CPU的計算能力,HTML5提出了web worker標準,允許JavaScript創(chuàng)建多個線程,但是子線程完全受主線程控制,且不得操作DOM,所以也并沒有改變JavaScript單線程的本質(zhì)。
那么,單線程就意味著,所有任務(wù)需要排隊,前一個任務(wù)結(jié)束才會執(zhí)行后一個任務(wù),所以為了提高CPU的利用效率,就把所有任務(wù)分成了同步任務(wù)(synchronous)和異步任務(wù)(asynchronous),同步任務(wù)在主線程順序執(zhí)行,異步任務(wù)則不進入主線程而是進入到任務(wù)隊列(task queue)中。在主線程上會形成一個執(zhí)行棧,等執(zhí)行棧中所有任務(wù)執(zhí)行完畢之后,會去任務(wù)隊列中查看有哪些事件,此時異步任務(wù)結(jié)束等待狀態(tài),進入執(zhí)行棧中,開始執(zhí)行。
主線程從任務(wù)隊列中讀取事件,這個過程是循環(huán)不斷的,所以整個的這種運行機制又稱為Event Loop(事件循環(huán))。
對宏任務(wù)和微任務(wù)的理解,微任務(wù)有哪些
異步任務(wù)又分為宏任務(wù)(macrotask)和微任務(wù)(microtask),那么任務(wù)隊列就有了宏任務(wù)隊列和微任務(wù)隊列,微任務(wù)總是在宏任務(wù)之前執(zhí)行,也就是說:同步任務(wù)>微任務(wù)>宏任務(wù)。
宏任務(wù)包括:
微任務(wù)包括:
setTimeout用作倒計時為什么會產(chǎn)生誤差
考察的其實是同一個概念,正因為setTimeout屬于宏任務(wù),那么如果當前 執(zhí)行棧 所花費的時間大于 定時器 時間,那么定時器的回調(diào)在 宏任務(wù) 里,來不及去調(diào)用,所有這個時間會有誤差。
對new的過程和原理的理解
JavaScript中new一個對象,我們需要了解主要發(fā)生了以下四步:
1.創(chuàng)建一個空的對象。
let obj = { }2.設(shè)置新對象的constructor屬性為構(gòu)造函數(shù)的名稱,設(shè)置新對象的proto屬性指向構(gòu)造函數(shù)的prototype對象。
obj.proto = ClassA.prototype
3.使用新對象調(diào)用函數(shù),函數(shù)中的this被指向新實例對象。
ClassA.call(obj); //{}.構(gòu)造函數(shù)()4.將初始化完畢的新對象地址,保存到等號左邊的變量中。
// new構(gòu)造函數(shù)的模擬實現(xiàn)
const _new = function(){
let obj = new Object();
let _constructor = [].shift.call(arguments);
// 使用中間函數(shù)來維護原型關(guān)系
const F = function(){};
F.prototype = _constructor.prototype;
obj = new F();
let res = _constructor.apply(obj,arguments);
return typeof res === 'object' ? res || obj : obj;
}
有了解過js的垃圾回收機制嘛
JavaScript內(nèi)存管理有一個主要概念是可達性,“可達性” 值是那些以某種方式可訪問或可用的值,它們被保證存儲在內(nèi)存中。有一組基本的固有可達值,由于顯而易見的原因無法刪除,比如:本地函數(shù)的局部變量和參數(shù),全局變量,當前嵌套調(diào)用鏈上的其他函數(shù)的變量和參數(shù),這些值被稱為"根",如果引用或引用鏈可以從根訪問任何其他值,則認為該值是可訪問的。
JavaScript在創(chuàng)建變量時自動進行了內(nèi)存分配,并且在它們不使用時**"自動"**釋放,釋放的過程就被稱為垃圾回收。
現(xiàn)在各大瀏覽器通常采用的垃圾回收有兩種方法:標記清除、引用計數(shù)。
引用計數(shù)垃圾收集:把“對象是否不再需要”簡化定義為“對象有沒有其他對象引用到它”。如果沒有引用指向該對象(零引用),對象將被垃圾回收機制回收。但是有一個限制是"無法處理循環(huán)引用問題"。
let element = document.getElementById("some_element");
let myObj = new Object();
myObj.element = element;
element.someObject = myObj;
// 變量myObj有一個element屬性執(zhí)行element,而變量element有一個名為someObject屬性指回myObj,因為循環(huán)引用,引用計數(shù)法將沒辦法回收該內(nèi)存
// 我們需要手動切斷它們的循環(huán)引用,防止內(nèi)存泄露
myObj.element = null;
element.someObject =null;標記清除:是js中最常用的垃圾回收方式,把“對象是否不再需要”簡化定義為“對象是否可以獲得”,定期會執(zhí)行以下的"垃圾回收"步驟(這正是標記清除算法垃圾收集的工作原理):首先垃圾回收器獲取根并**“標記”**它們?nèi)缓笏L問并“標記”所有來自它們的引用接著它訪問標記的對象并標記它們的引用(子孫代的引用)以此類推,直至有未訪問的引用,此時進程中不能訪問的對象將被認為是不可訪問的除標記的對象外,其余對象將被刪除。
對函數(shù)柯里化有了解嘛
柯里化(Currying)是函數(shù)式編程的一個很重要的概念,將使用多個參數(shù)的一個函數(shù)轉(zhuǎn)換成一系列使用一個參數(shù)的函數(shù)。
主要有三個作用:1. 參數(shù)復(fù)用**;**2. 提前返回;3. 延遲計算/運行。
參數(shù)復(fù)用
// 舉個栗子:正則驗證字符串
// 函數(shù)封裝后
function check(reg, txt) {
return reg.test(txt)
}
check(/\d+/g, 'test') //false
check(/[a-z]+/g, 'test') //true
// 需要復(fù)用第一個reg參數(shù),Currying后,將兩個參數(shù)分開,可以直接調(diào)用hasNumber,hasLetter等函數(shù)
function curryingCheck(reg) {
return function(txt) {
return reg.test(txt)
}
}
let hasNumber = curryingCheck(/\d+/g)
let hasLetter = curryingCheck(/[a-z]+/g)
hasNumber('test1') // true
hasLetter('21212') // false
提前返回
// 比如:解決原生方法在現(xiàn)代瀏覽器和IE之間的兼容問題
// 提前返回: 使用函數(shù)立即調(diào)用進行了一次兼容判斷(部分求值),返回兼容的事件綁定方法
// 延遲執(zhí)行:返回新函數(shù),在新函數(shù)調(diào)用兼容的事件方法。等待addEvent新函數(shù)調(diào)用,延遲執(zhí)行
const addEvent = (function() {
if(window.addEventListener) {
return function(ele, type, fn, isCapture) {
ele.addEventListener(type, fn, isCapture)
}
} else if(window.attachEvent) {
return function(ele, type, fn) {
ele.attachEvent("on" + type, fn)
}
}
})()
延遲執(zhí)行
// js中bind實現(xiàn)機制正是Currying
Function.prototype.bind = function (context) {
var _this = this
var args = Array.prototype.slice.call(arguments, 1)
return function() {
return _this.apply(context, args)
}
}
當前文章:一篇關(guān)于JavaScript的面試題
當前URL:http://www.dlmjj.cn/article/dpssjhi.html


咨詢
建站咨詢
