国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

50個知識點由淺入深掌握J(rèn)avascript

這篇具有很好參考價值的文章主要介紹了50個知識點由淺入深掌握J(rèn)avascript。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

前言

近期整理了JavaScript知識體系,50個知識點由淺入深掌握J(rèn)s建議收藏,如有問題,歡迎指正。

1. 說說你對JS的理解

1995年,布萊登·艾奇(美國人)在網(wǎng)景公司,用10天寫的一門語言。
Js是一門:動態(tài)的,弱類型的,解釋型的,基于對象的腳本語言,同時Js又是單線程的。

  • 動態(tài)類型語言:
    代碼在執(zhí)行過程中,才知道這個變量屬于的類型。
  • 弱類型:聲明變量一般用var,數(shù)據(jù)類型不固定,可以隨時改變。可以將字符串’12’和整數(shù)3進(jìn)行連接得到字符串’123’,在相加的時候會進(jìn)行強制類型轉(zhuǎn)換。
  • 解釋型:一邊執(zhí)行,一邊編譯,不需要程序在運行之前需要整體先編譯。
  • 基于對象:最終所有對象都指向Object。
  • 腳本語言 :一般都是可以嵌在其它編程語言當(dāng)中執(zhí)行。
  • 單線程:依次執(zhí)行,前面代碼執(zhí)行完后面才執(zhí)行。

組成部分:

ECMAscript DOM BOM
JavaScript的語法部分 文檔對象模型 瀏覽器對象模型
主要包含JavaScript語言語法 主要用來操作頁面元素和樣式 主要用來操作瀏覽器相關(guān)功能

2. JS數(shù)據(jù)類型有哪些?值是如何存儲的?

Js中一共有8種數(shù)據(jù)類型:7個基本數(shù)據(jù)類型和1個對象。

基本數(shù)據(jù)類型:

  • Number
  • String
  • Boolean
  • undefined
  • null
  • Symbol(ES6新增,表示獨一無二的值)
  • BigInt(ES6新增,以n結(jié)尾,表示超長數(shù)據(jù))

對象:

  • Object
  • function
  • Array
  • Date
  • RegExp

基本數(shù)據(jù)類型值是不可變的,多次賦值,只取最后一個。

var name = 'DarkHorse';
name.toUpperCase(); // 'DARKHOURSE'
console.log(name); //  'DarkHorse'

基本數(shù)據(jù)類型存儲在中,占據(jù)空間小、屬于被頻繁使用數(shù)據(jù)。
對象值是可變的,可以擁有屬性和方法,并且是可以動態(tài)改變的。

var a={name:'DarkHorse'};
a.name='xiaohong';
console.log(a.name) //xiaohong

引用數(shù)據(jù)類型存儲在堆中。引用數(shù)據(jù)類型占據(jù)空間大,如果存儲在棧中,將會影響程序運行的性能。

引用數(shù)據(jù)類型在棧中存儲了指針,該指針指向堆中該實體的起始地址。當(dāng)解釋器尋找引用值時,會首先檢索其在中的地址,取得地址后從中獲得實體。

50個知識點由淺入深掌握J(rèn)avascript

3. Undefined 與 undeclared 的區(qū)別?

變量聲明未賦值,是 undefined。

未聲明的變量,是 undeclared。瀏覽器會報錯a is not defined ,ReferenceError。

4. Null和undefined的區(qū)別

nullundefined 都是基本數(shù)據(jù)類型,這兩個數(shù)據(jù)類型只有一個值,nullundefined。

null表示空的,什么都沒有,不存在的對象,他的數(shù)據(jù)類型是object。
初始值賦值為null,表示將要賦值為對象,
不再使用的值設(shè)為null,瀏覽器會自動回收。

undefined表示未定義,常見的為undefined情況:
一是變量聲明未賦值,
二是數(shù)組聲明未賦值;
三是函數(shù)執(zhí)行但沒有明確的返回值;
四是獲取一個對象上不存在的屬性或方法。

5. JS數(shù)據(jù)類型轉(zhuǎn)換

JS的顯式數(shù)據(jù)類型轉(zhuǎn)換一共有三種:

(1)第一種是:轉(zhuǎn)字符串。有.toString()方法和String()函數(shù),Sting()函數(shù)相比于toString()函數(shù)適用范圍更廣,可以將nullundefined轉(zhuǎn)化為字符串,toString()轉(zhuǎn)化會報錯。

(2)第二種是:轉(zhuǎn)數(shù)值。可以用Number()函數(shù)轉(zhuǎn)數(shù)值,.parseInt轉(zhuǎn)整數(shù),parseFloat函數(shù)轉(zhuǎn)小數(shù)。

Number()函數(shù)適用于所有類型的轉(zhuǎn)換,比較嚴(yán)格,字符串合法數(shù)字則轉(zhuǎn)化成數(shù)字,不合法則轉(zhuǎn)化為NAN;空串轉(zhuǎn)化為0,nullundefined轉(zhuǎn)0和NAN;ture轉(zhuǎn)1,false轉(zhuǎn)0。

parseInt()是從左向右獲取一個字符串的合法整數(shù)位,parseFloat()獲取字符串的所有合法小數(shù)位。

(3)第三種是:轉(zhuǎn)布爾。像false、0、空串、null、undefinedNaN這6種會轉(zhuǎn)化為false。

常用的隱式類型轉(zhuǎn)換有:任意值+空串轉(zhuǎn)字符串、+a轉(zhuǎn)數(shù)值、a-0 轉(zhuǎn)數(shù)值等。

var a = 1 + 2 + '3';
// 123
// 任何值和字符串做加法運算,都會先轉(zhuǎn)換為字符串然后進(jìn)行拼串

var a = 10 - '5'
// 5
// 如果對非數(shù)字的值進(jìn)行算數(shù)運算,JS解析器會將值轉(zhuǎn)化為數(shù)值再運算

// 總結(jié):字符相連,數(shù)值相加(- * /)

6. 數(shù)據(jù)類型的判斷

(1)基本類型的判斷——typeof

typeof的返回值有六種,返回值是字符串,不能判斷數(shù)組和null的數(shù)據(jù)類型,返回object。

typeof ''; // string 有效
typeof 1; // number 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof new Function(); // function 有效

typeof null; //object 無效   這個是一個設(shè)計缺陷,造成的
typeof [] ; //object 無效

(2)引用數(shù)據(jù)類型判斷 —— instanceof

檢查對象原型鏈上有沒有該構(gòu)造函數(shù),可以精準(zhǔn)判斷引用數(shù)據(jù)類型,不能判斷基本數(shù)據(jù)類型。

[] instanceof Array; //true
{} instanceof Object;//true
new Date() instanceof Date;//true
new RegExp() instanceof RegExp//true

var arr = [1, 2, 3];
console.log(arr instanceof Array) // true
console.log(arr instanceof Object);  // true

function fn(){}
console.log(fn instanceof Function)// true
console.log(fn instanceof Object)// true

(3)類似instanceof of —— constructor

每一個對象實例都可以通過 constrcutor 對象來訪問它的構(gòu)造函數(shù)。既可以檢測基本類型又可以檢測對象,但不能檢測nullundefined

console.log((10).constructor===Number);//true
console.log([].constructor===Array);//true
var reg=/^$/;
console.log(reg.constructor===RegExp);//true
console.log(reg.constructor===Object);//false 

需要注意的一點是函數(shù)的 constructor 是不穩(wěn)定,如果把函數(shù)的原型進(jìn)行重寫,這樣檢測出來的結(jié)果會不準(zhǔn)確。

function Fn(){}
Fn.prototype = new Array()
var f = new Fn
console.log(f.constructor)//Array

(4)最準(zhǔn)確方式 —— Object.prototype.toString.call()

獲取Object原型上的toString方法,讓方法執(zhí)行,讓toString方法中的this指向第一個參數(shù)的值,最準(zhǔn)確方式。

第一個object:當(dāng)前實例是對象數(shù)據(jù)類型的(object),
第二個Object數(shù)據(jù)類型。

Object.prototype.toString.call('') ;   // [object String]
Object.prototype.toString.call(1) ;    // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]

7. a++ 和 ++a 區(qū)別

無論是前++還是后++,都會使原來變量立刻自增1。
不同在于a++是原值,++a是新值。

var a = 4;
console.log(a++); //4 原值

var b = 4; 
console.log(++b) //5 新值

8. 0.1+0.2 === 0.3嗎

在開發(fā)過程中遇到類似這樣的問題:

let n1 = 0.1, n2 = 0.2
console.log(n1 + n2)  // 0.30000000000000004

這里得到的不是想要的結(jié)果,要想等于0.3,就要把它進(jìn)行轉(zhuǎn)化:

(n1 + n2).toFixed(2) // 注意,toFixed為四舍五入

toFixed(num) 方法可把 Number 四舍五入為指定小數(shù)位數(shù)的數(shù)字。那為什么會出現(xiàn)這樣的結(jié)果呢?

toFixed(num) 方法可把 Number 四舍五入為指定小數(shù)位數(shù)的數(shù)字。那為什么會出現(xiàn)這樣的結(jié)果呢?

計算機是通過二進(jìn)制的方式存儲數(shù)據(jù)的,所以計算機計算0.1+0.2的時候,實際上是計算的兩個數(shù)的二進(jìn)制的和。0.1的二進(jìn)制是0.0001100110011001100...(1100循環(huán)),0.2的二進(jìn)制是:0.00110011001100...(1100循環(huán)),這兩個數(shù)的二進(jìn)制都是無限循環(huán)的數(shù)。那JavaScript是如何處理無限循環(huán)的二進(jìn)制小數(shù)呢?

一般我們認(rèn)為數(shù)字包括整數(shù)和小數(shù),但是在 JavaScript 中只有一種數(shù)字類型:Number,它的實現(xiàn)遵循IEEE 754標(biāo)準(zhǔn),使用64位固定長度來表示,也就是標(biāo)準(zhǔn)的double雙精度浮點數(shù)。在二進(jìn)制科學(xué)表示法中,雙精度浮點數(shù)的小數(shù)部分最多只能保留52位,再加上前面的1,其實就是保留53位有效數(shù)字,剩余的需要舍去,遵從“0舍1入”的原則。

根據(jù)這個原則,0.1和0.2的二進(jìn)制數(shù)相加,再轉(zhuǎn)化為十進(jìn)制數(shù)就是:0.30000000000000004。

9. JS的作用域和作用域鏈

作用域就變量起作用的范圍和區(qū)域。 作用域的目的是隔離變量,保證不同作用域下同名變量不會沖突。

