隱式類型轉(zhuǎn)換相關(guān)
字符串連接符與算數(shù)運算符
- 字符串連接符+:會把其他數(shù)據(jù)類型調(diào)用String()方法轉(zhuǎn)換成字符串然后拼接
- 算數(shù)運算符+:會把其他數(shù)據(jù)類型調(diào)用Number()方法轉(zhuǎn)換成數(shù)字然后做加法計算
關(guān)系運算符
關(guān)系運算符會把其他數(shù)據(jù)轉(zhuǎn)換成number之后再比較
- 一個為字符串另一個為數(shù)字,則將字符串轉(zhuǎn)換成數(shù)字比較
- 兩個都為字符串則依次比較兩個的charCodeAt()值
- undefined與null互相比較為相等,與其他類型比較為不等
- NaN與任何數(shù)據(jù)都不等
- 復(fù)雜數(shù)據(jù)類型會先調(diào)用valueOf()再調(diào)用toString()轉(zhuǎn)換成字符串,然后再轉(zhuǎn)換成number
邏輯非運算符
- 有邏輯非運算符的會將數(shù)據(jù)轉(zhuǎn)換成布爾類型
- 除了0、-0、NaN、undefined、null、""、false以外都為true
各類型隱式轉(zhuǎn)換規(guī)則
- String:變量 + 字符串
- Number:變量 + 自增自減運算符,算術(shù)運算符,關(guān)系運算符
- Boolean:變量 + 邏輯非運算符
字符串類型轉(zhuǎn)為數(shù)值類型:
- 字符串任意位置出現(xiàn)任意非數(shù)字、非空格的字符,均轉(zhuǎn)為NaN
- 數(shù)字中間存在空格,轉(zhuǎn)為NaN
閉包相關(guān)
什么是閉包
閉包就是一個函數(shù),這個函數(shù)能夠訪問其他函數(shù)的作用域中的變量。
優(yōu)點
- 封裝變量,模仿塊級作用域
缺點
- this指向問題
- 內(nèi)存泄漏問題
- 變量發(fā)生變化問題
作用域和變量提升相關(guān)
作用域
作用域分為全局作用域、函數(shù)作用域及塊級作用域
變量提升
js在執(zhí)行一個作用域的代碼之前,會先將變量聲明及函數(shù)聲明提到整個作用域頂部,隨后依次執(zhí)行代碼,其中若變量與函數(shù)同名,函數(shù)優(yōu)先級要高于變量。
即:
- 將變量聲明與函數(shù)聲明提到作用域頂部
- 函數(shù)聲明優(yōu)先級高于變量聲明
- 依次執(zhí)行代碼
作用域鏈
如果當(dāng)前作用域中找不到變量,則會想上層作用域查找,依次往上查找,形成作用域鏈
NaN是什么?有什么特別之處?
NaN 是 not a number 的縮寫,代表非數(shù)字值的特殊值,該屬性用于指示某個值不是數(shù)字,是一個全局對象的屬性
特別之處:
- NaN 屬性的初始值就是 NaN,和 Number.NaN 的值一樣
- 在現(xiàn)代瀏覽器中(ES5),NaN是一個不可配置(non-configurable),不可寫(non-writable)的屬性.但是在ES3中,這個屬性的值是可以更改的,但是也應(yīng)該避免覆蓋
- 編碼中很少直接用到 NaN ,通常都是計算失敗的時候,作為Math的某個方法的返回值出現(xiàn)(例如:Math.sqrt(-1)),或者嘗試將一個字符串解析成為數(shù)字,但是失敗了的時候(parseInt("blabla"))
- NaN和任何值比較,都不相等,包括它自己NaN是number類型 typeof NaN //'number'
== 和 === 的區(qū)別
兩等是值判斷,會調(diào)用隱式類型轉(zhuǎn)換,不判斷類型
三等是值判斷加類型判斷,判斷兩個值是否嚴(yán)格相等
typeof和instanceof的區(qū)別
typeof
用于判斷數(shù)據(jù)類型,返回值為6個字符串,分別為string、Boolean、number、function、object、undefined。其中null會返回object結(jié)果。
instanceof
instanceof用來判斷對象,判斷方法是根據(jù)對象的原型鏈依次向下查詢
js的數(shù)據(jù)類型有哪些
JS原生有5種簡單數(shù)據(jù)類型:Undefined,Null,Boolean,Number 和 String。還有一個復(fù)雜數(shù)據(jù)類型Object。 函數(shù)在JS中是對象,而不是一種數(shù)據(jù)類型。
undefined和null的區(qū)別
Undefined類型為變量聲明了但是沒有初始化(var box),而Null表示一個空對象引用(var box = null)。注意:未初始化的變量和賦值為 null 的 變量會相等,可通過typeof或者使用三等進(jìn)行判斷
call、apply、bind的區(qū)別
call 和 apply 是為了改變函數(shù)體內(nèi)部 this 的指向。對于 apply、call 二者而言,作用完全一樣,只是接受參數(shù)的方式不太一樣。bind()最簡單的用法是創(chuàng)建一個函數(shù),使這個函數(shù)不論怎么調(diào)用都有同樣的this值。
示例:
function func(arg1, arg2) {};
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
var obj = {
a: 123,
};
var foo = {
getV: function(k) {
return this.a + k;
}
}
console.log(foo.getV.bind(obj, 1)()); //124
console.log(foo.getV.call(obj, 1)); //124
console.log(foo.getV.apply(obj, [1])); //124
區(qū)別:
- 傳參不同,調(diào)用不同。bind 是返回對應(yīng)函數(shù),便于稍后調(diào)用;apply 、call 則是立即調(diào)用 。
js的假值有哪些
0、-0、NaN、undefined、null、""、false
js面向?qū)ο笈c原型、原型鏈相關(guān)
原型(對象屬性)
Javascript規(guī)定,每個函數(shù)都有一個prototype對象屬性。只有函數(shù)才有prototype屬性。指向另一個對象(原型鏈上面的)。prototype(對象屬性)的所有屬性和方法,都會被構(gòu)造的實例繼承。這意味著,我們可以把那些不變的屬性和方法,直接定義在prototype對象屬性上。
prototype就是調(diào)用構(gòu)造函數(shù)所創(chuàng)建的那個實例對象的原型(proto)。
prototype可以讓所有對象實例共享它所包含的屬性和方法。也就是說,不必在構(gòu)造函數(shù)中定義對象信息,而是可以直接將這些信息添加到原型中。
原型鏈 (JS原型與原型鏈繼承)
實例對象與原型之間的連接,叫做原型鏈。proto( 隱式連接 )
JS在創(chuàng)建對象的時候,都有一個叫做proto的內(nèi)置屬性,用于指向創(chuàng)建它的函數(shù)對象的原型對象prototype。
內(nèi)部原型(proto)和構(gòu)造器的原型(prototype)
1、每個對象都有一個proto屬性,原型鏈上的對象正是依靠這個屬性連結(jié)在一起
2、作為一個對象,當(dāng)你訪問其中的一個屬性或方法的時候,如果這個對象中沒有這個 方法或?qū)傩?,那么Javascript引擎將會訪問這個對象的proto屬性所指向上一個對 象,并在那個對象中查找指定的方法或?qū)傩?,如果不能找到,那就會繼續(xù)通過那個對象 的proto屬性指向的對象進(jìn)行向上查找,直到這個鏈表結(jié)束。
淺談constructor
在 Javascript 語言中,constructor 屬性是專門為 function 而設(shè)計的,它存在于每一個 function 的prototype 屬性中。這個 constructor 保存了指向 function 的一個引用。
__proto__
JS 在創(chuàng)建對象(不論是普通對象還是函數(shù)對象)的時候,都有一個叫做 __proto__ 的內(nèi)置屬性,用于指向創(chuàng)建它的構(gòu)造函數(shù)的原型對象。
var obj = []
console.log(obj.__proto__ === Array.prototype) //true
示例題
function Foo() {}
var f1 = new Foo()
請回答以下問題:
- f1.
__proto__=== - f1.constructor ===
- f1.prototype ===
- Foo.
__proto__=== - Foo.prototype ===
- Foo.constructor ===
- Foo.prototype.constructor ===
- Foo.prototype.
__proto__===
繼承的幾種寫法及優(yōu)缺點
this指向相關(guān)
- this 永遠(yuǎn)指向函數(shù)運行時所在的對象,而不是函數(shù)被創(chuàng)建時所在的對象。
- this的指向在函數(shù)定義的時候是確定不了的,只有函數(shù)執(zhí)行的時候才能確定this到底指向誰,實際上this的最終指向的是那個調(diào)用它的對象
js new一個對象都經(jīng)過了什么
new對象:
function Person(name, age) {
this.name = name;
this.age = age;
}
var person = new Person("Alice", 23);
new一個對象的四個過程:1、創(chuàng)建一個空對象?
var obj = new Object();
2、讓Person中的this指向obj,并執(zhí)行Person的函數(shù)體?
var result = Person.call(obj);
3、設(shè)置原型鏈,將obj的proto成員指向了Person函數(shù)對象的prototype成員對象?
obj.__proto__ = Person.prototype;
4、判斷Person的返回值類型,如果是值類型,返回obj。如果是引用類型,就返回這個引用類型的對象。
if (typeof(result) == "object")
person = result;
else
person = obj;
宏任務(wù)和微任務(wù)
- 宿主環(huán)境提供的叫宏任務(wù),宿主環(huán)境包括瀏覽器和node
- 宏任務(wù)有:
- script
- setTimeout
- setInterval
- setImmediate
- requestAnimationFrame
- I/O
- UI渲染
- 由語言標(biāo)準(zhǔn)提供的叫微任務(wù),即由語言標(biāo)準(zhǔn)(ECMA)提供的就是微任務(wù),比如ES6提供的promise。
- 微任務(wù)有
- process.nextTick
- MutationObserver
- Promise
執(zhí)行過程
- js引擎將代碼按照類別分到宏任務(wù)和微任務(wù)隊列中
- 執(zhí)行宏任務(wù),執(zhí)行完成,清空微任務(wù)隊列
- 執(zhí)行下一個宏任務(wù),執(zhí)行完成后,清空微任務(wù)隊列
- 依次執(zhí)行宏任務(wù)隊列的下一個宏任務(wù),執(zhí)行完成后,清空微任務(wù)隊列
怎樣理解setTimeout 執(zhí)行誤差
如果當(dāng)前 執(zhí)行棧 所花費的時間大于 定時器 時間,那么定時器的回調(diào)在 宏任務(wù)(macrotask) 里,來不及去調(diào)用,所以這個時間會有誤差。
如何實現(xiàn)深淺拷貝
淺拷貝
// 1. ...實現(xiàn)
let copy1 = {...{x:1}}
// 2. Object.assign實現(xiàn)
let copy2 = Object.assign({}, {x:1})
// 數(shù)組采用slice、concat方法
深拷貝
// 1. JOSN.stringify()/JSON.parse()
let obj = {a: 1, b: {x: 3}}
JSON.parse(JSON.stringify(obj))
// 2. 遞歸拷貝
function deepClone(obj) {
let copy = obj instanceof Array ? [] : {}
for (let i in obj) {
if (obj.hasOwnProperty(i)) {
copy[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
}
}
return copy
}
JSON.stringify()的缺點
- 會忽略 undefined
- 會忽略 symbol
- 不能序列化函數(shù)
- 不能解決循環(huán)引用的對象
- 不能正確處理new Date()
- 不能處理正則
數(shù)組去重的幾種寫法
let list = [1, 2, 3, 4, 5, 2, 2, 2, 7, 6, 4, 8, 1];
// 創(chuàng)建新數(shù)組,判斷數(shù)據(jù)是否在新數(shù)組中
function arrToRepeat1(arr) {
let repeatList = [];
list.forEach(value => {
if (!repeatList.includes(value)) repeatList.push(value);
})
return repeatList;
}
console.log(arrToRepeat1(list));
// 創(chuàng)建新數(shù)組,通過下標(biāo)判斷是否是重復(fù)的值
function arrToRepeat2(arr) {
let repeatList = [];
list.forEach((value, index) => {
// 左側(cè)的為第一個值的索引,右側(cè)為重復(fù)的當(dāng)前的值的索引
if (arr.indexOf(value) === index) repeatList.push(value);
})
return repeatList;
}
console.log(arrToRepeat2(list));
// 使用set集合
console.log([...new Set(list)]);
數(shù)組排序的問題
默認(rèn)數(shù)組排數(shù)函數(shù)sort有問題,不指定排序方法時,元素會按照轉(zhuǎn)換為的字符串的諸個字符的Unicode位點進(jìn)行排序
常用解決方法
list.sort((a, b) => a - b) // 從小到大
list.sort((a, b) => b - a) // 從大到小
數(shù)組降維
array = array.reduce((acc, val) => acc.concat(val), []); // 一層扁平化
array = [].concat(...array); // 一層扁平化
array = array.flat(); // 一層扁平化
手寫防抖和節(jié)流
js垃圾回收機制,怎么理解js中的內(nèi)存泄露
垃圾回收機制
垃圾回收是釋放已經(jīng)不再需要的變量等,來減少系統(tǒng)所占用的內(nèi)存。
標(biāo)記清除法
工作原理:
當(dāng)變量進(jìn)入環(huán)境時(例如在函數(shù)中聲明一個變量),將這個變量標(biāo)記為“進(jìn)入環(huán)境”,當(dāng)變量離開環(huán)境時,則將其標(biāo)記為“離開環(huán)境”。標(biāo)記“離開環(huán)境”的就回收內(nèi)存。
工作流程:
- 垃圾收集器會在運行的時候會給存儲在內(nèi)存中的所有變量都加上標(biāo)記。
- 去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標(biāo)記。
- 那些還存在標(biāo)記的變量被視為準(zhǔn)備刪除的變量。
- 最后垃圾收集器會執(zhí)行最后一步內(nèi)存清除的工作,銷毀那些帶標(biāo)記的值并回收它們所占用的內(nèi)存空間。
引用計數(shù)法
跟蹤記錄每個值的引用次數(shù)(值)
流程:
- 聲明一個變量,并給這個變量賦值,這個值的引用次數(shù)就是1
- 同一個值被賦給另一個變量,這個值的引用次數(shù)+1
- 變量的值被更改了 那原本那個值的引用次數(shù)-1
- 引用次數(shù)為0時,說明沒辦法訪問這個值了
- 垃圾收集器下一次運行時,會釋放引用次數(shù)為0的值所占的內(nèi)存
缺點:循環(huán)引用將不能被自動回收。
內(nèi)存泄漏
內(nèi)存泄漏是由于疏忽或錯誤造成程序未能釋放那些已經(jīng)不再使用的內(nèi)存,造成內(nèi)存的浪費。
引起內(nèi)存泄漏的情況
- 意外的全局變量
- 被遺忘的定時器和定時器的回調(diào)函數(shù)
- 閉包
- 循環(huán)引用問題
- 沒有清理的DOM元素引用