也到了自己快找工作的時候了,所以最近在複習前端的知識,這裡是自己對JavaScript的一些知識點的總結,以後會持續更新,分享自己的複習知識,有些是在不懂的時候參考大佬的講解。有些理解不到位的地方還請指正。祝自己找工作順利!!!
1、bind、call、apply
這三個函式都會改變this的指向,call和apply更適用於在函式執行時改變this;而bind會返回一個新的函式,新函式的this由bind傳入的引數決定,所以bind更適用於返回一個新函式,這個函式在將來才會執行,比如DOM新增事件。
// call
Function.prototype.myCall = function (ctx = window, ...arg) {
if (typeof this !== "function") return
ctx.fn = this
let res = ctx.fn(...arg)
delete ctx.fn
return res
}
// apply
Function.prototype.myApply = function (ctx = window, arg) {
if (typeof this !== "function") return
ctx.fn = this
if(!Array.isArray(arg)) {
throw new Error('需要陣列')
}
let res = ctx.fn(...arg)
delete ctx.fn
return res
}
// bind
Function.prototype.newbBind = function(target){
target = target || window
var self = this;
// 這裡的arguments是在呼叫時傳入的引數
var args = [].slice.call(arguments, 1);
var temp = function () {}
function f(){
// 這裡的arguments是bind返回的新函式傳入的參
var _args = [].slice.call(arguments,0)//將一個類陣列轉化為陣列
return self.apply(this instanceof temp? this : target, args.concat(_args))
}
temp.prototype = self.prototype
f.prototype = new temp()
return f
}
複製程式碼
2、函式柯里化
在Lambda演算(一套數理邏輯的形式系統,具體我也沒深入研究過)中有個小技巧:假如一個函式只能收一個引數,那麼這個函式怎麼實現加法呢,因為高階函式是可以當引數傳遞和返回值的,所以問題就簡化為:寫一個只有一個引數的函式,而這個函式返回一個帶引數的函式,這樣就實現了能寫兩個引數的函式了——這就是所謂的柯里化(Currying,以邏輯學家Hsakell Curry命名),也可以理解為一種在處理函式過程中的邏輯思維方式。
在電腦科學中,柯里化(Currying)是把接受多個引數的函式變換成接受一個單一引數(最初函式的第一個引數)的函式,並且返回接受餘下的引數且返回結果的新函式的技術。
function curry(fn, args) {
var length = fn.length;
var args = args || [];
return function(){
newArgs = args.concat(Array.prototype.slice.call(arguments));
if (newArgs.length < length) {
return curry.call(this,fn,newArgs);
}else{
return fn.apply(this,newArgs);
}
}
}
function multiFn(a, b, c) {
return a * b * c;
}
var multi = curry(multiFn);
multi(2)(3)(4);
multi(2,3,4);
multi(2)(3,4);
multi(2,3)(4);
// 參考:https://juejin.im/post/5c9c3989e51d454e3a3902b6
複製程式碼
3、原型、原型鏈
1、原型
原型是function的一個屬性,該屬性本質上是一個物件,它定義了建構函式構造出來的共有祖先,建構函式產生的例項物件可以繼承該屬性的方法和屬性,當例項訪問某個屬性找不到就會順著原型鏈訪問該屬性。
2、原型鏈
有了原型,原型還是一個物件,那麼這個名為原型的物件自然還有自己的原型,這樣的原型上還有原型的結構就構成了原型鏈。
原型鏈是是描述例項物件與建構函式的原型之間的關係,如果例項物件找不到某個屬性或者方法就會到建構函式的prototype上查詢,如果還是找不到就會訪問建構函式prototype屬性的__proto__
屬性,直到null。
4、繼承
在JavaScript中沒有類的概念,傳統語言中類通過拷貝實現繼承,JavaScript通過原型鏈、原型委託的方式實現繼承。
組合繼承:
function Father() {
this.name = "father"
}
Father.prototype.say = function() {
console.log('say')
}
function Child() {
Father.call(this)
this.age = 12
}
Child.prototype = Object.create(Father.prototype)
Child.prototype.constructor = Child
複製程式碼
聖盃模式
let inhert = (function() {
function F(){}
return function(father, child){
F.prototype = father.prototype
child.prototype = new F()
child.prototype.constructor = child
}
})
複製程式碼
5、this
1、什麼是this?
this是JavaScript中的一個關鍵字,被自動定義在所有函式的作用域中。**this是在執行的時候進行繫結,並不是在編寫的時候進行繫結,它的上下文取決於函式呼叫時的各種條件。**this的繫結和函式的宣告位置無關,只取決於函式的呼叫方式。
當一個函式被呼叫的時候,會建立一個活動記錄(也成為執行上下文)。這個記錄會包含函式在哪裡呼叫(呼叫棧)、函式呼叫的方法、傳入的引數等資訊。this就是記錄中的一個屬性,會在函式執行的過程中用到。
2、呼叫位置
呼叫位置指的是函式被調呼叫的位置而不是宣告的位置。
3、繫結規則
1、預設繫結
預設繫結的時候this指向window,預設繫結是指函式不帶任何修飾的函式引用進行呼叫。比如:
function foo() {
console.log(this)
}
foo() // window
複製程式碼
但是需要注意的是在嚴格模式下,預設繫結並不會指向window。
2、隱式繫結
隱式繫結通常以物件作為執行上下文呼叫。但是我們需要明白一個道理:不管是在物件中宣告一個函式,還是先定義再新增函式的引用,嚴格來叔這個函式都不屬於該物件。
隱式繫結規則會把函式呼叫中的this繫結到這個上下文物件,因為呼叫foo的時候this被繫結到該物件,因此this.a等同於obj.a。
物件屬性引用鏈中只有最後一層會影響呼叫的位置。
let obj2 = {
a:2,
foo1:foo1
}
let obj1 = {
a:1,
obj2:obj2
}
function foo1() {
console.log(this.a)
}
obj1.obj2.foo1() // 2
複製程式碼
1、隱式繫結丟失
var a = 'window'
let obj = {
a: 'obj',
foo() {
console.log(this.a)
}
}
let bar = obj.foo
bar()
複製程式碼
因為bar是obj.foo的一個引用,但是實際上引用的是foo函式的本身,因此bar()是一個不帶任何修飾符的呼叫所以是預設繫結,this指向window。
var a = 'window'
let obj = {
a: 'obj',
foo() {
console.log(this.a)
}
}
function doFoo(fn) {
fn()
}
doFoo(obj.foo)
複製程式碼
這裡呼叫doFoo的時候參入了obj.foo作為實參,並將obj.foo賦值給fn,所以fn是foo函式的引用,在呼叫fn的時候也是不帶任何修飾的呼叫,所以是預設呼叫this指向window。
以下這種情況this也是指向window。原因和上面一樣。
var a = 'window'
let obj = {
a: 'obj',
foo() {
console.log(this.a)
}
}
setTimeout(obj.foo, 1000)
複製程式碼
所以上面我們可以看出回撥函式丟失this是非常常見的。
3、顯示繫結
- call
- apply
- bind:bind會返回一個硬編碼的新函式,它會把引數設定為this的上下文並呼叫原始函式。
如果把null或者undefined作為this繫結的物件傳入其中,這些值會被忽略,實際上是預設繫結。
4、new繫結
5、繫結優先順序
new > call、apply、bind > 隱式繫結 > 預設繫結
6、防抖節流
節流(throttle)是防止使用者頻繁操作,造成瀏覽器效能消耗過大
防抖(debounce),就是指觸發事件後在 n 秒內函式只能執行一次,如果在 n 秒內又觸發了事件,則會重新計算函式執行時間。
// 節流函式(throttle)
function throttle (fn, wait=500) {
let pre_time = 0
return function(...arg) {
let curr_time = Date.now()
if(curr_time - pre_time > wait) {
fn.apply(this, arg)
pre_time = curr_time
}
}
}
// 防抖函式(debounce)
function debounce(fn, wait = 500, immediately = true) {
let timer
return function(...arg) {
if(immediately) {
fn.apply(this, arg)
immediately = false
}
clearTimout(timer)
timer = setTimout(()=> {
fn.apply(this, arg)
}, wait)
}
}
複製程式碼
7、Promise
面試常見問題:
1、瞭解 Promise 嗎?
2、Promise 解決的痛點是什麼?
3、Promise 解決的痛點還有其他方法可以解決嗎?如果有,請列舉。
4、Promise 如何使用?
5、Promise 常用的方法有哪些?它們的作用是什麼?如何使用?
6、Promise 在事件迴圈中的執行過程是怎樣的?
7、Promise 的業界實現都有哪些?
8、能不能手寫一個 Promise ?
function myPromise(constructor){
let self=this;
self.status="pending" //定義狀態改變前的初始狀態
self.value=undefined;//定義狀態為resolved的時候的狀態
self.reason=undefined;//定義狀態為rejected的時候的狀態
function resolve(value){
//兩個==="pending",保證了狀態的改變是不可逆的
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
//兩個==="pending",保證了狀態的改變是不可逆的
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕獲構造異常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case "resolved":
onFullfilled(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
}
}
// 來源:https://github.com/forthealllight/blog/issues/4
複製程式碼
1、promise含義
promise是非同步程式設計的一種解決方案,解決了回撥地獄的問題。Promise是一個容器儲存著某個未來才會結束的事件的結果,也可以說是一個物件從它可以獲取非同步操作的訊息。
特點:
- 物件狀態不受外界影響,只有
pending
(進行中)、fulfilled
(已成功)和rejected
(已失敗)三種狀態。 - 一旦狀態改變就不會再改變。只能從
pending
(進行中)到fulfilled
(已成功)或pending
(進行中)到reject
(以失敗)。
2、基本語法
const promise = new Promise(function(resolve, reject) {
if(/*success*/) {
resolve(val)
} else {
reject(val)
}
})
複製程式碼
Promise接受一個函式作為引數,該函式接受兩個引數,它們是兩個函式,由 JavaScript 引擎提供,不用自己部署。
resolve的作用是在非同步操作成功的時候呼叫,並將非同步操作的結果作為引數傳遞出去;reject是在非同步操作失敗的時候呼叫。
Promise例項生成之後可以用then方法分別指定成功和失敗的回撥函式。
promise.then(function() {
/*success*/
}, function() {
/*failure*/
})
複製程式碼
第一個引數是成功時呼叫,第二個是失敗時呼叫,這兩個函式都接受Promise物件傳出的值作為引數,第一個成功時的回撥函式時必須的失敗時的回撥函式不是必須的。
resolve
函式的引數除了正常的值以外,還可能是另一個 Promise 例項,比如像下面這樣。
const p1 = new Promise(function (resolve, reject) {
// ...
});
const p2 = new Promise(function (resolve, reject) {
// ...
resolve(p1);
})
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
// Error: fail
// 這裡p2的狀態決定p1的狀態,p2後的then都是針對p1的
複製程式碼
這裡p2的狀態決定p1的狀態,p2後的then都是針對p1的
Promise的具體例子:
function timeout(ms) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, ms, 'done');
})
}
let p = timeout(100).then((val) => {
console.log(val)
})
複製程式碼
Promise建立之後會立即執行
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// promise
// Hi!
// resolved.
複製程式碼
實現Ajax
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出錯了', error);
});
複製程式碼
3、promise.prototype.then
then方法是定義在原型物件Promise.prototype
上的,它的作用是為 Promise 例項新增狀態改變時的回撥函式。前面說過,then
方法的第一個引數是resolved
狀態的回撥函式,第二個引數(可選)是rejected
狀態的回撥函式。
then方法也可以返回一個新的Promise例項,因此可以採用鏈式呼叫:
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function funcA(comments) {
console.log("resolved: ", comments);
}, function funcB(err){
console.log("rejected: ", err);
});
複製程式碼
4、promise.prototype.catch
用於錯誤的捕獲
// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) { //cb
// success
})
.catch(function(err) {
// error
});
複製程式碼
上面程式碼中,第二種寫法要好於第一種寫法,理由是**第二種(catch)寫法可以捕獲前面then
方法執行中的錯誤,**也更接近同步的寫法(try/catch
)。因此,建議總是使用catch
方法,而不使用then
方法的第二個引數。
5、promise.prototype.finally
finally方法用於執行不管最後狀態如何,都會執行的操作。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
複製程式碼
6、promise.all()
該方法用於將多個Promise例項包裝成一個新的Promise例項。
const p = Promise.all([p1, p2, p3])
複製程式碼
Promise.all()接受一個陣列,陣列的值都是Promise物件,如果不是則會呼叫Promise.resolve()方法。(Promise.all
方法的引數可以不是陣列,但必須具有 Iterator 介面,且返回的每個成員都是 Promise 例項。)
p
的狀態由p1
、p2
、p3
決定,分成兩種情況。
(1)只有p1
、p2
、p3
的狀態都變成fulfilled
,p
的狀態才會變成fulfilled
,此時p1
、p2
、p3
的返回值組成一個陣列,傳遞給p
的回撥函式。
(2)只要p1
、p2
、p3
之中有一個被rejected
,p
的狀態就變成rejected
,此時第一個被reject
的例項的返回值,會傳遞給p
的回撥函式。
注意,如果作為引數的 Promise 例項,自己定義了catch
方法,那麼它一旦被rejected
,並不會觸發Promise.all()
的catch
方法。
7、promise.race()
Promise.race
方法同樣是將多個 Promise 例項,包裝成一個新的 Promise 例項。
const p = Promise.race([p1, p2, p3])
複製程式碼
上面程式碼中,只要p1
、p2
、p3
之中有一個例項率先改變狀態,p
的狀態就跟著改變。那個率先改變的 Promise 例項的返回值,就傳遞給p
的回撥函式。和Promise一樣,陣列的值必須是promise物件,如果不是則會呼叫Promise.resolve()方法。
8、Promise.resolve()
Promise.resolve()方法可以將現有的物件轉換為Promise物件。
Promise.resolve('foo')
// 等同於
new Promise(function(resolve) {
resolve('foo')
})
複製程式碼
9、Promise.reject()
Promise.reject(reason)
方法也會返回一個新的 Promise 例項,該例項的狀態為rejected
。
let p = Promise.reject('foo')
// 等同於
new Promise((resolve, reject) => reject('foo'))
複製程式碼
8、深拷貝
因為陣列和物件都是引用值,所以當我們直接使用=賦值,會是兩個物件的指標指向同一個空間,當我們改變其中一個值的時候,另一個物件也會受到影響。當我們使用深拷貝重新開闢了一個記憶體空間,將該物件的指標指向新開闢的空間。
針對陣列我們可以使用[...arr]
針對物件我們可以使用Object.assign({}, obj)、{...obj}
以上兩種都是淺拷貝
也可以使用JSON.parse(JOSN.stringify(obj))
function deepClone(obj) {
let res
if(typeof obj === "object") {
res = obj.constructor = Array?[]:{}
for(let i in obj) {
res[i] = typeof obj[i] === "object"?deepClone(obj[i]):obj[i]
}
} else {
res = obj
}
return obj
}
複製程式碼
9、JavaScript事件迴圈
JavaScript將任務分為同步任務和非同步任務,在第一次執行的時候會將整個script程式碼看作巨集任務,同步任務進入主執行緒,非同步任務進入Event Table註冊,當滿足條件非同步任務的回撥函式加入到Event Queue佇列中,當主執行緒空閒的時候,會從Event Queue取出對應的函式。巨集任務(script、setTimeout)和微任務(Promise、process.nextTick)分別進入不同的Event Table,它們的執行順序不一樣,當主執行緒空閒的時候首先會清空微任務佇列,然後再拿出一個巨集任務佇列的函式,然後再檢查微任務佇列,如此迴圈。
10、作用域、作用域鏈、執行上下文、預編譯
1、作用域
作用域是在執行時程式碼中的特定變數的有效範圍。作用域決定了程式碼區塊中變數和其他資源的可見性。作用域內層可以看見作用域外層,作用域外層不能看見作用域外層,所以作用域在不同作用域中宣告的變數不會造成汙染和命名衝突。
- 全域性作用域
定義在最外層的函式和變數,未經宣告就賦值的變數,window的屬性。這裡需要注意的是var宣告的全域性變數以及未經宣告就賦值的變數會掛載到window屬性上,但是var宣告的變數不能刪除,未經宣告的變數可以刪除。
- 函式作用域
當函式執行的時候就會在內部建立一個函式作用域,當函式執行完成就會銷燬該作用域。
- 塊級作用域
在ES6之前是沒有塊級作用域的,ES6引入了let、const關鍵字就可以建立塊級作用域。
2、作用域鏈
當在一個函式內部搜尋一個變數的時候,如果該函式沒有宣告該變數,那麼就會順著程式碼執行環境建立的作用域逐層向外搜尋,一直搜尋到全域性作用域。
3、執行上下文
解釋階段:
- 詞法分析
- 語法分析
- 作用域規則確定()
執行階段
- 建立執行上下文
- 執行函式程式碼
- 垃圾回收
JavaScript在解釋階段便會確定作用域規則,但是執行上下文是在函式執行的前一刻。
執行上下文最明顯的就是this指向是在執行的時候確定的。
區別:執行上下文在執行時確定,隨時可以改變;作用域在定義時就確定,並且不會改變。同一作用域下,不同的呼叫會產生不同的執行上下文,從而產生不同的結果。
4、預編譯
- 建立AO物件
- 尋找形參和變數宣告
- 形參實參相統一
- 找函式宣告,函式名作為屬性名,函式體作為屬性值
5、閉包
當函式可以記住並訪問所在的詞法作用域時,就產生了閉包,即使函式是在當前詞法作用域外執行。簡單講,閉包就是指有權訪問另一個函式作用域中的變數的函式。
建立閉包:
- 在函式內部引用外部函式
let a = 1
function foo() {
console.log(a)
}
function bar() {
let a = 2
foo()
}
bar() // 1
複製程式碼
- 在函式內部返回函式
let a = 'window'
function foo() {
let a = 'foo'
return function() {
console.log(a)
}
}
let bar = foo()
bar() // foo
複製程式碼
閉包的應用和缺陷
- 設計私有的方法和變數。
- 容易造成記憶體洩露。
11、DOM事件
1、DOM事件級別
- DOM0:
dom.onclick = function(){}
- DOM2:
dom.addEventListenner('click',function(){})
- DOM3:
dom.addEventListenner('keyup',fuction(){})
,增加了事件型別
2、DOM事件模型
dom.addEventListenner('keyup',fuction(){}, false|true)
,第三個引數為false表示在冒泡階段觸發,第三個引數為true表示在捕獲階段觸發。先捕獲後冒泡。
- 捕獲:父元素到子元素,
dom.addEventListenner('keyup',fuction(){}, true)
- 冒泡:子元素到父元素,
dom.addEventListenner('keyup',fuction(){}, false)
事件委託就是基於事件冒泡的,當子元素觸發點選事件會冒泡到父元素,然後通過e.target來判斷子元素。
3、DOM事件流
通過冒泡或者捕獲怎麼到達目標物件的階段?
事件首先通過捕獲到達目標元素,再通過目標元素冒泡到window物件,即先捕獲後冒泡。
4、Event常見物件
- 阻止預設行為:e.preventDefault()
- 阻止冒泡:e.stopPropagation()
- 阻止其他繫結的事件的執行(事件響應優先順序):e.stopImmediatePropagation()
- e.target
- e.currentTarget
5、自定義事件
參考:www.jianshu.com/p/71bb3cf19…
// 1.第一種
// 定義
let eve = new Event('coustome')
// 繫結
dom.addEventListenner('coustome', function(){})
// 觸發
dom.dispatch(eve)
// 2.第二種,可以新增資料
let eve1 = new CustomoeEvent('coustome', {data})
複製程式碼
12、new
- 建立一個新物件
- 將該物件的
__proto__
屬性指向函式的prototype
屬性 - 將this指向該物件
- 如果該函式沒有顯示的返回物件則返回建立的物件
function New(fn, ...arg) {
let res = {}
if(fn.prototype !== null) {
res = Object.create(fn.prototype)
}
let ret = fn.apply(res, arg)
if(ret === "object" || ret === "function" && ret !== null) {
return ret
}
retrun res
}
複製程式碼
13、JavaScript資料型別
- object:包括Function、Date、Array等
- number:數值,NaN和自身不相等,但是可以通過Object.is()來判斷
- string:字串
- boolean:布林
- null:原型鏈的終點
- undefined:表示變數宣告還沒有被賦值
- symbol:ES6新增,表示獨一無二的值
1、隱式轉換
2、顯示轉換
3、包裝類
語法:let str = new String('hello world')
當我們宣告一個字串變數的時候let str1 = 'hello'
,這是字面量的形式,並且是一個不可變的值。我們訪問str1.length
屬性、或其他屬性的時候,就會把該變數轉換成為一個String物件(這裡通常叫做包裝類),因為宣告的字串沒有該屬性,只有轉換為包裝類才有。在JavaScript中會把字串字面量轉化成String物件。
4、null、undefined比較
null在數值轉換時被轉換為0,undefined會被轉換為NaN
- nudefined
undefined只有一個值,即undefined。以下情況會出現undefined:
- 定義變數,但是沒有初始化;
- 呼叫某個函式時,實參個數小於形參個數時,未實參化的形參在函式呼叫過程中的值是undefined;
- 訪問物件沒有的屬性
- 函式預設的返回值
- 為初始化的變數執行typeof
- 未宣告的變數執行typeof
- null
null也只有一個值,但是當我們執行typeof null
的時候,會返回object。我們可以理解為null是一個空指標物件,還沒有儲存物件。以下幾種情況會使用出現null:
- 手動設定為null,比如在釋放變數的時候
- 未獲取到DOM節點
- 原型鏈頂端
- 在正則捕獲的時候,如果沒有捕獲到結果,預設也是null
5、判斷資料型別
- typeof
不能區別null、物件、陣列、正規表示式等
- instanceof
是基於原型鏈操作的:A instanceof B,判斷A的原型鏈上有沒有B的原型
- Object.prototype.toString.call()
比較好的方法,但是IE6/7/8中 Object.prototype.toString.apply(null)返回“[object Object]”。
- constructor
14、物件
1、語法
物件宣告可以使用字面量形式和建構函式形式
let obj = {}
let obj1 = new Object()
複製程式碼
這兩種方法生成的物件是一樣的,區別在於字面量形式可以新增多個鍵值對、建構函式形式只能逐個新增。
2、內建物件
JavaScript還有一些物件子型別,通常被稱為內建物件。
- String
- Number
- Boolean
- Object
- Function
- Array
- Date
- RegExp
- Error
3、內容
物件中的值通常不會儲存在物件內部,通常情況下,儲存在物件容器內部的是這些屬性的名稱,它們就像指標一樣,指向這些值的真正儲存位置。
1、訪問物件值的方法
let obj = {
a:1
}
obj.a
obj['a']
複製程式碼
obj.a,的語法被稱為屬性訪問,obj['a']的方法被稱為鍵訪問,它們在大都數情況下是可以互換的,區別在於.a要符合命名的規範性,['a']可以接受任意的UTF-8/Unicode字元作為屬性名。比如"super-Fun!",這時候就不可以使用屬性訪問了。
注意:在物件中屬性名永遠都是字串,如果不是者會被轉換為字串。
2、可計算的屬性名
let a = "foo"
let obj = {
[a + '1']: 'hello',
[a + '2']: 'hello2'
}
複製程式碼
4、物件常見的方法
1、屬性描述符
1、檢視屬性描述符:Object.getOwnPropertyDescriptor(obj, props)
語法:
let myObj = {
a: 1
}
console.log(Object.getOwnPropertyDescriptor(myObj, 'a'))
複製程式碼
- value,屬性值
- writable,是否可修改
- configurable,是否可配置,如果為true則可以通過Object.defineProperty(obj, props)方法,來修改這些屬性,所以需要注意的是把configurable修改為false是一個單向操作無法撤銷。除了無法修改,configurable還會禁止刪除該屬性。
- enumerable,是否可列舉
2、設定屬性描述符:Object.defineProperty(obj, props)
語法:
Object.defineProperty(myObj, 'b', {
value:2,
writable: false,
configurable: true,
enumerable: true
})
複製程式碼
所以我們通過設定writable,configurable為false來設定一個物件常量。
2、不變性
1、通過設定writable,configurable為false來設定一個物件常量。
2、禁止擴充:Object.preventzectensions(obj)
語法:
let myObj1 = {
a: 1
}
Object.preventExtensions(myObj1)
myObj1.b = 2
myObj1.b // undefined
複製程式碼
3、密封:Object.seal(obj)
實際上這個方法會呼叫Object.preventzectensions(obj)方法,並將現有屬性的configurable設為false,所以密封之後既不能新增新的屬性,也不能刪除和配置現有屬性。
4、凍結:Object.freeze(obj)
這個方法會呼叫Object.seal()方法,並將現有屬性的writable設為false,故既不能新增新的屬性,也不能刪除、配置、修改現有屬性。
3、get、set
get、set會劫持你對物件資料的操作。
let data = {}
Object.defineProperty(data, 'key', {
// value: 1,
enumerable: true,
configurable: false, // 不能再定義
get: function () {
// Dep.target && dep.addDep(Dep.target)
return this.value
},
set: function (newVal) {
if (newVal === this.value) {
return
}
console.log(`發生了變化${this.value}=>${newVal}`)
this.value = newVal
// dep.notify() // 通知所有訂閱者
}
})
複製程式碼
4、存在性
1、in:檢查物件及原型鏈
2、hasOwnProperty()
5、其他常見方法
- Object.keys(),返回所有可列舉屬性
- Object.values(),返回所有可列舉屬性的值
- Object.entries(),返回所有可列舉屬性的鍵和值
- Object.getOwnPropertyNames,返回所有屬性,不管是否可列舉
15、陣列
1、類陣列
具有length屬性,可以通過數字下標訪問元素,如arguments、獲取的DOM節點。Array.from(arguments)可以將一個類陣列轉化為陣列
2、陣列常見方法
- push\pop:在陣列尾部新增刪除元素
- unshift\shift:在陣列頭部新增刪除元素
- concat:合併陣列
- join:
- slice:切片陣列,返回一個新的陣列
- splice:刪除、修改、增加陣列元素
- sort:排序,sort((a,b)=>{return a - b})
3、去重
// es6最簡單的方式
[...new Set(arr)]
function unique(arr) {
let list = [...arr]
let res = []
list.forEach(item => {
if(!res.include(item)) {
res.push(item)
}
})
return res
}
複製程式碼