JS中,作用域分為三種,全局作用域、函數(shù)作用域和塊級作用域。 全局作用域在script標(biāo)簽對中,無論在哪都能訪問到。在函數(shù)內(nèi)部定義的變量,擁有函數(shù)作用域。塊級作用域則是使用letconst聲明的變量,如果被一個大括號括住,那么這個大括號括住的變量區(qū)域就形成了一個塊級作用域。

作用域?qū)訉忧短?,形成的關(guān)系叫做作用域鏈,作用域鏈也就是查找變量的過程。 查找變量的過程:當(dāng)前作用域 --》上一級作用域 --》上一級作用域 … --》直到找到全局作用域 --》還沒有,則會報錯。

作用域鏈?zhǔn)怯脕肀WC——變量和函數(shù)在執(zhí)行環(huán)境中有序訪問。

10. LHS和RHS查詢

LHS和RHS查詢是JS引擎查找變量的兩種方式,這里的“Left”和“Right”,是相對于賦值操作來說,當(dāng)變量出現(xiàn)在賦值操作左側(cè)時,執(zhí)行LHS操作。

LHS 意味著 變量賦值寫入內(nèi)存,,他強調(diào)是寫入這個動作。

var name = '小明'

當(dāng)變量出現(xiàn)在賦值操作右側(cè)或沒有賦值操作時,是RHS。

var Myname = name
console.log(name)

RHS意味著 變量查找讀取內(nèi)存,它強調(diào)的是這個動作。

11. 詞法作用域和動態(tài)作用域

Js底層遵循的是詞法作用域,從語言的層面來說,作用域模型分兩種:

詞法作用域:也稱靜態(tài)作用域,是最為普遍的一種作用域模型

動態(tài)作用域:相對“冷門”,bash腳本、Perl等語言采納的是動態(tài)作用域

詞法作用域:在代碼書寫時完成劃分,作用域沿著它定義的位置往外延伸。
動態(tài)作用域:在代碼運行時完成劃分,作用域鏈沿著他的調(diào)用棧往外延伸。

12. 什么是匿名函數(shù),有什么作用

匿名函數(shù)也叫一次性函數(shù),沒用名字,且在定義時執(zhí)行,且執(zhí)行一次,不存在預(yù)解析(函數(shù)內(nèi)部執(zhí)行的時候會發(fā)生)。

匿名函數(shù)的基本形式為:

(function(){
  ...
}());

除此之外,還有常見的以下寫法

(function(){
  console.log('我是一個匿名函數(shù)')
})();

var a = function(){
  console.log('我是一個匿名函數(shù)')  
}()

//  使用多種運算符開頭,一般是用!
!function(){
  console.log('我是一個匿名函數(shù)')
}();

匿名函數(shù)的作用有:

(1)對項目的初始化,頁面加載時調(diào)用,保證頁面有效的寫入Js,不會造成全局變量污染
(2)防止外部命名空間污染
(3)隱藏內(nèi)部代碼暴露接口

13. 什么是回調(diào)函數(shù),常見的回調(diào)函數(shù)有哪些

回調(diào)函數(shù)是一段可執(zhí)行的代碼段,它作為一個參數(shù)傳遞給其他的代碼,其作用是在需要的時候方便調(diào)用這段(回調(diào)函數(shù))。

在Js中函數(shù)也是對象的一種,同樣對象可以作為參數(shù)傳遞給函數(shù),因此函數(shù)也可以作為參數(shù)傳遞給另一個函數(shù),這個做為參數(shù)的函數(shù)就是回調(diào)函數(shù)。

簡單來說:回調(diào)函數(shù)即別人調(diào)用了這個函數(shù),即函數(shù)作為參數(shù)傳入另一個函數(shù)。

一般來說回調(diào)函數(shù)滿足三個條件:自己定義的函數(shù)、自己沒有調(diào)用,函數(shù)最終執(zhí)行了。

