基本數據類型:javascript
number、html
string、前端
boolean、java
null、git
undefined 引用數據類型: object(普通對象、數組對象、正則對象、日期對象、Math、實例、 prototype等)github
function(普通函數和類) 特殊數據類型:Symbolweb
基本數據類型按值操做,引用數據類型按照堆內存的引用地址來操做面試
數據類型檢測四種方式: typeof編程
instanceofjson
constructor
Object.prototype.toString.call()
webkit、
Gecko、
Trident、
Presto等
函數執行會造成一個私有的做用域(私有棧內存),這就是閉包,在真實項目 中主要應用閉包的兩大做用 保護
保存 以前研究過像JQUERY等類庫的源碼,爲了防止全局變量污染,基本上全部代碼 都是放到閉包中保護起來的 項目中遇到循環事件綁定,也會基於閉包保存的做用,去存儲對應的索引 以前研究過JS高階編程技巧:柯理化函數編程思想,這個就是閉包的應用,重 寫過debounce、throttle函數節流和防抖的方法 ...... 由於閉包會產生不釋放的棧內存,因此儘量在項目中少使用
面向對象是一種編程思想,js自己就是基於面向對象構建出來的(例如:js中有不少內置類,像Promise就是ES6中新增的一個內置類,咱們能夠基於new Promise來建立一個實例,來管理異步編程,咱們在項目中Promise也常常用,本身也研究過他的源碼。。。)咱們平時用的VUE/REACT/JQUERY也是基於面向對象構建出來的,他們都是類,平時開發的時候都是建立他們的額實例來操做的;固然我本身在真實項目中,也封裝過一些組件插件,(例如:DIALOG、拖拽、、、)也是基於面向對象開發的,這樣能夠創造不一樣的實例,來管理私有的屬性和公有的方法,很方便。。。。
JS中的面向對對象,和其餘編程語言還略有不一樣,JS中類和實例都是基於原型和原型鏈機制來處理的; 並且js中關於類的重載、重寫、繼承也和其餘語言不太同樣
// 這個題我知道,我以前看過部分JQ源碼
// 1.$(document).ready() 採用的是DOM2事件綁定,監聽的是DOMContentLoaded這個事件,因此只要DOM結構加載完成就會被觸發執行,並且同一個頁面中可使用屢次(綁定不一樣的方法,由於基於DOM2事件池綁定機制完成的) // 2.window.onload必須等待全部資源都加載完成纔會被觸發執行,採用DOM0事件綁定,同一個頁面只能綁定一次(一個方法),想綁定多個也須要改成window.addEventListener('load', function () {})DOM2綁定方式
let 和 var 的區別
- 不存在變量提高
- 不容許重複聲明
- 在全局做用域下設置變量不會給window設置屬性
- 存在塊級做用域
- 解決了一些暫時性死區問題
let 和 const 的區別 const 建立的是常量,存儲的值不能被修改(準確說是不能修改變量的指 向)
call 和 apply 的區別 給方法傳參的時候,call是按順序,一個個傳遞;apply是把傳遞的參數放 到一個數組中傳遞;
在傳遞參數較多的狀況下, call的性能要高於apply; call 和 bind 的區別 call在改變函數this指向的時候,會把函數當即執行,而bind不會把函數立 即執行,只是預先處理了this和實參信息;
真實項目中,咱們須要改變this指向的時候,會應用這三個方法,例如: 給元素進行事件綁定,咱們須要把事件觸發,所執行的函數中this和參數進 行預先處理,此時可使用bind進行處理;
咱們能夠基於call方法,讓類數組借用數組原型上的方法,例如:把類數組 轉換爲數組
能夠基於apply傳參是一個數組,借用Math.max獲取數組中的最大值 ......
forEach、
map、
find、
some、
filter、
reduce、
every、
sort等
// 開發者A
let AModule = (function () {
let n = 10;
let query = function () {
//...
};
let fn = function () {
//...
//調取開發者B編寫的QUERY方法
BModule.query();
};
return {
query: query,
init: function () {
query();
fn();
}
}
})();
// 開發者B
let BModule = (function () {
let n = 20;
let query = function () {
//...
};
let sum = function () {
//...
//調取開發者A編寫的QUERY方法
AModule.query();
};
return {
query: query,
init: function () {
query();
sum();
}
}
})();
AModule.init();
BModule.init();
複製代碼
var r2 = typeof typeof typeof sum; // "string"
執行順序:先執行最後一個 typeof sum -> "function" -> typeof typeof "function" -> typeof "string" -> "string"
只要是兩個及以上的typeof 最後返回結果就是 "string"
第一次握手:創建鏈接。客戶端發送鏈接請求報文段,將SYN位置爲1,Sequence Number爲x;而後,客戶端進入SYN_SEND狀態,等待服務器的確認;
第二次握手:服務器收到SYN報文段。服務器收到客戶端的SYN報文段,須要對這個SYN報文段進行確認,設置Acknowledgment Number爲x+1(Sequence Number+1);同時,本身本身還要發送SYN請求信息,將SYN位置爲1,Sequence Number爲y;服務器端將上述全部信息放到一個報文段(即SYN+ACK報文段)中,一併發送給客戶端,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK報文段。而後將Acknowledgment Number設置爲y+1,向服務器發送ACK報文段,這個報文段發送完畢之後,客戶端和服務器端都進入ESTABLISHED狀態,完成TCP三次握手。
完成了三次握手,客戶端和服務器端就能夠開始傳送數據。
ACK:此標誌表示應答域有效,就是說前面所說的TCP應答號將會包含在TCP數據包中;有兩個取值:0和1,爲1的時候表示應答域有效,反之爲0。
TCP協議規定,只有ACK=1時有效,也規定鏈接創建後全部發送的報文的ACK必須爲1。
SYN(SYNchronization) : 在鏈接創建時用來同步序號。當SYN=1而ACK=0時,代表這是一個鏈接請求報文。對方若贊成創建鏈接,則應在響應報文中使SYN=1和ACK=1. 所以, SYN置1就表示這是一個鏈接請求或鏈接接受報文。 FIN (finis)即完,終結的意思, 用來釋放一個鏈接。當 FIN = 1 時,代表此報文段的發送方的數據已經發送完畢,並要求釋放鏈接。
發送HTTP請求,服務器處理請求,返回響應結果 TCP鏈接創建後,瀏覽器就能夠利用HTTP/HTTPS協議向服務器發送請求了。服務器接受到請求,就解析請求頭,若是頭部有緩存相關信息如if-none-match與if-modified-since,則驗證緩存是否有效,如有效則返回狀態碼爲304,若無效則從新返回資源,狀態碼爲200. 關閉TCP鏈接
第一次分手:主機1(可使客戶端,也能夠是服務器端),設置Sequence Number和Acknowledgment Number,向主機2發送一個FIN報文段;此時,主機1進入FIN_WAIT_1狀態;這表示主機1沒有數據要發送給主機2了;
第二次分手:主機2收到了主機1發送的FIN報文段,向主機1回一個ACK報文段,Acknowledgment Number爲Sequence Number加1;主機1進入FIN_WAIT_2狀態;主機2告訴主機1,我「贊成」你的關閉請求;
第三次分手:主機2向主機1發送FIN報文段,請求關閉鏈接,同時主機2進入LAST_ACK狀態;
第四次分手:主機1收到主機2發送的FIN報文段,向主機2發送ACK報文段,而後主機1進入TIME_WAIT狀態;主機2收到主機1的ACK報文段之後,就關閉鏈接;此時,主機1等待2MSL後依然沒有收到回覆,則證實Server端已正常關閉,那好,主機1也能夠關閉鏈接了。
小提示: 若是日常自身有使用場景可結合使用場景進行講解,好比我在這裏使用過的場景是CORS和Nginx反向代理。
小提示:若是你提到JSONP,面試官確定會問你整個詳細的實現過程,因此必定要搞懂JSONP的實現原理,若是不是很理解能夠本身起一個Express服務實踐一下。
Web前端事先定義一個用於獲取跨域響應數據的回調函數,並經過沒有同源策略限制的script標籤發起一個請求(將回調函數的名稱放到這個請求的query參數裏),而後服務端返回這個回調函數的執行,並將須要響應的數據放到回調函數的參數裏,前端的script標籤請求到這個執行的回調函數後會立馬執行,因而就拿到了執行的響應數據。
缺點: JSONP只能發起GET請求
這裏給出幾個連接:
segmentfault.com/a/119000001… zhangguixu.github.io/2016/12/02/… www.cnblogs.com/iovec/p/531…
前端構造一個惡意頁面,請求JSONP接口,收集服務端的敏感信息。若是JSONP接口還涉及一些敏感操做或信息(好比登陸、刪除等操做),那就更不安全了。
解決方法:驗證JSONP的調用來源(Referer),服務端判斷Referer是不是白名單,或者部署隨機Token來防護。
不嚴謹的 content-type致使的 XSS 漏洞,想象一下 JSONP 就是你請求 http://youdomain.com?callback=douniwan
, 而後返回 douniwan({ data })
,那假如請求 http://youdomain.com?callback=<script>alert(1)</script>
不就返回 <script>alert(1)</script>({ data })
了嗎,若是沒有嚴格定義好 Content-Type( Content-Type: application/json ),再加上沒有過濾 callback
參數,直接當 html 解析了,就是一個赤裸裸的 XSS 了。
解決方法:嚴格定義 Content-Type: application/json,而後嚴格過濾 callback
後的參數而且限制長度(進行字符轉義,例如<換成<,>換成>)等,這樣返回的腳本內容會變成文本格式,腳本將不會執行。
能夠將執行的代碼轉發到服務端進行校驗JSONP內容校驗,再返回校驗結果。
小提示:若是你回答跨域解決方案CORS,那麼面試官必定會問你實現CORS的響應頭信息Access-Control-Allow-Origin。
CORS(跨域資源共享 Cross-origin resource sharing)容許瀏覽器向跨域服務器發出XMLHttpRequest請求,從而克服跨域問題,它須要瀏覽器和服務器的同時支持。
請求方法是如下三種方法之一:
HTTP的請求頭信息不超出如下幾種字段:
後端的響應頭信息:
非簡單請求是那種對服務器有特殊要求的請求,好比請求方法是PUT或DELETE,或者Content-Type字段的類型是application/json。非簡單請求的CORS請求,會在正式通訊以前,增長一次HTTP查詢請求,稱爲"預檢"請求(preflight)。
若是瀏覽器否認了"預檢"請求,會返回一個正常的HTTP迴應,可是沒有任何CORS相關的頭信息字段。這時,瀏覽器就會認定,服務器不一樣意預檢請求,所以觸發一個錯誤,被XMLHttpRequest對象的onerror回調函數捕獲。
Nginx反向代理
postMessage
document.domain
function Dog(name) {
this.name = name;
}
Dog.prototype={
bark(){
console.log('wangwang');
},
sayName(){
console.log('my name is ' + this.name);
}
};
function _new() {
//=>完成你的代碼
}
let sanmao = _new(Dog, '三毛');
sanmao.bark(); //=>"wangwang"
sanmao.sayName(); //=>"my name is 三毛"
console.log(sanmao instanceof Dog); //=>true
================================================
//——> 重寫 _new
function _new(Fn, ...arg) {
let obj = {};
obj.__proto__ = Fn.prototype;
Fn.call(obj, ...arg);
return obj;
}
複製代碼
/* 實現一個$attr(name,value)遍歷 * 屬性爲name * 值爲value的元素集合 * 例以下面示例: */
let ary = $attr('class','box'); //=>獲取頁面中全部class爲box的元素
=====================================
function $attr(property, value) {
let elements = document.getElementsByTagName('*'),
arr = [];
elements = Array.from(elements);
elements.forEach(item => {
let itemValue = item.getAttribute(property);
if (property==='class') {
new RegExp("\\b" + value +
"\\b").test(itemValue)?arr.push(item):null;
return;
}
if (itemValue === value) {
arr.push(item);
}
});
return arr;
}
console.log($attr('class', 'box'));
複製代碼
~function(){
//=>bind方法在IE6~8中不兼容,接下來咱們本身基於原生JS實現這個方法
function bind(){
};
Function.prototype.bind=bind;
}();
var obj = {name:'zhufeng'};
function func(){
console.log(this,arguments);
//=>當點擊BODY的時候,執行func方法,輸出:obj
[100,200,MouseEvent事件對象]
}
document.body.onclick = func.bind(obj,100,200);
=============================================
~function(){
//=>bind方法在IE6~8中不兼容,接下來咱們本身基於原生JS實現這個方法
function bind(context){
context=context||window;
var _this = this,
outerArg=[].slice.call(arguments,1);
return function anonymous() {
var innerArg=[].slice.call(arguments,0);
_this.apply(context, outerArg.concat(innerArg));
}
};
Function.prototype.bind=bind;
}();
複製代碼
function Modal(x,y){
this.x=x;
this.y=y;
}
Modal.prototype.z=10;
Modal.prototype.getX=function(){
console.log(this.x);
}
Modal.prototype.getY=function(){
console.log(this.y);
}
Modal.n=200;
Modal.setNumber=function(n){
this.n=n;
};
let m = new Model(10,20);
===============================================
class Modal{
constructor(x,y){
this.x=x;
this.y=y;
}
getX(){
console.log(this.x);
}
getY(){
console.log(this.y);
}
static setNumber(n){
this.n=n;
}
}
Modal.n=200;
Modal.prototype.z=10;
複製代碼
~function(){
function changeThis(context){
context=context||window;
//let arg=[].slice.call(arguments,1);
let arg=[],
_this=this,
result=null;
for(let i=1;i<arguments.length;i++){
arg.push(arguments[i]);
}
context.$fn=_this;
result=context.$fn(...arg);
delete context.$fn;
return result;
};
Function.prototype.changeThis=changeThis;
}();
let obj = {name:'Alibaba',$fn:100};
function fn(x,y){
this.total=x+y;
return this;
}
let res = fn.changeThis(obj,100,200);
//res => {name:'Alibaba',total:300}
複製代碼
~function(){
/*生成隨機函數名:時間戳的方式*/
function queryRandomName(){
let time=new Date().getTime();
return '$zhufeng'+time;
}
/*模擬CALL方法改變函數中的THIS*/
function changeThis(context=window,arg=[]){
let _this=this,
result=null,
ran=queryRandomName();
context[ran]=_this;
result=context[ran](...arg);
delete context[ran];
return result;
};
Function.prototype.changeThis=changeThis;
}();
let res = fn.changeThis(obj,[100,200]);
複製代碼
/* 已知寬高 */
.box {
position: absolute;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -50px;
width: 100px;
height: 100px;
}
========================
/* 未知寬高 */
.box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
==========================
.box {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
===============================
.container {
display: flex;
justify-content: center;
align-items: center;
}
.container .box {}
複製代碼
/*
* 1.編寫一個方法「flatten」,將數組扁平化 (至少兩種辦法)
* 2.而後實現「unique」數組去重方法,把數組進行去重 (至少兩種辦法)
*/
let arr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9,[11, 12, [12,
13, [14]]]], 10];
ary.flatten().unique().sort((a,b)=>a-b); //=>[1, 2, 3, 4, 5,
6, 7, 8, 9....]
============================================
~function(){
function flatten(){
/*第一種*/
return JSON.stringify(this).replace(/(\
[|\])/g,'').split(',').map(item=>{
return Number(item);
});
/*第二種*/
return this.flat(Infinity);
/*第三種*/
return this.toString().split(',').map(item=>{
return Number(item);
});
/*第四種*/
let arr=this.slice(0);
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
/*第五種*/
let result = [];
let fn = function (ary) {
for (let i = 0; i < ary.length; i++) {
let item = ary[i];
if (Array.isArray(ary[i])) {
fn(item);
} else {
result.push(item);
}
}
}
fn(this);
return result;
}
function unique(){
/*第一種*/
return Array.from(new Set(this));
/*第二種*/
return [...new Set(this)];
/*第三種*/
let obj={};
for(let i=0;i<this.length;i++){
let item=this[i];
if(typeof obj[item]!=='undefined'){
this[i]=this[this.length-1];
this.length--;
i--;
continue;
}
obj[item]=item;
}
return this;
}
Array.prototype.flatten=flatten;
Array.prototype.unique=unique;
}();
let arr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9,[11, 12, [12,
13, [14]]]], 10];
ary.flatten().unique().sort((a,b)=>a-b); //=>[1, 2, 3, 4, 5,
6, 7, 8, 9....]
複製代碼