JS基礎(chǔ)系列(X): ES6入門

字符串?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, has

  • set的遍歷

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ù)分別是resolvereject儒喊。它們是兩個(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í)有以下幾步:

  1. 創(chuàng)建 this = {}.
  2. new執(zhí)行的過(guò)程中可能改變this属划,然后添加屬性和方法;
  3. 返回被改變的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'
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锨亏,一起剝皮案震驚了整個(gè)濱河市痴怨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌器予,老刑警劉巖浪藻,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異乾翔,居然都是意外死亡爱葵,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門反浓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)萌丈,“玉大人,你說(shuō)我怎么就攤上這事雷则×疚恚” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵月劈,是天一觀的道長(zhǎng)度迂。 經(jīng)常有香客問(wèn)我藤乙,道長(zhǎng),這世上最難降的妖魔是什么英岭? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任湾盒,我火速辦了婚禮湿右,結(jié)果婚禮上诅妹,老公的妹妹穿的比我還像新娘。我一直安慰自己毅人,他們只是感情好吭狡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著丈莺,像睡著了一般划煮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缔俄,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天弛秋,我揣著相機(jī)與錄音,去河邊找鬼俐载。 笑死蟹略,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的遏佣。 我是一名探鬼主播挖炬,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼状婶!你這毒婦竟也來(lái)了意敛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤膛虫,失蹤者是張志新(化名)和其女友劉穎草姻,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稍刀,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡撩独,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掉丽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跌榔。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖捶障,靈堂內(nèi)的尸體忽然破棺而出僧须,到底是詐尸還是另有隱情,我是刑警寧澤项炼,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布担平,位于F島的核電站示绊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏暂论。R本人自食惡果不足惜面褐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望取胎。 院中可真熱鬧展哭,春花似錦、人聲如沸闻蛀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)觉痛。三九已至役衡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間薪棒,已是汗流浹背手蝎。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留俐芯,地道東北人棵介。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像泼各,于是被迫代替她去往敵國(guó)和親鞍时。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 你不知道JS:異步 第三章:Promises 接上篇3-1 錯(cuò)誤處理(Error Handling) 在異步編程中...
    purple_force閱讀 1,398評(píng)論 0 2
  • 你不知道JS:異步 第三章:Promises 在第二章扣蜻,我們指出了采用回調(diào)來(lái)表達(dá)異步和管理并發(fā)時(shí)的兩種主要不足:缺...
    purple_force閱讀 2,067評(píng)論 0 4
  • 異步編程對(duì)JavaScript語(yǔ)言太重要逆巍。Javascript語(yǔ)言的執(zhí)行環(huán)境是“單線程”的,如果沒(méi)有異步編程莽使,根本...
    呼呼哥閱讀 7,311評(píng)論 5 22
  • 弄懂js異步 講異步之前锐极,我們必須掌握一個(gè)基礎(chǔ)知識(shí)-event-loop。 我們知道JavaScript的一大特點(diǎn)...
    DCbryant閱讀 2,711評(píng)論 0 5
  • 這是一個(gè)簡(jiǎn)單的故事芳肌,《親愛(ài)的陌生人》名字就頗有浪漫電影的邂逅意味灵再,無(wú)論它想講述什么,應(yīng)該總歸是個(gè)溫暖的故事:主人公...
    普賽閱讀 1,043評(píng)論 0 4