在開發(fā)中經(jīng)??吹降幕卣{(diào)函數(shù)有:

// 點擊事件的回調(diào)函數(shù)
$('#btn').click(function(){
  console.log('click btn');
})

// 異步請求的回調(diào)函數(shù)
$.get('ajax/test.html',function(data){
  $('#box').html(data);
})

// 計時器
var timeId = setTimeout(function{
  console.log('hello')                 
},1000)

14. 什么是構(gòu)造函數(shù),與普通函數(shù)的區(qū)別

在ES6之前,我們都是通過構(gòu)造函數(shù)創(chuàng)建類,從而生成對象實例。
構(gòu)造函數(shù)就是一個函數(shù),只不過通常我們把構(gòu)造函數(shù)的名字寫成大駝峰, 構(gòu)造函數(shù)和普通函數(shù)的區(qū)別,構(gòu)造函數(shù)通過new關(guān)鍵字進(jìn)行調(diào)用,而普通函數(shù)直接調(diào)用。

// 創(chuàng)建一個類(函數(shù))
function Person(name,age){
   this.name = name;
   this.age = age;
   this.eat = function(){
      console.log('我愛吃');
   }
}
// 普通函數(shù)調(diào)用
var result = Person('張三',18);
console.log(result);

// 構(gòu)造函數(shù)調(diào)用            
var p1 = new Person('李四',16);
console.log(p1);

var c2 = new Person('王五',14);
console.log(p2);

15. 函數(shù)中arguments 的對象是什么

函數(shù)在調(diào)用時JS引擎會向函數(shù)中傳遞兩個的隱含參數(shù),一個是this(后面我們會說到),另一個就是argumentsarguments是一個偽數(shù)組,主要作用是:獲取函數(shù)中在調(diào)用時傳入的實參。

function add(){
    console.log(arguments);
    return arguments[0] + arguments[1];
}
add(10,20);

使用arguments.length可以獲取傳遞實參的個數(shù),同時也可以讓我們的函數(shù)具有多重功能。

function addOrSub(a,b,c){
   if(arguments.length == 2){
      return a - b;
   }else if(arguments.length == 3){
      return a + b + c;
   }
}
console.log(addOrSub(10,20));//傳遞兩個實參就做減法
console.log(addOrSub(10,20,30));//傳遞的是三個實參就做加法

16. 列舉常用字符串方法

方法名 功能 原字符串是否改變
charAt() 返回指定索引的字符 n
charCodeAt(0) 返回指定索引的字符編碼 n
concat() 將原字符串和指定字符串拼接,不指定相當(dāng)于復(fù)制一個字符串 n
String.fromCharCode() 返回指定編碼的字符 n
indexOf() 查詢并返回指定子串的索引,不存在返回-1 n
lastIndexOf() 反向查詢并返回指定子串的索引,不存在返回-1 n
localeCompare() 比較原串和指定字符串:原串大返回1,原串小返回-1,相等返回0 n
slice() 截取指定位置的字符串,并返回。包含起始位置但是不包含結(jié)束位置,位置可以是負(fù)數(shù) n
substr() 截取指定起始位置固定長度的字符串 n
substring() 截取指定位置的字符串,類似slice。起始位置和結(jié)束位置可以互換并且不能是負(fù)數(shù) n
split() 將字符串切割轉(zhuǎn)化為數(shù)組返回 n
toLowerCase() 將字符串轉(zhuǎn)化為小寫 n
toUpperCase() 將字符串轉(zhuǎn)化為大寫 n
valueOf() 返回字符串包裝對象的原始值 n
toString() 直接轉(zhuǎn)為字符串并返回 n
includes() 判斷是否包含指定的字符串 n
startsWith() 判斷是否以指定字符串開頭 n
endsWith() 判斷是否以指定字符串結(jié)尾 n
repeat() 重復(fù)指定次數(shù) n

17. 列舉常用數(shù)組方法

方法名 功能 原數(shù)組是否改變
concat() 合并數(shù)組,并返回合并之后的數(shù)據(jù) n
join() 使用分隔符,將數(shù)組轉(zhuǎn)為字符串并返回 n
pop() 刪除最后一位,并返回刪除的數(shù)據(jù),在原數(shù)組 y
shift() 刪除第一位,并返回刪除的數(shù)據(jù),在原數(shù)組 y
unshift() 在第一位新增一或多個數(shù)據(jù),返回長度,在原數(shù)組 y
push() 在最后一位新增一或多個數(shù)據(jù),返回長度 y
reverse() 反轉(zhuǎn)數(shù)組,返回結(jié)果,在原數(shù)組 y
slice() 截取指定位置的數(shù)組,并返回 n
sort() 排序(字符規(guī)則),返回結(jié)果,在原數(shù)組 y
splice() 刪除指定位置,并替換,返回刪除的數(shù)據(jù) y
toString() 直接轉(zhuǎn)為字符串,并返回 n
valueOf() 返回數(shù)組對象的原始值 n
indexOf() 查詢并返回數(shù)據(jù)的索引 n
lastIndexOf() 反向查詢并返回數(shù)據(jù)的索引 n
forEach() 參數(shù)為回調(diào)函數(shù),會遍歷數(shù)組所有的項,回調(diào)函數(shù)接受三個參數(shù),分別為value,index,self;forEach沒有返回值 n
map() 同forEach,同時回調(diào)函數(shù)返回數(shù)據(jù),組成新數(shù)組由map返回 n
filter() 同forEach,同時回調(diào)函數(shù)返回布爾值,為true的數(shù)據(jù)組成新數(shù)組由filter返回 n
Array.from() 將偽數(shù)組對象或可遍歷對象轉(zhuǎn)換為真數(shù)組 n
Array.of() 將一系列值轉(zhuǎn)換成數(shù)組 n
find 找出第一個滿足條件返回true的元素 n
findIndex 找出第一個滿足條件返回true的元素下標(biāo) n

注意:重點關(guān)注方法的:功能、參數(shù)、返回值

18. 什么是DOM和BOM

DOM:文檔對象模型,將文檔看做是一個對象,這個對象主要定義了處理網(wǎng)頁內(nèi)容的方法和接口,通過JS操作頁面元素

BOM:瀏覽器對象模型,將瀏覽器看做是一個對象,定義了與瀏覽器進(jìn)行交互的方法和接口,通過JS操作瀏覽器。

BOM的核心是window,window 對象子有 location navigator history

DOM的最根本的document對象也是window 對象的子對象。

50個知識點由淺入深掌握J(rèn)avascript

window對象

  • window對象是BOM的頂級對象,稱作瀏覽器窗口對象
  • 全局變量會成為window對象的屬性
  • 全局函數(shù)會成為window對象的方法
- window.onload   
- window.onresize   
- window.onscroll

Location對象

  • 提供了url相關(guān)的屬性和方法。一些常用的有:
// url相關(guān)屬性
location.href
// 返回當(dāng)前加載頁面的完整URL
location.protocal
// 返回頁面使用的協(xié)議
location.search
// 返回URL的查詢字符串,查詢?開頭的的字符串

location.reload();
// reload():實現(xiàn)的是頁面刷新
location.assign("https://www.baidu.com");
// assign():可以打開新的頁面,并且可以返回,可以產(chǎn)生歷史紀(jì)錄的
location.replace("https://www.baidu.com");
// replace():用新文檔替換當(dāng)前的文檔,但不能返回,沒有產(chǎn)生歷史記錄

history對象

  • 提供了對象包含瀏覽器的歷史記錄, 這些歷史記錄以棧的形式保存。頁面前進(jìn)則入棧,頁面返回則出棧。
history.back();//歷史記錄返回上一頁
history.forward();//去到下一頁
history.go(-2);//去到指定的歷史記錄頁  0代表當(dāng)前頁  -1代表之前   1代表之后

navigator對象

  • 提供了瀏覽器相關(guān)的信息,比如瀏覽器的名稱、版本、語言、系統(tǒng)平臺等信息。
console.log(window.navigator.appName);//Netscape  
console.log(window.navigator.appVersion);//瀏覽器版本
console.log(window.navigator.appCodeName);//瀏覽器內(nèi)核版本,但是打印出來一般

screen對象

  • 提供了用戶顯示屏幕的相關(guān)屬性,比如顯示屏幕的寬度、高度。
 console.log(window.screen.width);//屏幕的寬   分辨率
 console.log(window.screen.height);//屏幕的高  

19. DOM樹簡單描述一下

Html為根節(jié)點,形成的一棵倒立的樹狀結(jié)構(gòu),我們稱作DOM樹。這個樹上所有的東西都叫節(jié)點,節(jié)點有很多類(元素、屬性、文本),通過DOM方法去獲取或者去操作節(jié)點,就叫DOM對象。

50個知識點由淺入深掌握J(rèn)avascript

Document對象

指這份文件,也就是這份 HTML 檔的開端。當(dāng)瀏覽器載入 HTML 文檔, 它就會成為 Document 對象。

重繪:DOM元素的樣式發(fā)生改變,瀏覽器會重新渲染這個元素。

回流:DOM元素結(jié)構(gòu)或者位置發(fā)生改變(刪除、增加、改變位置大?。?,瀏覽器重新計算渲染整個DOM樹。

20. DOM操作

(1)查找節(jié)點

- getElementById  //按照 id 查詢
- getElementsByTagName  //按照標(biāo)簽名查詢
- getElementsByClassName  //按照類名查詢
- querySelectorAll  //按照css 選擇器查詢

(2)創(chuàng)建節(jié)點

document.write()
innerHTML
createElement()和appendChild()

(3)添加、移除、替換、插入

appendChild(node);  //插入節(jié)點 
removeChild(node);  //移除節(jié)點
replaceChild(new,old);  //替換節(jié)點
insertBefore(new,old)  //追加節(jié)點

(4)屬性操作

getAttribute(key);  //獲取自定義屬性
setAttribute(key, value); //設(shè)置自定義屬性
hasAttribute(key);  //是否存在該屬性
removeAttribute(key);  //移除屬性

(5)內(nèi)容修改

InnerText(); // 無標(biāo)簽效果
InnerHTML(); // 有標(biāo)簽效果
Text-content // IE9以上支持,類似innerText

21. 什么是事件傳播

當(dāng)事件發(fā)生在DOM對象上,該事件并不完全發(fā)生在這個元素上。

關(guān)于事件傳播,IE和網(wǎng)景公司有不同的理解,IE認(rèn)為,事件應(yīng)該是冒泡階段,網(wǎng)景公司認(rèn)為事件應(yīng)該是捕獲階段,隨后W3C綜合了兩個公司的方案,JS同時支持冒泡和捕獲流,并以此確定事件流標(biāo)準(zhǔn)。這個標(biāo)準(zhǔn)也叫DOM2事件流。

事件傳播的三個階段:

(1)事件捕獲

事件從window開始,從外向內(nèi),直到到達(dá)目標(biāo)事件或event.target

(1)目標(biāo)階段

事件到達(dá)目標(biāo)元素,觸發(fā)監(jiān)聽事件

(2)事件冒泡

事件從目標(biāo)元素開始冒泡,從內(nèi)向外,直到到達(dá)window。

50個知識點由淺入深掌握J(rèn)avascript

當(dāng)事件被觸發(fā)時,首先經(jīng)歷的是一個捕獲過程,事件會從最外層元素開始“穿梭”,逐層“穿梭”到最內(nèi)層元素。這個穿梭過程會持續(xù)到事件抵達(dá)他目標(biāo)的元素(也就是真正觸發(fā)這個事件的元素)為止。此時事件流接切換到了“目標(biāo)階段”——事件被目標(biāo)元素所接收然后事件會會彈,進(jìn)入到冒泡階段——他會沿著來時的路“逆流而上”,一層一層再走回去。

也就是說當(dāng)事件在層層DOM元素穿梭時,所到之處都會觸發(fā)事件處理函數(shù)。

23. 三種事件模型是什么

DOM0級事件模型

通過對象.onclick形式綁定,同一元素綁定多個相同事件,后會覆蓋前邊,事件不會傳播,不存在事件流概念。

<body>
 <div id="box"></div>
 <button>解綁</button>
<script>
 window.onload = function () {
     var box = document.querySelector('#box');
     var btn = document.querySelector('button');
     //dom0 綁定
     box.onclick = function () {
         console.log('我是dom0級事件1')
     };//我是dom0級事件1
      
     box.onclick = function () {
       console.log('我是dom0級事件2')
     };//我是dom0級事件
}				

解綁 事件類型 = null

btn.onclick = function () {
   box.onclick = null;
}

DOM2級事件模型

通過addEventListener綁定,三個參數(shù),不帶on的事件類型,回調(diào)函數(shù),事件階段,默認(rèn)是false,冒泡階段??梢越壎ǘ鄠€相同事件,事件從上到下執(zhí)行。this指向當(dāng)前綁定事件對象。

  box.addEventListener("click",function(){
    console.log('今天中午吃多了')
  },false)
 
  box.addEventListener('click',fun,false);   
    function fun() {
      console.log('晚上就不吃了')
 }

通過removeEventListener解綁,解綁參數(shù)與綁定參數(shù)一致,且事件需要解綁,那么函數(shù)必須定義成有名函數(shù)。

 box.onclick=function(){
     box.removeEventListener(fun)
  }

IE事件模型(低級瀏覽器)

通過attachEvent綁定,兩個參數(shù),帶on的事件類型,回調(diào)函數(shù)。this指向window。

box.attachEvent("onclick",function(){
  console.log("今天晚上又吃了")
})
	
box.attachEvent("onclick",fun);
  function fun(){
  console.log("傷心")
}

通過detachEvent解綁,解綁參數(shù)與綁定參數(shù)一致。

btn.onclick = function(){
   box.detachEvent("onclick",fun)
}

24. 事件對象有哪些常用屬性

當(dāng)DOM接受了一個事件,對應(yīng)的事件處理函數(shù)被觸發(fā)時,就會產(chǎn)生一個事件對象event作為事件處理函數(shù)的入?yún)?。這個對象中包含著與事件有關(guān)的信息,比如事件是由哪個元素觸發(fā)的,事件類型等。常用屬性有:

  • target

事件綁定的元素

  • currentTarget

觸發(fā)事件的元素,兩者沒有冒泡的情況下,是一樣的值,但在使用了事件委托的情況下,就不一樣了。

preventDafault

阻止默認(rèn)行為,比如阻止超鏈接跳轉(zhuǎn)、在form中按回車會自動提交表單。

e.preventDefault();

stopPropagation

阻止事件冒泡,將事件處理函數(shù)的影響控制在目標(biāo)元素范圍內(nèi)

e.stopPropagation();

阻止事件冒泡,需要注意一點的是:谷歌火狐的組織行為是:event.stopPropagation(),而IE:event.cancelBubble=true

25. 什么是事件委托

原理:

如果子元素有很多,且子元素的事件監(jiān)聽邏輯都相同,將事件監(jiān)聽綁定到父元素身上或者共有的祖先元素上 。事件委托原理是利用事件冒泡,子元素觸發(fā),父元素執(zhí)行回調(diào)函數(shù)。

好處:

(1)減少事件的綁定次數(shù)

(2)新增元素不需要單獨綁定

應(yīng)用

頁面上有多個li,點擊每一個元素,都輸出他的文本內(nèi)容。

<body>
  <ul id="poem">
    <li>鵝鵝鵝</li>
    <li>曲項向天歌</li>
    <li>白毛浮綠水</li>
    <li>紅掌撥清波</li>
    <li>鋤禾日當(dāng)午</li>
    <li>汗滴禾下土</li>
    <li>誰知盤中餐</li>
    <li>粒粒皆辛苦</li>
    <li>背不動了</li>
    <li>我背不動了</li>
  </ul>
</body>

一個直觀的思路是讓每一個li元素都去監(jiān)聽一個點擊動作,但是這樣子并不好,我們可以采用事件委托的方式實現(xiàn)。

var ul = document.getElementById('poem')
ul.addEventListener('click', function(e){
   console.log(e.target.innerHTML)
})

點擊任何一個li,點擊事件都會被冒泡到li共同的Ul上,我們通過Ul感知到這個冒泡來的事件,在通過e.target 拿到實際觸發(fā)事件的那個元素,通過事件委托只執(zhí)行一次DOM操作,減少了內(nèi)存開銷,大大提升了開發(fā)效率。

26. ECMAScript 是什么

ES是JS的標(biāo)準(zhǔn),約束條件。廣義的JS=ES+DOM+BOM,狹義的JS就是ES。EC是為了保證JS在瀏覽器運行結(jié)果一致。

是由歐洲計算機協(xié)會(ECMA)這個組織制定的。這個組織的目標(biāo)是制定和發(fā)布腳本語言規(guī)范。組織會定期定期召開會議,會議由一些公司的代表與特邀專家出席。

27. ECMAScript 2015(ES6)有哪些新特性?

在2011年ECMA組織就開始著手制作第6個版本規(guī)范,由于這個版本引入的功能語法太多,最終標(biāo)準(zhǔn)的制作者決定在每年6月份發(fā)布一次,版本號為年號代替,ES6正式發(fā)布于2015 年 6 月。我們現(xiàn)在所說的ES6是一個泛指,泛指ES2015之后的版本。

  • 塊級作用域
  • 對象數(shù)組解構(gòu)賦值
  • 模板字符串
  • 箭頭函數(shù)
  • 延展運算符
  • 剩余參數(shù)
  • 聲明類
  • set、map集合
  • Promise

28. Var,Let和Const的區(qū)別是什么

let 關(guān)鍵字用來聲明變量,使用let聲明的變量有以下特點:

不允許重復(fù)聲明

 let name = '張三'
 let name = '李四'
 console.log(name);
 // SyntaxError

 let num = 1;
     num = 2;
 console.log(num);//2
 // 可以重復(fù)賦值

不存在預(yù)解析

預(yù)解析:JS引擎在JS代碼正式執(zhí)行之前會做一些預(yù)解析的工作。

  • 先把var變量聲明提前
  • 再把以function開頭的整個函數(shù)提前
console.log(num)
//undefined
var num = 10

console.log(num)
let num = 10
// ReferenceError

具有塊級作用域

塊級作用域:使用let聲明變量,如果被一個大括號括住,那么這個大括號括住的變量就形成了一個塊級作用域。

塊級作用域定義的變量只在當(dāng)前塊中生效,這和函數(shù)作用域類似。

{
    let num = 10;
    console.log(num);
}

console.log(num); // 報錯

ES6中規(guī)定,let/const命令會使區(qū)塊形成封閉作用域,在聲明之前使用變量,就會報錯。這在語法上,稱為“暫時性死區(qū)

塊級作用域還有的一個好處:防止循環(huán)變量變成全局變量

for(var i=0;i<2;i++){
}    
console.log(i);
//2

for(let i=0;i<2;i++){
}    
console.log(i);
//i is not defined

const 關(guān)鍵字

let類似,不可重復(fù)聲明,不存在預(yù)解析,擁有塊級作用域。同時使用const聲明的變量值無法改變,常用于聲明常量,常量名一般為大寫,單詞間用下劃線。

const PI = 3.14
PI = 100
// Missing initializer in const declaration

必須要有初始值

const PI
console.log(PI)
// Missing initializer in const declaration

可以修改數(shù)組和對象元素

const obj = {}
obj.age = 10
console.log(obj.age)10

const arr = []
arr.push(10)
console.log(arr)//[10]

29. const對象的屬性可以修改嗎

const保證的并不是變量的值不能改動,而是指向那個內(nèi)存地址不能改動,對于基本數(shù)據(jù)類型(數(shù)值、字符串、布爾值),其值就保存在變量指向的那個內(nèi)存地址,因此等同于常量。

但對于引用數(shù)據(jù)類型(主要是對象和數(shù)組),變量指向數(shù)據(jù)的內(nèi)存地址,保存的只是一個指針,const只能保證這個指針是固定不變的,至于他指向的數(shù)據(jù)結(jié)構(gòu)是不是可變的,就不完全能控制了。

30. 什么是解構(gòu)賦值

在es6之前,獲取對象或者數(shù)組中數(shù)據(jù),只能通過屬性訪問的形式并賦值給本地變量,這樣需要寫許多相似的代碼。

  const obj = {
      name: '張三',
      age: 20,
    } 

const name = obj.name
const age = obj.age
console.log(name,age)// 張三 20

有了解構(gòu)賦值可以方便獲取數(shù)組中的數(shù)據(jù),獲取對象中屬性和方法。
我們可以直接從數(shù)組或?qū)ο笾刑崛?shù)據(jù),并賦值給變量。

