字符串?dāng)U展
- 模板字符串
模板字符串可以很方便的代替ES5字符串拼接转绷,格式如下:
`字符串${變量}字符串`
代碼示例
let a = {
name: 'Adam',
age: 10,
sex: 'male'
}
let b = `
<p>Hello</p>
<h1>${a.name}</h1>
<p>${a.age}</p>
`;
console.log(b);
/*
<p>Hello</p>
<h1>Adam</h1>
<p>10</p>
*/
- API
//判斷字符串中是否包含某個(gè)字符
let str = 'ddfferrgg';
str.includes('d'); //true
//判斷字符串是否以某些字母開始或結(jié)束
let str = 'string';
str.startsWith('str'); //true
str.endsWith('ng'); //true
//重復(fù)字符串
let str = 'abc';
str.repeat(2); // abcabc
//字符串補(bǔ)白(ES7草案)
console.log('4'.padStart(3, '1')); 114
console.log('4'.padEnd(5, '0')); 40000
//輸出字符串模板原始內(nèi)容, 不轉(zhuǎn)意
console.log(String.raw`Hi\n${1+2}`) //Hi\n${1+2}
函數(shù)擴(kuò)展
- 函數(shù)默認(rèn)值
//ES5
function show(a, b) {
var a = a || 8;
return a + b;
}
//ES6
function show(a=6, b) {
return a + b;
}
- 箭頭函數(shù)
//箭頭函數(shù)寫法
let func = (a=6, b) => {
console.log(a + b);
return a + b;
}
console.log(func(undefined, 2)); //8
- 箭頭函數(shù)相當(dāng)于匿名函數(shù), 由于箭頭函數(shù)的無(wú)法綁定this,所以它的this會(huì)綁定到上層函數(shù)
對(duì)象的擴(kuò)展
- 對(duì)象的簡(jiǎn)寫
let a = 5;
let obj = {
a, //相當(dāng)于 a: a
b() {
console.log(this.a);
} //相當(dāng)于 b: function() {console.log(a)}
}
Object.keys(obj) -獲得對(duì)象所有的key名
Object.assign(obj1, obj2,[...obj]) -后面所有的對(duì)象合并至obj1中
Object.defineProperty(obj, key, {})
Object.defineProperty(obj, key, {
value: 5, //屬性的值
writable: false, //如果為false, 屬性值就不能被重寫, 只能為只讀
configurable: false, //一旦為false, 就不能設(shè)置其他屬性(value, writable, enumerable)
enumerable: false, //是否能在for...in循環(huán)中遍歷或Object.keys中列舉出來(lái)
get: function() {}, //get訪問(wèn)器
set: function() {} //set訪問(wèn)器
})
- class
//創(chuàng)建類
class Person {
//構(gòu)造函數(shù)
constructor(name, sex) {
this.name = name;
this.sex = sex;
}
//靜態(tài)方法 在一個(gè)方法前加上static關(guān)鍵字, 該方法就不會(huì)被實(shí)例繼承, 直接通過(guò)類調(diào)用
static getMethod() {
return 'hello';
}
//對(duì)象的方法
showName() {
console.log(this.name);
}
}
//類的繼承
class Man extends Person {
constructor(name, sex, age) {
super(name, sex); //name, sex屬性繼承
this.age = age;
}
}
數(shù)組的擴(kuò)展
- Array.of()
Array.of()方法是為了解決Array構(gòu)造函數(shù)傳入一個(gè)數(shù)值贫途,數(shù)組length屬性被設(shè)定為該值的的問(wèn)題
//Array構(gòu)造函數(shù)
let items = new Array(2);
console.log(items.length); //2
console.log(items[0]); //undefined
console.log(items[1]); //undefined
//Array.of
let items = Array.of(2);
console.log(items.length); //1
console.log(items[0]); //2
- Array.from()
Array.from()方法可以接受可迭代對(duì)象或者類數(shù)組對(duì)象作為第一個(gè)參數(shù), 最終返回一個(gè)數(shù)組长赞,第二個(gè)參數(shù)為一個(gè)映射函數(shù),用來(lái)將類數(shù)組對(duì)象中的每一個(gè)值轉(zhuǎn)換成其他形式, 最后將結(jié)果存儲(chǔ)在結(jié)果數(shù)組的相應(yīng)索引中
function translate() {
return Array.from(arguments, value => value + 1);
}
let numbers = translate(1, 2, 3);
console.log(numbers); //[2,3,4]
Array.from()的第三個(gè)參數(shù)表示映射函數(shù)的this值
let helper = {
diff: 1,
add(value) {
return value + this.diff
}
};
function translate() {
return Array.from(arguments, helper.add, helper)
}
let numbers = translate(1, 2, 3);
console.log(numbers);
- find()和findIndex()方法
find()和findIndex()方法都接受兩個(gè)參數(shù): 一個(gè)是回調(diào)函數(shù); 另一個(gè)是可選參數(shù), 用于指定回調(diào)函數(shù)中this的值锋叨。執(zhí)行回調(diào)函數(shù)時(shí), 傳入的參數(shù)分別為: 數(shù)組中的
某個(gè)元素和該元素在數(shù)組中的索引以及數(shù)組本身, 與傳入map()和forEach()方法的參數(shù)相同
let numbers = [25, 30, 35, 40, 45];
console.log(numbers.find(n => n > 33)); //35
console.log(numbers.findIndex(n => n > 33)); //2
- fill()
fill()方法可以用指定的值填充一至多個(gè)數(shù)組元素, 當(dāng)傳入一個(gè)值時(shí), fill()方法會(huì)用這個(gè)值重寫數(shù)組中的所有值, 第二個(gè)參數(shù)表示從哪個(gè)索引開始, 第三個(gè)參數(shù)表
示從哪兒索引結(jié)束(不包括該索引), 若不傳第三個(gè)值, 則從索引開始填充至數(shù)組末尾
// 一個(gè)參數(shù)
let numbers = [1, 2, 3, 4];
numbers.fill(1);
console.log(numbers); //[1, 1, 1, 1]
//兩個(gè)參數(shù)
let numbers = [1, 2, 3, 4];
numbers.fill(1, 2);
console.log(numbers); //[1, 2, 1, 1]
//三個(gè)參數(shù)
let numbers = [1, 2, 3, 4];
numbers.fill(0, 1, 3);
console.log(numbers); //[1, 0, 0, 4]
- copyWithin()
copyWithin()方法與fill()方法相似, 其也可以改變數(shù)組中的多個(gè)元素子房。fill()方法是將數(shù)組元素賦值為一個(gè)指定的值, 而copyWithin()方法則是從數(shù)組中復(fù)制元素的值, 調(diào)用copyWithin()方法時(shí),需要傳入兩個(gè)參數(shù): 一個(gè)是該方法的開始填充值的索引位置, 另一個(gè)是開始復(fù)制值得索引位置, 第三個(gè)參是可選參數(shù), 用于指定停止復(fù)制的索引位置(不包括該索引)
let numbers = [1, 2, 3, 4];
numbers.copyWithin(2, 0);
console.log(numbers); //[1, 2, 1, 2]
- 定型數(shù)組
待填充
解構(gòu)
- 數(shù)組解構(gòu)
數(shù)組解構(gòu)一般有以下幾種形式
let [a,b,c] = [1,2,3] //1, 2, 3
let [a,b,c] = [1,2] //1, 2, undefined
let [a,b,c=2] = [1,2] //1, 2, 2
let [a,,,b] = [1,2,3,4] // 1, 4
let [a,b,...rest] = [1,2,3,4,5,6] //1,2,[3,4,5,6]
數(shù)組解構(gòu)賦值的使用場(chǎng)景
//變量交換
let a = 1
let b = 2
[a,b] = [b,a]
console.log(a,b) //2, 1
//接收函數(shù)返回值
function f() {
return [1, 2]
}
let [a,b] = f() //1,2
- 對(duì)象結(jié)構(gòu)賦值
let o = {
p: 23,
q: 'Adam'
}
let {p, q} = o //23 Adam
let o = {
p:23
}
let {p=20, q=45} = o // 23 45
let o = {
p: 23,
q: 'Adam'
}
let {p:a, q} = o
console.log(a, q) //23 Adam
對(duì)象解構(gòu)賦值應(yīng)用
對(duì)象的嵌套
let metaData = {
title: 'abc',
test: [{
title: 'test',
desc: 'description'
}]
}
let {title:esTitle, test:[{title:cnTitle}]} = metaData
console.log(esTitle, cnTitle) //abc test
解構(gòu)表達(dá)式改變值
let node = {
type: 'Identifier',
name: 'foo'
},
type = 'Literal',
name = 5
// 使用解構(gòu)來(lái)分配不同的值
({type, name} = node)
console.log(type) //Identifier
console.log(name) //foo
對(duì)象解構(gòu)表達(dá)式給函數(shù)傳參
let node = {
type: 'Identifier',
name: 'foo'
},
type = 'Literal',
name = 5
function outputInfo(value) {
console.log(value === node) //true
}
outputInfo({type, name} = node)
console.log(type) //Identifier
console.log(name) //foo
Set-Map
set
set集合內(nèi)不能有重復(fù)的值
- set集合聲明
//方法1
let list = new Set();
list.add(1);
list.add(2);
list.add(3);
//方法2
let list = new Set([1,2,3]);
API
add, delete, clear, hasset的遍歷
let list = new Set(['a', 'b', 'c'])
//set的key和value一樣
for(let key of list.keys()) {
console.log(key) //a b c
}
for(let value of list.values()) {
console.log(value) //a b c
}
for(let value of list) {
console.log(value) //a b c
}
for(let [key, value] of list.entries()) {
console.log(key, value) //a a b b c c
}
list.forEach(function(item) {
console.log(item) //a b c
})
- WeakSet
WeakSet跟Set的區(qū)別就是數(shù)值類型不同, 只能是對(duì)象, 而且對(duì)象是地址引用, 并且不檢測(cè)垃圾回收機(jī)制, WeakSet不能遍歷
let weakList = new WeakSet()
map
map集合是一種類似于對(duì)象的數(shù)據(jù)結(jié)構(gòu), 也是以鍵值對(duì)形式出現(xiàn)的, map的鍵支持多種數(shù)據(jù)類型
- map聲明
//第一種
let map = new Map()
let arr=['123']
map.set(arr, 456)
console.log(map.get(arr)) //456
//第二種
let map = new Map([['a', 123], ['b', 456], ['c', 789]])
console.log(map) //a=>123 b=>456 c=>789
API
跟set差不多, 遍歷也一樣WeakMap
跟set與WeakSet區(qū)別一樣map與array對(duì)比
//數(shù)據(jù)的增刪查改
let map = new Map()
let array = []
//增
map.set('t', 1)
array.push({t:1})
//查
let map_exist = map.has('t') //true
let array_exist = array.find(item => item.t) //Object{t:1}
//改
map.set('t', 2)
array,forEach(item => item.t?item.t=2:'')
//刪
map.delete('t')
let index=array.findIndex(item => item.t)
array.splice(index, 1)
- set和array對(duì)比
let set = new Set()
let array = []
//增
set.add({t:1})
array.push({t:1})
//查
let set_exist = set.has({t:1})
let array_exsist = array.find(item => item.t)
//改
set.forEach(item => item.t?item.t = 2 : '')
array,forEach(item => item.t?item.t=2:'')
//刪
set.forEach(item => item.t?set.delete(item):'')
let index=array.findIndex(item => item.t)
array.splice(index, 1)
- map, set與object對(duì)比
let item = {t:1}
let map = new Map()
let set = new Set()
let obj = {}
//增
map.set('t', 1)
set.add(item)
obj['t'] = 1
//查
let map_exist = map.has('t')
let set_exist = set.has(item)
let obj_exist = 't' in obj
//改
map.set('t', 2)
item.t = 2
obj['t'] = 2
//刪
map.delete('t')
set.delete(item)
delete obj['t']
promise與異步編程
- 異步編程的背景知識(shí)
JavaScript引擎是基于單線程(Single-threaded)事件循環(huán)的概念構(gòu)建的, 同一時(shí)刻只允許一個(gè)代碼塊在執(zhí)行, 與之相反的是像Java和C++一樣的語(yǔ)言, 它們?cè)试S多個(gè)不同的代碼塊同時(shí)執(zhí)行。對(duì)于基于線程的軟件而言, 當(dāng)多個(gè)代碼塊同時(shí)訪問(wèn)并改變狀態(tài)時(shí), 程序很難維護(hù)并保證狀態(tài)不會(huì)出錯(cuò)僵娃。
JavaScript引擎同一時(shí)刻只能執(zhí)行一個(gè)代碼塊, 所以需要跟蹤即將運(yùn)行的代碼, 那些代碼被放在一個(gè)任務(wù)隊(duì)列(job queue)中, 每當(dāng)一段代碼準(zhǔn)備執(zhí)行時(shí), 都會(huì)被添加到任務(wù)隊(duì)列。每當(dāng)JavaScript引擎中的一段代碼結(jié)束執(zhí)行, 事件循環(huán)(event loop)會(huì)執(zhí)行隊(duì)列中的下一個(gè)任務(wù), 它是JavaScript引擎中的一段程序, 負(fù)責(zé)監(jiān)控代碼執(zhí)行并管理任務(wù)隊(duì)列腋妙。隊(duì)列中的任務(wù)會(huì)從第一個(gè)按順序執(zhí)行到最后一個(gè)默怨。
JavaScript作為一門為web而生的語(yǔ)言, 他一開始就需要能夠響應(yīng)異步的用戶交互, 如點(diǎn)擊等操作。所以異步編程一直作為JavaScript的一個(gè)強(qiáng)大功能而存在,再加上Nodejs用回調(diào)函數(shù)代替了事件, 使異步編程在JavaScript領(lǐng)域變得更加流行, 但是隨著更多程序開始使用異步編程后, 事件和回調(diào)函數(shù)已經(jīng)無(wú)法滿足開發(fā)者的需求, 因此ES6中就加入了Promise用來(lái)處理更復(fù)雜的需求骤素。
- 事件模型
用戶點(diǎn)擊按鈕或按下鍵盤上的按鍵會(huì)觸發(fā)類似onclick這樣的事件, 它會(huì)向任務(wù)隊(duì)列添加一個(gè)新任務(wù)來(lái)響應(yīng)用戶的操作, 這是JavaScript種最基礎(chǔ)的異步編程形式, 直到事件觸發(fā)時(shí)才執(zhí)行事件處理程序, 且執(zhí)行上下文與定義時(shí)的相同匙睹。
let button = document.querySelector('.btn');
button.addEventListener('click', function(event) {
console.log('clicked');
})
這段代碼中, 點(diǎn)擊button后會(huì)執(zhí)行console.log('clicked'), 監(jiān)聽器內(nèi)的函數(shù)被添加到任務(wù)隊(duì)列中, 只有當(dāng)前面的任務(wù)都完成后它才會(huì)被執(zhí)行。
事件模型適用于處理簡(jiǎn)單的交互, 然后將多個(gè)獨(dú)立的異步調(diào)用連接在一起會(huì)是程序更加復(fù)雜, 你必須跟蹤每個(gè)事件的事件目標(biāo)(如代碼中的button)济竹。此外, 必須要保證事件在添加事件處理程序之后才會(huì)被觸發(fā)痕檬。盡管事件模型適用于響應(yīng)用戶交互和完成類似的低頻功能, 但是對(duì)于更復(fù)雜的需求來(lái)說(shuō), 它并不是很靈活。
- 回調(diào)模式
Nodejs通過(guò)普及回調(diào)函數(shù)來(lái)改進(jìn)異步編程模型, 回調(diào)模式與事件模型類似, 異步代碼會(huì)在未來(lái)的某個(gè)時(shí)間點(diǎn)執(zhí)行, 二者的區(qū)別是回調(diào)模式中被調(diào)用的函數(shù)時(shí)作為參數(shù)傳入的
readFile('example.txt', function(err, contents) {
if (err) {
throw err;
}
console.log(contents);
})
console.log('Hi!');
上面nodejs代碼就是典型的回調(diào)模式送浊。readFile()函數(shù)讀取磁盤上的某個(gè)文件(example.txt), 讀取結(jié)束后執(zhí)行回調(diào)函數(shù)(function(err, contents){})梦谜。如果出現(xiàn)錯(cuò)誤, 錯(cuò)誤對(duì)象會(huì)被賦值給回調(diào)函數(shù)的err參數(shù); 如果一切正常, 文件內(nèi)容會(huì)以字符串的形式被賦值給contents參數(shù)。
由于使用了回調(diào)模式, readFile()函數(shù)在讀取文件時(shí), 暫時(shí)不會(huì)執(zhí)行回調(diào)函數(shù)袭景。也就是說(shuō), 調(diào)用readFile()函數(shù)后, 會(huì)先執(zhí)行后面的console.log('Hi!'), 當(dāng)readFile()結(jié)束執(zhí)行時(shí), 會(huì)向任務(wù)隊(duì)列末尾添加一個(gè)新任務(wù), 該任務(wù)包含回調(diào)函數(shù), 當(dāng)隊(duì)列前面所有的任務(wù)完成后才執(zhí)行該任務(wù)改淑。
回調(diào)模式比事件模型更靈活, 可以通過(guò)回調(diào)函數(shù)鏈接多個(gè)回調(diào)
readFile('example.txt', function(err, contents) {
if (err) {
throw err;
}
writeFile('example.txt', function() {
if (err) {
throw err;
}
console.log('File was written!');
});
});
上面的例子就是一個(gè)典型的回調(diào)鏈接, 這段代碼在成功調(diào)用readFile()函數(shù)后會(huì)執(zhí)行另一個(gè)writeFile()函數(shù)的異步調(diào)用。當(dāng)readFile()函數(shù)執(zhí)行完成后, 會(huì)向任務(wù)隊(duì)列中添加一個(gè)任務(wù), 如果沒(méi)有錯(cuò)誤產(chǎn)生, 則執(zhí)行writeFile()函數(shù), 然后當(dāng)writeFile()函數(shù)執(zhí)行結(jié)束后, 也像任務(wù)隊(duì)列中添加一個(gè)任務(wù)浴讯。
雖然這個(gè)模式很靈活, 運(yùn)行效果也不錯(cuò), 但是大家想想, 層層嵌套回調(diào)函數(shù), 很快大家就會(huì)進(jìn)入回調(diào)地獄中, 把自己搞暈了朵夏。另外并行執(zhí)行兩個(gè)異步操作時(shí), 當(dāng)兩個(gè)操作都結(jié)束時(shí)通知你; 又或者同時(shí)進(jìn)行兩個(gè)異步操作, 只取優(yōu)先完成的結(jié)果。這種情況, 你就需要跟蹤多個(gè)回調(diào)函數(shù)并清理操作, 而promise就能非常好地改進(jìn)這種情況榆纽。
- Promise的基礎(chǔ)知識(shí)
Promise的意思就是承諾, 承諾在未來(lái)的某個(gè)時(shí)刻完成函數(shù)執(zhí)行, 它相當(dāng)于異步操作結(jié)果的占位符, 它不會(huì)去綁定一個(gè)事件, 也不會(huì)傳遞一個(gè)回調(diào)函數(shù)給目標(biāo)函數(shù), 而是讓函數(shù)返回一個(gè)Promise對(duì)象
- Promise生命周期
每個(gè)promise都會(huì)經(jīng)歷一個(gè)短暫的生命周期: 先是進(jìn)行中(pending)的狀態(tài), 此時(shí)操作尚未完成, 所以它也是未處理(unsettled)的; 一旦異步操作執(zhí)行結(jié)束, Promise則變?yōu)橐烟幚?settled)狀態(tài)仰猖,已處理狀態(tài)包括fulfilled(已成功)和rejected(已失敗)兩種情況奈籽。只有異步操作的結(jié)果饥侵,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無(wú)法改變這個(gè)狀態(tài)衣屏。
一旦狀態(tài)改變躏升,就不會(huì)再變,任何時(shí)候都可以得到這個(gè)結(jié)果狼忱。Promise對(duì)象的狀態(tài)改變膨疏,只有兩種可能:從pending變?yōu)閒ulfilled和從pending變?yōu)閞ejected。只要這兩種情況發(fā)生钻弄,狀態(tài)就凝固了佃却,不會(huì)再變了,會(huì)一直保持這個(gè)結(jié)果窘俺,這時(shí)就變?yōu)槲覀兩厦嬲f(shuō)的已處理(settled)狀態(tài)饲帅。如果改變已經(jīng)發(fā)生了,你再對(duì)Promise對(duì)象添加回調(diào)函數(shù),也會(huì)立即得到這個(gè)結(jié)果灶泵。這與事件(Event)完全不同育八,事件的特點(diǎn)是,如果你錯(cuò)過(guò)了它赦邻,再去監(jiān)聽髓棋,是得不到結(jié)果的。
- 基本用法
Promise對(duì)象是一個(gè)構(gòu)造函數(shù)深纲,用來(lái)生成Promise實(shí)例仲锄。
下面代碼創(chuàng)造了一個(gè)Promise實(shí)例。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise構(gòu)造函數(shù)接受一個(gè)函數(shù)作為參數(shù)湃鹊,該函數(shù)的兩個(gè)參數(shù)分別是resolve
和reject
儒喊。它們是兩個(gè)函數(shù),由 JavaScript 引擎提供币呵,不用自己部署怀愧。
resolve函數(shù)的作用是,將Promise對(duì)象的狀態(tài)從“未完成”變?yōu)椤俺晒Α保磸?pending 變?yōu)?resolved)余赢,在異步操作成功時(shí)調(diào)用芯义,并將異步操作的結(jié)果,作為參數(shù)傳遞出去妻柒;
reject函數(shù)的作用是扛拨,將Promise對(duì)象的狀態(tài)從“未完成”變?yōu)椤笆 保磸?pending 變?yōu)?rejected),在異步操作失敗時(shí)調(diào)用举塔,并將異步操作報(bào)出的錯(cuò)誤绑警,作為參數(shù)傳遞出去。
Promise實(shí)例生成以后央渣,可以用then方法分別指定resolved狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)计盒。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
then方法可以接受兩個(gè)回調(diào)函數(shù)作為參數(shù)。第一個(gè)回調(diào)函數(shù)是Promise對(duì)象的狀態(tài)變?yōu)閞esolved時(shí)調(diào)用芽丹,第二個(gè)回調(diào)函數(shù)是Promise對(duì)象的狀態(tài)變?yōu)閞ejected時(shí)調(diào)用北启。其中,第二個(gè)函數(shù)是可選的拔第,不一定要提供咕村。這兩個(gè)函數(shù)都接受Promise對(duì)象傳出的值作為參數(shù)。下面是一個(gè)promise實(shí)例
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
上面代碼中楼肪,Promise 新建后立即執(zhí)行培廓,所以首先輸出的是Promise。then方法指定的回調(diào)函數(shù)春叫,將在當(dāng)前腳本所有同步任務(wù)執(zhí)行完才會(huì)執(zhí)行,所以會(huì)先執(zhí)行Hi, resolved最后輸出。
this
this就是call的第一個(gè)參數(shù), 所謂參數(shù), 只有在調(diào)用的時(shí)候才能傳進(jìn)去, 因此this作為參數(shù), 只有在調(diào)用的時(shí)候才知道綁定對(duì)象
- 被當(dāng)做對(duì)象的方法調(diào)用
如果該函數(shù)是被當(dāng)做某一個(gè)對(duì)象的方法暂殖,那么該函數(shù)的this指向該對(duì)象
let obj = {
foo: function(){
console.log(this)
}
}
let bar = obj.foo;
obj.foo(); //this是obj
bar(); //this是window
var john = {
firstName: "John"
}
function func() {
alert(this.firstName + ": hi!")
}
john.sayHi = func
john.sayHi() // this = john
這里有一點(diǎn)值得注意价匠,當(dāng)一個(gè)對(duì)象的方法被取出來(lái)賦值給一個(gè)變量時(shí),該方法變?yōu)楹瘮?shù)觸發(fā)呛每,this指向window或underfind(嚴(yán)格模式)踩窖。
- 函數(shù)之內(nèi)調(diào)用
當(dāng)函數(shù)中有 this,其實(shí)就意味著它被當(dāng)做方法調(diào)用晨横,之間調(diào)用相當(dāng)于把他當(dāng)做window對(duì)象的方法洋腮,this指向window,值得注意的是ES5其實(shí)是規(guī)定這種情況this=undefined的手形,只瀏覽器大多還是按照老的方法執(zhí)行(本人在最新版的Chrome啥供,Safari,F(xiàn)irefox中測(cè)試都指向window(201607)),在火狐下使用嚴(yán)格模式指向undefined库糠;
func()
function func() {
alert(this) // [object Window] or [object global] or kind of..
}
為了傳遞this伙狐,()之前應(yīng)該為引用類型,類似于obj.a 或者 obj['a'],不能是別的了瞬欧。
這里還存在一個(gè)小坑贷屎,當(dāng)對(duì)象的方法中還存在函數(shù)時(shí),該函數(shù)其實(shí)是當(dāng)做函數(shù)模式觸發(fā)艘虎,所以其this默認(rèn)為window(嚴(yán)格模式下為undefined)解決辦法是給該函數(shù)綁定this唉侄。
var numbers = {
numberA: 5,
numberB: 10,
sum: function() {
console.log(this === numbers); // => true
function calculate() {
// this is window or undefined in strict mode
console.log(this === numbers); // => false
return this.numberA + this.numberB;
}
return calculate();
}
};
numbers.sum(); // => NaN or throws TypeError in strict mode
var numbers = {
numberA: 5,
numberB: 10,
sum: function() {
console.log(this === numbers); // => true
function calculate() {
console.log(this === numbers); // => true
return this.numberA + this.numberB;
}
// use .call() method to modify the context
return calculate.call(this);
}
};
numbers.sum(); // => 15
- 在new中調(diào)用
一個(gè)引用對(duì)象的變量實(shí)際上保存了對(duì)該對(duì)象的引用,也就是說(shuō)變量實(shí)際保存的是對(duì)真實(shí)數(shù)據(jù)的一個(gè)指針野建。使用new關(guān)鍵字時(shí)this的改變其實(shí)有以下幾步:
- 創(chuàng)建 this = {}.
- new執(zhí)行的過(guò)程中可能改變this属划,然后添加屬性和方法;
- 返回被改變的this.
function Animal(name) {
this.name = name
this.canWalk = true
}
var animal = new Animal("beastie")
alert(animal.name)
需要注意的是如果構(gòu)造函數(shù)返回一個(gè)對(duì)象贬墩,那么this指向返回的那個(gè)對(duì)象榴嗅;
function Animal() {
this.name = 'Mousie';
this.age = '18';
return {
name: 'Godzilla'
} // <-- will be returned
}
var animal = new Animal()
console.log(animal.name) // Godzilla
console.log(animal.age)//undefined
這里需要注意的是不要忘記使用new,否則不會(huì)創(chuàng)建一個(gè)新的函數(shù)陶舞。而是只是執(zhí)行了函數(shù)嗽测,相當(dāng)于函數(shù)調(diào)用,this其實(shí)指向window
function Vehicle(type, wheelsCount) {
this.type = type;
this.wheelsCount = wheelsCount;
return this;
}
// Function invocation
var car = Vehicle('Car', 4);
car.type; // => 'Car'
car.wheelsCount // => 4
car === window // => true
- 明確調(diào)用this肿孵,使用call和apply
第一個(gè)參數(shù)將作為this的指代對(duì)象唠粥,之后的參數(shù)將被作為函數(shù)的參數(shù),解決方法是使用bind停做。
function Animal(type, legs) {
this.type = type;
this.legs = legs;
this.logInfo = function() {
console.log(this === myCat); // => true
console.log('The ' + this.type + ' has ' + this.legs + ' legs');
};
}
var myCat = new Animal('Cat', 4);
// logs "The Cat has 4 legs"
setTimeout(myCat.logInfo.bind(myCat), 1000);
// setTimeout??
var john = {
firstName: "John",
surname: "Smith"
};
function func(a, b) {
alert( this[a] + ' ' + this[b] );
}
func.call(john, 'firstName', 'surname'); // "John Smith"
至于apply晤愧,其只是以數(shù)組的方傳入?yún)?shù),其它部分是一樣的蛉腌,如下:
func.call(john, 'firstName', 'surname');
func.apply(john, ['firstName', 'surname']);
它們也可用于在 ES5 中的類繼承中官份,調(diào)用父級(jí)構(gòu)造器浑塞。
function Runner(name) {
console.log(this instanceof Rabbit); // => true
this.name = name;
}
function Rabbit(name, countLegs) {
console.log(this instanceof Rabbit); // => true
// 間接調(diào)用,調(diào)用了父級(jí)構(gòu)造器
Runner.call(this, name);
this.countLegs = countLegs;
}
var myRabbit = new Rabbit('White Rabbit', 4);
myRabbit; // { name: 'White Rabbit', countLegs: 4 }
- bind()
對(duì)比方法 .apply() 和 .call()趴捅,它倆都立即執(zhí)行了函數(shù)兵钮,而 .bind() 函數(shù)返回了一個(gè)新方法,綁定了預(yù)先指定好的 this 钠右,并可以延后調(diào)用赋元。.bind() 方法的作用是創(chuàng)建一個(gè)新的函數(shù),執(zhí)行時(shí)的上下文環(huán)境為 .bind() 傳遞的第一個(gè)參數(shù)飒房,它允許創(chuàng)建預(yù)先設(shè)置好 this 的函數(shù)搁凸。
var numbers = {
array: [3, 5, 10],
getNumbers: function() {
return this.array;
}
};
// Create a bound function
var boundGetNumbers = numbers.getNumbers.bind(numbers);
boundGetNumbers(); // => [3, 5, 10]
// Extract method from object
var simpleGetNumbers = numbers.getNumbers;
simpleGetNumbers(); // => undefined or throws an error in strict mode
使用.bind()時(shí)應(yīng)該注意,.bind() 創(chuàng)建了一個(gè)永恒的上下文鏈并不可修改狠毯。一個(gè)綁定函數(shù)即使使用 .call() 或者 .apply()傳入其他不同的上下文環(huán)境护糖,也不會(huì)更改它之前連接的上下文環(huán)境,重新綁定也不會(huì)起任何作用垃你。只有在構(gòu)造器調(diào)用時(shí)椅文,綁定函數(shù)可以改變上下文,然而這并不是特別推薦的做法惜颇。
- 箭頭函數(shù)
箭頭函數(shù)并不創(chuàng)建它自身執(zhí)行的上下文皆刺,使得 this 取決于它在定義時(shí)的外部函數(shù)。箭頭函數(shù)一次綁定上下文后便不可更改凌摄,即使使用了上下文更改的方法:
var numbers = [1, 2];
(function() {
var get = () => {
console.log(this === numbers); // => true
return this;
};
console.log(this === numbers); // => true
get(); // => [1, 2]
// 箭頭函數(shù)使用 .apply() 和 .call()
get.call([0]); // => [1, 2]
get.apply([0]); // => [1, 2]
// Bind
get.bind([0])(); // => [1, 2]
}).call(numbers);
這是因?yàn)榧^函數(shù)擁有靜態(tài)的上下文環(huán)境羡蛾,不會(huì)因?yàn)椴煌恼{(diào)用而改變。因此不要使用箭頭函數(shù)定義方法
function Period (hours, minutes) {
this.hours = hours;
this.minutes = minutes;
}
Period.prototype.format = () => {
console.log(this === window); // => true
return this.hours + ' hours and ' + this.minutes + ' minutes';
};
var walkPeriod = new Period(2, 30);
walkPeriod.format(); // => 'undefined hours and undefined minutes'