1. IE 和 DOM 事件流的區(qū)別
1.事件流的區(qū)別
IE 采用冒泡型事件 Netscape 使用捕獲型事件
DOM 使用先捕獲后冒泡型事件
示例:
?body?
?div?
?button?點(diǎn)擊這里?/button?
?/div?
?/body?
冒泡型事件模型: button-?div-?body (IE 事件流)
捕獲型事件模型: body-?div-?button (Netscape 事件流)
DOM 事件模型: body-?div-?button-?button-?div-?body (先捕獲后冒泡)
2.事件偵聽函數(shù)的區(qū)別
IE 使用:
[Object].attachEvent("name_of_event_handler", fnHandler); //綁定函數(shù)
[Object].detachEvent("name_of_event_handler", fnHandler); //移除綁定
DOM 使用:
[Object].addEventListener("name_of_event", fnHandler, bCapture); //綁定函數(shù)
[Object].removeEventListener("name_of_event", fnHandler, bCapture);
//移除綁定bCapture 參數(shù)用于設(shè)置事件綁定的階段荆烈,true 為捕獲階段拯勉,false 為冒泡階段竟趾。
2.變量提升
js 代碼執(zhí)行的過(guò)程
變量提升
代碼從上到下依次執(zhí)行
var 關(guān)鍵字和 function 關(guān)鍵字聲明的變量會(huì)進(jìn)行變量提升
變量提升發(fā)生的環(huán)境:
發(fā)生在代碼所處的當(dāng)前作用域。
變量提升
var 關(guān)鍵字進(jìn)行的變量提升宫峦,會(huì)把變量提前聲明岔帽,但是不會(huì)提前賦值 。
function 關(guān)鍵字對(duì)變量進(jìn)行變量提升导绷,既會(huì)把變量提前聲明犀勒,又會(huì)把變量提前賦值,也就是把整個(gè)函數(shù)體提升到代碼的頂部
有一些代碼是不會(huì)執(zhí)行的但是仍舊會(huì)發(fā)生變量提升,規(guī)則適用于 1,2
return 之后的代碼依舊會(huì)發(fā)生變量提升妥曲,規(guī)則適用于 1贾费,2
代碼報(bào)錯(cuò)之后的代碼依舊會(huì)發(fā)生變量提升,規(guī)則適用于 1檐盟,2
break 之后的代碼依舊會(huì)發(fā)生變量提升褂萧,規(guī)則適用于 1,2
有一些代碼是不會(huì)執(zhí)行但是仍舊會(huì)發(fā)生變量提升,但是規(guī)則要發(fā)生變化
if 判斷語(yǔ)句 if 判斷語(yǔ)句中 var 關(guān)鍵字以及 function 關(guān)鍵字聲明的變量只會(huì)發(fā)生提前聲明葵萎,不會(huì)發(fā)生提前賦值,也就是不會(huì)吧函數(shù)體整體提升到當(dāng)前作用域頂部导犹。規(guī)則跟 1,2 不適用
switch case 規(guī)則跟 1,2 不適用
3 do while 規(guī)則跟 1,2 不適用
try catch catch 中聲明的變量只會(huì)發(fā)生提前聲明,不會(huì)發(fā)生提前賦值羡忘。
在條件判斷語(yǔ)句和 try catch 中的聲明的變量不管是否能夠執(zhí)行谎痢,都只會(huì)發(fā)生提前聲明,不會(huì)發(fā)生提前賦值卷雕。
解析:
// 如果一個(gè)變量聲明了但是未賦值节猿,那么輸出這個(gè)變量就會(huì)輸出 undefined
var num;
console.log(num);
// 如果一個(gè)變量沒有聲明也沒有賦值,那么就會(huì)報(bào)一個(gè)錯(cuò):
console.log(num);
// 輸出一個(gè)不存在的變量 Uncaught ReferenceError: num is not defined
// var 關(guān)鍵字進(jìn)行的變量提升
console.log(num);
var num = 123;
console.log(num);
var num = 456;
console.log(num);
// 變量提升之后的代碼:
var num;
console.log(num);
num = 123;
console.log(num);
num = 456;
console.log(num);
// function 關(guān)鍵字的變量提升
console.log(fn);
function fn() {
console.log(1);
}
// 變量提升之后的代碼:
function fn() {
console.log(1);
}
console.log(fn); // 輸出fn的函數(shù)體
// 3.1 return 之后的代碼依舊會(huì)發(fā)生變量提升 規(guī)則適用于1漫雕,2
function fn() {
console.log(num);
return;
var num = 123;
}
fn();
// 變量提升之后的代碼:
function fn() {
var num;
console.log(num);
return;
num = 123;
}
fn(); // undefined
function fn() {
console.log(fo);
return;
function fo() {}
}
fn();
// 變量提升之后的代碼:
function fn() {
function fo() {}
console.log(fo);
return;
}
fn(); //輸出fo的函數(shù)體
//3.2 代碼報(bào)錯(cuò)之后的代碼依舊會(huì)進(jìn)行變量提升沐批,規(guī)則適用于1,2
console.log(num);
xsasfgdsfqdfsdf; //報(bào)一個(gè)錯(cuò)
var num = 123;
console.log(num);
// 變量提升之后的代碼:
var num;
console.log(num); //輸出 undefined
dsagdsqghdwfh; // 報(bào)一個(gè)錯(cuò)誤 ,錯(cuò)誤之后的代碼不會(huì)被執(zhí)行
num = 123;
console.log(num);//function 關(guān)鍵字
console.log(fn);
sasgfdhwhsdqg;
function fn() {}
console.log(fn);
// 變量提升之后的代碼:
function fn() {}
console.log(fn); // 輸出 fn 的函數(shù)體
asdgsdgdfgfdg; // 報(bào)一個(gè)錯(cuò)誤蝎亚,報(bào)錯(cuò)之后的代碼不會(huì)被執(zhí)行
console.log(fn);
//4 代碼不執(zhí)行,但是會(huì)進(jìn)行變量提升先馆,不過(guò)規(guī)則不適用于1,2
//4.1 if判斷語(yǔ)句
console.log(num);
if (false) {
var num = 123;
}
console.log(num)
// 變量提升之后的代碼:
var num;
console.log(num); //undefined
if (false) {
num = 123;
}
console.log(num) //undefined
console.log(fn);
if (false) {
function fn() {}
}
console.log(fn);
// 變量提升之后的代碼:
var fn;
function fn;
console.log(fn) //undefined
if (false) {
function fn() {}
}
console.log(fn) //undefined
if(false){
function fn() {}
}
console.log(fn) //undefined
// try catch
try {
console.log(num);
} catch (e) {
var num = 123;
}
console.log(num);
var num;
try {
console.log(num); // undefined
} catch (e) {
num = 123;
}
console.log(num); // undefined
try {
console.log(fn);
} catch (e) {
function fn() {}
}
console.log(fn);
var fn;
try {
console.log(fn); // undefined
} catch (e) {
num = 123;
}
console.log(fn); // undefined
3.如何阻止冒泡與默認(rèn)行為
阻止冒泡行為:
非 IE 瀏覽器 stopPropagation()发框,IE 瀏覽器 window.event.cancelBubble = true
阻止默認(rèn)行為:
非 IE 瀏覽器 preventDefault(),IE 瀏覽器 window.event.returnValue = false
解析:
當(dāng)需要阻止冒泡行為時(shí)煤墙,可以使用
function stopBubble(e) {
//如果提供了事件對(duì)象梅惯,則這是一個(gè)非IE瀏覽器
if (e && e.stopPropagation)
//因此它支持W3C的stopPropagation()方法
e.stopPropagation();
//否則,我們需要使用IE的方式來(lái)取消事件冒泡
else window.event.cancelBubble = true;
}
當(dāng)需要阻止默認(rèn)行為時(shí)仿野,可以使用
//阻止瀏覽器的默認(rèn)行為
function stopDefault(e) {
//阻止默認(rèn)瀏覽器動(dòng)作(W3C)
if (e && e.preventDefault) e.preventDefault();
//IE中阻止函數(shù)器默認(rèn)動(dòng)作的方式
else window.event.returnValue = false;
return false;
}
4.js 中 this 閉包 作用域
this:指向調(diào)用上下文
閉包:定義一個(gè)函數(shù)就開辟了一個(gè)局部作用域铣减,整個(gè) js 執(zhí)行環(huán)境有一個(gè)全局作用域
作用域:一個(gè)函數(shù)可以訪問(wèn)其他函數(shù)中的變量(閉包是一個(gè)受保護(hù)的變量空間)
var f = (function fn() {
var name = 1;
return function () {
name++;
console.log(name)
}
})()
5.javascript 的同源策略
一段腳本只能讀取來(lái)自于同一來(lái)源的窗口和文檔的屬性
解析:
同源策略:限制從一個(gè)源加載的文檔或腳本如何與來(lái)自另一個(gè)源的資源進(jìn)行交互。這是一個(gè)用于隔離潛在惡意文件的關(guān)鍵的安全機(jī)制脚作。(來(lái)自 MDN 官方的解釋)
簡(jiǎn)單來(lái)說(shuō)就是:一段腳本只能讀取來(lái)自于同一來(lái)源的窗口和文檔的屬性葫哗,這里的同一來(lái)源指的是主機(jī)名缔刹、協(xié)議和端口號(hào)的組合
具體解釋:
(1)源包括三個(gè)部分:協(xié)議、域名劣针、端口(http 協(xié)議的默認(rèn)端口是 80)校镐。如果有任何一個(gè)部分不同,則源不同捺典,那就是跨域了鸟廓。
(2)限制:這個(gè)源的文檔沒有權(quán)利去操作另一個(gè)源的文檔。這個(gè)限制體現(xiàn)在:(要記捉蠹骸)Cookie引谜、LocalStorage 和 IndexDB 無(wú)法獲取。無(wú)法獲取和操作 DOM擎浴。不能發(fā)送 Ajax 請(qǐng)求员咽。我們要注意,Ajax 只適合同源的通信退客。
同源策略帶來(lái)的麻煩:ajax 在不同域名下的請(qǐng)求無(wú)法實(shí)現(xiàn)骏融,需要進(jìn)行跨域操作
6.事件冒泡與事件捕獲
事件冒泡:由最具體的元素(目標(biāo)元素)向外傳播到最不具體的元素
事件捕獲:由最不確定的元素到目標(biāo)元素
7.復(fù)雜數(shù)據(jù)類型如何轉(zhuǎn)變?yōu)樽址?/h5>
首先,會(huì)調(diào)用 valueOf 方法萌狂,如果方法的返回值是一個(gè)基本數(shù)據(jù)類型档玻,就返回這個(gè)值
如果調(diào)用 valueOf 方法之后的返回值仍舊是一個(gè)復(fù)雜數(shù)據(jù)類型,就會(huì)調(diào)用該對(duì)象的 toString 方法
如果 toString 方法調(diào)用之后的返回值是一個(gè)基本數(shù)據(jù)類型茫藏,就返回這個(gè)值误趴,
如果 toString 方法調(diào)用之后的返回值是一個(gè)復(fù)雜數(shù)據(jù)類型,就報(bào)一個(gè)錯(cuò)誤务傲。
8.javascript 中 this 的指向問(wèn)題
全局環(huán)境凉当、普通函數(shù)(非嚴(yán)格模式)指向 window
普通函數(shù)(嚴(yán)格模式)指向 undefined
函數(shù)作為對(duì)象方法及原型鏈指向的就是上一級(jí)的對(duì)象
構(gòu)造函數(shù)指向構(gòu)造的對(duì)象
DOM 事件中指向觸發(fā)事件的元素
箭頭函數(shù)
解析:
1、全局環(huán)境
全局環(huán)境下售葡,this 始終指向全局對(duì)象(window)看杭,無(wú)論是否嚴(yán)格模式;
// 在瀏覽器中挟伙,全局對(duì)象為 window 對(duì)象:
console.log(this === window); // true
this.a = 37;
console.log(window.a); // 37
2楼雹、函數(shù)上下文調(diào)用
普通函數(shù)
普通函數(shù)內(nèi)部的 this 分兩種情況,嚴(yán)格模式和非嚴(yán)格模式尖阔。
(1)非嚴(yán)格模式下贮缅,沒有被上一級(jí)的對(duì)象所調(diào)用,this 默認(rèn)指向全局對(duì)象 window。
function f1() {
return this;
}
f1() === window; // true
(2)嚴(yán)格模式下介却,this 指向 undefined谴供。
function f2() {
"use strict"; // 這里是嚴(yán)格模式
return this;
}
f2() === undefined; // true
函數(shù)作為對(duì)象的方法
(1)函數(shù)有被上一級(jí)的對(duì)象所調(diào)用,那么 this 指向的就是上一級(jí)的對(duì)象齿坷。
(2)多層嵌套的對(duì)象桂肌,內(nèi)部方法的 this 指向離被調(diào)用函數(shù)最近的對(duì)象(window 也是對(duì)象数焊,其內(nèi)部對(duì)象調(diào)用方法的 this 指向內(nèi)部對(duì)象, 而非 window)轴或。
//方式1
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
//當(dāng) o.f()被調(diào)用時(shí)昌跌,函數(shù)內(nèi)的this將綁定到o對(duì)象。
console.log(o.f()); // logs 37
//方式2
var o = { prop: 37 };
function independent() {
return this.prop;
}
//函數(shù)f作為o的成員方法調(diào)用
o.f = independent;
console.log(o.f()); // logs 37
//方式3
//this 的綁定只受最靠近的成員引用的影響
o.b = { g: independent, prop: 42 };
console.log(o.b.g()); // 42
特殊例子
// 例子1
var o = {
a: 10,
b: {
// a:12,
fn: function() {
console.log(this.a); //undefined
console.log(this); //{fn: ?}
}
}
};
o.b.fn();
// 例子2
var o = {
a: 10,
b: {
a: 12,
fn: function() {
console.log(this.a); //undefined
console.log(this); //window
}
}
};
var j = o.b.fn;
j();
// this永遠(yuǎn)指向的是最后調(diào)用它的對(duì)象照雁,也就是看它執(zhí)行的時(shí)候是誰(shuí)調(diào)用的蚕愤,例子2中雖然函數(shù)fn是被對(duì)象b所引用,但是在將fn賦值給變量j的時(shí)候并沒有執(zhí)行所以最終指向的是window饺蚊,這和例子1是不一樣的萍诱,例子1是直接執(zhí)行了fn
原型鏈中的 this
(1)如果該方法存在于一個(gè)對(duì)象的原型鏈上,那么 this 指向的是調(diào)用這個(gè)方法的對(duì)象污呼,就像該方法在對(duì)象上一樣裕坊。
var o = {
f: function() {
return this.a + this.b;
}
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
上述例子中,對(duì)象 p 沒有屬于它自己的 f 屬性燕酷,它的 f 屬性繼承自它的原型籍凝。當(dāng)執(zhí)行 p.f()時(shí),會(huì)查找 p 的原型鏈苗缩,找到 f 函數(shù)并執(zhí)行饵蒂。因?yàn)?f 是作為 p 的方法調(diào)用的,所以函數(shù)中的 this 指向 p酱讶。
(2)相同的概念也適用于當(dāng)函數(shù)在一個(gè) getter 或者 setter 中被調(diào)用退盯。用作 getter 或 setter 的函數(shù)都會(huì)把 this 綁定到設(shè)置或獲取屬性的對(duì)象。
(3)call()和 apply()方法:當(dāng)函數(shù)通過(guò) Function 對(duì)象的原型中繼承的方法 call() 和 apply() 方法調(diào)用時(shí)泻肯, 其函數(shù)內(nèi)部的 this 值可綁定到 call() & apply() 方法指定的第一個(gè)對(duì)象上渊迁, 如果第一個(gè)參數(shù)不是對(duì)象,JavaScript 內(nèi)部會(huì)嘗試將其轉(zhuǎn)換成對(duì)象然后指向它灶挟。
function add(c, d) {
return this.a + this.b + c + d;
}
var o = { a: 1, b: 3 };
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
function tt() {
console.log(this);
}
// 第一個(gè)參數(shù)不是對(duì)象琉朽,JavaScript內(nèi)部會(huì)嘗試將其轉(zhuǎn)換成對(duì)象然后指向它。
tt.call(5); // 內(nèi)部轉(zhuǎn)成 Number {[[PrimitiveValue]]: 5}
tt.call("asd");
// 內(nèi)部轉(zhuǎn)成 String {0: "a", 1: "s", 2: "d", length: 3, [[PrimitiveValue]]: "asd"}
(4)bind()方法:由 ES5 引入稚铣, 在 Function 的原型鏈上箱叁, Function.prototype.bind
通過(guò) bind 方法綁定后, 函數(shù)將被永遠(yuǎn)綁定在其第一個(gè)參數(shù)對(duì)象上榛泛, 而無(wú)論其在什么情況下被調(diào)用。
function f() {
return this.a;
}
var g = f.bind({ a: "azerty" });
console.log(g()); // azerty
var o = { a: 37, f: f, g: g };
console.log(o.f(), o.g()); // 37, azerty2.4
構(gòu)造函數(shù)中的 this當(dāng)一個(gè)函數(shù)用作構(gòu)造函數(shù)時(shí)(使用 new 關(guān)鍵字)噩斟,它的 this 被綁定到正在構(gòu)造的新對(duì)象曹锨。構(gòu)造器返回的默認(rèn)值是 this 所指的那個(gè)對(duì)象,也可以手動(dòng)返回其他的對(duì)象剃允。
function C() {
this.a = 37;
}
var o = new C();
console.log(o.a); // 37
// 為什么this會(huì)指向o沛简?
//首先new關(guān)鍵字會(huì)創(chuàng)建一個(gè)空的對(duì)象齐鲤,然后會(huì)自動(dòng)調(diào)用一個(gè)函數(shù)apply方法,將this指向這個(gè)空對(duì)象椒楣,這樣的話函數(shù)內(nèi)部的this就會(huì)被這個(gè)空的對(duì)象替代给郊。
function C2() {
this.a = 37;
return { a: 38 }; // 手動(dòng)設(shè)置返回{a:38}對(duì)象
}
o = new C2();
console.log(o.a); // 38
特殊例子
當(dāng) this 碰到 return 時(shí)
// 例子1
function fn() {
this.user = "追夢(mèng)子";
return {};
}
var a = new fn();
console.log(a.user); //undefined
// 例子2
function fn() {
this.user = "追夢(mèng)子";
return function() {};
}
var a = new fn();
console.log(a.user); //undefined
// 例子3
function fn() {
this.user = "追夢(mèng)子";
return 1;
}
var a = new fn();
console.log(a.user); //追夢(mèng)子
// 例子4
function fn() {
this.user = "追夢(mèng)子";
return undefined;
}
var a = new fn();
console.log(a.user); //追夢(mèng)子
// 例子5
function fn() {
this.user = "追夢(mèng)子";
return undefined;
}
var a = new fn();
console.log(a); //fn {user: "追夢(mèng)子"}
// 例子6
// 雖然null也是對(duì)象,但是在這里this還是指向那個(gè)函數(shù)的實(shí)例捧灰,因?yàn)閚ull比較特殊
function fn() {
this.user = "追夢(mèng)子";
return null;
}
var a = new fn();
console.log(a.user); //追夢(mèng)子
// 總結(jié):如果返回值是一個(gè)對(duì)象淆九,那么this指向的就是那個(gè)返回的對(duì)象,如果返回值不是一個(gè)對(duì)象那么this還是指向函數(shù)的實(shí)例毛俏。
setTimeout & setInterval
(1)對(duì)于延時(shí)函數(shù)內(nèi)部的回調(diào)函數(shù)的 this 指向全局對(duì)象 window炭庙;
(2)可以通過(guò) bind()方法改變內(nèi)部函數(shù) this 指向。
//默認(rèn)情況下代碼
function Person() {
this.age = 0;
setTimeout(function() {
console.log(this);
}, 3000);
}
var p = new Person(); //3秒后返回 window 對(duì)象
//通過(guò)bind綁定
function Person() {
this.age = 0;
setTimeout(
function() {
console.log(this);
}.bind(this),
3000
);
}
var p = new Person(); //3秒后返回構(gòu)造函數(shù)新生成的對(duì)象 Person{...}
3煌寇、在 DOM 事件中
作為一個(gè) DOM 事件處理函數(shù)
當(dāng)函數(shù)被用作事件處理函數(shù)時(shí)焕蹄,它的 this 指向觸發(fā)事件的元素(針對(duì) addEventListener 事件)。
// 被調(diào)用時(shí)阀溶,將關(guān)聯(lián)的元素變成藍(lán)色
function bluify(e) {
//this指向所點(diǎn)擊元素
console.log("this === e.currentTarget", this === e.currentTarget); // 總是 true
// 當(dāng) currentTarget 和 target 是同一個(gè)對(duì)象時(shí)為 true
console.log("this === e.target", this === e.target);
this.style.backgroundColor = "#A5D9F3";
}
// 獲取文檔中的所有元素的列表
var elements = document.getElementsByTagName("*");
// 將bluify作為元素的點(diǎn)擊監(jiān)聽函數(shù)腻脏,當(dāng)元素被點(diǎn)擊時(shí),就會(huì)變成藍(lán)色
for (var i = 0; i ? elements.length; i++) {
elements[i].addEventListener("click", bluify, false);
}
作為一個(gè)內(nèi)聯(lián)事件處理函數(shù)
(1)當(dāng)代碼被內(nèi)聯(lián)處理函數(shù)調(diào)用時(shí)银锻,它的 this 指向監(jiān)聽器所在的 DOM 元素永品;
(2)當(dāng)代碼被包括在函數(shù)內(nèi)部執(zhí)行時(shí),其 this 指向等同于 普通函數(shù)直接調(diào)用的情況徒仓,即在非嚴(yán)格模式指向全局對(duì)象 window腐碱,在嚴(yán)格模式指向 undefined:
?button onclick="console.log(this)"?show me?/button?
?button onclick="(function () {console.log(this)})()"?show inner this?/button?
?button onclick="(function () {'use strict'; console.log(this)})()"?
use strict
?/button?
// 控制臺(tái)打印
?button onclick="console.log(this)"?show me?/button?
Window {postMessage: ?, blur: ?, focus: ?, close: ?, parent: Window, …}
undefined
4、箭頭函數(shù)
全局環(huán)境中
在全局代碼中掉弛,箭頭函數(shù)被設(shè)置為全局對(duì)象:
var globalObject = this;
var foo = () =? this;
console.log(foo() === globalObject); // true
this 捕獲上下文
箭頭函數(shù)沒有自己的 this症见,而是使用箭頭函數(shù)所在的作用域的 this,即指向箭頭函數(shù)定義時(shí)(而不是運(yùn)行時(shí))所在的作用域殃饿。
//1谋作、箭頭函數(shù)在函數(shù)內(nèi)部,以非方法的方法使用
function Person() {
this.age = 0;
setInterval(() =? {
this.age++;
}, 3000);
}
var p = new Person(); //Person{age: 0}
//普通函數(shù)作為內(nèi)部函數(shù)
function Person() {
this.age = 0;
setInterval(function() {
console.log(this);
this.age++;
}, 3000);
}
var p = new Person(); //Window{...}
在 setTimeout 中的 this 指向了構(gòu)造函數(shù)新生成的對(duì)象乎芳,而普通函數(shù)指向了全局 window 對(duì)象遵蚜。
箭頭函數(shù)作為對(duì)象的方法
使用箭頭函數(shù)作為對(duì)象的方法使用,指向全局 window 對(duì)象奈惑;而普通函數(shù)作為對(duì)象的方法使用吭净,則指向調(diào)用的對(duì)象。
var obj = {
i: 10,
b: () =? console.log(this.i, this),
c: function() {
console.log(this.i, this);
}
};
obj.b(); // undefined window{...}
obj.c(); // 10 Object {...}
箭頭函數(shù)中肴甸,call()寂殉、apply()、bind()方法無(wú)效
var adder = {
base: 1,
//對(duì)象的方法內(nèi)部定義箭頭函數(shù)原在,this是箭頭函數(shù)所在的作用域的this友扰,
//而方法add的this指向adder對(duì)象彤叉,所以箭頭函數(shù)的this也指向adder對(duì)象。
add: function(a) {
var f = v =? v + this.base;
return f(a);
},
//普通函數(shù)f1的this指向window
add1: function() {
var f1 = function() {
console.log(this);
};
return f1();
},
addThruCall: function inFun(a) {
var f = v =? v + this.base;
var b = {
base: 2
};
return f.call(b, a);
}
};
console.log(adder.add(1)); // 輸出 2
adder.add1(); //輸出全局對(duì)象 window{...}
console.log(adder.addThruCall(1)); // 仍然輸出 2(而不是3村怪,其內(nèi)部的this并沒有因?yàn)閏all() 而改變秽浇,其this值仍然為函數(shù)inFun的this值,指向?qū)ο骯dder
this 指向固定化
箭頭函數(shù)可以讓 this 指向固定化甚负,這種特性很有利于封裝回調(diào)函數(shù)
var handler = {
id: "123456",
init: function() {
document.addEventListener(
"click",
event =? this.doSomething(event.type),
false
);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};
上面代碼的 init 方法中柬焕,使用了箭頭函數(shù),這導(dǎo)致這個(gè)箭頭函數(shù)里面的 this腊敲,總是指向 handler 對(duì)象击喂。如果不使用箭頭函數(shù)則指向全局 document 對(duì)象。
箭頭函是不適用場(chǎng)景
(1)箭頭函數(shù)不適合定義對(duì)象的方法(方法內(nèi)有 this)碰辅,因?yàn)榇藭r(shí)指向 window懂昂;
(2)需要?jiǎng)討B(tài) this 的時(shí)候,也不應(yīng)使用箭頭函數(shù)没宾。
//例1凌彬,this指向定義箭頭函數(shù)所在的作用域,它位于對(duì)象cat內(nèi)循衰,但cat不能構(gòu)成一個(gè)作用域铲敛,所以指向全局window,改成普通函數(shù)后this指向cat對(duì)象会钝。
const cat = {
lives: 9,
jumps: () =? {
this.lives--;
}
};
//例2伐蒋,此時(shí)this也是指向window,不能動(dòng)態(tài)監(jiān)聽button迁酸,改成普通函數(shù)后this指向按鈕對(duì)象先鱼。
var button = document.getElementById("press");
button.addEventListener("click", () =? {
this.classList.toggle("on");
});
9.ES6 都有什么 Iterator 遍歷器
Set、Map
解析:
遍歷器(Iterator)是一種接口奸鬓,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問(wèn)機(jī)制焙畔。任何數(shù)據(jù)結(jié)構(gòu)只要部署 Iterator 接口,就可以完成遍歷操作(即依次處理該數(shù)據(jù)結(jié)構(gòu)的所有成員)
Iterator 的作用有三個(gè):
一是為各種數(shù)據(jù)結(jié)構(gòu)串远,提供一個(gè)統(tǒng)一的宏多、簡(jiǎn)便的訪問(wèn)接口;
二是使得數(shù)據(jù)結(jié)構(gòu)的成員能夠按某種次序排列澡罚;
三是 ES6 創(chuàng)造了一種新的遍歷命令 for...of 循環(huán)伸但,Iterator 接口主要供 for...of 消費(fèi)。
默認(rèn)部署了 Iterator 的數(shù)據(jù)有 Array留搔、Map更胖、Set、String、TypedArray函喉、arguments、NodeList 對(duì)象荣月,ES6 中有的是 Set管呵、Map**、
10. ES6 中類的定義
// 1哺窄、類的基本定義
class Parent {
constructor(name = "小白") {
this.name = name;
}
}
// 2捐下、生成一個(gè)實(shí)例
let g_parent = new Parent();
console.log(g_parent); //{name: "小白"}
let v_parent = new Parent("v"); // 'v'就是構(gòu)造函數(shù)name屬性 , 覆蓋構(gòu)造函數(shù)的name
屬性值
console.log(v_parent); // {name: "v"}
// 3、繼承
class Parent {
//定義一個(gè)類
constructor(name = "小白") {
this.name = name;
}
}
class Child extends Parent {}
console.log("繼承", new Child()); // 繼承 {name: "小白"}
// 4萌业、繼承傳遞參數(shù)
class Parent {
//定義一個(gè)類
constructor(name = "小白") {
this.name = name;
}
}
class Child extends Parent {
constructor(name = "child") {
// 子類重寫name屬性值
super(name); // 子類向父類修改 super一定放第一行
this.type = "preson";
}
}
console.log("繼承", new Child("hello")); // 帶參數(shù)覆蓋默認(rèn)值 繼承{name: "hello", type: "preson"}
// 5坷襟、ES6重新定義的ES5中的訪問(wèn)器屬性
class Parent {
//定義一個(gè)類
constructor(name = "小白") {
this.name = name;
}
get longName() {
// 屬性
return "mk" + this.name;
}
set longName(value) {
this.name = value;
}
}
let v = new Parent();
console.log("getter", v.longName); // getter mk小白
v.longName = "hello";
console.log("setter", v.longName); // setter mkhello//
6、類的靜態(tài)方法
class Parent {
//定義一個(gè)類
constructor(name = "小白") {
this.name = name;
}
static tell() {
// 靜態(tài)方法:通過(guò)類去調(diào)用生年,而不是實(shí)例
console.log("tell");
}
}
Parent.tell(); // tell//
7婴程、類的靜態(tài)屬性:
class Parent {
//定義一個(gè)類
constructor(name = "小白") {
this.name = name;
}
static tell() {
// 靜態(tài)方法:通過(guò)類去調(diào)用,而不是實(shí)例
console.log("tell"); // tell
}
}
Parent.type = "test"; // 定義靜態(tài)屬性
console.log("靜態(tài)屬性", Parent.type); // 靜態(tài)屬性 test
let v_parent = new Parent();
console.log(v_parent); // {name: "小白"} 沒有tell方法和type屬性
11.說(shuō)說(shuō)你對(duì) promise 的了解
Promise 是異步編程的一種解決方案抱婉,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件監(jiān)聽——更合理和更強(qiáng)大档叔。
所謂 Promise,簡(jiǎn)單說(shuō)就是一個(gè)容器蒸绩,里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果衙四。從語(yǔ)法上說(shuō),Promise 是一個(gè)對(duì)象患亿,從它可以獲取異步操作的消息传蹈。Promise 提供統(tǒng)一的 API,各種異步操作都可以用同樣的方法進(jìn)行處理步藕。Promise 對(duì)象有以下兩個(gè)特點(diǎn):
對(duì)象的狀態(tài)不受外界影響惦界,Promise 對(duì)象代表一個(gè)異步操作,有三種狀態(tài):Pending(進(jìn)行中)漱抓、Resolved(已完成表锻,又稱 Fulfilled)和 Rejected(已失敗)
一旦狀態(tài)改變乞娄,就不會(huì)再變瞬逊,任何時(shí)候都可以得到這個(gè)結(jié)果。
12.Set 數(shù)據(jù)結(jié)構(gòu)
es6 方法,
Set 本身是一個(gè)構(gòu)造函數(shù)仪或,它類似于數(shù)組确镊,但是成員值都是唯一的。
const set = new Set([1, 2, 3, 4, 4]);
console.log([...set]); // [1,2,3,4]
console.log(Array.from(new Set([2, 3, 3, 5, 6]))); //[2,3,5,6]
13.箭頭函數(shù)需要注意的地方
箭頭函數(shù)有幾個(gè)使用注意點(diǎn)范删。
(1)函數(shù)體內(nèi)的 this 對(duì)象蕾域,就是定義時(shí)所在的對(duì)象,而不是使用時(shí)所在的對(duì)象。
(2)不可以當(dāng)作構(gòu)造函數(shù)旨巷,也就是說(shuō)巨缘,不可以使用 new 命令,否則會(huì)拋出一個(gè)錯(cuò)誤采呐。
(3)不可以使用 arguments 對(duì)象若锁,該對(duì)象在函數(shù)體內(nèi)不存在。如果要用斧吐,可以用 rest 參數(shù)代替又固。
(4)不可以使用 yield 命令,因此箭頭函數(shù)不能用作 Generator 函數(shù)煤率。
上面四點(diǎn)中仰冠,第一點(diǎn)尤其值得注意。this 對(duì)象的指向是可變的蝶糯,但是在箭頭函數(shù)中洋只,它是固定的。
function foo() {
setTimeout(() =? {
console.log("id:", this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
// id: 42
14.ES6 如何動(dòng)態(tài)加載 import
import("lodash").then(_ =? {
// Do something with lodash (a.k.a '_')...
});
15.ECMAScript6 怎么寫class么昼捍,為什么會(huì)出現(xiàn)class這種東西?
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '('+this.x+', '+this.y+')';
}
}
16.對(duì)象淺拷貝和深拷貝有什么區(qū)別
在 JS 中木张,除了基本數(shù)據(jù)類型,還存在對(duì)象端三、數(shù)組這種引用類型舷礼。
基本數(shù)據(jù)類型,拷貝是直接拷貝變量的值郊闯,而引用類型拷貝的其實(shí)是變量的地址妻献。
let o1 = {a: 1}
let o2 = o1
在這種情況下,如果改變 o1 或 o2 其中一個(gè)值的話团赁,另一個(gè)也會(huì)變育拨,因?yàn)樗鼈兌贾赶蛲粋€(gè)地址。
o2.a = 3
console.log(o1.a) // 3
而淺拷貝和深拷貝就是在這個(gè)基礎(chǔ)之上做的區(qū)分欢摄,如果在拷貝這個(gè)對(duì)象的時(shí)候绍载,只對(duì)基本數(shù)據(jù)類型進(jìn)行了拷貝扶歪,而對(duì)引用數(shù)據(jù)類型只是進(jìn)行了引用的傳遞震檩,而沒有重新創(chuàng)建一個(gè)新的對(duì)象撩匕,則認(rèn)為是淺拷貝。反之绿淋,在對(duì)引用數(shù)據(jù)類型進(jìn)行拷貝的時(shí)候闷畸,創(chuàng)建了一個(gè)新的對(duì)象,并且復(fù)制其內(nèi)的成員變量吞滞,則認(rèn)為是深拷貝佑菩。
17.JS 怎么實(shí)現(xiàn)一個(gè)類盾沫。怎么實(shí)例化這個(gè)類
嚴(yán)格來(lái)講 js 中并沒有類的概念,不過(guò) js 中的函數(shù)可以作為構(gòu)造函數(shù)來(lái)使用殿漠,通過(guò) new 來(lái)實(shí)例化赴精,其實(shí)函數(shù)本身也是一個(gè)對(duì)象。
18.如何編寫高性能的 Javascript绞幌?
使用 DocumentFragment 優(yōu)化多次 append
通過(guò)模板元素 clone 祖娘,替代 createElement
使用一次 innerHTML 賦值代替構(gòu)建 dom 元素
使用 firstChild 和 nextSibling 代替 childNodes 遍歷 dom 元素
使用 Array 做為 StringBuffer ,代替字符串拼接的操作
將循環(huán)控制量保存到局部變量
順序無(wú)關(guān)的遍歷時(shí)啊奄,用 while 替代 for
將條件分支,按可能性順序從高到低排列
在同一條件子的多( ?2 )條件分支時(shí)掀潮,使用 switch 優(yōu)于 if
使用三目運(yùn)算符替代條件分支
需要不斷執(zhí)行的時(shí)候菇夸,優(yōu)先考慮使用 setInterval
19.數(shù)組和對(duì)象有哪些原生方法,列舉一下仪吧?
Array.concat( ) 連接數(shù)組
Array.join( ) 將數(shù)組元素連接起來(lái)以構(gòu)建一個(gè)字符串
Array.length 數(shù)組的大小
Array.pop( ) 刪除并返回?cái)?shù)組的最后一個(gè)元素
Array.push( ) 給數(shù)組添加元素
Array.reverse( ) 顛倒數(shù)組中元素的順序
Array.shift( ) 將元素移出數(shù)組
Array.slice( ) 返回?cái)?shù)組的一部分
Array.sort( ) 對(duì)數(shù)組元素進(jìn)行排序
Array.splice( ) 插入庄新、刪除或替換數(shù)組的元素
Array.toLocaleString( ) 把數(shù)組轉(zhuǎn)換成局部字符串
Array.toString( ) 將數(shù)組轉(zhuǎn)換成一個(gè)字符串
Array.unshift( ) 在數(shù)組頭部插入一個(gè)元素
Object.hasOwnProperty( ) 檢查屬性是否被繼承
Object.isPrototypeOf( ) 一個(gè)對(duì)象是否是另一個(gè)對(duì)象的原型
Object.propertyIsEnumerable( ) 是否可以通過(guò) for/in 循環(huán)看到屬性
Object.toLocaleString( ) 返回對(duì)象的本地字符串表示
Object.toString( ) 定義一個(gè)對(duì)象的字符串表示
Object.valueOf( ) 指定對(duì)象的原始值
20.documen.write 和 innerHTML 的區(qū)別?
document.write 是重寫整個(gè) document, 寫入內(nèi)容是字符串的 html
innerHTML 是 HTMLElement 的屬性,是一個(gè)元素的內(nèi)部 html 內(nèi)容