數(shù)組解構(gòu)賦值

 let arr=[1,2,3];
 // 定義變量并接收
 let [s1,s2,s3]=arr;// 完全解構(gòu)
 let [s1,,s3]=arr; // 不完全解構(gòu)
 console.log(s1);// 1 

對象的解構(gòu)賦值

const obj = {
  name: '張三',
  age,
  sayHi: function () {
    console.log('你好')
  }
}
// 定義變量,對應(yīng)屬性,想要什么屬性就寫什么屬性
const { name, sayHi } = obj
console.log(name);// 張三 
sayHi();// 你好

// 為屬性取別名
const {name:defaultName,sayhi} = obj;
console.log(defaultName);

// 設(shè)置屬性默認(rèn)值
const {age=20} = obj;
console.log(age);//20

31. 什么是模板字符串

模板字符串是增強版的字符串,用反引號 表示,模板字符串可以當(dāng)普通字符串使用,也可以用來定義多行字符串,作用是簡化字符串的拼接。特點如下:

可以出現(xiàn)換行符

// 可以出現(xiàn)換行符
document.write(`
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<button>5</button>
`)

可以輸出變量

// 通過 ${} 形式輸出變量
const age = 100
const text = `今年過年,小明的年齡已經(jīng)是:${age}`;
console.log(text)

32. 箭頭函數(shù)和普通函數(shù)區(qū)別

ES6中允許使用箭頭 => 定義函數(shù),主要作用不僅僅是:簡化function寫法,更重要的是改變this指向。

基本用法

// es6以前寫法
const add =function(a,b){
    return a + b
}
const result = add(10,20)
console.log(result) //30

// es6寫法
const add =(a,b)=>{
  return a + b
}
const result = add(10,20)
console.log(result) //30

與普通函數(shù)區(qū)別

  • 不能作為構(gòu)造函數(shù)實例化
 const Person =()=>{
      name:'小明'
    };
 var per = new Person();
 console.log(per.name)
 // Person is not a constructor
  • 不能使用 arguments
 const f4 =(arguments)=>{
      console.log(arguments.length)
    }
f4(1,2,3,4,5)
// undefined
  • 箭頭函數(shù)的this是不能改變
window.name = '小明'
const f5 = () => {
  console.log(this.name) // 小明
};
const f6 = function () {
  console.log(this.name) // 小明
};
f5();
f6();

var obj = {
  name: '大明'
}
f5.call(obj) // 小明
f6.call(obj) // 大明
  • this指向包裹箭頭函數(shù)的第一個普通函數(shù)
let school = {
 name: '小明',
 getName(){
 let fn7 = () => {
 console.log(this); // 小明
 }
 fn7();
 }
};

33. 什么是剩余運算符

剩余運算符中最重要的特點就是代替以前的arguments,利用剩余運算符可以獲取函數(shù)調(diào)用時傳遞的參數(shù),并且返回值是一個真數(shù)組。

  function f1(...args) {
      console.log(args);
      // [1,2,3]
  }
  f2(1, 2, 3)
  // 形參較多,放在最后位置
  function f3(a, b, ...args) {
      console.log(a,b);//1,2
      console.log(args);// [3,4,5]
      //
  }
  f3(1, 2, 3, 4, 5)

34. 延展運算符使用過嗎

拆包和打包數(shù)組、對象

function f2(...args) {
    console.log(args)
}
f2(1, 2, 3, 4, 5);
// [1, 2, 3, 4, 5]

function f3(...args) {
    console.log(...args)
}
f3(1, 2, 3, 4, 5);
// 1 2 3 4 5

數(shù)組合并

var arr1=[10,20,30];
var arr2=[40,50,60];
var arr=[...arr1,...arr2];
console.log(arr);
// [10, 20, 30, 40, 50, 60]

對象合并

字面量復(fù)制對象 let obj={ } {…obj}

var obj1={ 
  name:'自來也',
  age:45
}

var obj2={
  gender:'男',
  hobby(){
      console.log(console.log('吃飯'))
  }
}

var obj={
  name:'菲兒',
  ...obj1,
  ...obj2
}

console.log(obj);
// {name: "自來也", age: 45, gender: "男", hobby: ?}

數(shù)組的克隆

const arr3 = [10, 20, 30]
const arr4 = [...arr3]
console.log(arr4)
// [10, 20, 30]

偽數(shù)組轉(zhuǎn)真數(shù)組

const arr5 = document.getElementsByTagName('button');
console.log(arr5);
//[button, button, button]

console.log(arr5 instanceof Array);//false;
console.log([...arr5] instanceof Array);//true
console.log(arr5);

35. 什么是類

類(class)是ES6中語法糖,最終還是轉(zhuǎn)化成構(gòu)造函數(shù)去執(zhí)行,使用class創(chuàng)建的類會將方法自動加到原型上。

 class Person{
    // 通過構(gòu)造函數(shù) -- 初始化實例化對象屬性
    constructor(name,age){
      this.name=name;
      this.age= age;
    }
    // 添加方法 不需要添加,
    eat(){
      console.log('哈哈')
    }
  }
  const per = new Person('小明',20);
  console.log(per.name);
  per.eat();

36. set集合和map集合了解多少

set

  • 是一個構(gòu)造函數(shù),用來存儲任意數(shù)據(jù)類型唯一值
  • 可以存儲數(shù)組、字符串,返回值是一個對象

定義Set集合

// 定義set集合
const s1 = new Set()
console.log(s1)
// { }
?
//打印長度
console.log(s1.size);// 0 

傳入數(shù)據(jù)

const s = new Set([10,20,30,40,40]);
?
console.log(s);
// {10, 20, 30, 40}
// 打印出來是一個集合 需要拆包
console.log(...s);
// 10 20 30 40

set方法

  // 添加數(shù)據(jù)
  const s = new Set();
  // 向Set集合中添加一個數(shù)據(jù)
  s.add('a').add('b');
  console.log(...s);
  // a b

  // 移除數(shù)據(jù)
  r1 = s.delete('a');
  console.log(r1);
  // 返回結(jié)果是ture值,代表刪除成功

  // 是否存在這個數(shù)據(jù)
  r2 = s.has(9);
  console.log(r2);//false

  // 清空數(shù)據(jù)
  r3 = s.clear()
  console.log(r2);// undefined

應(yīng)用

  • 數(shù)組去重
let arr1=[1,2,3,4,4,5,1];
// {1, 2, 3, 4, 5}  1,2,3,4,5  []
?
let arr2=[...new Set(arr1)];
console.log(arr2);
// [1, 2, 3, 4, 5]
  • 交集操作
const arr3 = [1, 2, 3, 4, 5, 6, 7];
const arr4 = [1, 2, 3, 10, 11];
//對數(shù)組進(jìn)行拆包  過濾 判斷4里面是否存在item
const result = [...new Set(arr3)].filter(item => new Set(arr4).has(item));
console.log(result);
  • 并集操作
const arr5 = [1, 2, 3, 4, 5];
const arr6 = [1, 2, 3, 6, 7, 8];
const result = [...new Set([...arr5,...arr6])]
console.log(result);
  • 差集操作(我有的你沒有,或者你有的我沒有)
