[TOC]es6
基本類型: null,undefined,boolean,number(浮點類型),string,symbol(es6)。
對象:Object。
複製代碼
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof b // b 沒有聲明,可是還會顯示 undefined
typeof [] // 'object'
typeof {} // 'object typeof null // 'object' typeof console.log // 'function' 複製代碼
valueOf面試
對象在轉換基本類型時,首先會調用 valueOf 而後調用 toString。而且這兩個方法你是能夠重寫的。瀏覽器
let a = {
valueOf() {
return 0
toString() {
return '1';
},
// Symbol.toPrimitive ,該方法在轉基本類型時調用優先級最高。
[Symbol.toPrimitive]() {
return 2;
}
}
1 + a // => 3
'1' + a // => '12'
複製代碼
若是是對象,就經過 toPrimitive 轉換對象
若是是字符串,就經過 unicode 字符索引來比較
複製代碼
只有當加法運算時,其中一方是字符串類型,就會把另外一個也轉爲字符串類型。
其餘運算只要其中一方是數字,那麼另外一方就轉爲數字。
而且加法運算會觸發三種類型轉換:將值轉換爲原始值,轉換爲數字,轉換爲字符串。
複製代碼
1 + '1' // '11'
2 * '2' // 4
[1, 2] + [2, 1] // '1,22,1'
// [1, 2].toString() -> '1,2'
// [2, 1].toString() -> '2,1'
// '1,2' + '2,1' = '1,22,1'
// 對於加號須要注意這個表達式 'a' + + 'b'
'a' + + 'b' // -> "aNaN"
// 由於 + 'b' -> NaN
複製代碼
// 新生成了一個對象
// 連接到原型
// 綁定 this
// 返回新對象
function new() {
// 建立一個空的對象
let obj = new Object()
// 得到構造函數
let Con = [].shift.call(arguments)
// 連接到原型
obj.__proto__ = Con.prototype
// 綁定 this,執行構造函數
let result = Con.apply(obj, arguments)
// 確保 new 出來的是個對象
return typeof result === 'object' ? result : obj
}
複製代碼
function Foo() {
return this;
}
Foo.getName = function () {
console.log('1');
};
Foo.prototype.getName = function () {
console.log('2');
};
new Foo.getName(); // -> 1
new Foo().getName(); // -> 2
// new Foo() 的優先級大於 new Foo
複製代碼
new (Foo.getName());
(new Foo()).getName();
// 對於第一個函數來講,先執行了 Foo.getName() ,因此結果爲 1;
// 對於後者來講,先執行 new Foo() 產生了一個實例,
// 而後經過原型鏈找到了 Foo 上的 getName 函數,因此結果爲 2。
複製代碼
function foo() {
console.log(this.a)
}
var a = 1
foo()
var obj = {
a: 2,
foo: foo
}
obj.foo()
// 以上二者狀況 `this` 只依賴於調用函數前的對象,優先級是第二個狀況大於第一個狀況
// 如下狀況是優先級最高的,`this` 只會綁定在 `c` 上,不會被任何方式修改 `this` 指向
var c = new foo()
c.a = 3
console.log(c.a)
// 還有種就是利用 call,apply,bind 改變 this,這個優先級僅次於 new
複製代碼
變量對象 (縮寫爲VO)就是與執行上下文相關的對象,它存儲下列內容:bash
激活對象是函數上下文裏的激活對象AO中的內部對象,它包括下列屬性:閉包
b() // call b
console.log(a) // undefined
var a = 'Hello world'
function b() {
console.log('call b')
}
複製代碼
以上衆所周知由於函數和變量提高的緣由。一般提高的解釋是說將聲明的代碼移動到了頂部。可是更準確的解釋應該是:在生成執行上下文時,會有兩個階段。第一個階段是建立的階段(具體步驟是建立 VO),JS解釋器會找出須要提高的變量和函數,而且給他們提早在內存中開闢好空間,函數的話會將整個函數存入內存中,變量只聲明而且賦值爲 undefined,因此在第二個階段,也就是代碼執行階段,咱們能夠直接提早使用。app
在提高的過程當中,相同的函數會覆蓋上一個函數,而且函數優先於變量提高異步
b() // call b second
function b() {
console.log('call b fist')
}
function b() {
console.log('call b second')
}
var b = 'Hello world'
複製代碼
var foo = 1
(function foo() {
foo = 10
console.log(foo)
}()) // -> ƒ foo() { foo = 10 ; console.log(foo) }
// 內部獨立做用域,不會影響外部的值
複製代碼
循環中使用閉包解決 var 定義函數的問題函數
for ( var i=1; i<=5; i++) {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
// 由於 setTimeout 是個異步函數,全部會先把循環所有執行完畢,這時候 i 就是 6 了,因此會輸出一堆 6。
複製代碼
解決辦法post
第一種使用閉包學習
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j);
}, j * 1000);
})(i);
}
複製代碼
第二種就是使用 setTimeout 的第三個參數
for ( var i=1; i<=5; i++) {
setTimeout( function timer(j) {
console.log( j );
}, i*1000, i);
}
// 第三個參數及之後的參數均可以做爲func函數的參數,例:
function a(x, y) {
console.log(x, y) // 2 3
}
setTimeout(a, 1000, 2, 3)
複製代碼
第三種就是使用 let 定義 i 了
for ( let i=1; i<=5; i++) {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
複製代碼
由於對於 let 來講,他會建立一個塊級做用域,至關於
{ // 造成塊級做用域
let i = 0
{
let ii = i
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
i++
{
let ii = i
}
i++
{
let ii = i
}
...
}
複製代碼
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
複製代碼
let a = {
age: 1
}
let b = {...a}
a.age = 2
console.log(b.age) // 1
複製代碼
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
複製代碼
該方法也是有侷限性的:會忽略 undefined,忽略函數,不能解決循環引用的對象
let obj = {
a: 1,
b: {
c: 2,
d: 3,
},
}
obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let newObj = JSON.parse(JSON.stringify(obj)) // 會報錯
console.log(newObj)
複製代碼
function structuralClone(obj) {
return new Promise(resolve => {
const {port1, port2} = new MessageChannel();
port2.onmessage = ev => resolve(ev.data);
port1.postMessage(obj);
});
}
var obj = {a: 1, b: {
c: b
}}
// 注意該方法是異步的
// 能夠處理 undefined 和循環引用對象
const clone = await structuralClone(obj);
複製代碼
文章爲學習筆記,整理自面譜InterviewMap。複製代碼