原型繼承和class繼承
class:js中并不存在類的概念,class只是語法糖,本質還是函數;
提升&暫時性死區
console.log(a)// ? a() {}
var a=8
function a(){}
復制代碼
1、這里說明函數的提升要優先于變量的提升;函數提升會把整個函數挪到作用域頂部,變量提升只會把聲明挪到作用域頂部
2、如果把函數代碼刪掉,把var變成let,則會報錯a is not defined這時候的報錯則是因為暫時性死區的原因導致,我們不能在聲明前就使用變量;
疑問:為什要有提升這個東西? 存在原因:其實提升存在的根本原因就是為了解決函數間互相調用的情況
原型
每個對象當我們打印的時候下面都會有個__proto__屬性,該屬性指向了原型,原型也是一個對象,且這個對象包含了很多的函數,還有一個 constructor屬性,也就是構造函數,打開 constructor 屬性我們又可以發現其中還有一個 prototype 屬性,并且這個屬性對應的值和先前我們在 proto 中看到的一模一樣。所以我們又可以得出一個結論:原型的 constructor 屬性指向構造函數,構造函數又通過 prototype 屬性指回原型,但是并不是所有函數都具有這個屬性,Function.prototype.bind() 就沒有這個屬性。
總結: Object 是所有對象的爸爸,所有對象都可以通過 proto 找到它 Function 是所有函數的爸爸,所有函數都可以通過 proto 找到它 函數的 prototype 是一個對象 對象的 proto 屬性指向原型, proto 將對象和原型連接起來組成了原型鏈
淺拷貝&&深拷貝
淺拷貝:
1、const obj=Object.assign(target,...sources)將sources對象的所有屬性值拷貝到target對象上,實現的是淺拷貝,改變sources的屬性值,obj不會發生改變
2、通過展開運算符 ... 來實現淺拷貝
let b={...a}
//這時改變a的屬性值b也不會改變
復制代碼
淺拷貝只解決了第一層的問題,如果目標對象的值中還有對象的話,那么就又回到最開始的話題了,兩者享有相同的地址。要解決這個問題,我們就得使用深拷貝了。
深拷貝: 1、通過 JSON.parse(JSON.stringify(object)) 來實現深拷貝
let b = JSON.parse(JSON.stringify(a))
復制代碼
但是此方法會忽略 undefined和symbol、不能序列化函數、不能解決循環引用的對象
2、使用new MessageChannel()
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: 2}
}obj.b.d = obj.b// 注意該方法是異步的
// 可以處理 undefined 和循環引用對象
const test = async () => {const clone = await structuralClone(obj)console.log(clone)
}
test()
復制代碼
bind(),call(), apply()
前面講到了this指向 那么這次就說說改變this指向的三個方法
bind方法是用于創建一個函數,使這個函數不論怎么調用都有同樣的this,該方法返回一個新函數,你必須調用它才會生效,參數跟call一樣第一個是this,參數是直接放進去的,第二第三第n個參數全都用逗號分隔,直接放到后面
call與bind的區別就是它返回的是一個執行結果不需要調用
apply它與call的區別是第二個參數接受的是一個數組
以上三個的參數都沒有限制可以是各種類型
bind()函數內部怎么實現的:
Function.prototype.myBind=function(context){if(typeof this!=='function'){throw new TypeError('err')}const _this=thisconst args=[...arguments].slice(1)//返回一個函數return F(){//最后來說通過 new 的方式,在之前的章節中我們學習過如何判斷 //this,對于 new 的情況來說,不會被任何方式改變 //this,所以對于這種情況我們需要忽略傳入的 thisif(this instanceof F){return new _this(...args,...arguments)}//對于直接調用來說,這里選擇了 apply //的方式實現,但是對于參數需要注意以下情況:因為 bind //可以實現類似這樣的代碼 f.bind(obj, //1)(2),所以我們需要將兩邊的參數拼接起來,于是就有了這樣的實現 //args.concat(...arguments)return _this.apply(context,args.concat(...arguments))}
}
復制代碼
call()函數內部怎么實現的:
Function.prototype().myCall=function(context){if(typeoof this!=='function'){throw new TypeError('err')}context=context||window //context是可選參數,不傳默認windowcontext.fn=this //給context創建fn屬性,并將值設置為需要調用的函數const args=[...arguments].slice(1)//因為call可以傳入多個參數,需要將參數剝離出來const resule=context.fn(...args)//調用函數delete context.fn//調用完后刪除對象上的函數return result
}
復制代碼
apply()函數內部怎么實現的:
Function.prototype.myApply = function(context) {if (typeof this !== 'function') {throw new TypeError('Error')}context = context || windowcontext.fn = thislet result// 處理參數和 call 有區別if (arguments[1]) {result = context.fn(...arguments[1])} else {result = context.fn()}delete context.fnreturn result
}
復制代碼
this指向
對于this指向很多人都會混淆,其實很多網上把它說負復雜了看以下場景:
function foo(){console.log(this)
}
foo()
let num=1
const obj={num:1,foo:foo
}
obj.foo()
const c=new foo()
復制代碼
傳一張最近看的小冊的老哥畫的圖,賊穩
instanceof
類似于typeof判斷數據類型,因為起內部機制是通過原型鏈來判斷的所以要更加嚴謹,但是對于原始類型想直接通過instanceof來判斷是不行的,但是我們還是有辦法讓它判斷原始類型的代碼如下:
class PrimitiveString{static [symbol.hasInstance(x){return typeof x=='string'}]
}
console.log('hello' instanceof PrimitiveString) //true
復制代碼
typeof()
typeof()對于原始數據類型:number,string,boolean,symbol(es6引入的新的數據類型表示第一無二的值)除了null都可以顯示正確類型,除了null,對于對象來說除了function都會返回object,所以此方法并不能正確返回數據類型
every()
用于檢測數組中的所有元素是否都符合指定條件 every() 方法使用指定函數檢測數組中的所有元素: 如果數組中檢測到有一個元素不滿足,則整個表達式返回 false ,且剩余的元素不會再進行檢測。 如果所有元素都滿足條件,則返回 true。 注意: every() 不會對空數組進行檢測。 注意: every() 不會改變原始數組。 example1:數組中的元素是否滿足大于等于4
let arr=[12,4,56,78,90]
let boolean=arr.every(item=>item>=4)//true
復制代碼
map
map() 方法返回一個由原數組中的每個元素調用一個指定方法后的返回值組成的新數組。 傳遞給map()的函數的調用方式和傳遞給forEach()的函數的調用方式一樣。但傳遞給map()的函數應該有返回值。注意:map()返回的是新數組:它不修改調用 的數組。
function square(arr){return arr.map(function(item){return item*item})
}
let arr=[1,2,3,4,5,6]
let arr2=square(arr)
console.log(arr2) //[1, 4, 9, 16, 25, 36]
復制代碼
includes
與ES5中的indexOf類似,indexOf用來查找某個元素在數組中的位置有則返回元素位置索引,沒有則返回-1,但是不能判斷是否有NaN的元素;ES6中提供了Array.inclides()此方法只返回true和false即包含不包含,不能定位元素可判斷NaN,可兩個結合使用。 Array.inclides()的第二個參數表示判斷的起始位置,若為負數表示從右起第幾個但是不改變判斷方向。
filter:
用于把Array的某些元素過濾掉返回剩下的元素,參數接受一個函數,函數作用于每一個元素,根據返回值是true或false決定保留還是丟棄該元素。filter是一個高階函數關鍵在于正確實現一個篩選函數。 filter()接收的回調函數有三個,第一個是表示某個元素,第二個表示位置,第三個表示數組本身
example1:數組去重
letr,arr = ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry'];
r = arr.filter(function (element, index, self) {return self.indexOf(element) === index;
});
復制代碼
去除重復元素依靠的是indexOf總是返回第一個元素的位置,后續的重復元素位置與indexOf返回的位置不相等,因此被filter濾掉了。
example2:a數組中刪除b數組中的元素
let a=[1,2,3,4,5,6],b=[1,2,3];
let c=a.filter(item=>!b.includes(item))
console.log(c) //[4,5,6]
復制代碼