const arr7 = [1, 2, 3, 4, 5];
const arr8 = [1, 2, 3, 8, 9];
?
// 判斷arr8中是否含有數(shù)組中每一項數(shù)據(jù)
const result=[...new Set(arr7)].filter(!(item=>new Set(arr8).has(item)));
const result=[...new Set(arr8)].filter()

map集合

類似于對象,存放鍵值對,鍵和值可以是任何數(shù)據(jù)類型。

  • 鍵值的方式添加數(shù)據(jù)
var m = new Map();
map.set('name', '強哥')
map.set(obj, function(){console.log('真好')})
  • 讀取 刪除 判斷 清空
// 根據(jù)鍵獲取值
console.log(map.get(obj))
// 根據(jù)鍵進(jìn)行刪除
map.delete('name')
// 根據(jù)鍵進(jìn)行判斷
console.log(map.has('name'))
// 清空map
map.clear()

37. Proxy 可以實現(xiàn)什么功能

在 Vue3.0 中通過 Proxy 來替換原本的 Object.defineProperty 來實現(xiàn)數(shù)據(jù)響應(yīng)式。

Proxy 是 ES6 中新增的功能,它可以用來自定義對象中的操作。

let p = new Proxy(target, handler)

target 代表需要添加代理的對象,handler 用來自定義對象中的操作,比如可以用來自定義 set 或者 get 函數(shù)。

下面來通過 Proxy 來實現(xiàn)一個數(shù)據(jù)響應(yīng)式:

let onWatch = (obj, setBind, getLogger) => {
  let handler = {
    get(target, property, receiver) {
      getLogger(target, property)
      return Reflect.get(target, property, receiver)
    },
    set(target, property, value, receiver) {
      setBind(value, property)
      return Reflect.set(target, property, value)
    }
  }
  return new Proxy(obj, handler)
}
let obj = { a: 1 }
let p = onWatch(
  obj,
  (v, property) => {
    console.log(`監(jiān)聽到屬性${property}改變?yōu)?/span>${v}`)
  },
  (target, property) => {
    console.log(`'${property}' = ${target[property]}`)
  }
)
p.a = 2 // 監(jiān)聽到屬性a改變
p.a // 'a' = 2

在上述代碼中,通過自定義 setget 函數(shù)的方式,在原本的邏輯中插入了我們的函數(shù)邏輯,實現(xiàn)了在對對象任何屬性進(jìn)行讀寫時發(fā)出通知。

當(dāng)然這是簡單版的響應(yīng)式實現(xiàn),如果需要實現(xiàn)一個 Vue 中的響應(yīng)式,需要在 get 中收集依賴,在 set 派發(fā)更新,之所以 Vue3.0 要使用 Proxy 替換原本的 API 原因在于 Proxy 無需一層層遞歸為每個屬性添加代理,一次即可完成以上操作,性能上更好,并且原本的實現(xiàn)有一些數(shù)據(jù)更新不能監(jiān)聽到,但是 Proxy 可以完美監(jiān)聽到任何方式的數(shù)據(jù)改變,唯一缺陷就是瀏覽器的兼容性不好。

38. promise

  • 產(chǎn)生

ES6中新技術(shù),解決異步回調(diào)地域問題。

回調(diào)地獄: 回調(diào)嵌套或者函數(shù)很亂的調(diào)用,簡單來說,就是:發(fā)四個請求,第四個依賴第三個結(jié)果,第三個依賴第二個的結(jié)果,第二個依賴第一個的結(jié)果。 回調(diào)函數(shù)弊端: 不利于閱讀,不利于捕獲異常,不能直接return 。

 setTimeout(() => {
    console.log(1)
    setTimeout(() => {
        console.log(2)
        setTimeout(() => {
            console.log(3)   
        },3000)         
    },2000)
},1000)

常見的回調(diào)函數(shù): 計時器、AJAX、數(shù)據(jù)庫操作、fs,其中,經(jīng)常使用的場景是 ,AJAX請求以及各種數(shù)據(jù)庫操作會產(chǎn)生回調(diào)地獄。 promise解決異步避免回調(diào)地獄

function f1() {
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve(111), 1000);
    }).then(data => console.log(data));
  }
function f2() {
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve(222), 2000);
    }).then(data => console.log(data));;
  }
 function f3() {
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve(333), 3000);
    }).then(data => console.log(data));;
  }
?
f1().then(f2).then(f3)
  • 基礎(chǔ)
  1. promise對象表示一個異步操作的最終完成或失敗,及其結(jié)果值 是一個代理值。
  2. 語法上:Promise是一個構(gòu)造函數(shù),用來生成Promise的實例對象。
  3. 功能上:Promise對象用來包裹一個異步操作,并獲取成功、失敗結(jié)果值。
  • 三種狀態(tài)
  1. pending:初始狀態(tài),既不成功,也不失敗。
  2. fulfilled:操作成功完成
  3. rejected:操作失敗 狀態(tài)要不成功要不失敗,此過程不可逆,且只能修改一次。
  • 基本使用

promise構(gòu)造函數(shù)有兩個參數(shù)(resolve,reject), 操作成功,調(diào)用resolve函數(shù),將promise對象的狀態(tài)改為fulfilled。 操作成功,調(diào)用rejected函數(shù),將promise對象的狀態(tài)改為rejected。

  • resolve函數(shù)
 let obj = new Promise((resolve, reject) => { 
    resolve('ok') ;
  });
  //1. 如果傳入的是非Promise類型的數(shù)據(jù),則返回成功的promise
  let p = Promise.resolve('abc');
  //2. 如果是 Promise ,那么該對象的結(jié)果就決定了 resolve 的返回結(jié)果
  let p2 = Promise.resolve(obj);
  //3. 嵌套使用
  let p3 = Promise.resolve(Promise.resolve(Promise.resolve('ABC')));
?
  console.log(p3);
  • reject函數(shù)
//Promise.prototype.reject 返回的始終是失敗的 Promise
let p = Promise.reject(1231231);
let p2 = Promise.reject('abc');
let p3 = Promise.reject(Promise.resolve('OK'));
console.log(p3);
  • API
  1. than

當(dāng)前promise指定成功或失敗的回調(diào),返回一個新的promise供我們調(diào)用。成功的參數(shù)一般是value,失敗的參數(shù)reason。 than里的數(shù)據(jù)—>resolve里的數(shù)據(jù) than返回結(jié)果—>than里的回調(diào)函數(shù)決定

let p=new Promise((resolve,reject)=>{
  resolve('ok')
})
// value是resolve的參數(shù),第一個箭頭函數(shù)叫resolve
p.then(value=>{
  console.log(value)//ok
},reason=>{
   console.log('onRejected1', reason)
  // (1)如果返回非Promise類型的數(shù)據(jù),則返回成功的promise
    return 1000
  // (2)返回Promise ,那么該對象的結(jié)果就決定了函數(shù)的返回結(jié)果
    return Promise.resolve(300)
  // (3)拋出錯誤,失敗的promise
   throw 100
  // (4)無返回值,返回undefined,也是成功的promise
})
  1. catch

指定失敗的回調(diào)

let p =new Promise((resolve,reject)=>{
  reject('失敗了');
})
p.then(value=>{},reason=>{
  console.error(reason);
})
p.catch(reason=>{
  console.error(reason)
})

3 .all

Promise.all([promise1,promise2,promise3]) 批量一次性發(fā)送多個異步請求 只有當(dāng)都成功是返回的promise才會成功 返回一個新的promise 問題: 發(fā)3請求成功后再4個請求

function ajax(url) {
  return axios.get(url)
}
const p1 = ajax(url1)
const p2 = ajax(url2)
const p3 = ajax(url3)
Promise.all([p1, p2, p3])
// values和數(shù)組中數(shù)據(jù)的順序有關(guān)系
  .then(values => {
  return ajax(url4)
})
  .then(value => {
  console.log(value) // 就是第4個請求成功的value
})
  .catch(error => {
?
})

4 .race

多個promise任務(wù)同步執(zhí)行,返回最先結(jié)束的promise任務(wù)結(jié)束,不論是成功還是失敗,簡單來說就先到先得。

// race 賽跑
let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('OK');
  }, 1000);
});
?
let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Yes');
  }, 500);
});
?
let result = Promise.race([p1, p2]);
?
console.log(result);

39. 什么是asyn await

new Promise(resolve,reject)=>{
  setTimeout(()=>resolve(111),1000)
}).then(data=>{
  console.log(data)
})

es8新增promise語法糖 建立在Promise之上異步編程終極解決方案,使用同步代碼實現(xiàn)異步代碼。 相對于 Promise 和回調(diào),它的可讀性和簡潔度都更高,更好地處理 then 鏈。

  • async

異步,函數(shù)前加上async,聲明一個函數(shù)是異步的 那么該函數(shù)就會返回一個 Promise,async返回的promise成功還是失敗,看函數(shù)return

async function main(){
  // 1. 如果返回的是非 Promise 對象,所有數(shù)據(jù)類型
  // 返回成功的promise
  return 'iloveyou';
?
  //2. 如果返回的是 Promise 對象
  // 看返回的promise是成功還是失敗,成功則成功,失敗則失敗。
  return new Promise((resolve ,reject) => {
    resolve('123');
    reject('失敗');
  });
?
  //3. 函數(shù)拋出異常 promise對象也是失敗的
  throw '有點問題';
}
// let result = main();
// console.log(result);
?
main().then(value => {}, reason=>{
  console.error(reason);
});
  • await

await 異步等待 等待一個異步方法執(zhí)行完成,后面常放返回promise對象表達(dá)式,必須放在async中。

await相當(dāng)于promise的then

try 放到成功的操作 catch 放失敗的操作

// await必須寫在async函數(shù)中,但async函數(shù)中可以沒有await
async function main() {
  // 1、看返回的promise是成功還是失敗,看promise的返回結(jié)果
  let result = await Promise.resolve('OK');
  console.log(result);
  // 2、返回其他值,返回變量值
  let one = await 1;
  console.log(one);//1
}

40. new關(guān)鍵字做了什么

  1. 創(chuàng)建一個空對象(實例化對象)
  2. this指向新對象
  3. 屬性方法賦值
  4. 將這個新對象返回

41. 談?wù)勀銓υ偷睦斫?/h2>

為什么要有原型?構(gòu)造函數(shù)中的實例每調(diào)用一次方法,就會在內(nèi)存中開辟一塊空間,從而造成內(nèi)存浪費。

