1. 閉包
閉包是指有權(quán)訪問另一個函數(shù)作用域中變量的函數(shù)宜雀,創(chuàng)建閉包的最常見的方式就是在函數(shù)內(nèi)創(chuàng)建函數(shù),通過函數(shù)訪問函數(shù)的局部變量,利用閉包可以突破作用鏈域
-
特性:
- 函數(shù)內(nèi)再嵌套函數(shù)
- 內(nèi)部函數(shù)可以引用外層的參數(shù)和變量
- 參數(shù)和變量不會被垃圾回收機(jī)制回收
-
優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn)是可以避免全局變量的污染宪萄,設(shè)計私有的方法和變量來實(shí)現(xiàn)封裝
- 缺點(diǎn)是閉包會常駐內(nèi)存,會增大內(nèi)存使用量,使用不當(dāng)很容易造成內(nèi)存泄露周伦,所以不能濫用閉包郑现,退出函數(shù)之前湃崩,將不使用的局部變量全部刪除荧降。
-
作用
- 讓函數(shù)外部可以操作函數(shù)內(nèi)部的數(shù)據(jù)
- 讓這些變量始終保持在內(nèi)存中,延長局部變量生命周期
- 封裝對象的私有屬性和私有方法
2. 作用域鏈
-
作用域鏈的作用是保證執(zhí)行環(huán)境里有權(quán)訪問的變量和函數(shù)是有序的攒读,作用域鏈的變量只能向上訪問朵诫,變量訪問到
window
對象即被終止,作用域鏈向下訪問變量是不被允許的 - 簡單的說薄扁,作用域就是變量與函數(shù)的可訪問范圍剪返,即作用域控制著變量與函數(shù)的可見性和生命周期
-
全局作用域
編寫在script標(biāo)簽中的js代碼,都在全局作用域邓梅。全局作用域在頁面打開時創(chuàng)建脱盲,在頁面關(guān)閉時銷毀。在全局作用域中有一個全局對象window日缨,代表瀏覽器窗口钱反,由瀏覽器創(chuàng)建,可以直接使用匣距。我們創(chuàng)建的全局變量會作為window對象的屬性保存面哥,創(chuàng)建的方法都會作為window對象的方法保存 -
函數(shù)作用域
調(diào)用函數(shù)時創(chuàng)建,函數(shù)執(zhí)行完畢后銷毀毅待,每調(diào)用一次函數(shù)就會創(chuàng)建一個函數(shù)作用域尚卫,相互獨(dú)立。在函數(shù)作用域中可以訪問到全局作用域的變量尸红,在全局作用域中不能訪問到函數(shù)作用域的變量吱涉。當(dāng)在函數(shù)作用域中操作某個變量時,先在自身作用域中尋找這個變量驶乾,沒有的話再向上一級作用域找邑飒,直到找到全局作用域(也就是順著作用域鏈找)如果在函數(shù)作用域里有和全局作用域里同名的變量,但是要訪問全局作用域里的變量级乐,可通過window對象調(diào)用 -
塊級作用域
一般是通過let
聲明和const
聲明產(chǎn)生的變量疙咸,在所屬的塊的作用域外不能訪問。塊通常是一個被花括號包含的代碼塊风科。優(yōu)點(diǎn)是使外部作用域不能訪問內(nèi)部作用域的變量撒轮,規(guī)避命名沖突
3. 原型鏈
每個對象都會在其內(nèi)部初始化一個屬性,就是
__proto__
贼穆,當(dāng)我們訪問一個對象的屬性時如果這個對象內(nèi)部不存在這個屬性题山,那么他就會去__proto__
里找這個屬性,這個__proto__
又會有自己的__proto__
故痊,于是就這樣一直找下去顶瞳,也就是我們平時所說的原型鏈的概念-
關(guān)系
instance.constructor.prototype = instance.__proto__
-
特點(diǎn)
-
JavaScript
對象通過引用傳遞,創(chuàng)建的每個新對象實(shí)體中并沒有一份屬于自己的原型副本。當(dāng)修改原型時慨菱,與之相關(guān)的對象也會繼承這一改變
-
當(dāng)我們需要一個屬性時焰络,
Javascript
引擎會先看當(dāng)前對象中是否有這個屬性,如果沒有就會查找他的原型對象是否有這個屬性符喝,如此遞推下去闪彼,一直檢索到Object
內(nèi)建對象
4. 繼承
-
父類
// 父類
function Superclass(name) {
this.name = name;
this.say = function() {
alert(this.name);
}
}
Superclass.prototype.age = 10;
-
原型鏈繼承
讓新實(shí)例的原型等于父類的實(shí)例,可以方便的繼承父類型的原型中的方法
缺點(diǎn):無法向父類構(gòu)造函數(shù)傳參协饲;所有新實(shí)例都會共享父類實(shí)例的屬性畏腕,即原型上的屬性共享,一個實(shí)例修改了原型屬性茉稠,另一個實(shí)例的原型屬性也會被修改描馅,屬性的繼承無意義
// 原型鏈繼承
function Subclass() {
this.name = "sub name";
}
Subclass.prototype = new Superclass();
const sub = new Subclass();
-
借用構(gòu)造函數(shù)繼承
用.call()
和.apply()
將父類構(gòu)造函數(shù)引入子類函數(shù),等于復(fù)制父類的實(shí)例屬性給子類战惊;創(chuàng)建子類實(shí)例時流昏,可以向父類傳遞參數(shù)扎即,可以實(shí)現(xiàn)多繼承吞获,可以方便的繼承父類型的屬性,但是無法繼承原型中的方法
缺點(diǎn):只能繼承父類構(gòu)造函數(shù)的屬性谚鄙,方法在構(gòu)造函數(shù)中定義各拷,函數(shù)復(fù)用無從談起;超類型原型中定義的方法對子類型不可見闷营。實(shí)例并不是父類的實(shí)例烤黍,只是子類的實(shí)例
// 借用構(gòu)造函數(shù)繼承
function Subclass(name) {
Superclass.call(this, name);
this.age = 12;
}
const sub = new Subclass("sub name");
-
組合繼承
結(jié)合原型鏈繼承和借用構(gòu)造函數(shù)繼承兩種模式的優(yōu)點(diǎn):傳參與復(fù)用,既通過在原型上定義方法實(shí)現(xiàn)函數(shù)復(fù)用傻盟,又能保證每個實(shí)例都有自己的屬性速蕊;既是子類的實(shí)例,也是父類的實(shí)例
缺點(diǎn):調(diào)用兩次父類構(gòu)造函數(shù)娘赴,在繼承父類函數(shù)的時候調(diào)用了父類構(gòu)造函數(shù)规哲,導(dǎo)致子類的原型上多了不需要的父類屬性,存在內(nèi)存上的浪費(fèi)
// 組合原型鏈繼承和借用構(gòu)造函數(shù)繼承
function Subclass(name) {
Superclass.call(this, name); // 借用構(gòu)造函數(shù)繼承
}
Subclass,prototype = new Superclass(); // 原型鏈繼承
const sub = new Subclass("sub name");
-
ES6 Class通過
extends
關(guān)鍵字實(shí)現(xiàn)繼承
class
實(shí)現(xiàn)繼承的核心在于使用 extends
表明繼承自哪個父類诽表,并在子類構(gòu)造函數(shù)中必須調(diào)用 super
class Parent {
constructor(value) {
this.val = value;
}
getValue() {
console.log(this.val);
}
}
class Child extends Parent {
constructor(value) {
super(value);
this.val = value;
}
}
const child = new Child(1);
child.getValue() // 1
child instanceof Parent // true
-
原型式繼承
在函數(shù)內(nèi)部創(chuàng)建一個臨時性構(gòu)造函數(shù)唉锌,將傳入的對象作為構(gòu)造函數(shù)的原型,最終返回臨時構(gòu)造函數(shù)的一個實(shí)例
// 封裝一個容器竿奏,用于輸出對象和承載繼承的原型
function content(obj) {
function F() {}
F.prototype = obj; // 繼承傳入的原型
return new F(); // 返回對象
}
const sup = new Superclass(); // 獲取父類實(shí)例
const sub = content(sup);
-
寄生式繼承
function content(obj) {
function F() {}
F.prototype = obj; // 繼承傳入的原型
return new F(); // 返回對象
}
const sup = new Superclass(); // 獲取父類實(shí)例
// 以上為原型式繼承袄简, 給原型式繼承再套一個殼子來傳遞參數(shù)
function subobject(obj) {
const sub = content(obj);
sub.name = "sub name";
return sub;
}
var sub = subobject(sup);
-
寄生組合式繼承
// 寄生組合式繼承,解決了組合式繼承調(diào)用兩次構(gòu)造函數(shù)的缺點(diǎn)
function content(obj) {
function F() {}
F.prototype = obj; // 繼承傳入的原型
return new F(); // 返回對象
}
// con實(shí)例的原型繼承了父類的原型
var con = content(Superclass.prototype);
function Subclass(name) {
Superclass.call(this, name); // 借用構(gòu)造函數(shù)繼承泛啸,繼承父類構(gòu)造函數(shù)的屬性
}
Subclass.prototype = con;
con.constructor = Subclass; // 修復(fù)實(shí)例
const sub = new Subclass();
5. ES5和ES6繼承的區(qū)別
- ES5的繼承實(shí)質(zhì)上是先創(chuàng)建子類的實(shí)例對象绿语,然后再將父類的方法添加到this上
Parent.apply(this)
- ES6的繼承機(jī)制實(shí)質(zhì)上是先創(chuàng)建父類的實(shí)例對象
this
(所以必須先調(diào)用父類的super()
方法),然后再用子類的構(gòu)造函數(shù)修改this
。 - ES5的繼承時通過原型或構(gòu)造函數(shù)機(jī)制來實(shí)現(xiàn)吕粹。
- ES6通過
class
關(guān)鍵字定義類伍纫,有構(gòu)造方法,類之間通過extends
關(guān)鍵字實(shí)現(xiàn)繼承昂芜。子類必須在constructor
方法中調(diào)用super
方法莹规,否則新建實(shí)例報錯。因?yàn)樽宇悰]有自己的this
對象泌神,而是繼承了父類的this
對象良漱,然后對其進(jìn)行加工。如果不調(diào)用super
方法欢际,子類得不到this
對象母市。 - super() 與 Parent.call(this) 是不同的,在繼承原生構(gòu)造函數(shù)的情況下损趋,體現(xiàn)得很明顯患久,ES6 中的子類實(shí)例可以繼承原生構(gòu)造函數(shù)實(shí)例的內(nèi)部屬性,而在 ES5 中做不到浑槽。ES5 中 A.call(this) 中的 this 是構(gòu)造函數(shù) B 的實(shí)例蒋失,也就是在實(shí)現(xiàn)實(shí)例屬性繼承上,ES5 是先創(chuàng)造構(gòu)造函數(shù) B 的實(shí)例桐玻,然后在讓這個實(shí)例通過 A.call(this) 實(shí)現(xiàn)實(shí)例屬性繼承篙挽,在 ES6 中,是先新建父類的實(shí)例對象this镊靴,然后再用子類的構(gòu)造函數(shù)修飾 this铣卡,使得父類的所有行為都可以繼承。
作者:T_superlu
鏈接:https://juejin.im/post/6844903924015120397
來源:掘金
著作權(quán)歸作者所有偏竟。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)煮落,非商業(yè)轉(zhuǎn)載請注明出處。
6. this對象
-
this
總是指向函數(shù)的執(zhí)行上下文踊谋,根據(jù)調(diào)用方式不同蝉仇,this的指向不同。 - 以匿名函數(shù)或直接調(diào)用的函數(shù)褪子;
this
指向全局上下文(瀏覽器window NodeJS global)量淌; - 以方法調(diào)用則
this
指向調(diào)用方法的那個對象; - 以構(gòu)造函數(shù)調(diào)用時嫌褪,
this
指向新創(chuàng)建的對象呀枢; - 使用
call
和apply
調(diào)用時this
為指定的那個對象; - 在事件響應(yīng)函數(shù)中笼痛,指向觸發(fā)這個事件的對象裙秋,特殊的是IE中的
attachEvent
中的this
總是指向全局對象window
- 箭頭函數(shù)中this的指向取決于箭頭函數(shù)聲明的位置琅拌,捕獲其所在的上下文的this值,作為自己的this值
7. call摘刑、apply进宝、bind
-
call
函數(shù)實(shí)例的方法,指定該函數(shù)內(nèi)部this
的指向枷恕,在所指定的作用域中立即執(zhí)行該函數(shù)党晋。傳遞兩個參數(shù)。第一個參數(shù)是指定函數(shù)內(nèi)部中this的指向徐块,第二個參數(shù)是函數(shù)調(diào)用時需要傳遞的參數(shù)未玻。實(shí)現(xiàn)如下:
- 首先
context
為可選參數(shù),如果不傳的話默認(rèn)上下文為window
- 給
context
創(chuàng)建一個fn
屬性胡控,并將值設(shè)置為需要調(diào)用的函數(shù) - 因?yàn)?code>call可以傳入多個參數(shù)作為調(diào)用函數(shù)的參數(shù)扳剿,所以需要將參數(shù)剝離出來
- 然后調(diào)用函數(shù)并將對象上的函數(shù)刪除
Function.prototype.myCall = function (context) {
const context = context || window;
conetxt.fn = this;
// 取出context后面的參數(shù)
const args = [...arguments].slice(1);
const result = context.fn(...args);
// 刪除 fn
delete context.fn;
return result;
}
-
apply
apply方法與call方法類似,也是改變this指向昼激,然后在指定的作用域中立即調(diào)用該函數(shù)庇绽。唯一的區(qū)別就是,它接收一個數(shù)組作為函數(shù)執(zhí)行時的參數(shù)橙困。實(shí)現(xiàn)如下:
Function.prototype.myApply = function (context) {
const context = context || window;
conetxt.fn = this;
// 判斷是否存在第二個參數(shù)瞧掺,如果存在則將第二個參數(shù)展開
const result = arguments[1] ? context.fn(...arguments[1]) : context.fn();
// 刪除 fn
delete context.fn;
return result;
}
-
bind
bind方法用于指定函數(shù)內(nèi)部的this指向(執(zhí)行時所在的作用域),然后返回一個新函數(shù)纷宇。bind方法并非立即執(zhí)行一個函數(shù)夸盟,需要再調(diào)用一次才行。實(shí)現(xiàn)如下:
- bind 返回了一個函數(shù)像捶,對于函數(shù)來說有兩種方式調(diào)用,一種是直接調(diào)用桩砰,一種是通過 new 的方式
- 對于直接調(diào)用拓春,選擇了
apply
的方式實(shí)現(xiàn),但是對于參數(shù)需要注意:bind
可以實(shí)現(xiàn)類似f.bind(obj, 1)(2)
的代碼亚隅,所以需要將兩邊的參數(shù)拼接起來硼莽,于是就有了args.concat(...arguments)
- 對于
new
的情況來說,不會被任何方式改變this
煮纵,所以對于這種情況需要忽略傳入的this
Function.prototype.myBind = function (context) {
const _this = this;
const args = [...arguments].slice(1);
// 返回一個函數(shù)F
return function F() {
// 判斷是否為 new F()
if (this instanceof F) {
return new _this(...args, ...arguments);
}
return _this.apply(context, args.concat(...arguments));
}
}
-
聯(lián)系與區(qū)別
- 第一個參數(shù)都是指定函數(shù)內(nèi)部中this的指向(函數(shù)執(zhí)行時所在的作用域)懂鸵,然后根據(jù)指定的作用域,調(diào)用該函數(shù)
- 都可以在函數(shù)調(diào)用時傳遞參數(shù)行疏。call匆光,bind方法需要直接傳入,而apply方法需要以數(shù)組的形式傳入
- call酿联,apply方法是在調(diào)用之后立即執(zhí)行函數(shù)终息,而bind方法沒有立即執(zhí)行夺巩,需要將函數(shù)再執(zhí)行一遍
8. 事件模型(事件流)及各個階段
事件流三階段:捕獲階段——>目標(biāo)階段——>冒泡階段
document
——>target
目標(biāo) ——>document
document.documentElement
獲取html
標(biāo)簽;document.body
獲取body
標(biāo)簽-
事件流分為捕獲事件流和冒泡事件流
- 捕獲事件流從根節(jié)點(diǎn)開始執(zhí)行周崭,向子節(jié)點(diǎn)查找執(zhí)行柳譬,直到執(zhí)行到目標(biāo)節(jié)點(diǎn)
- 冒泡事件流從目標(biāo)節(jié)點(diǎn)開始執(zhí)行,向父節(jié)點(diǎn)冒泡查找執(zhí)行续镇,直到根節(jié)點(diǎn)美澳。
- 事件觸發(fā)一般來說會按照上面的順序進(jìn)行,但是也有特例摸航,如果給一個目標(biāo)節(jié)點(diǎn)同時注冊冒泡和捕獲事件人柿,事件觸發(fā)會按照注冊的順序執(zhí)行
-
addEventListener
的第三個參數(shù)設(shè)置為-
true
表示該元素在事件的“捕獲階段”(由外往內(nèi)傳遞時)響應(yīng)事件 -
false
表示該元素在事件的“冒泡階段”(由內(nèi)向外傳遞時)響應(yīng)事件
-
-
阻止冒泡
-
W3C
中,使用stopPropagation()
方法 - IE下設(shè)置
cancelBubble = true
-
-
阻止事件的默認(rèn)行為
-
W3C
中忙厌,使用preventDefault()
方法 - IE下設(shè)置
window.event.returnValue = false
-
9. 事件代理(事件委托)
- 事件代理
Event Delegation
凫岖,又稱事件委托。是常用綁定事件的常用技巧逢净,即是把原本需要綁定的事件委托給父元素哥放,讓父元素?fù)?dān)當(dāng)事件監(jiān)聽的職務(wù)。 -
原理:DOM元素的事件冒泡
event對象里記錄有“事件源”爹土,它就是發(fā)生事件的子元素甥雕。存在兼容性問題,在老的IE下胀茵,事件源是 window.event.srcElement
社露,其他瀏覽器是 event.target
。綁定事件的元素為event.currentTarget
- 好處是可以大量節(jié)省內(nèi)存占用琼娘,減少事件注冊峭弟,提高性能,實(shí)現(xiàn)當(dāng)新增子對象時無需再次對其綁定
10. 事件循環(huán)
首先脱拼,
JS
是單線程的瞒瘸,主要的任務(wù)是處理用戶的交互,使用事件隊列的形式熄浓,JS
在執(zhí)行的過程中會產(chǎn)生執(zhí)行環(huán)境情臭,這些執(zhí)行環(huán)境會被順序的加入到執(zhí)行棧中。如果遇到異步的代碼赌蔑,會被掛起并加入到task
隊列中俯在。一旦執(zhí)行棧為空,Event Loop
就會從task
隊列中拿出需要執(zhí)行的代碼并放入執(zhí)行棧中執(zhí)行娃惯,所以本質(zhì)上來說JS
中的異步還是同步行為-
JS中分為兩種任務(wù)類型
-
macrotask
宏任務(wù)跷乐,可以理解是每次執(zhí)行棧執(zhí)行的代碼就是一個宏任務(wù)(包括每次從事件隊列中獲取一個事件回調(diào)并放到執(zhí)行棧中執(zhí)行)
每一個task會從頭到尾將這個任務(wù)執(zhí)行完畢,不會執(zhí)行其它
瀏覽器為了能夠使得JS內(nèi)部task與DOM任務(wù)能夠有序的執(zhí)行石景,會在一個task執(zhí)行結(jié)束后劈猿,在下一個 task 執(zhí)行開始前拙吉,對頁面進(jìn)行重新渲染 (task->渲染->task->...) -
microtask
微任務(wù),可以理解是在當(dāng)前 task 執(zhí)行結(jié)束后立即執(zhí)行的任務(wù)揪荣,在某一個macrotask執(zhí)行完后筷黔,就會將在它執(zhí)行期間產(chǎn)生的所有microtask都執(zhí)行完畢(在渲染前)
-
-
運(yùn)行機(jī)制
- 執(zhí)行宏任務(wù)
- 執(zhí)行過程中將遇到的微任務(wù)添加到微任務(wù)隊列中
- 宏任務(wù)執(zhí)行完畢后,立即執(zhí)行當(dāng)前微任務(wù)隊列中的所有微任務(wù)
- 當(dāng)前宏任務(wù)執(zhí)行完畢仗颈,渲染
- 渲染完畢后佛舱,開始下一個宏任務(wù)(從事件隊列中獲取)
11. addEventListener()和attachEvent()的區(qū)別
-
addEventListener()
是符合W3C規(guī)范的標(biāo)準(zhǔn)方法;attachEvent()
是IE低版本的非標(biāo)準(zhǔn)方法 -
addEventListener()
支持事件冒泡和事件捕獲;而attachEvent()
只支持事件冒泡 -
addEventListener()
的第三個參數(shù)可以是布爾值挨决,也可以是對象请祖。對于布爾值useCapture
參數(shù)來說,該參數(shù)默認(rèn)值為 false脖祈,決定了注冊的事件是捕獲事件還是冒泡事件肆捕。對于對象參數(shù)來說,可以使用以下幾個屬性:-
capture
布爾值盖高,和useCapture
作用一樣 -
once
布爾值慎陵,值為true
表示該回調(diào)只會調(diào)用一次,調(diào)用后會移除監(jiān)聽 -
passive
布爾值喻奥,表示永遠(yuǎn)不會調(diào)用preventDefault
-
-
addEventListener()
的第一個參數(shù)中,事件類型不需要添加on
;attachEvent()
需要添加'on'
- 如果為同一個元素綁定多個事件,
addEventListener()
會按照事件綁定的順序依次執(zhí)行,attachEvent()
會按照事件綁定的順序倒序執(zhí)行
12. 自定義事件
-
事件的創(chuàng)建
var myEvent = new CustomEvent('event_name', {
detail: {
// 將需要傳遞的數(shù)據(jù)寫在detail中席纽,以便在EventListener中通過event.detail中得到
}
});
-
事件的監(jiān)聽
window.addEventListener('event_name', function(event) {
console.log(event.detail);
});
-
事件的觸發(fā)
自定義的事件由于不是JS內(nèi)置的事件,所以我們需要在JS代碼中去顯式地觸發(fā)它撞蚕。方法是使用window.dispatchEvent
觸發(fā)润梯;IE8低版本兼容,使用window.fireEvent
if(window.dispatchEvent) {
window.dispatchEvent(myEvent);
} else {
window.fireEvent(myEvent);
}
13. new操作符
- 創(chuàng)建一個空對象甥厦,鏈接原型纺铭,綁定this指向
- 屬性和方法被加入到
this
引用的對象中,即執(zhí)行構(gòu)造函數(shù) - 返回新創(chuàng)建的對象
14. 定義對象的方法
- 對象字面量:
var obj = {};
- 構(gòu)造函數(shù):
var obj = new Object();
- Object.create():
var obj = Object.create(Object.prototype);
- 工廠模式:無法復(fù)用
- 原型模式:原型上的屬性共享
- 組合模式(原型+構(gòu)造函數(shù))
15. 判斷對象類型
- 通過
Object.prototype.toString.call(xx)
可以獲得類似 [object Type] 的字符串矫渔。(不能精準(zhǔn)判斷自定義對象彤蔽,對于自定義對象只會返回[object Object]) -
instanceof
可以正確的判斷對象的類型,但對原始類型不可以 -
constructor
除了undefined
和null
庙洼,其他類型的變量均能使用constructor
判斷出類型,但constructor屬性是可以被修改的镊辕,會導(dǎo)致檢測出的結(jié)果不正確
16. typeof與instanceof
-
typeof
對于原始類型來說油够,除了null
都可以顯示正確的類型 -
typeof
對于對象來說,除了函數(shù)都會顯示object
征懈,所以說typeof
并不能準(zhǔn)確判斷變量到底是什么類型 - 如果我們想判斷一個對象的正確類型石咬,這時候可以考慮使用 `instanceof``,但不能判斷原始類型
-
instanceof
的原理及手動實(shí)現(xiàn)
// 表達(dá)式:A instanceof B
// 如果B的顯式原型對象在A對象的原型鏈上卖哎,返回true
function myInstanceof(left, right) {
let prototype = right.prototype;
left = left._proto_;
while(true) {
if(left === null || left === undefined)
return false;
if(prototype === left)
return true;
left = left._proto_;
}
}
17. 類型轉(zhuǎn)換
https://www.cnblogs.com/Juphy/p/7085197.html
-
顯式轉(zhuǎn)換
-
Number(object)
boolean
:true/false ——>1/0
nubmer
:返回本身
null
:0
undefined
:NaN
string
:純數(shù)字轉(zhuǎn)換為十進(jìn)制鬼悠,忽略前導(dǎo)0删性;有效浮點(diǎn)格式轉(zhuǎn)換為浮點(diǎn)數(shù)值,忽略前導(dǎo)0焕窝;空字符串轉(zhuǎn)換為0蹬挺;其他轉(zhuǎn)換為NaN
object
:調(diào)用對象valueOf()方法,依據(jù)上面的轉(zhuǎn)換規(guī)則轉(zhuǎn)換返回的值它掂,若轉(zhuǎn)換結(jié)果為NaN巴帮,則調(diào)用對象toString()方法,再依據(jù)上面的轉(zhuǎn)換規(guī)則轉(zhuǎn)換字符串值 parseInt(string,radix)
將字符串轉(zhuǎn)換為整數(shù)類型的數(shù)值(非字符串先轉(zhuǎn)換為字符串)
(1)忽略字符串前面的空格虐秋,直至找到第一個非空字符
(2)如果第一個字符不是數(shù)字符號或者負(fù)號榕茧,返回NaN
(3)如果第一個字符是數(shù)字,則繼續(xù)解析直至字符串解析完畢或者遇到一個非數(shù)字符號為止
(4) 如果上步解析的結(jié)果以0開頭客给,則將其當(dāng)作八進(jìn)制來解析;如果以0x開頭用押,則將其當(dāng)作十六進(jìn)制來解析
(5)如果指定radix參數(shù),則以radix為基數(shù)進(jìn)行解析parseFloat(string)
規(guī)則與parseInt基本相同靶剑,區(qū)別在于字符串中第一個小數(shù)點(diǎn)符號是有效的摇零,會忽略所有前導(dǎo)0薇组,若包含一個可解析為整數(shù)的數(shù),則返回整數(shù)而不是浮點(diǎn)數(shù)-
toString()
除undefined和null之外的所有類型的值都具有toString()方法,其作用是返回對象的字符串表示 String(mix)
有toString()方法則調(diào)用
null返回"null"; undefined返回"undefined"Boolean(mix)
除false递惋、空字符串、NaN溺忧、0突梦、null以及undefined外都為true
-
-
隱式轉(zhuǎn)換
-
遞增遞減操作符、一元正負(fù)符號操作符
與Number規(guī)則基本相同 -
加法運(yùn)算操作符
有一個操作值為字符串笛辟,則將另一個操作值轉(zhuǎn)換為字符串功氨,最后連接起來 -
邏輯操作符( !、&&手幢、|| )
邏輯非( ! )操作符首先通過Boolean()函數(shù)將操作值轉(zhuǎn)換為布爾值捷凄,然后求反
邏輯與( && )操作符,如果一個操作值不是布爾值時围来,遵循以下規(guī)則:
(1) 如果第一個操作數(shù)經(jīng)Boolean()轉(zhuǎn)換后為true跺涤,則返回第二個操作值,否則返回第一個操作值
(2) 如果有一個操作值為null
/NaN
/undefined
监透,返回null
/NaN
/undefined
邏輯或( || )操作符桶错,如果一個操作值不是布爾值,遵循以下規(guī)則:
(1) 如果第一個操作值經(jīng)Boolean()轉(zhuǎn)換后為false胀蛮,則返回第二個操作值院刁,否則返回第一個操作值
(2) 對于undefined
、null
和NaN
的處理規(guī)則與邏輯與( && )相同 -
關(guān)系操作符( <, >, <=, >= )
(1) 如果兩個操作值都是數(shù)值粪狼,則進(jìn)行數(shù)值比較
(2) 如果兩個操作值都是字符串退腥,則比較字符串對應(yīng)的字符編碼值
(3) 如果只有一個操作值是數(shù)值任岸,則將另一個操作值轉(zhuǎn)換為數(shù)值,進(jìn)行數(shù)值比較
(4) 如果一個操作數(shù)是對象狡刘,則調(diào)用valueOf()方法(如果對象沒有valueOf()方法則調(diào)用toString()方法)享潜,得到的結(jié)果按照前面的規(guī)則執(zhí)行比較
(5)如果一個操作值是布爾值,則將其轉(zhuǎn)換為數(shù)值颓帝,再進(jìn)行比較
注:NaN
是非常特殊的值米碰,它不和任何類型的值相等,包括它自己购城,同時它與任何類型的值比較大小時都返回false吕座。 -
相等操作符( == )
(1) 如果一個操作值為布爾值,則先將其轉(zhuǎn)換為數(shù)值
(2) 如果一個操作值為字符串瘪板,另一個操作值為數(shù)值吴趴,則通過Number()函數(shù)將字符串轉(zhuǎn)換為數(shù)值
(3) 如果一個操作值是對象,另一個不是侮攀,則調(diào)用對象的valueOf()方法锣枝,得到的結(jié)果按照前面的規(guī)則進(jìn)行比較
(4) null與undefined是相等的
(5) 如果一個操作值為NaN,則相等比較返回false
(6) 如果兩個操作值都是對象兰英,則比較它們是不是指向同一個對象
-
遞增遞減操作符、一元正負(fù)符號操作符
18. Ajax原理
-
Ajax
的原理簡單來說是在用戶和服務(wù)器之間加了—個中間層(AJAX
引擎)撇叁,通過XMLHttpRequest
對象來向服務(wù)器發(fā)異步請求,從服務(wù)器獲得數(shù)據(jù)畦贸,然后用javascript
來操作DOM
更新頁面陨闹。使用戶操作與服務(wù)器響應(yīng)異步化。這其中最關(guān)鍵的一步就是從服務(wù)器獲得請求數(shù)據(jù) -
Ajax
的過程只涉及JavaScript
薄坏、XMLHttpRequest
和DOM
趋厉。XMLHttpRequest
是ajax
的核心機(jī)制
var xhr = null;
// 創(chuàng)建XMLHttpRequest對象
if(window.XMLHttpRequest) {
xhr = new XMLHttpRequest(); // IE7+/Firefox/Chrome/Opera/Safari
} else {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
// open(method, url, async)
// - method 請求類型; GET或POST
// - url 請求地址
// - async 是否異步
xhr.open('get', url, true);
// send(string) 將請求發(fā)送至服務(wù)器
// - string 僅用于POST請求
xhr.send(null);
// 當(dāng)readyState發(fā)生改變時會觸發(fā)onreadystatechange 事件
xhr.onreadystatechange = function() {
// readyState存有XMLHTTPRequest的狀態(tài)
// 0: 請求未初始化
// 1: 服務(wù)器連接已建立
// 2: 請求已接收
// 3: 請求處理中
// 4: 請求已完成胶坠,且響應(yīng)已就緒
if(xhr.readyState == 4) {
// 200 "OK"
if(xhr.status == 200) {
// responseText 獲得字符串形式的響應(yīng)數(shù)據(jù)
// responseXML 獲得XML形式的響應(yīng)數(shù)據(jù)
console.log(xhr.responseText);
} else {
console.log(xhr.status)
}
}
}
-
優(yōu)點(diǎn):
- 通過異步模式君账,提升了用戶體驗(yàn)
- 優(yōu)化瀏覽器和服務(wù)器之間的傳輸,減少不必要的數(shù)據(jù)往返沈善,減少帶寬占用
-
Ajax
在客戶端運(yùn)行乡数,承擔(dān)了一部分本來由服務(wù)器承擔(dān)的工作,減少了大用戶量下的服務(wù)器負(fù)載 - 實(shí)現(xiàn)動態(tài)局部刷新
-
缺點(diǎn):
- 安全問題闻牡,
AJAX
暴露了與服務(wù)器交互的細(xì)節(jié) - 對搜索引擎的支持比較弱
- 不容易調(diào)試
- 在動態(tài)更新頁面的情況下瞳脓,用戶無法回到前一個頁面狀態(tài)
- 安全問題闻牡,
19. 同源策略與跨域問題
同源策略指的是:協(xié)議,域名澈侠,端口相同,同源策略是一種安全協(xié)議埋酬,如果缺少了同源策略哨啃,瀏覽器很容易受到XSS烧栋、CSFR等攻擊
-
對于瀏覽器而言只要域名、協(xié)議拳球、端口其中一個不同就會引發(fā)同源策略审姓,從而限制他們之間如下的交互行為:
- Cookie、LocalStorage和IndexDB無法讀茸>魔吐;
- DOM無法獲得;
- AJAX請求不能發(fā)送莱找。
-
解決跨域問題
-
跨域資源共享(CORS Cross-Origin Resource Sharing)
定義了在訪問跨域資源時酬姆,瀏覽器與服務(wù)器應(yīng)該如何溝通。CORS背后的基本思想就是使用自定義的HTTP頭部讓瀏覽器與服務(wù)器進(jìn)行溝通奥溺,從而決定請求或響應(yīng)是應(yīng)該成功還是失敗辞色。服務(wù)器端對于CORS的支持,主要就是通過設(shè)置Access-Control-Allow-Origin來進(jìn)行的浮定。如果瀏覽器檢測到相應(yīng)的設(shè)置相满,就可以允許Ajax進(jìn)行跨域的訪問 -
JSONP 即JSON with Padding(填充式j(luò)son)
在js中,直接用XMLHttpRequest請求不同域上的數(shù)據(jù)時桦卒,是不可以的立美。但在頁面上引入不同域上的js腳本文件卻是可以的,jsonp正是利用這個特性來實(shí)現(xiàn)方灾。 例如:a.html頁面需要利用ajax獲取一個不同域上的json數(shù)據(jù)建蹄,假設(shè)這個json數(shù)據(jù)地址是http://example.com/data.php,那么a.html中的代碼就可以這樣:
-
跨域資源共享(CORS Cross-Origin Resource Sharing)
<script type="text/javascript">
function dosomething(jsondata) {
//處理獲得的json數(shù)據(jù)
}
</ script>
<script src="http://example.com/data.php?callback=dosomething"></script>
js文件載入成功后會執(zhí)行我們在url參數(shù)中指定的函數(shù)迎吵,并且會把我們需要的json數(shù)據(jù)作為參數(shù)傳入躲撰。所以jsonp是需要服務(wù)器端的頁面進(jìn)行相應(yīng)的配合的。(即用JavaScript動態(tài)加載一個script文件击费,同時定義一個callback函數(shù)給script執(zhí)行)
優(yōu)點(diǎn):不像XMLHttpRequest對象實(shí)現(xiàn)的Ajax請求那樣受到同源策略的限制拢蛋;它的兼容性更好,在更加古老的瀏覽器中都可以運(yùn)行蔫巩,不需要XMLHttpRequest或ActiveX的支持谆棱;并且在請求完畢后可以通過調(diào)用callback的方式回傳結(jié)果。
缺點(diǎn):它只支持GET請求而不支持POST等其它類型的HTTP請求圆仔;它只支持跨域HTTP請求這種情況垃瞧,不能解決不同域的兩個頁面之間如何進(jìn)行JavaScript調(diào)用的問題。
CORS與JSONP相比坪郭,無疑更為先進(jìn)个从、方便和可靠。
(1)JSONP只能實(shí)現(xiàn)GET請求,CORS支持所有類型的HTTP請求嗦锐;
(2)使用CORS嫌松,開發(fā)者可以使用普通的XMLHttpRequest發(fā)起請求和獲得數(shù)據(jù),比起JSONP有更好的錯誤處理奕污;
(3)JSONP主要被老的瀏覽器支持萎羔,它們往往不支持CORS,而絕大多數(shù)現(xiàn)代瀏覽器都已經(jīng)支持了CORS碳默;
-
修改document.domain來跨子域
如果是需要處理 Cookie 和 iframe 該怎么辦呢贾陷?這時候就可以通過修改document.domain來跨子域。兩個網(wǎng)頁一級域名相同嘱根,只是二級域名不同髓废,瀏覽器允許通過設(shè)置document.domain共享 Cookie或者處理iframe。比如A網(wǎng)頁是http://w1.example.com/a.html
儿子,B網(wǎng)頁是http://w2.example.com/b.html
瓦哎,那么只要設(shè)置相同的document.domain,兩個網(wǎng)頁就可以共享Cookie柔逼。LocalStorage 和 IndexDB 無法通過這種方法規(guī)避同源策略蒋譬。服務(wù)器也可以在設(shè)置Cookie的時候,指定Cookie的所屬域名為一級域名愉适,比如.example.com
-
HTML5的window.postMessage
HTML5為了解決跨域通信問題犯助,引入了一個全新的API:跨文檔通信 API(Cross-document messaging)。
這個API為window對象新增了一個window.postMessage方法维咸,允許跨窗口通信剂买,不論這兩個窗口是否同源。
a頁面向b頁面發(fā)消息調(diào)用postMessage方法, b頁面通過監(jiān)聽message事件可以接受到來自a頁面的消息
// a頁面
<iframe id="frame1" src="http://127.0.0.1/JSONP/b.html" frameborder="1"></iframe>
document.getElementById('frame1').onload = function() {
var win = document.getElementById( 'frame1 ').contentwindow;
win.postMessage("我是來自a頁面的", "http://127.0.0.1/JSONP/b.html")
}
// b頁面
window.onmessage = function(e) {
e = e ll event;
console.log(e.data); //我是來自a頁面的
}
-
WebSocket進(jìn)行跨域
web sockets是一種瀏覽器的API癌蓖,它的目標(biāo)是在一個單獨(dú)的持久連接上提供全雙工瞬哼、雙向通信。
原理:在js創(chuàng)建了web socket之后租副,會有一個HTTP請求發(fā)送到瀏覽器以發(fā)起連接坐慰。取得服務(wù)器響應(yīng)后,建立的連接會使用HTTP升級從HTTP協(xié)議交換為web sockt協(xié)議用僧。只有在支持web socket協(xié)議的服務(wù)器上才能正常工作结胀。
var socket = new websockt( 'ws: //www.baidu.com');
socket.send( "hello websockt" );
socket.onmessage = function(event) {
var data = event.data;
}
20. 異步加載JS(延遲加載)
JavaScript默認(rèn)是同步加載(又稱阻塞模式),這種模式在加載js文件時责循,會影響后續(xù)頁面的渲染糟港,一旦網(wǎng)速不好,整個頁面將會等待js文件的加載院仿,從而不進(jìn)行后續(xù)頁面的渲染秸抚。
另外速和,有些js文件是按需加載的,用的時候加載耸别,不用時不必加載健芭。所以引入了異步加載模式(非阻塞模式),即瀏覽器在下載執(zhí)行js文件時秀姐,會同時進(jìn)行后續(xù)頁面的處理。
-
動態(tài)添加script標(biāo)簽
這樣js腳本都會在onload事件后才加載若贮,onload事件會在所有文件內(nèi)容(包括文本省有、圖片、CSS文件等)加載完成后才開始執(zhí)行谴麦,極大的優(yōu)化了網(wǎng)頁的加載速度蠢沿,提高了用戶體驗(yàn)
function loadScript(url, callback){
var s = document.createElement('script');
s.type = 'text/javascript';
if(s.readyState){
s.onreadystatechange = function(){ //兼容IE
if(s.readyState == 'complete' || s.readyState == 'loaded'){
callback();
}
}
}else{
s.onload = function(){ //safari chrome opera firefox
callback();
}
}
s.src = url;
document.head.appendChild(s);
}
-
defer/async
defer
不會阻塞dom
樹構(gòu)建,立即異步加載匾效。加載好后舷蟀,如果 dom 樹還沒構(gòu)建好,則先等 dom 樹解析好再執(zhí)行面哼;如果 dom 樹已經(jīng)準(zhǔn)備好野宜,則立即執(zhí)行async
不會阻塞dom
樹構(gòu)建,立即異步加載魔策,加載好后立即執(zhí)行-
defer
和async
在網(wǎng)絡(luò)讀刃僮印(下載)方面都是異步的(相較于 HTML 解析),區(qū)別在于腳本下載完之后何時執(zhí)行,并且defer
按照加載順序執(zhí)行腳本闯袒,而async
腳本的加載和執(zhí)行是緊緊挨著的,所以不管聲明的順序如何,只要加載完了就會立刻執(zhí)行
21. 造成內(nèi)存泄漏的操作
- 意外的全局變量引起的內(nèi)存泄漏:js中如果不用
var
聲明變量,該變量將被視為 window 對象(全局對象)的屬性,也就是全局變量. - 閉包引起的內(nèi)存泄漏
- 沒有清理的dom元素引用
- 被遺忘的定時器或者回調(diào)
22. JSON
-
JSON(JavaScript Object Notation)
是一種輕量級的數(shù)據(jù)交換格式,基于JavaScript的一個子集虎敦。數(shù)據(jù)格式簡單, 易于讀寫, 占用帶寬小 -
JSON字符串轉(zhuǎn)換為JSON對象
var obj = eval('(' + str + ')');
var obj = str.parseJSON;
var obj = JSON.parse(str);
-
JSON對象轉(zhuǎn)換為JSON字符串
var str = obj.toJSONString();
var str = JSON.stringify(obj);
23. XML和JSON的區(qū)別
-
數(shù)據(jù)體積:
JSON
相對于XML
來講,數(shù)據(jù)的體積小政敢。 -
數(shù)據(jù)交互:
JSON
與JavaScript
的交互更加方便其徙,更容易解析處理 -
數(shù)據(jù)描述:
JSON
對數(shù)據(jù)的描述性比XML
較差 -
傳輸速度:
JSON
的速度要遠(yuǎn)遠(yuǎn)快于XML
24. JS設(shè)計模式
https://www.cnblogs.com/tugenhua0707/p/5198407.html#_labe1
-
工廠模式(解決多個相似的問題)
function createPerson (name, age, sex){
var obj= new Object() ;
obj.name = name;
obj.age = age;
obj.sex = sex;
obj.sayName = function() {
return this.name;
}
return obj;
}
當(dāng)然工廠模式并不僅僅是用來 new
出實(shí)例。假設(shè)有一份很復(fù)雜的代碼需要用戶去調(diào)用喷户,用戶只負(fù)責(zé)傳遞需要的參數(shù)唾那,至于參數(shù)的使用,內(nèi)部的邏輯是不關(guān)心的摩骨,只需要最后返回一個實(shí)例通贞。這個構(gòu)造過程就是工廠。作用就是隱藏了創(chuàng)建實(shí)例的復(fù)雜度恼五,只需要提供一個接口昌罩,簡單清晰。
-
單體模式
單體模式是一個用來劃分命名空間并將一批屬性和方法組織在一起的對象灾馒,如果它可以被實(shí)例化茎用,那么它只能被實(shí)例化一次。優(yōu)點(diǎn)是:
1.可以用來劃分命名空間,減少全局變量的數(shù)量轨功。
2.使用單體模式可以使代碼組織的更為一致旭斥,使代碼容易閱讀和維護(hù)。
3.可以被實(shí)例化古涧,且實(shí)例化一次垂券。
單例模式的核心就是保證全局只有一個對象可以訪問,只需要用一個變量確保實(shí)例只創(chuàng)建一次
var singleton = function (name) {
this.name =name ;
};
singleton.prototype.getName = function() {
return this.name;
}
// 獲取實(shí)例對象
var getinstance = ( function () {
var instance = null;
return function (name) {
if( !instance) {
instance = new singleton (name) ;
}
return instance;
}
})();
-
發(fā)布者-訂閱者模式
定義了對象間的一種一對多的關(guān)系羡滑,當(dāng)一個對象發(fā)生改變時菇爪,所有依賴于它的對象都將得到通知
優(yōu)點(diǎn):支持廣播通信,發(fā)布者與訂閱者耦合度低
缺點(diǎn):創(chuàng)建訂閱者消耗時間和內(nèi)存柒昏;代碼不易理解和維護(hù)
var shoeobj = {}; // 定義發(fā)布者
shoeobj.list = []; // 緩存列表存放訂閱者回調(diào)函數(shù)
//增加訂閱者
shoeobj.listen = function (key,fn) {
if(!this.list[key]){
// 如果還沒有訂閱過此類消息凳宙,給該類消息創(chuàng)建一個緩存列表
this.list[key] = [];
}
this.list[key].push(fn); // 訂閱消息添加到緩存列表
}
// 發(fā)布消息
shoeobj.trigger = function () {
var key = Array.prototype.shift.call(arguments); // 取出消息類型名稱
var fns = this.list[key]; // 取出該消息對應(yīng)的回調(diào)函數(shù)的集合
//如果沒有訂閱過該消息的話,則返回
if(!fns || fns.length === 0)
return;
for(let i = 0; i < fns.length; i++){
fns[i].apply(this,arguments); //arguments是發(fā)布消息時附送的參數(shù)
}
};
-
代理模式
代理是為了控制對對象的訪問职祷,不讓外部直接訪問到對象氏涩,事件代理就是很好的例子
-
外觀模式`
外觀模式提供了一個接口,隱藏了內(nèi)部的邏輯有梆,更加方便外部調(diào)用是尖。如實(shí)現(xiàn)一個兼容多種瀏覽器的添加事件方法。
function addEvent(elm, evType, fn, useCapture){
if (elm.addEventListener){
elm.addEventListener(evType, fn, useCapture);
return true;
} else if (elm.attachEvent ){
var r = elm.attachEvent("on" +evType, fn);
return r;
} else {
elm[ "on" +evType] = fn;
}
}
25. offsetWidth/offsetHeight;clientWidth/clientHeight與scrollWidth/scrollHeight的區(qū)別
offsetWidth // width + padding + border-width
offsetHeight // height + padding + border-width
clientWidth // width + padding
clientHeight // height + padding
scrollWidth // width + padding + 溢出尺寸淳梦,無溢出時為盒子的clientWidth
scrollHeight // height + padding + 溢出尺寸析砸,無溢出時為盒子的clientHeight
offsetTop // 返回元素的上外緣距離最近采用定位父元素內(nèi)壁的距離,如果父元素中沒有采用定位的爆袍,則是獲取上外邊緣距離文檔內(nèi)壁的距離
//其中所謂的定位就是position屬性值為relative首繁、absolute或者fixed。返回值是一個整數(shù)陨囊,單位是像素弦疮。此屬性是只讀的
offsetLeft // 和offsetTop的原理是一樣的,只不過方位不同
scrolTop // 獲取或者設(shè)置對象的最頂部到對象在當(dāng)前窗口顯示的范圍內(nèi)的頂邊的距離蜘醋,也就是元素滾動條被向下拉動的距離
//返回值是一個整數(shù)胁塞,單位是像素。此屬性是可讀寫的
scrollLeft // 此屬性可以獲取或者設(shè)置對象的最左邊到對象在當(dāng)前窗口顯示的范圍內(nèi)的左邊的距離压语,也就是元素被滾動條向左拉動的距離
// 當(dāng)鼠標(biāo)事件發(fā)生時(不管是onclick啸罢,還是omousemove,onmouseover等)
clientX // 鼠標(biāo)相對于瀏覽器(瀏覽器的有效區(qū)域)左上角x軸的坐標(biāo);不隨滾動條滾動而改變;
clientY // 鼠標(biāo)相對于瀏覽器(瀏覽器的有效區(qū)域)左上角y軸的坐標(biāo);不隨滾動條滾動而改變;
pageX // 鼠標(biāo)相對于瀏覽器(瀏覽器的有效區(qū)域)左上角x軸的坐標(biāo);隨滾動條滾動而改變;
pageY // 鼠標(biāo)相對于瀏覽器(瀏覽器的有效區(qū)域)左上角y軸的坐標(biāo);隨滾動條滾動而改變;
screenx // 鼠標(biāo)相對于顯示器屏幕左上角x軸的坐標(biāo);
screenY // 鼠標(biāo)相對于顯示器屏幕左上角y軸的坐標(biāo);
offsetX // 鼠標(biāo)相對于事件源左上角X軸的坐標(biāo)
offsetY // 鼠標(biāo)相對于事件源左上角Y軸的坐標(biāo)
26. JS數(shù)據(jù)類型
- 基本數(shù)據(jù)類型:
Undefined
胎食,Null
扰才,Boolean
,Number
厕怜、String
- 引用數(shù)據(jù)類型:對象衩匣、數(shù)組和函數(shù)
- 兩種類型的區(qū)別是:存儲位置不同蕾总;
- 基本數(shù)據(jù)類型直接存儲在棧
stack
中的簡單數(shù)據(jù)段,占據(jù)空間小琅捏、大小固定生百,屬于被頻繁使用數(shù)據(jù),所以放入棧中存儲柄延; - 引用數(shù)據(jù)類型存儲在堆
heap
中的對象,占據(jù)空間大蚀浆、大小不固定,如果存儲在棧中,將會影響程序運(yùn)行的性能拦焚;引用數(shù)據(jù)類型在棧中存儲了指針蜡坊,該指針指向堆中該實(shí)體的起始地址。當(dāng)解釋器尋找引用值時赎败,會首先檢索其在棧中的地址,取得地址后從堆中獲得實(shí)體
27. null蠢甲,undefined 的區(qū)別
undefined
:是一個表示"無"的原始值或者說表示"缺少值"僵刮,就是此處應(yīng)該有一個值,但是還沒有定義鹦牛,當(dāng)嘗試讀取時會返回undefined
搞糕。例如變量被聲明了,但沒有賦值時曼追,就等于undefined
null
: 是一個對象(空對象, 沒有任何屬性和方法)在驗(yàn)證
null
時窍仰,一定要使用===
,因?yàn)?==
無法分別null
和undefined
(null == undefined 為 true)
在JavaScript規(guī)范中提到礼殊,要比較相等性之前驹吮,不能將null
和undefined
轉(zhuǎn)換成其他任何值,并且規(guī)定null
和undefined
是相等的晶伦,都代表無效的值碟狞,全等于的狀態(tài)下為false
,因?yàn)椴粚儆谕粩?shù)據(jù)類型
typeof null // object
typeof undefined // undefined
28. Js內(nèi)置對象
-
Object
是JavaScript
中所有對象的父對象 - 數(shù)據(jù)封裝類對象:
Object
婚陪、Array
族沃、Boolean
、Number
和String
- 其他對象:
Function
泌参、Arguments
脆淹、Math
、Date
沽一、RegExp
盖溺、Error
29. 常見兼容性問題
- 標(biāo)準(zhǔn)的事件綁定方法函數(shù)為
addEventListener
,IE下是attachEvent
锯玛; - W3C標(biāo)準(zhǔn)規(guī)定咐柜,事件是作為函數(shù)的參數(shù)傳入的兼蜈,IE采用了一種非標(biāo)準(zhǔn)的方式,將事件作為
window
對象的event
屬性 -
ajax
的實(shí)現(xiàn)方式不同拙友,獲取XMLHttpRequest
的不同为狸,IE(5/6)下是activeXObject
- 不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)丁
margin
和內(nèi)補(bǔ)丁padding
不同 解決方案: css 里增加通配符 * { margin: 0; padding: 0; } - 獲得DOM節(jié)點(diǎn)的父節(jié)點(diǎn)、子節(jié)點(diǎn)的方式不同:其他瀏覽器:
parentNode
/parentNode.childNodes
遗契;IE:parentElement
/parentElement.children
- 當(dāng)標(biāo)簽的高度設(shè)置小于10px辐棒,在IE6、IE7中會超出自己設(shè)置的高度;解決方案:超出高度的標(biāo)簽設(shè)置overflow:hidden,或者設(shè)置line-height的值小于你的設(shè)置高度
- cursor:hand 顯示手型在safari 上不支持;解決方案:統(tǒng)一使用 cursor:pointer
30. eval
- 功能是把對應(yīng)的字符串解析成
JS
代碼并運(yùn)行 - 應(yīng)該避免使用
eval
牍蜂,不安全漾根,非常耗性能(2
次,一次解析成js
語句鲫竞,一次執(zhí)行) - 由
JSON
字符串轉(zhuǎn)換為JSON對象的時候可以用eval
var obj =eval('('+ str +')');
31. "use strict";是什么意思
-
use strict
是一種ECMAscript 5
添加的嚴(yán)格運(yùn)行模式,這種模式使得Javascript
在更嚴(yán)格的條件下運(yùn)行,使JS
編碼更加規(guī)范化的模式,消除Javascript
語法的一些不合理辐怕、不嚴(yán)謹(jǐn)之處,減少一些怪異行為,消除代碼運(yùn)行的一些不安全之處从绘,保證代碼運(yùn)行的安全寄疏;提高編譯器效率,增加運(yùn)行速度僵井;為未來新版本的Javascript
做好鋪墊陕截。 - 指令
use strict
直接寫在<script></script>
中的第一行,表示該篇js都處于嚴(yán)格模式下
寫在方法中的第一行批什,表示該方法下的代碼格式都處于嚴(yán)格模式下 - 嚴(yán)格模式的限制:
- 不允許使用未聲明的變量
- 不允許刪除變量农曲、對象、函數(shù)驻债,只能刪除configurable設(shè)置為true的對象屬性
- 不允許變量重名
- 不允許使用八進(jìn)制
- 不允許使用轉(zhuǎn)義字符
- 不允許對只讀屬性賦值
- 不允許對一個使用getter方法讀取的屬性進(jìn)行賦值
- 不允許刪除一個不允許刪除的屬性
- 變量名不能使用 "eval" 乳规、"arguments"字符串
- 不允許使用with語句
- 在作用域 eval() 創(chuàng)建的變量不能被調(diào)用
- 禁止this關(guān)鍵字指向全局對象
- 新增保留關(guān)鍵字:let、public却汉、private驯妄、static等
32. 數(shù)組去重
1. 利用ES6 Set去重(ES6中最常用)
Array.from(new Set(arr));
[...new Set(arr)];
2. 利用for嵌套for,然后splice去重(ES5中最常用)
function unique(arr) {
for(let i = 0; i < arr.length; i++) {
for(let j = i + 1; j < arr.length; j++) {
if(arr[i] == arr[j]) { // 第一個等同于第二個合砂,splice方法刪除第二個
arr.splice(j,1);
j--;
}
}
}
return arr;
}
3. 利用indexOf
/includes
去重
function unique(arr) {
var array =[];
for (let i = 0; i < arr.length; i++) {
if (array.indexof(arr[i]) === -1) { // 也可以用 array.includes(arr[i]) 作為判斷條件
array .push(arr[i])
}
}
return array;
}
4. 利用sort()
function unique(arr) {
arr = arr. sort();
var arrry = [arr[0]];
for (let i = 1; i < arr.length; i++){
if (arr[i] !== arr[i-1]) {
arrry.push(arr[i]);
}
}
return arrry;
}
5. 利用filter
function unique(arr) {
return arr.filter(function(item, index, arr) {
return arr.indexof(item青扔,0) === index;
});
}
6. 利用遞歸去重
function unique(arr) {
var array= arr;
var len = array.length;
array.sort();
function loop(index){
if(index >= 1) {
if(array[index] === array[index-1]){
array.splice(index,1);
}
loop(index - 1); //遞歸loop,然后數(shù)組去重
}
}
loop(len-1);
return array;
}
7. 利用reduce+includes
function unique(arr) {
return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]);
}
33. 數(shù)組降維
-
數(shù)組字符串化
var arr = [[222,333,444,], [55,66,77],{a:1}];
arr += '';
arr = arr.split(',');
console.log(arr); // [222,333,444,55,66,77,[object Object]]
-
遞歸
function reduceDimension(arr) {
let ret = [];
let toArr = function(arr) {
arr.forEach((item) => {
item instanceof Array ? toArr(item) : ret.push(item);
});
}
toArr(arr);
return ret;
}
-
Array.prototype.flat()
var arr1 = [1, 2, [3, 4]];
arr1.flat(); // [1, 2, 3, 4]
var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat(); // [1, 2, 3, 4, [5, 6]]
var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2); // [1, 2, 3, 4, 5, 6]
//使用工nfinity作為深度翩伪,展開任意深度的嵌套數(shù)組
arr3.flat(Infinity); // [1, 2, 3, 4, 5, 6]
-
棧
var arr1 = [1,2,3,[1,2,3,4,[2,3,4]]];
var res;
// 不使用遞歸微猖,使用stack反嵌套多層嵌套數(shù)組
function flatten(input) {
const stack = [...input];
const res = [];
while (stack.length) {
// 使用pop 從 stack中取出并移除值
const next = stack.pop();
if(Array.isArray(next)) {
// 使用push送回內(nèi)層數(shù)組中的元素
stack.push(...next);
} else {
res.push(next);
}
}
//使用reverse恢復(fù)原數(shù)組的順序
return res.reverse();
}
res = flatten(arr1);
console.log(res); // [1,2,3,1,2,3,4,2,3,4]
34. 數(shù)組/對象遍歷
-
數(shù)組遍歷
for循環(huán)
最簡單的一種循環(huán)遍歷方法,也是使用頻率最高的一種缘屹,使用臨時變量緩存長度可優(yōu)化性能
var arr = [1, 2, 3, 4, 5, 6];
var len = arr.length;
for(var i = 0; i < len; i++) {
console.log(arr[i]);
} // 1 2 3 4 5 6
-
for in
主要是用來循環(huán)遍歷對象的屬性凛剥,用來遍歷數(shù)組效率最低(輸出的 key 是數(shù)組索引)
var arr = ['我', '是', '誰', '我', '在', '哪'];
for(var key in arr) {
console.log(key);
} // 0 1 2 3 4 5
-
for of(ES6)
性能要好于for…in…
,但仍然比不上普通的for
循環(huán)(不能循環(huán)對象)
var arr = ['我', '是', '誰', '我', '在', '哪'];
for(var key of arr) {
console.log(key);
} // 我 是 誰 我 在 哪
-
foreach
數(shù)組自帶的遍歷方法轻姿,使用頻率略高犁珠,但是性能仍然比普通循環(huán)略低
var arr = [1, 2, 3, 4, 5, 6];
arr.forEach(function (item, idnex, array) {
console.log(item); // 1 2 3 4 5 6
console.log(array); // [1, 2, 3, 4, 5, 6]
})
-
map
遍歷每一個元素并且返回對應(yīng)的元素(可以返回處理后的元素) 逻炊,返回的新數(shù)組和舊數(shù)組的長度是一樣的,使用比較廣泛犁享,但其性能差于forEach
var arr = [1, 2, 3, 4, 5, 6];
var newArr = arr.map(function (item, idnex) {
return item * item;
})
console.log(newArr); // [1, 4, 9, 16, 25, 36]
-
對象遍歷
for in
主要用于遍歷對象的可枚舉屬性余素,包括自有屬性、繼承自原型的屬性炊昆,可以搭配hasOwnProperty方法篩選自有屬性Object.keys
主要用于遍歷對象自有的可枚舉屬性桨吊,不包括繼承自原型的屬性和不可枚舉的屬性Object.getOwnProperty
主要用于返回對象的自有屬性,包括可枚舉和不可枚舉的屬性凤巨,不包括繼承自原型的屬性
35. map與forEach的區(qū)別
-
相同點(diǎn)
- 都是循環(huán)遍歷數(shù)組中的每一項(xiàng)
- 每次執(zhí)行匿名函數(shù)都支持三個參數(shù)视乐,參數(shù)分別為item(當(dāng)前每一項(xiàng)),index(索引值)敢茁,arr(原數(shù)組)
- 匿名函數(shù)中的this都是指向window
- 只能遍歷數(shù)組
-
不同點(diǎn)
-
map()
會分配內(nèi)存空間存儲新數(shù)組并返回佑淀,而不會改變原數(shù)組的值,所以在callback需要有return
值彰檬,如果沒有會返回undefined
-
forEach()
允許callback更改原始數(shù)組的元素
-
36. 將函數(shù)參數(shù)arguments轉(zhuǎn)為數(shù)組
- 傳統(tǒng)方法:遍歷
arguments
渣聚,將元素添加至新數(shù)組中 - Array.prototype.slice.call(arguments);
37. 面向?qū)ο缶幊?/h2>
- 基本思想:使用對象僧叉,類,繼承棺榔,封裝等基本概念來進(jìn)行程序設(shè)計
- 優(yōu)點(diǎn):易維護(hù)瓶堕、易擴(kuò)展、重用性降低重復(fù)工作量症歇、縮短開發(fā)周期
- 與面向過程編程的區(qū)別
-
面向過程編程是分析出解決問題所需要的步驟郎笆,然后用函數(shù)把這些步驟實(shí)現(xiàn),使用的時候依次調(diào)用
-
面向?qū)ο缶幊?/strong>是把構(gòu)成問題事務(wù)分解成各個對象忘晤,建立對象的目的不是為了完成一個步驟宛蚓,而是為了描敘某個事物在整個解決問題的步驟中的行為,以功能來劃分問題设塔,而不是步驟
38. 函數(shù)柯里化
- 在一個函數(shù)中凄吏,先填充幾個參數(shù),然后再返回一個新的函數(shù)的技術(shù)闰蛔,稱為函數(shù)的柯里化痕钢。通常用于在不侵入函數(shù)的前提下,為函數(shù)預(yù)置通用參數(shù)序六,供多次重復(fù)調(diào)用任连。
-
bind
可用于實(shí)現(xiàn)函數(shù)柯里化
39. Js動畫與CSS動畫區(qū)別及相應(yīng)實(shí)現(xiàn)
- CSS3的動畫的優(yōu)點(diǎn)
- 在性能上會稍微好一些,瀏覽器會對
CSS3
的動畫做一些優(yōu)化
- 代碼相對簡單
- 缺點(diǎn)
- 在動畫控制上不夠靈活
- 兼容性不好
-
JavaScript
的動畫正好彌補(bǔ)了這兩個缺點(diǎn)例诀,控制能力很強(qiáng)随抠,可以單幀的控制裁着、變換,同時寫得好完全可以兼容IE6
拱她,并且功能強(qiáng)大
- 對于一些復(fù)雜控制的動畫二驰,使用
javascript
會比較靠譜。而在實(shí)現(xiàn)一些小的交互動效的時候椭懊,就多考慮考慮CSS
40. callee和caller的作用
-
caller
javascript函數(shù)類型的一個屬性诸蚕,它引用調(diào)用當(dāng)前函數(shù)的函數(shù);如果在javascript
程序中氧猬,函數(shù)是由頂層調(diào)用的垛叨,則返回null
-
callee
函數(shù)上下文中arguments對象的屬性,引用的是函數(shù)自身
41. BOM
-
瀏覽器對象模型志秃,提供一組對象來完成對瀏覽器的操作柬脸,包括
-
Window
代表瀏覽器窗口,同時也是網(wǎng)頁中的全局對象
-
Navigator
代表當(dāng)前瀏覽器信息妄均,通過該對象可識別不同的瀏覽器
-
Location
代表當(dāng)前瀏覽器的地址欄信息柱锹,可以獲取地址欄信息或操作瀏覽器跳轉(zhuǎn)頁面
-
History
代表瀏覽器的歷史記錄,可通過該對象操作瀏覽器歷史記錄丰包,不能獲取具體歷史記錄禁熏,只能操作向前向后翻頁且只在當(dāng)次訪問時有效
-
Screen
代表用戶屏幕信息,屏幕寬高及分辨率等信息
BOM對象在瀏覽器中都作為window
對象的屬性保存邑彪,可通過window
對象調(diào)用瞧毙,也可直接使用
-
Navigator
-
userAgent
含有用來描述瀏覽器信息的內(nèi)容
- 如果不能通過
userAgent
不能判斷,還可以通過一些瀏覽器中的特有對象來判斷(主要因?yàn)镮E11+無法再通過userAgent判斷)寄症,如ActiveXObject
-
History
-
length
屬性獲取當(dāng)次訪問的鏈接數(shù)量宙彪;
-
back()
回退上一個頁面;
-
forward()
前進(jìn)下一個頁面有巧;
-
go()
跳轉(zhuǎn)指定頁面释漆,以整數(shù)作為參數(shù),整數(shù)為向前跳轉(zhuǎn)頁面數(shù)篮迎,負(fù)數(shù)為向后跳轉(zhuǎn)頁面數(shù)
-
Location
- 直接打印
location
可以獲取當(dāng)前頁面的完整路徑男图;
- 修改
location
為一個絕對或相對路徑,則頁面會自動跳轉(zhuǎn)并生成相應(yīng)的歷史記錄柑潦;
-
assign()
跳轉(zhuǎn)至其他頁面享言,與直接修改location的作用一樣;
-
reload()
重新加載當(dāng)前頁面渗鬼;傳入true為參數(shù)則強(qiáng)制清空緩存刷新頁面览露;
-
replace()
使用新的頁面替換當(dāng)前頁面,但不生成歷史記錄譬胎;
42. 異步編程
-
回調(diào)函數(shù)
- 優(yōu)點(diǎn):簡單差牛、容易理解和部署
- 缺點(diǎn):不利于維護(hù)命锄,代碼耦合高,而且每個任務(wù)只能指定一個回調(diào)函數(shù)
-
事件監(jiān)聽(采用事件驅(qū)動模式偏化,取決于某個事件是否發(fā)生)
- 優(yōu)點(diǎn):容易理解脐恩,可以綁定多個事件,每個事件可以指定多個回調(diào)函數(shù)
- 缺點(diǎn):事件驅(qū)動型侦讨,流程不夠清晰
-
發(fā)布/訂閱(觀察者模式)
- 類似于事件監(jiān)聽驶冒,存在一個"信號中心",某個任務(wù)執(zhí)行完成韵卤,就向信號中心"發(fā)布"(publish)一個信號骗污,其他任務(wù)可以向信號中心"訂閱"(subscribe)這個信號,從而知道什么時候自己可以開始執(zhí)行沈条,但是可以通過‘消息中心’需忿,了解現(xiàn)在有多少發(fā)布者,多少訂閱者蜡歹,從而監(jiān)控程序的運(yùn)行屋厘,故優(yōu)于事件監(jiān)聽模式
-
Promise對象
- 優(yōu)點(diǎn):可以利用then方法,進(jìn)行鏈?zhǔn)綄懛ㄔ露鞒糖宄谷鳎幸徽椎呐涮追椒ǎ梢詫?shí)現(xiàn)許多強(qiáng)大的功能父款;如果一個任務(wù)已經(jīng)完成仲翎,再添加回調(diào)函數(shù)會立即執(zhí)行,不用擔(dān)心是否錯過了某個事件或信號铛漓;
- 缺點(diǎn):編寫和理解相對比較難
-
Generator函數(shù)
- 優(yōu)點(diǎn):函數(shù)體內(nèi)外的數(shù)據(jù)交換、錯誤處理機(jī)制
- 缺點(diǎn):流程管理不方便
-
async函數(shù)
- 優(yōu)點(diǎn):內(nèi)置執(zhí)行器鲫构、更好的語義浓恶、更廣的適用性、返回的是Promise结笨、結(jié)構(gòu)清晰包晰。
- 缺點(diǎn):await 將異步代碼改造成同步代碼,如果多個異步操作沒有依賴性而使用 await 會導(dǎo)致性能上的降低炕吸。
43. WebSocket
-
由于 http
存在一個明顯的弊端伐憾,即消息只能由客戶端推送到服務(wù)器端,而服務(wù)器端不能主動推送到客戶端赫模,導(dǎo)致如果服務(wù)器如果有連續(xù)的變化树肃,這時只能使用輪詢。而輪詢效率過低瀑罗,并不適合胸嘴,于是 WebSocket
被發(fā)明出來雏掠,相比與 http
具有以下優(yōu)點(diǎn)
- 支持雙向通信,實(shí)時性更強(qiáng)劣像;
- 可以發(fā)送文本乡话,也可以二進(jìn)制文件;
- 協(xié)議標(biāo)識符是 ws耳奕,加密后是 wss 绑青;
- 較少的控制開銷。連接創(chuàng)建后屋群,ws客戶端闸婴、服務(wù)端進(jìn)行數(shù)據(jù)交換時,協(xié)議控制的數(shù)據(jù)包頭部較小谓晌。
- 支持?jǐn)U展掠拳。
- 無跨域問題。
- 實(shí)現(xiàn)比較簡單纸肉,服務(wù)端庫如
socket.io
溺欧、ws
,可以很好的幫助入門柏肪〗愕螅客戶端也只需要參照 api
實(shí)現(xiàn)即可
-
如何兼容低版本瀏覽器
- Adobe Flash Socket
- ActiveX HTMLFile (IE)
- 基于 multipart 編碼發(fā)送 XHR:原理是讓客戶端在一次請求中保持和服務(wù)端連接不斷開,然后服務(wù)端源源不斷傳送數(shù)據(jù)給客戶端烦味,就好比數(shù)據(jù)流一樣聂使,并不是一次性將數(shù)據(jù)全部發(fā)給客戶端
- 長輪詢
44. 防抖/節(jié)流
-
防抖
- 面向過程編程是分析出解決問題所需要的步驟郎笆,然后用函數(shù)把這些步驟實(shí)現(xiàn),使用的時候依次調(diào)用
- 面向?qū)ο缶幊?/strong>是把構(gòu)成問題事務(wù)分解成各個對象忘晤,建立對象的目的不是為了完成一個步驟宛蚓,而是為了描敘某個事物在整個解決問題的步驟中的行為,以功能來劃分問題设塔,而不是步驟
bind
可用于實(shí)現(xiàn)函數(shù)柯里化- 在性能上會稍微好一些,瀏覽器會對
CSS3
的動畫做一些優(yōu)化 - 代碼相對簡單
- 在動畫控制上不夠靈活
- 兼容性不好
JavaScript
的動畫正好彌補(bǔ)了這兩個缺點(diǎn)例诀,控制能力很強(qiáng)随抠,可以單幀的控制裁着、變換,同時寫得好完全可以兼容IE6
拱她,并且功能強(qiáng)大javascript
會比較靠譜。而在實(shí)現(xiàn)一些小的交互動效的時候椭懊,就多考慮考慮CSS
caller
javascript函數(shù)類型的一個屬性诸蚕,它引用調(diào)用當(dāng)前函數(shù)的函數(shù);如果在javascript
程序中氧猬,函數(shù)是由頂層調(diào)用的垛叨,則返回null
callee
函數(shù)上下文中arguments對象的屬性,引用的是函數(shù)自身瀏覽器對象模型志秃,提供一組對象來完成對瀏覽器的操作柬脸,包括
-
Window
代表瀏覽器窗口,同時也是網(wǎng)頁中的全局對象 -
Navigator
代表當(dāng)前瀏覽器信息妄均,通過該對象可識別不同的瀏覽器 -
Location
代表當(dāng)前瀏覽器的地址欄信息柱锹,可以獲取地址欄信息或操作瀏覽器跳轉(zhuǎn)頁面 -
History
代表瀏覽器的歷史記錄,可通過該對象操作瀏覽器歷史記錄丰包,不能獲取具體歷史記錄禁熏,只能操作向前向后翻頁且只在當(dāng)次訪問時有效 -
Screen
代表用戶屏幕信息,屏幕寬高及分辨率等信息
BOM對象在瀏覽器中都作為window
對象的屬性保存邑彪,可通過window
對象調(diào)用瞧毙,也可直接使用
Navigator
-
userAgent
含有用來描述瀏覽器信息的內(nèi)容 - 如果不能通過
userAgent
不能判斷,還可以通過一些瀏覽器中的特有對象來判斷(主要因?yàn)镮E11+無法再通過userAgent判斷)寄症,如ActiveXObject
History
-
length
屬性獲取當(dāng)次訪問的鏈接數(shù)量宙彪; -
back()
回退上一個頁面; -
forward()
前進(jìn)下一個頁面有巧; -
go()
跳轉(zhuǎn)指定頁面释漆,以整數(shù)作為參數(shù),整數(shù)為向前跳轉(zhuǎn)頁面數(shù)篮迎,負(fù)數(shù)為向后跳轉(zhuǎn)頁面數(shù)
Location
- 直接打印
location
可以獲取當(dāng)前頁面的完整路徑男图; - 修改
location
為一個絕對或相對路徑,則頁面會自動跳轉(zhuǎn)并生成相應(yīng)的歷史記錄柑潦; -
assign()
跳轉(zhuǎn)至其他頁面享言,與直接修改location的作用一樣; -
reload()
重新加載當(dāng)前頁面渗鬼;傳入true為參數(shù)則強(qiáng)制清空緩存刷新頁面览露; -
replace()
使用新的頁面替換當(dāng)前頁面,但不生成歷史記錄譬胎;
回調(diào)函數(shù)
- 優(yōu)點(diǎn):簡單差牛、容易理解和部署
- 缺點(diǎn):不利于維護(hù)命锄,代碼耦合高,而且每個任務(wù)只能指定一個回調(diào)函數(shù)
事件監(jiān)聽(采用事件驅(qū)動模式偏化,取決于某個事件是否發(fā)生)
- 優(yōu)點(diǎn):容易理解脐恩,可以綁定多個事件,每個事件可以指定多個回調(diào)函數(shù)
- 缺點(diǎn):事件驅(qū)動型侦讨,流程不夠清晰
發(fā)布/訂閱(觀察者模式)
- 類似于事件監(jiān)聽驶冒,存在一個"信號中心",某個任務(wù)執(zhí)行完成韵卤,就向信號中心"發(fā)布"(publish)一個信號骗污,其他任務(wù)可以向信號中心"訂閱"(subscribe)這個信號,從而知道什么時候自己可以開始執(zhí)行沈条,但是可以通過‘消息中心’需忿,了解現(xiàn)在有多少發(fā)布者,多少訂閱者蜡歹,從而監(jiān)控程序的運(yùn)行屋厘,故優(yōu)于事件監(jiān)聽模式
Promise對象
- 優(yōu)點(diǎn):可以利用then方法,進(jìn)行鏈?zhǔn)綄懛ㄔ露鞒糖宄谷鳎幸徽椎呐涮追椒ǎ梢詫?shí)現(xiàn)許多強(qiáng)大的功能父款;如果一個任務(wù)已經(jīng)完成仲翎,再添加回調(diào)函數(shù)會立即執(zhí)行,不用擔(dān)心是否錯過了某個事件或信號铛漓;
- 缺點(diǎn):編寫和理解相對比較難
Generator函數(shù)
- 優(yōu)點(diǎn):函數(shù)體內(nèi)外的數(shù)據(jù)交換、錯誤處理機(jī)制
- 缺點(diǎn):流程管理不方便
async函數(shù)
- 優(yōu)點(diǎn):內(nèi)置執(zhí)行器鲫构、更好的語義浓恶、更廣的適用性、返回的是Promise结笨、結(jié)構(gòu)清晰包晰。
- 缺點(diǎn):await 將異步代碼改造成同步代碼,如果多個異步操作沒有依賴性而使用 await 會導(dǎo)致性能上的降低炕吸。
由于 http
存在一個明顯的弊端伐憾,即消息只能由客戶端推送到服務(wù)器端,而服務(wù)器端不能主動推送到客戶端赫模,導(dǎo)致如果服務(wù)器如果有連續(xù)的變化树肃,這時只能使用輪詢。而輪詢效率過低瀑罗,并不適合胸嘴,于是 WebSocket
被發(fā)明出來雏掠,相比與 http
具有以下優(yōu)點(diǎn)
- 支持雙向通信,實(shí)時性更強(qiáng)劣像;
- 可以發(fā)送文本乡话,也可以二進(jìn)制文件;
- 協(xié)議標(biāo)識符是 ws耳奕,加密后是 wss 绑青;
- 較少的控制開銷。連接創(chuàng)建后屋群,ws客戶端闸婴、服務(wù)端進(jìn)行數(shù)據(jù)交換時,協(xié)議控制的數(shù)據(jù)包頭部較小谓晌。
- 支持?jǐn)U展掠拳。
- 無跨域問題。
- 實(shí)現(xiàn)比較簡單纸肉,服務(wù)端庫如
socket.io
溺欧、ws
,可以很好的幫助入門柏肪〗愕螅客戶端也只需要參照api
實(shí)現(xiàn)即可
如何兼容低版本瀏覽器
- Adobe Flash Socket
- ActiveX HTMLFile (IE)
- 基于 multipart 編碼發(fā)送 XHR:原理是讓客戶端在一次請求中保持和服務(wù)端連接不斷開,然后服務(wù)端源源不斷傳送數(shù)據(jù)給客戶端烦味,就好比數(shù)據(jù)流一樣聂使,并不是一次性將數(shù)據(jù)全部發(fā)給客戶端
- 長輪詢
防抖
防抖是將多次執(zhí)行變?yōu)樽詈笠淮螆?zhí)行
/**
* @desc函數(shù)防抖
* @param func函數(shù)
* @param wait延遲執(zhí)行毫秒數(shù)
* @param immediate true表立即執(zhí)行,false表非立即執(zhí)行
*/
funtion debounce(func, wait, immediate) {
let timeout;
return function () {
let context = this;
let args = arguments;
if (timeout)
clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait);
if (callNow)
func.apply(context谬俄,args);
} else {
timeout = setTimeout(function() {
func.apply(context柏靶,args)
}, wait);
}
}
}
-
節(jié)流
節(jié)流是將多次執(zhí)行變成每隔一段時間執(zhí)行
function throttle(func, wait) {
let timeout;
return function() {
let context - this;
let args = arguments;
if(!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args);
},wait);
}
}
}
45. 變量提升/函數(shù)提升/暫時性死區(qū)
-
變量提升
當(dāng)執(zhí)行 JS 代碼時溃论,會生成執(zhí)行環(huán)境屎蜓,在生成執(zhí)行環(huán)境時,會有兩個階段钥勋。第一個階段是創(chuàng)建的階段炬转,JS 解釋器會找出需要提升的變量和函數(shù),并且給他們提前在內(nèi)存中開辟好空間算灸,變量只聲明并且賦值為 undefined
console.log(a); //undefined
var a = 123;
-
函數(shù)提升
具名函數(shù)的聲明有兩種方式:函數(shù)聲明式/函數(shù)字面量式
函數(shù)字面量式聲明和普通變量一樣扼劈,提升的只是一個沒有值的變量
函數(shù)聲明式的提升現(xiàn)象和變量提升略有不同,函數(shù)聲明式會提升到作用域最前邊菲驴,并且將聲明內(nèi)容一起提升到最上邊
bar()
//函數(shù)字面量式
var bar = function() {
console.log(1);
} // 報錯:TypeError: bar is not a function
bar()
//函數(shù)聲明式
function bar() {
console.log(1);
} //輸出結(jié)果1
同名變量聲明荐吵,Javascript采用的是忽略原則,后聲明的會被忽略,變量聲明和賦值操作可以寫在
一起捍靠,但是只有聲明會被提升沐旨,提升后變量的值默認(rèn)為undefined,結(jié)果是在賦值操作執(zhí)行前變量的值必為undefined同名函數(shù)聲明榨婆,Javascript采用的是覆蓋原則磁携,先聲明的會被覆蓋,因?yàn)楹瘮?shù)在聲明時會指定函數(shù)
的內(nèi)容良风,所以同一作用域下一系列同名函數(shù)聲明的最終結(jié)果是調(diào)用時函數(shù)的內(nèi)容和最后一次函數(shù)聲明相同對于同名的函數(shù)聲明和變量聲明谊迄,采用的是忽略原則,由于在提升時函數(shù)聲明會提升到變量聲明之前烟央,變量聲明一定會被忽略统诺,所以結(jié)果是函數(shù)聲明有效
-
暫時性死區(qū)
ES6規(guī)定,let/const 命令會使區(qū)塊形成封閉的作用域疑俭。若在聲明之前使用變量粮呢,就會報錯ReferenceError
。在代碼塊內(nèi)钞艇,使用 let 命令聲明變量之前啄寡,該變量都是不可用的。這在語法上哩照,稱為 “暫時性死區(qū)”temporal dead zone TDZ
46. 執(zhí)行上下文棧
- JavaScript引擎創(chuàng)建了執(zhí)行上下文棧來管理執(zhí)行上下文挺物。可把執(zhí)行上下以文棧認(rèn)為是一個存儲函數(shù)調(diào)棧結(jié)構(gòu)用的飘弧,遵循先進(jìn)后出的原則
- 一開始瀏覽器執(zhí)行全局的代碼時识藤,首先創(chuàng)建全局執(zhí)行上下文,壓入執(zhí)行棧頂部
- 每當(dāng)進(jìn)入一個函數(shù)的執(zhí)行就會創(chuàng)建函數(shù)的執(zhí)行上下文次伶,并且把它壓入執(zhí)行棧的頂部痴昧。當(dāng)前函數(shù)執(zhí)行完成后,當(dāng)前函數(shù)的執(zhí)行上下文出棧冠王,并等待垃圾回收剪个。
- 瀏覽器的JS執(zhí)行引擎總是訪問棧頂?shù)膱?zhí)行上下文。
- 全局上下文只有唯一的一個版确,它在瀏覽器關(guān)閉時出棧。
47. ajax/axios/fetch區(qū)別
- 傳統(tǒng)
ajax
隸屬于原始js乎折,核心使用XMLHttpRequest對象绒疗,多個請求之間如果有先后關(guān)系的話,會出現(xiàn)回調(diào)地獄骂澄。JQuery ajax 是對原生XHR的封裝吓蘑。 缺點(diǎn)如下:- 本身是針對
MVC
的編程,不符合現(xiàn)在前端MVVM
的浪潮 - 基于原生的XHR開發(fā),XHR本身的架構(gòu)不清晰
- JQuery整個項(xiàng)目太大,單純使用ajax卻要引入整個JQuery非常的不合理
- 不符合關(guān)注分離(Separation of Concerns)的原則
- 配置和調(diào)用方式非衬ハ猓混亂溃蔫,而且基于事件的異步模型不友好
- 本身是針對
-
axios
本質(zhì)上也是對原生XHR的封裝,只不過它是Promise的實(shí)現(xiàn)版本琳猫,符合最新的ES規(guī)范伟叛,具有以下特征:- 從瀏覽器中創(chuàng)建 XMLHttpRequest
- 支持 Promise API
- 客戶端支持防止CSRF
- 提供了一些并發(fā)請求的接口(重要,方便了很多的操作)
- 從 node.js 創(chuàng)建 http 請求
- 攔截請求和響應(yīng)
- 轉(zhuǎn)換請求和響應(yīng)數(shù)據(jù)
- 取消請求
- 自動轉(zhuǎn)換JSON數(shù)據(jù)
-
fetch
是XMLHttpRequest的一種替代方案,而不是ajax的進(jìn)一步封裝脐嫂,沒有使用XMLHttpRequest對象,是基于Promise設(shè)計统刮,調(diào)用簡單,掛在BOM上的账千,屬于全局的方法侥蒙,很友好的解決了回調(diào)地獄問題,優(yōu)點(diǎn)如下:- 語法簡潔匀奏,更加語義化
- 基于標(biāo)準(zhǔn) Promise 實(shí)現(xiàn)鞭衩,支持 async/await
- 更加底層,提供的API豐富(request, response)
- 脫離了XHR娃善,是ES規(guī)范里新的實(shí)現(xiàn)方式
try {
let response = await fetch(url);
let data = response.json();
console.log(data);
} catch(e) {
console.log("Oops, error", e);
}
- 但
fetch
是一個低層次的API论衍,可以把它考慮成原生的XHR,所以使用起來并不是那么舒服会放,需要進(jìn)行封裝饲齐,且沒有辦法原生監(jiān)測請求的進(jìn)度,而XHR可以
48. Hybrid
Hybrid
技術(shù)是一種混合開發(fā)模式咧最,即同時使用Native
與Web
搭建的App捂人。定義是: "同時使用網(wǎng)頁語言與程序語言開發(fā),通過應(yīng)用商店區(qū)分移動操作系統(tǒng)分發(fā),用戶安裝使用”矢沿±拇睿總體特性更接近Native App
,以Javascript + Native兩者相互調(diào)用為主,從開發(fā)層面實(shí)現(xiàn)“一次開發(fā)捣鲸,多處運(yùn)行”的機(jī)制瑟匆,成為真正適合跨平臺的開發(fā),兼具了Native App良好用戶交互體驗(yàn)的優(yōu)勢,也兼具了Web App使用H5跨平臺開發(fā)低成本的優(yōu)勢栽惶。Hybrid App
的本質(zhì)其實(shí)是在原生的 App 中愁溜,使用 WebView 作為容器直接承載 Web頁面。因此外厂,最核心的點(diǎn)就是 Native端 與 H5端 之間的雙向通訊層冕象,即需要一套跨語言通訊方案,來完成 Native與 JavaScript 的通訊-
JSBridge通信原理
- Native調(diào)用JS
webview 作為 H5 的宿主汁蝶,Native 可以通過 webview 的 API直接執(zhí)行 Js 代碼渐扮,例如:
ios可以通過webview的evaluateJavaScript:completionHandler方法來運(yùn)行js的代碼
android可以通過webview的loadUrl()去調(diào)用js代碼论悴,也可以使用evaluateJavascript()來調(diào)用js代碼 - JS調(diào)用Native,有3種常見的方案:
1.WebView URL Scheme 跳轉(zhuǎn)攔截
URL SCHEME 是一種類似于url的鏈接墓律,是為了方便app直接互相調(diào)用設(shè)計的膀估,形式和普通的 url 近似,主要區(qū)別是 protocol 和 host 一般是自定義的耻讽。攔截 URL SCHEME 的主要流程是:Web 端通過某種方式(例如 iframe.src)發(fā)送 URL Scheme 請求察纯,之后 Native 攔截到請求并根據(jù) URL SCHEME(包括所帶的參數(shù))進(jìn)行相關(guān)操作。
2.WebView中的prompt/console/alert攔截
通常使用prompt齐饮,因?yàn)檫@個方法在前端中使用頻率低捐寥,比較不會出現(xiàn)沖突
3.WebView API注入
通過 WebView 提供的接口,向 JavaScript 的 Context(window)中注入對象或者方法祖驱,讓 JavaScript 調(diào)用時握恳,直接執(zhí)行相應(yīng)的 Native 代碼邏輯,達(dá)到 JavaScript 調(diào)用 Native 的目的
- Native調(diào)用JS
-
現(xiàn)在比較流行的混合方案主要有三種捺僻,主要是在UI渲染機(jī)制上的不同
- 基于 WebView UI的基礎(chǔ)方案乡洼,通過 JSBridge 完成 H5 與 Native 的雙向通訊,從而賦予H5一定程度的原生能力匕坯。
- 基于Native UI的方案束昵,例如 React-Native、Weex葛峻。在賦予 H5 原生API能力的基礎(chǔ)上锹雏,進(jìn)一步通過 JSBridge 將js解析成的虛擬節(jié)點(diǎn)樹(Virtual DOM)傳遞到 Native 并使用原生渲染。
- 小程序方案术奖,也是通過更加定制化的 JSBridge礁遵,并使用雙 WebView 雙線程的模式隔離了JS邏輯與UI渲染,形成了特殊的開發(fā)模式采记,加強(qiáng)了 H5 與 Native 混合程度佣耐,提高了頁面性能及開發(fā)體驗(yàn)。
以上的3種方案唧龄,其實(shí)同樣都是基于 JSBridge 完成的通訊層兼砖,第2、3種方案既棺,其實(shí)可以看做是在方案1的基礎(chǔ)上讽挟,繼續(xù)通過不同的新技術(shù)進(jìn)一步提高了應(yīng)用的混合程度。因此丸冕,JSBridge 也是整個混合應(yīng)用最關(guān)鍵的部分耽梅。