在函數(shù)對象中,有一個屬性prototype,它指向了一個對象,這個對象就是原型對象,這個對象的所有屬性和方法,都會被構(gòu)造函數(shù)所擁有。

 function Person(name, age){
 }		
 console.log(Person.prototype)
// {constructor: ?}

普通函數(shù)調(diào)用,prototype沒有任何作用,構(gòu)造函數(shù)調(diào)用,該類所有實例有隱藏一個屬性(proto)指向函數(shù)的prototype。(實例的隱式原型指向類的顯示原型)

//實例的隱式原型指向構(gòu)造函數(shù)的顯示原型
 console.log(p1.__proto__ === Person.prototype);
//true

原型就相當(dāng)于一個公共區(qū)域,可以被類和該類的所有實例訪問到。
50個知識點由淺入深掌握J(rèn)avascript

所以我們在定義類時,公共屬性定義到構(gòu)造函數(shù)里面,公共的方法定義到構(gòu)造函數(shù)外部的原型對象上

原型優(yōu)點:資源共享,節(jié)省內(nèi)存;改變原型指向,實現(xiàn)繼承。缺點:查找數(shù)據(jù)的時候有的時候不是在自身對象中查找。

42. 談?wù)勀銓υ玩湹睦斫?/h2>

原型鏈:實際上是指隱式原型鏈,從對象的__proto__開始,連接所有的對象,就是對象查找屬性或方法的過程。
50個知識點由淺入深掌握J(rèn)avascript

  1. 當(dāng)訪問一個對象屬性時,先往實例化對象在自身中尋找,找到則是使用。
  2. 找不到(通過_proto_屬性)去它的原型對象中找,找到則是使用。
  3. 沒有找到再去原型對象的原型Object原型對象)中尋找,直到找到Object為止,如果依然沒有找到,則返回undefined。

43. 談?wù)勀銓his,call,apply,bind理解

當(dāng)一個函數(shù)被調(diào)用時,會創(chuàng)建一個執(zhí)行上下文,其中this就是執(zhí)行上下文的一個屬性,this是函數(shù)在調(diào)用時JS引擎向函數(shù)內(nèi)部傳遞的一個隱含參數(shù)。

this指向完全是由它的調(diào)用位置決定,而不是聲明位置。除箭頭函數(shù)外,this指向最后調(diào)用它的那個對象。

  1. 全局作用域中,無論是否嚴(yán)格模式都指向window
  2. 普通函數(shù)調(diào)用,指向window;嚴(yán)格模式下指向undefined;
  3. 對象方法使用,該方法所屬對象;
  4. 構(gòu)造函數(shù)調(diào)用,指向?qū)嵗瘜ο螅?/li>
  5. 匿名函數(shù)中,指向window;
  6. 計時器中,指向window
  7. 事件綁定方法,指向事件源;
  8. 箭頭函數(shù)指向其上下文中this;

call、applybind,都是用來改變this指向的,三者是屬于大寫 Function原型上的方法,只要是函數(shù)都可以使用。

callapply的區(qū)別,體現(xiàn)在對入?yún)⒌囊蟛煌?code>call的實參是一個一個傳遞,apply的實參需要封裝到一個數(shù)組中傳遞。

call、apply相比bind方法,函數(shù)不會執(zhí)行,所以我們需要定義一個變量去接收執(zhí)行。

更多詳細(xì)的內(nèi)容可看我之前文章:《this指向詳解及自定義call、apply、bind》

44. 什么是閉包,哪些地方用到過

很多編程語言都支持閉包,閉包不是語言特性,而是一種編程習(xí)慣。閉包(Closure)是指具有一個封閉對外不公開包裹結(jié)構(gòu),或空間。

在JS中,我們可以理解為閉包是函數(shù)在特定情況下執(zhí)行產(chǎn)生的一種現(xiàn)象。

所謂閉包,是一種引用關(guān)系,該引用關(guān)系存在內(nèi)部函數(shù)中,內(nèi)部函數(shù)引用外部函數(shù)數(shù)據(jù),引用的數(shù)據(jù)可以在函數(shù)詞法作用域(函數(shù)外部)之外使用。

產(chǎn)生閉包必滿足三個條件:函數(shù)嵌套、內(nèi)部函數(shù)引用外部函數(shù)數(shù)據(jù)、外部函數(shù)調(diào)用,凡是所有的閉包都滿足以上三個條件,否則不構(gòu)成閉包。

閉包本質(zhì):內(nèi)部函數(shù)里的一個對象,這個對象非Js對象(有屬性有方法的對象),這個對象是函數(shù)在運行時,本該釋放的活動對象,這個活動對象里包含著我們引用的變量。

閉包的作用:模擬私有變量、柯里化、偏函數(shù)、防抖、節(jié)流、實現(xiàn)緩存。

模擬私有變量:將私有變量放在外在的立即執(zhí)行函數(shù)中,并通過立即執(zhí)行這個函數(shù),創(chuàng)造一個閉包環(huán)境(私有變量:只允許函數(shù)內(nèi)部,或?qū)ο蠓椒ㄔL問的變量)。

柯里化:把接受n個參數(shù)的一個函數(shù)轉(zhuǎn)化成只接受一個參數(shù)n個函數(shù)互相嵌套的函數(shù)過程,目標(biāo)是把函數(shù)拆解為精準(zhǔn)的n部分,也就是將fn(a,b,c)轉(zhuǎn)化成fn(a)(b)(c)的過程。

偏函數(shù):固定函數(shù)中的某一個或幾個參數(shù),然后返回一個新的函數(shù)。

防抖:只執(zhí)行最后一次。

節(jié)流:隔一段時間執(zhí)行一次。

閉包與內(nèi)存泄露:閉包造成內(nèi)存泄漏是誤傳,誤傳由于早期IE垃圾回收機機制是基于基于引用計數(shù)法,閉包當(dāng)中如果包含循環(huán)引用,那么IE瀏覽器無法回收閉包中引用的變量,但這內(nèi)存泄漏和閉包沒有關(guān)系,而是IE的bug。

更多詳細(xì)的內(nèi)容可看我之前文章:《這次把閉包給你講的明明白白》和《 閉包典型應(yīng)用用及性能問題》

45. 常見的內(nèi)存泄漏有哪些

  • “手滑”導(dǎo)致的全局變量
function f1() {
  name = '小明'
}

在非嚴(yán)格模式下引用未聲明的變量,會在全局對象中創(chuàng)建一個新變量,在瀏覽器中,全局對象是window,這就意味著name這個變量將泄漏到全局。全局變量是在網(wǎng)頁關(guān)閉時才會釋放,這樣的變量一多,內(nèi)存壓力也會隨之增高。

  • 遺忘清理的計時器

程序中我們經(jīng)常會用到計時器,也就是setIntervalsetTimeout

var timeId = setInterval(function(){
  // 函數(shù)體
},1000)
  • 遺忘清理的dom元素引用
var divObj = document.getElementById('mydiv')

// dom刪除myDiv
document.body.removeChild(divObj);
console.log(divObj);
// 能console出整個div 說明沒有被回收,引用存在

// 移出引用
divObj = null;
console.log(divObj) 
// null

46. JS微任務(wù)紅任務(wù)執(zhí)行順序

(1)JS引擎首先執(zhí)行所有同步代碼

(2)宏隊列:保存待執(zhí)行宏任務(wù)

(3)微隊列:保存待執(zhí)行的微任務(wù)

50個知識點由淺入深掌握J(rèn)avascript

50個知識點由淺入深掌握J(rèn)avascript

47. 簡單介紹一下JS的垃圾回收機制

每隔一段時間,JS的垃圾收集器就會對變量做“巡檢”。當(dāng)它判斷一個變量不再被需要之后,它就會把這個變量所占的內(nèi)存空間給釋放掉,這個過程叫做垃圾回收。
常用的垃圾回收算法有兩種——引用計數(shù)法和標(biāo)記清除法。

  • 引用計數(shù)法

這是最初級的垃圾回收算法,在現(xiàn)代瀏覽器里幾乎被淘汰的干干凈凈。

當(dāng)我們創(chuàng)建一個變量,對應(yīng)的也就創(chuàng)建了一個針對這個值的引用。

const students = ['小紅','小明']

在引用這塊計數(shù)法的機制下,內(nèi)存中每一個值都會對應(yīng)一個引用計數(shù)。當(dāng)垃圾收集器感知到某個值的引用計數(shù)為0時,就判斷它“沒用”了,隨即這塊內(nèi)存就會被釋放。

50個知識點由淺入深掌握J(rèn)avascript

比如我們此時如果把student指向一個null:

students = null

那么這個數(shù)組所應(yīng)用的引用計數(shù)就會變成0(如下圖),它就變成一塊沒用的內(nèi)存,即將面臨著作為垃圾,被回收的命運。

50個知識點由淺入深掌握J(rèn)avascript

引用計數(shù)法弊端

大家現(xiàn)在來看這樣一個例子:

function badCycle() {
  var cycleObj1 = {}
  var cycleObj2 = {}
  cycleObj1.target = cycleObj2
  cycleObj2.target = cycleObj1
}
badCycle()

當(dāng)執(zhí)行了badCycle這個函數(shù),作用域內(nèi)的變量也會全部被視為“垃圾”進(jìn)而移除。

但如果咱們用了引用計數(shù)法,那么即使 badCycle 執(zhí)行完畢,cycleObj1 和 cycleObj2 還是會活得好好的 —— 因為 cycleObj2 的引用計數(shù)為 1(cycleObj1.target),而 cycleObj1 的引用計數(shù)也為 1 (cycleObj2.target)(如下圖)。

50個知識點由淺入深掌握J(rèn)avascript

這就是引用計數(shù)法的弊端,無法甄別循環(huán)引用場景下的“垃圾”。

  • 標(biāo)記清除法

引用計數(shù)法無法甄別“循環(huán)引用”場景下的“垃圾”,自 2012年起,所有瀏覽器都使用了標(biāo)記清除算法??梢哉f,標(biāo)記清除法是現(xiàn)代瀏覽器的標(biāo)準(zhǔn)垃圾回收算法。

在標(biāo)記清除算法中,一個變量是否被需要的判斷標(biāo)準(zhǔn),是它是否可抵達(dá) 。

這個算法有兩個階段,分別是標(biāo)記階段和清除階段:

  • 標(biāo)記階段:垃圾收集器會先找到根對象,在瀏覽器里,根對象是 Window;在 Node 里,根對象是 Global。從根對象出發(fā),垃圾收集器會掃描所有可以通過根對象觸及的變量,這些對象會被標(biāo)記為“可抵達(dá) ”。
  • 清除階段: 沒有被標(biāo)記為“可抵達(dá)” 的變量,就會被認(rèn)為是不需要的變量,這波變量會被清除

現(xiàn)在大家按照標(biāo)記清除法的思路,再來看這段代碼:

function badCycle() {
  var cycleObj1 = {}
  var cycleObj2 = {}
  cycleObj1.target = cycleObj2
  cycleObj2.target = cycleObj1
}

badCycle()

badCycle 執(zhí)行完畢后,從根對象 Window 出發(fā),cycleObj1 和 cycleObj2 都會被識別為不可達(dá)的對象,它們會按照預(yù)期被清除掉。這樣一來,循環(huán)引用的問題,就被標(biāo)記清除干脆地解決掉了。

48. JS的深淺拷貝

JS基本數(shù)據(jù)類型不存在深淺拷貝問題,深拷貝和淺拷貝主要針對引用數(shù)據(jù)類型(數(shù)組、函數(shù)、對象)

淺拷貝:
拷貝對象的時候,如果屬性是基本數(shù)據(jù)類型,拷貝就是基本數(shù)據(jù)類型的值,如果屬性是引用數(shù)據(jù)類型,拷貝的就是內(nèi)存地址,因此修改新拷貝對象屬性會影響原對象。

深拷貝:將一個對象從內(nèi)存中完整的拷貝出來,從堆內(nèi)存中開辟一個新的區(qū)域存放新對象,且修改新對象不影響原對象。

區(qū)別:深拷貝修改拷貝對象影響原對象,淺拷貝不影響。

淺拷貝數(shù)組

(1)concat方法

 var arr =[1,2,3,{name:'小明',age:20}];
 var newArr=arr.concat();
 arr[3].name="小紅";
 console.log(arr);
 //{name:"小紅",age:20}
 console.log(newArr);
 //{name:"小紅",age:20}

(2)slice方法

var arr =[1,2,3,{name:'小明',age:20}];
var newArr = arr.slice(0);
newArr[3].name = '小紅';
console.log(arr);
console.log(newArr);

(3)延展運算符

var arr =[1,2,3,{name:'小明',age:20}];
var newArr = [...arr];
arr[3].name = '小紅';
console.log(arr);
console.log(newArr);

淺拷貝對象

(1)直接拷貝

var obj1={
  name:'小明',
  cars:[
    '奔馳',
    ‘寶馬’,
  ]
}
var obj2=obj1

(2)assign
對象的合并,將源對象的所有可枚舉屬性,復(fù)制到目標(biāo)對象

var obj1={
  name:'小明',
  cars:[
    '奔馳',
    ‘寶馬’,
  ]
}
// 目標(biāo)對象 源對象
var obj2=object.assign({},obj1)

深拷貝

(1)JSON

先使用JSON.stringify將JS對象轉(zhuǎn)化成JSON串,再使用JSON.parse將JSON字符串轉(zhuǎn)化為對象。

不足:忽略對象中的函數(shù)、undefiend、RegExp、Date。

對象中的函數(shù)、undefined屬性會直接忽略,對象中的RegExp,拷貝后會為空,對象中的Date會轉(zhuǎn)化為字符串。

const school={
      name :'慕課網(wǎng)',
      type:['前端','java','go'],
      fn(){
        console.log("我愛學(xué)習(xí)");
      }
  }

  //將對象轉(zhuǎn)化為字符串
  let str =JSON.stringify(school);
  //{"name":"慕課網(wǎng)","type":["前端","java","go"]}
  //將字符串轉(zhuǎn)化為JS對象
  let newSchool=JSON.parse(str);
  
  //修改新對象屬性
  newSchool.type[0]="c++";
  console.log(school);
  // ["前端", "java", "go"]
  console.log(newSchool);
  // ["c++", "java", "go"]
  • 手寫深拷貝
  // 遞歸實現(xiàn)深拷貝
        let school = {
            name: '慕課網(wǎng)',
            type: ['前端', '后端', '大數(shù)據(jù)'],
            subtype: {
                name: '前端',
                type: 'vue'
            },
            fn() {
                console.log('我愛學(xué)習(xí)')
            }
        }

        // 獲取數(shù)據(jù)類型
        function getType(data) {
            return Object.prototype.toString.call(data).slice(8, -1)
        }
        console.log(getType(school))

        // 遞歸實現(xiàn)深拷貝
        function deepClone(data) {
            // 1、判斷數(shù)據(jù)類型
            let type = getType(data);
            let container;
            if (type === 'Array') {
                container = [];
            }
            if (type === 'Object') {
                container = {}
            }

            // 2、遍歷
            for (let i in data) {
                let t = getType(data[i])
                if (t === 'Array' || t === 'Objcet') {
                    container[i] = deepClone(data[i])
                } else {
                    container[i] = data[i]
                }
            }
            return container;
        }

        const newSchool = deepClone(school);
        newSchool.type[0] = '前端端'

        console.log(school)
        console.log(newSchool)

49. 手寫防抖節(jié)流

防抖

// fn是我們需要包裝的事件回調(diào), delay是每次推遲執(zhí)行的等待時間
function debounce(fn, delay) {
  // 定時器
  let timer = null
  
  // 將debounce處理結(jié)果當(dāng)作函數(shù)返回
  return function () {
    // 保留調(diào)用時的this上下文
    let context = this
    // 保留調(diào)用時傳入的參數(shù)
    let args = arguments
    // 每次事件被觸發(fā)時,都去清除之前的舊定時器
    if(timer) {
        clearTimeout(timer)
    }
    // 設(shè)立新定時器
    timer = setTimeout(function () {
      fn.apply(context, args)
    }, delay)
  }
}
// 用debounce來包裝scroll的回調(diào)
const better_scroll = debounce(() => console.log('觸發(fā)了滾動事件'), 1000)
document.addEventListener('scroll', better_scroll)

節(jié)流

// fn是我們需要包裝的事件回調(diào), delay是每次推遲執(zhí)行的等待時間
function debounce(fn, delay) {
  // 定時器
  let timer = null
  
  // 將debounce處理結(jié)果當(dāng)作函數(shù)返回
  return function () {
    // 保留調(diào)用時的this上下文
    let context = this
    // 保留調(diào)用時傳入的參數(shù)
    let args = arguments
    // 每次事件被觸發(fā)時,都去清除之前的舊定時器
    if(timer) {
        clearTimeout(timer)
    }
    // 設(shè)立新定時器
    timer = setTimeout(function () {
      fn.apply(context, args)
    }, delay)
  }
}
// 用debounce來包裝scroll的回調(diào)
const better_scroll = debounce(() => console.log('觸發(fā)了滾動事件'), 1000)
document.addEventListener('scroll', better_scroll)

50. 手寫call、apply、bind

  • 自定義call

在實現(xiàn) call 方法之前,我們先來看一個 call 的調(diào)用示范:

var me = {
  name: '張三'
}

function showName() {
  console.log(this.name)
}

showName.call(me) // 張三

前面我們說過call方法是大寫Function中方法,所有的函數(shù)都可以繼承使用,所以我們自定義call 方法應(yīng)該定義在 Function.prototype上,這里我們定義一個myCall。

Function.prototype.myCall=function(){
}

我們想,如果用myCall方法進(jìn)行綁定,就相當(dāng)于在傳入的對象(這里是me)里面添加了一個原本的函數(shù),然后在使用對象.函數(shù)調(diào)用,也就是:

var me ={
  name :'張三',
  person:function(){
    console.log(this.name)
  }
}

me.person()

根據(jù)這個思路,我們往原型對象中添加內(nèi)容:

// context:我們傳入的對象
Function.prototype.newCall = function(context){
  //  person.newcall調(diào)用,也就是函數(shù).方法調(diào)用,JS中函數(shù)也是對象,所以對象方法調(diào)用,指向該方法所屬對象,也就是person。
  // 注意!這里的this是person,我們還沒開始綁定呢
  console.log(this)
  
  // 1、我們?yōu)閭魅氲膶ο筇砑訉傩?/span>
  context.fnkey = this;
  // 2、調(diào)用函數(shù)
  context.fnkey();
  // 3、執(zhí)行完,方法刪除,我們不能改寫對象
  delete context.fnkey 
}
person.newCall(me)
復(fù)制代碼

當(dāng)我們?yōu)樾螀⒆兞刻砑訉傩詴r,此時的代碼就如下,然后在調(diào)用這個函數(shù),因為是對象方法調(diào)用所以this指向了me,也就是obj。

function person(){
  console.log(this.name);
}

var me = {
    name:'張三',
    fnkey:function(){
      console.log(this.name);
    }
}

現(xiàn)在我們的mycall就實現(xiàn)了call的基本能力——改變this指向,第二步讓我們的mycall具備讀取函數(shù)入?yún)⒛芰?,也就是讀取call方法第二個到最后一個入?yún)?,這里我們用到ES6中的剩余參數(shù)...args

剩余參數(shù)可以幫助我們將不定數(shù)量的入?yún)⒆兂蓴?shù)組,具體用法如下:

 function readArr(...args) {
    console.log(args)
}
readArr(1,2,3) // [1,2,3]

我們通過args這個數(shù)組拿到我們想要的入?yún)?,再?args數(shù)組代表目標(biāo)入?yún)⒄归_,傳入目標(biāo)方法,一個call方法就實現(xiàn)了。

Function.prototype.myCall = function(context, ...args) {
    context.fnkey = this;
    context.fnkey(...args);
    delete context.fnkey;
}

以上,就實現(xiàn)了mycall的基本框架~~

但是上面的mycall還并不完善,比如說第一個參數(shù)傳了null怎么辦?是不是默認(rèn)給他指到windowglobal上去;第一個參數(shù)不是對象怎么辦?我們改如何保證為對象?如果context里面有這個屬性怎么辦?我們怎樣保證屬性的唯一性?

我們進(jìn)行以下補充優(yōu)化:

 Function.prototype.myCall = function (context, ...args) {
    // 補充1 如果第一個參數(shù)沒傳,默認(rèn)指向window / Global
    // globalThis瀏覽器環(huán)境中指window,node.js環(huán)境中指向global
    if (context == null) context = globalThis

    // 補充2:如果第一個參數(shù)傳的值類型,數(shù)字類型,或者布爾類型
    // 我們通過new Object 生成一個值類型對象,數(shù)字類型對象,布爾類型對象
    if (typeof context !== 'objext') context = new Object(context)

    // 補充3:防止傳入對象作為屬性,與context重名屬性覆蓋
    // symbol類型不會出現(xiàn)屬性名稱覆蓋
    const fnkey = Symbol();
    context[fnkey] = this
    globalThis  // window/global

    console.log(new Object('哈哈'));// String {"哈哈"}
    console.log(new Object(1)); // Number { 1 }
    console.log(new Object(true)); //Boolean { true }
    console.log(new Object(undefined));// {}

    let symbol1 = Symbol(); //Symbol()
    let symbol2 = Symbol(); //Symbol()
    consoele.log(symbol1 === symbol2);//false 

這樣,我們就實現(xiàn)了完整mycall方法,使用mycall調(diào)用時,就相當(dāng)于在傳入的對象里面添加了一個原本的函數(shù),這是實現(xiàn)mycall的核心,一定要理解。完整版mycall方法如下:

  Function.prototype.myCall = function (context, ...args) {
    // 補充1 如果第一個參數(shù)沒傳,默認(rèn)指向window / Global
    // globalThis瀏覽器環(huán)境中指window,node.js環(huán)境中指向global
    if (context == null)  context = globalThis

    // 補充2:如果第一個參數(shù)傳的值類型,數(shù)字類型,或者布爾類型
    // 我們通過new Object 生成一個值類型對象,數(shù)字類型對象,布爾類型對象
    if (typeof context !== 'objext')  context = new Object(context)

    // 補充3:防止傳入對象作為屬性,與context重名屬性覆蓋
    // symbol類型不會出現(xiàn)屬性名稱覆蓋
    const fnkey = Symbol();
    
    // step1: 給傳入對象添加原函數(shù)(this就是我們要改造的原函數(shù))
    context[fnkey] = this
    // step2: 執(zhí)行函數(shù),并傳遞參數(shù)
    context[fnkey](...args)
    // step3: 刪除 step1 中掛到目標(biāo)對象上的函數(shù)
    delete context[fnkey].
}

// 測試如下:
function showFullName(secondName) {
    console.log(`${this.name} ${secondName}`)
}
var me = {
    name: '張三'
}

showFullName.myCall(me, '李四') // 張三 李四
showFullName.myCall(null, '李四') // 李四
showFullName.myCall(1, '李四') // undefined 李四

理解了call,那么實現(xiàn)applybind方法就小菜一碟了,apply方法關(guān)鍵在于更改參數(shù)的讀取方式,bind方法關(guān)鍵在于延遲目標(biāo)函數(shù)的執(zhí)行時機。

  • 自定義apply
  Function.prototype.myCall = function (context, ...args) {
    if (context == null) context = globalThis
    if (typeof context !== 'objext') context = new Object(context)
    const fnkey = Symbol();
    context[fnkey] = this;
    // 此時,傳入的數(shù)組,不需要對數(shù)組進(jìn)行拆包
    context.fnkey(args);
    delete context[fnkey];
}

// 測試如下:
function showFullName(secondName) {
    console.log(`${this.name} ${secondName}`)
}
var me = {
    name: '張三'
}

showFullName.myCall(me, ['李四','王五']) // 張三 李四 王五
  • 自定義bind

前面我們說過,bind方法不會立即執(zhí)行函數(shù),實際上bind方法是返回了一個原函數(shù)的拷貝,函數(shù)體內(nèi)的參數(shù)會和bind方法第一個以外的其他參數(shù)合并。

在實現(xiàn) bind 方法之前,我們先來看一個 bind 的調(diào)用示范:

var me = {
    value: 1
}

function person(name, age) {
    return {
        value: this.value,
        name: name,
        age: age
    }
}

var bar = person.bind(me, '張三', 18);
console.log(bar);
// 這里將會輸出person函數(shù)
console.log(bar());
// {value: 1, name: "張三", age: 18}

var bar2 = person.bind(me, '張三');
console.log(bar2(18));
// {value: 1, name: "張三", age: 18}

完整版myBind如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-513265.html

  Function.prototype.myBind = function (context, ...args) {    
    // step1: 保存下當(dāng)前 this(這里的 this 就是我們要改造的的那個函數(shù))
    const self = this;
    
    // step2: 返回一個函數(shù)
    // bind整體上會return一個函數(shù),并還可以接受參數(shù)
    return function (...argus) {
        // step3: 拼接完整參數(shù),將bind執(zhí)行參數(shù)和函數(shù)調(diào)用時傳入?yún)?shù)拼接
        const fullArgs = args.concat(argus)
        // step4: 調(diào)用函數(shù)
        return self.apply(context,fullArgs)
    }
}

// 測試如下:
function showFullName(secondName) {
    console.log(`${this.name} ${secondName}`)
}
var me = {
    name: '張三'
}

var result = showFullName.myBind(me, '李四')
result() // 張三 李四

到了這里,關(guān)于50個知識點由淺入深掌握J(rèn)avascript的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • React - redux 使用(由淺入深)

    React - redux 使用(由淺入深)

    中文文檔: http://www.redux.org.cn/ 英文文檔: https://redux.js.org/ Github: https://github.com/reactjs/redux 可直接參照 目錄十 進(jìn)行使用 react-redux redux 是一個專門用于做狀態(tài)管理的JS庫(不是react插件庫)。 它可以用在 react, angular, vue 等項目中, 但基本與 react 配合使用。 作用: 集中式管理 re

    2024年02月07日
    瀏覽(24)
  • 【個人筆記】由淺入深分析 ClickHouse

    項目中不少地方使用到ClickHouse,就對它做了一個相對深入一點的了解和研究。并對各種知識點及整理過程中的一些理解心得進(jìn)行了匯總并分享出來,希望對其他同學(xué)能有幫助。 本文主要講解ClickHouse的特點、讀寫過程、存儲形式、索引、引擎、物化視圖等特性。 適合 入門和

    2024年01月20日
    瀏覽(29)
  • 由淺入深理解C#中的事件

    本文較長,給大家提供了目錄,可以直接看自己感興趣的部分。 前面介紹了C#中的委托,事件的很多部分都與委托類似。實際上,事件就像是專門用于某種特殊用途的簡單委托,事件包含了一個私有的委托,如下圖所示: 有關(guān)事件的私有委托需要了解的重要事項如下: 1、事

    2024年02月03日
    瀏覽(30)
  • Springboot3+EasyExcel由淺入深

    Springboot3+EasyExcel由淺入深

    環(huán)境介紹 技術(shù)棧 springboot3+easyexcel 軟件 版本 IDEA IntelliJ IDEA 2022.2.1 JDK 17 Spring Boot 3 EasyExcel是一個基于Java的、快速、簡潔、解決大文件內(nèi)存溢出的Excel處理工具。 他能讓你在不用考慮性能、內(nèi)存的等因素的情況下,快速完成Excel的讀、寫等功能。 官網(wǎng)https://easyexcel.opensource.ali

    2024年01月16日
    瀏覽(28)
  • 【由淺入深學(xué)習(xí)MySQL】之索引進(jìn)階

    【由淺入深學(xué)習(xí)MySQL】之索引進(jìn)階

    本系列為:MySQL數(shù)據(jù)庫詳解,為千鋒資深教學(xué)老師獨家創(chuàng)作 致力于為大家講解清晰MySQL數(shù)據(jù)庫相關(guān)知識點,含有豐富的代碼案例及講解。如果感覺對大家有幫助的話,可以【關(guān)注】持續(xù)追更~ 文末有本文重點總結(jié),技術(shù)類問題,也歡迎大家和我們溝通交流! 從今天開始本系列

    2024年02月05日
    瀏覽(22)
  • 手拉手Vue組件由淺入深

    手拉手Vue組件由淺入深

    組件 (Component) 是 Vue.js 最強大的功能之一,它是html、css、js等的一個聚合體,封裝性和隔離性非常強。 組件化開發(fā): ??? 1、將一個具備完整功能的項目的一部分分割多處使用 ??? 2、加快項目的進(jìn)度 ??? 3、可以進(jìn)行項目的復(fù)用 組件注冊分為:全局注冊和局部注冊 目錄

    2024年01月18日
    瀏覽(22)
  • 由淺入深介紹 Python Websocket 編程

    由淺入深介紹 Python Websocket 編程

    1.1 websocket 協(xié)議簡介 Websocket協(xié)議是對http的改進(jìn),可以實現(xiàn)client 與 server之間的雙向通信; websocket連接一旦建立就始終保持,直到client或server 中斷連接,彌補了http無法保持長連接的不足,方便了客戶端應(yīng)用與服務(wù)器之間實時通信。 適用場景 html頁面實時更新, 客戶端的html頁面

    2024年02月03日
    瀏覽(22)
  • 由淺入深剖析 Apollo(阿波羅)架構(gòu)

    由淺入深剖析 Apollo(阿波羅)架構(gòu)

    目錄 一、介紹 二、架構(gòu)和模塊 三、架構(gòu)剖析 1.最簡架構(gòu) ?2. 高可用保障 ?3.多接口擴(kuò)展 四、總結(jié) Apollo(阿波羅)是攜程框架部研發(fā)并開源的一款生產(chǎn)級的配置中心產(chǎn)品,它能夠集中管理應(yīng)用在不同環(huán)境、不同集群的配置,配置修改后能夠?qū)崟r推送到應(yīng)用端,并且具備規(guī)范的

    2024年02月13日
    瀏覽(24)
  • 【由淺入深學(xué)MySQL】- MySQL連接查詢

    【由淺入深學(xué)MySQL】- MySQL連接查詢

    本系列為:MySQL數(shù)據(jù)庫詳解,為千鋒教育資深Java教學(xué)老師獨家創(chuàng)作 致力于為大家講解清晰MySQL數(shù)據(jù)庫相關(guān)知識點,含有豐富的代碼案例及講解。如果感覺對大家有幫助的話,可以【點個關(guān)注】持續(xù)追更~ 文末有重點總結(jié)和福利內(nèi)容! 技術(shù)類問題,也歡迎大家和我們溝通交流!

    2024年02月05日
    瀏覽(23)
  • 由淺入深了解機器學(xué)習(xí)和GPT原理

    由淺入深了解機器學(xué)習(xí)和GPT原理

    我不是一個機器學(xué)習(xí)專家,本來是一名軟件工程師,與人工智能的互動很少。我一直渴望深入了解機器學(xué)習(xí),但一直沒有找到適合自己的入門方式。這就是為什么,當(dāng)谷歌在2015年11月開源TensorFlow時,我非常興奮,知道是時候開始學(xué)習(xí)之旅了。不想過于夸張,但對我來說,這就

    2024年02月09日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包