本篇文章屬於知識總結型,概括出許多比較零散的知識點,都是乾貨噢~css
若是你是小白那麼這篇文章正好適合你,若是你是老手那麼不妨鞏固一下看看還有哪些邊角料沒補!html
建議:適合有js基礎的小夥伴觀看,篇幅較長,建議先收藏再慢慢瀏覽前端
整整花了一週時間總結了一些比較重點也有些比較偏的知識,但願各位小夥伴慢慢品嚐,若是有不對的地方或者是須要優化的地方望請告知,儘可能給你們呈現最有價值的文章。我的水平有限,還請各位大佬指點迷津。但願各位看了這篇文章能有本身的想法,在前端道路上還很漫長,與我一同探索吧!html5
1、 變量類型node
2、 深拷貝與淺拷貝jquery
3、 原型與原型鏈ios
4、 實現class與extendsnginx
5、 繼承與實現es6
6、 做用域、執行上下文與閉包web
7、 this
9、 同步與異步
12、 改變數組自己的api
十4、 ajax與fetch
十5、 WebSocket
十7、 長鏈接與短鏈接
十8、 存儲
十9、 跨域
二12、 事件
二十3、 總結
二十4、 其餘文章
// 如下結果都爲true
console.log([5]==5,['5']==5)
console.log({name:'5'}=='[object Object]')
console.log('5'==5,true==1,false==0)
console.log(undefined==null)
console.log([5,6]=='5,6',['5','6']=='5,6')
複製代碼
大白話:優先比較類型,同類型,比大小,非原始,調ToPrimitive,爲對象調valueOf,還非原始調toString,最後還非原始則報錯,若是爲原始則進行類型對比,若是不一樣類型再轉換,以後對比大小。
所謂==比較就是要轉換成同類型比較,若是沒法轉成同類型就報錯
優先比類型,再比null與undefined,再比string和number,再比boolean與any,再比object與string、number、symbol;以上若是轉爲原始類型比較,則進行類型轉換,直到類型相同再比較值的大小。這就是==的隱式轉換對比,比較繞,給個圖就清晰了!
以下爲判斷步驟
思考?如何判斷此表達式(注意==!與!==) []==![]
雖然過程複雜,記住判斷的思路便可,非對象之間,先類型轉換再比大小,對象比較則調用獲取原始值方法再進一步比較。
以下爲toString與valueOf轉換
const a=[]
const b=a
a===b //true
---------------
const a=[]
const b=[]
a===b //false
複製代碼
判斷數組
Array.isArray()
判斷數組[] instanceof Array
判斷是否在Array的原型鏈上,便可判斷是否爲數組[].constructor === Array
經過其構造函數判斷是否爲數組Object.prototype.toString.call([])
判斷值是否爲'[object Array]'來判斷數組判斷對象
Object.prototype.toString.call({})
結果爲'[object Object]'則爲對象{} instanceof Object
判斷是否在Object的原型鏈上,便可判斷是否爲對象{}.constructor === Object
經過其構造函數判斷是否爲對象判斷函數
func typeof function
判斷func是否爲函數func instanceof Function
判斷func是否爲函數func.constructor === Function
判斷是否爲函數Object.prototype.toString.call(func)
判斷值是否爲'[object Function]'來判斷func判斷null
null===null
來判斷是否爲null(!a && typeof (a) != 'undefined' && a != 0 && a==a)
判斷a是否爲nullObject.prototype.__proto__===a
判斷a是否爲原始對象原型的原型即nulltypeof (a) == 'object' && !a
經過typeof判斷null爲對象,且對象類型只有null轉換爲Boolean爲false判斷是否爲NaN
isNaN(any)
直接調用此方法判斷是否爲非數值一些其餘判斷
Object.is(a,b)
判斷a與b是否徹底相等,與===基本相同,不一樣點在於Object.is判斷+0不等於-0
,NaN等於自身
prototypeObj.isPrototypeOf(object)
判斷object的原型是否爲prototypeObj,不一樣於instanceof,此方法直接判斷原型,而非instanceof 判斷的是右邊的原型鏈一個簡單的類型驗證函數
function isWho(x) {
// null
if (x === null) return 'null'
const primitive = ['number', 'string', 'undefined',
'symbol', 'bigint', 'boolean', 'function'
]
let type = typeof x
//原始類型以及函數
if (primitive.includes(type)) return type
//對象類型
if (Array.isArray(x)) return 'array'
if (Object.prototype.toString.call(x) === '[object Object]') return 'object'
if (x.hasOwnProperty('constructor')) return x.constructor.name
const proto = Object.getPrototypeOf(x)
if (proto) return proto.constructor.name
// 沒法判斷
return "can't get this type"
}
複製代碼
在項目中有許多地方須要數據克隆,特別是引用類型對象,咱們沒法使用普通的賦值方式克隆,雖然咱們通常使用第三方庫如lodash來實現深拷貝,可是咱們也須要知道一些其中的原理
Object.assign({},obj)
淺拷貝objectobj1={...obj2}
經過spread展開運算符淺拷貝obj2Object.fromEntries(Object.entries(obj))
經過生成迭代器再經過迭代器生成對象Object.create({},Object.getOwnPropertyDescriptors(obj))
淺拷貝objObject.defineProperties({},Object.getOwnPropertyDescriptors(obj))
淺拷貝obj簡單實現淺拷貝
// a原拷貝對象,b新對象
for (const key in a) {
b[key] = a[key]
}
------------------------------------------
for (const key of Object.keys(a)) {
b[key] = a[key]
}
複製代碼
淺拷貝只拷貝一層屬性對於引用類型沒法拷貝
JSON.parse(JSON.stringify(obj))
經過JSON的2次轉換深拷貝obj,不過沒法拷貝undefined與symbol屬性,沒法拷貝循環引用對象簡單深拷貝
//簡單版深拷貝,只能拷貝基本原始類型和普通對象與數組,沒法拷貝循環引用
function simpleDeepClone(a) {
const b=Array.isArray(a) ? [] : {}
for (const key of Object.keys(a)) {
const type = typeof a[key]
if (type !== 'object' || a[key] === null) {
b[key] = a[key]
} else {
b[key] = simpleDeepClone(a[key])
}
}
return b
}
//精簡版深拷貝只能拷貝基本原始類型和普通對象與數組,能夠拷貝循環引用
function deepClone(a, weakMap = new WeakMap()) {
if (typeof a !== 'object' || a === null) return a
if (s = weakMap.get(a)) return s
const b = Array.isArray(a) ? [] : {}
weakMap.set(a, b)
for (const key of Object.keys(a)) b[key] = clone(a[key], weakMap)
return b
}
//js原生深拷貝,沒法拷貝Symbol、null、循環引用
function JSdeepClone(data) {
if (!data || !(data instanceof Object) || (typeof data == "function")) {
return data || undefined;
}
const constructor = data.constructor;
const result = new constructor();
for (const key in data) {
if (data.hasOwnProperty(key)) {
result[key] = deepClone(data[key]);
}
}
return result;
}
複製代碼
比較完善的深拷貝
//深拷貝具體版,非徹底,但大部分均可以
function deepClonePlus(a, weakMap = new WeakMap()) {
const type = typeof a
if (a === null || type !== 'object') return a
if (s = weakMap.get(a)) return s
const allKeys = Reflect.ownKeys(a)
const newObj = Array.isArray(a) ? [] : {}
weakMap.set(a, newObj)
for (const key of allKeys) {
const value = a[key]
const T = typeof value
if (value === null || T !== 'object') {
newObj[key] = value
continue
}
const objT = Object.prototype.toString.call(value)
if (objT === '[object Object]' || objT === '[object Array]') {
newObj[key] = deepClonePlus(value, weakMap)
continue
}
if (objT === '[object Set]' || objT === '[object Map]') {
if (objT === '[object Set]') {
newObj[key] = new Set()
value.forEach(v => newObj[key].add(deepClonePlus(v, weakMap)))
} else {
newObj[key] = new Map()
value.forEach((v, i) => newObj[key].set(i, deepClonePlus(v, weakMap)))
}
continue
}
if (objT === '[object Symbol]') {
newObj[key] = Object(Symbol.prototype.valueOf.call(value))
continue
}
newObj[key] = new a[key].constructor(value)
}
return newObj
}
複製代碼
刨析深拷貝(我的思路)
Reflect.OwnKeys(obj)
取出對象自身全部的鍵,包括Symbol的鍵也能取出Object.prototype.toString.call(obj)
來進行對象具體類型的判斷以上就是本人在實現過程當中的思路,可能講的比較囉嗦,可是我仍是但願使用通俗的話讓各位明白,表達能力有限,望諒解。
接下來讓咱們看看WeakMap的好處
let obj = {
name: {
age: [{
who: 'me'
}]
}
}
let wm = new WeakMap()
deepClonePlus(obj, wm)
obj=null
console.dir(wm) // No properties 即爲空
複製代碼
從上面能夠看出若是原拷貝對象被清空那麼WeakMap保存的拷貝表也將被清空,總的來講方便一點,總比麻煩一點好
看看這種狀況
const obj = {
name: {
age: [{
who: 'me'
}]
}
}
let wm = new WeakMap()
console.time('start')
for (let i = 0; i < 1000000; i++) {
deepClonePlus(obj, wm) // wm爲手動傳入的weakmap
// 此處爲了與下面對比,這裏故意重置weakmap存儲的拷貝值
wm = new WeakMap()
}
console.timeEnd('start') // 耗時2645ms
------------------------------------------------
let wm = new WeakMap()
let m
console.time('start')
for (let i = 0; i < 1000000; i++) {
deepClonePlus(obj, wm)
// 這次爲對照組,也執行建立WeakMap可是不重置以前拷貝的wm
m = new WeakMap()
}
console.timeEnd('start') // 耗時73ms
複製代碼
從以上對比能夠看出若是是屢次拷貝同一對象,最好使用WeakMap來存儲拷貝表,那麼以後的每次拷貝只需從拷貝表中取出值便可,因爲是淺拷貝因此時間較短(注意:不過這種直接從WeakMap中取出的值屬於淺拷貝,使用同一個wm對象拷貝出來的都是淺拷貝,若是每一個都須要深拷貝那麼只能每次從新建立WeakMap)
爲了方便後續講解,這裏先介紹幾個知識點:
1、__proto__
屬性
對象的
__proto__
屬性並不是ECMAScript標準,因爲早期沒法獲取對象原型即對象內部[[Prototype]]屬性,各大瀏覽器廠家對Object.prototype經過訪問描述符實現__proto__
的getter與setter來達到訪問調用對象的[[Prototype]],[[Prototype]]屬性屬於對象內部屬性沒法直接訪問,此屬性指向對象原型。
__proto__
大體實現
Object.defineProperty(Object.prototype,'__proto__',{
get: function(){
return Object.getPrototypeOf(this) // 獲取引用對象的[[Prototype]]
},
set: function(o){
Object.setPrototypeOf(this,o) // 設置引用對象[[Prototype]]屬性關聯的原型爲o
return o
}
})
複製代碼
因此本質上是經過訪問器屬性來獲取與設置對象關聯的原型,能夠理解經過__proto__
能獲取與設置原型的引用
這裏先把普通對象的__proto__
屬性就稱呼爲對象原型,以便接下來的講解
2、函數的prototype屬性
全部函數都有的prototype屬性,js中函數也屬於對象的子類型,因此函數也具有對象的
__proto__
與普通對象相似都指向其原型。而這裏的prototype屬性,是函數獨有的。當函數使用new關鍵字修飾時,咱們能夠理解爲此函數被看成構造函數使用也就是構造器。當函數被用做構造函數調用時,其prototype發揮了做用,使得由構造器new出來對象的__proto__
指向構造函數的prototype。
如下演示函數prototype屬性在實例化時的做用
function Foo(){} // 定義構造函數
console.dir(Foo.prototype) // 定義Foo構造函數時,自動建立的「乾淨的實例原型」,在原型鏈第二幅圖的左下角有體現
const obj = new Foo() //建立一個實例對象
console.dir(obj.__proto__===Foo.prototype) // true,表名實例關聯的原型即爲構造函數的prototype指向的原型對象
複製代碼
爲了便於講解,這裏把函數的prototype稱呼爲構造器原型,以便接下來的講解。這裏要區分函數的__proto__
屬性是做爲對象時,關聯的原型(即對象原型),函數的prototype
做爲構造函數調用時關聯的原型(即構造器原型),這裏要先弄清楚其中的區別,以便接下來的講解
3、各種方法與屬性的統稱
構造函數中定義的方法,咱們統稱爲靜態方法,構造函數中定義的屬性咱們統稱爲靜態屬性。在原型中定義的屬性,咱們統稱爲原型屬性,在原型中定義的方法,咱們統稱爲原型方法。實例中的屬性以及方法,咱們也就稱呼爲實例屬性/方法。固然方法也屬於屬性,只是咱們一般把定義在對象中的函數稱爲方法
__proto__
屬性,此屬性實際上是個訪問器屬性,並非真實存在的屬性,或者能夠使用es6的Reflect.getPrototypeOf(obj)
和Object.getPrototypeOf(obj)
方法獲取對象的原型,其關係Reflect.getPrototypeOf({}) === Object.getPrototypeOf({}) === {}.__proto__
__proto__
(與普通對象相似),還有一個是函數專有的prototype
屬性,由於函數有雙重身份,便可以是實例也能夠是構造器,因此關係比較特殊Object.prototype
的原型Object.prototype.__proto__
就指向null,字典對象的原型也爲null(把對象的__proto__
設置爲null,或者使用Object.create(null)
建立一個沒有原型的字典對象,可是這個對象仍是屬於對象類型),因此原始對象原型(Object.prototype)就是最原始的原型,其餘對象類型都要繼承自它。這裏會詳細介紹原型、原型鏈、實例、構造器的關係 先看最原始的關係
由如上關係能夠驗證console.log(Function.prototype.__proto__.constructor.__proto__.constructor === Function) //true
__proto__
就是Function,可是構造器的prototype屬性指向的原型,是此構造器實例化出來的實例所指向的原型;簡單說構造器的prototype就是做爲它的實例的原型看看函數的原型鏈
const Class = (function () {
function Constructor(name) {
this.name = name
}
//添加原型方法
Constructor.prototype.getName = function name(name) {
console.log('原型方法getName:' + this.name);
}
//添加原型屬性
Constructor.prototype.age = '原型屬性age'
//添加靜態方法
Constructor.log = function log() {
console.log('我是構造器的靜態方法log');
}
//添加靜態屬性
Constructor.isWho = '構造器靜態屬性isWho'
return Constructor
})()
const i = new Class('我是實例')
複製代碼
實現class語法糖,只需封裝一層函數。
以上只實現class的定義,接下來要實現可以兼容繼承的寫法
//父類
const Parent = (function () {
function Constructor(age) {
this.age = age
}
Constructor.prototype.getName = function () {
console.log(this.name);
}
return Constructor
})()
//子類
const Class = (function (_Parent = null) {
if (_Parent) {
Constructor.prototype = Object.create(_Parent.prototype, {
constructor: {
value: Constructor,
enumerable: false,
writable: true,
configurable: true
}
})
Constructor.__proto__ = _Parent
}
function Constructor(name, age) {
_Parent ? _Parent.call(this, age) : this
this.name = name
}
Constructor.prototype.getAge = function () {
console.log(this.age);
}
return Constructor
})(Parent)
複製代碼
__proto__
function Parent(){} // 定義父類構造器
function Children(){} // 定義子類構造器
let ChildPrototype = Children.prototype // 構造器原型
let ChildPrototypeProto = Children.prototype.__proto__ // 構造器原型的對象原型
// 方法一
ChildPrototypeProto = Parent.prototype // 父類構造器原型做爲子類構造器原型(ChildPrototype)的對象原型(ChildPrototypeProto)
//方法二
ChildPrototype = Object.create(Parent.prototype) // Object.create返回一個對象,其__proto__指向傳入的參數,也就實現返回的對象繼承參數對象
//方法三
Object.setPrototypeOf(ChildPrototype, Parent.prototype) // 直接設置參數1的原型(__proto__)爲參數2
複製代碼
以上僅實現了原型之間的繼承
prototype
與__proto__
,其中prototype
是給實例用的,而__proto__
是給本身用的。__proto__
),來實現靜態屬性繼承function Parent() {} // 定義父構造函數
function Children() {} //定義子構造函數
// 定義父構造函數的靜態方法
Parent.foo = function () {
console.log(this.name)
}
// 方法一
Children.__proto__ = Parent // 子構造函數的對象原型指向父構造函數,也就實現繼承
// 方法二
Object.setPrototypeOf(Children, Parent) // 同原型繼承
console.log(Children.foo) // function(){ console.log(this.name) } ,實現繼承
複製代碼
以上即爲構造函數之間經過對象原型繼承靜態屬性,注:函數也是對象
// 定義父構造函數
function Parent(name) {
this.name = name
}
//定義子構造函數
function Children(name,age) {
Parent.call(this,name) // 這裏調用父構造器,實現實例屬性繼承
this.age = age
}
const obj = new Children('tom', 5)
console.log(obj) // {name: 'tom', age: 5} ,實現實例屬性繼承
複製代碼
經過實例屬性繼承,能夠把父構造器中默認生成的實例屬性追加到子構造器實例化出來的對象上
綜合以上繼承,如今實現真正的繼承
// 定義父構造函數,功能:初始化實例name屬性
function Parent(name) {
'use strict'
this.name = name
}
// 定義父構造函數的靜態方法,功能:設置調用對象的name屬性
Parent.setName = function setName(obj, name) {
obj.name = name
}
// 定義父構造器原型(prototype)的方法,功能:獲取調用對象的name屬性
Parent.prototype.getName = function getName() {
return this.name
}
/*-----以上已定義父類的原型方法(獲取name),父類靜態方法(設置name),以及構造器默認初始化的屬性name------*/
// 定義子構造函數,功能:初始化實例age屬性,以及經過父構造器初始化實例name屬性
function Children(name, age) {
'use strict'
Parent.call(this, name) // 調用父構造器,初始化name屬性
this.age = age // 子構造器初始化age屬性
}
// 定義子構造函數的靜態方法,功能:設置調用對象的age屬性
Children.setAge = function setAge(obj, age) {
obj.age = age
}
// 原型繼承
// 設置Children.prototype['[[Prototype]]']= Parent.prototype,此處的'[[Prototype]]'與設置__proto__相同
Children.prototype = Object.create(Parent.prototype)
// 注意此處原型繼承以後,不帶有constructor屬性,應該手動指明爲Children
Object.defineProperty(Children.prototype, 'constructor', {
value: Children,
writable: true, // 可寫
enumerable: false, // 不可枚舉
configurable: true, // 可配置
})
//以上2句能夠直接寫成一句
/*
Children.prototype = Object.create(Parent.prototype, {
constructor: {
value: Children,
writable: true, // 可寫
enumerable: false, // 不可枚舉
configurable: true, // 可配置
}
})
*/
// 因爲子構造器原型方法必須在繼承以後再定義,不然會被繼承覆蓋
// 定義子構造器原型(prototype)的方法,功能:獲取調用對象的age屬性
Children.prototype.getAge = function getAge() {
return this.age
}
// 構造函數(繼承靜態屬性)繼承
// 設置Children.__proto__ = Parent,注意此處不能使用Children = Object.create(Parent),由於Object.create返回的是一個對象不能替換構造函數
Object.setPrototypeOf(Children, Parent)
// 測試父級
const obj = new Parent('tom') // 實例化父級實例
console.log(obj.getName()) // tom
Parent.setName(obj, 'jerry') // 經過父級靜態方法設置name
console.log(obj.getName()) // jerry
console.log(obj instanceof Parent) // true
// 測試子級
const obj1 = new Children(null, 5) // 實例化子級實例
console.log(obj1.getAge()) // 5
Children.setAge(obj1, 8) // 經過子級靜態方法設置age
console.log(obj1.getAge()) // 8
console.log(obj1 instanceof Parent) // true
console.log(obj1 instanceof Children) // true
// 完整測試繼承
const test = new Children('tom', 5) // 實例化子級實例,name='tom',age=5
console.log(test.getName()) // tom
Parent.setName(test, 'jerry') // 經過父級靜態方法設置name=jerry
console.log(test.getName()) // jerry
console.log(test.getAge()) // 5
Children.setAge(test, 8) // 經過子級靜態方法設置age=8
console.log(test.getAge()) // 8
class P {
constructor(name) {
this.name = name
}
static setName(obj, name) {
obj.name = name
}
getName() {
return this.name
}
}
class C extends P {
constructor(name, age) {
super(name)
this.age = age
}
static setAge(obj, age) {
obj.age = age
}
getAge() {
return this.age
}
}
// 這裏就不帶測試了,能夠自行驗證,比對一下有什麼區別
console.dir(Children)
console.dir(C)
複製代碼
實現繼承,須要對原型、構造器、實例屬性都加以實現繼承
a=1 // 隱式全局變量 嚴格模式報錯
var b=2 // 顯式全局變量
console.log(a,b) //1 2
delete a // 嚴格模式報錯
delete b // 嚴格模式報錯
console.log(b,a) // 2 a is not defined
複製代碼
// 如下語句使用let聲明不報錯,說明爲不一樣做用域
for (let i = 0; i < 5; i++) {
let i = 5
}
--------------------------------------------
// 此語句報錯,說明循環體爲條件語句塊的子做用域
// for循環執行順序爲:條件語句塊1->條件語句塊2->循環體->條件語句塊3->條件語句塊2 依次類推
for (let i = 0; i < 5; i=x) { // x is not defined
let x = 5
}
複製代碼
b = 1
function a() {
// 定義b,找到
const b = 2
function s() {
// 使用到b,當前做用域並無,向上找
console.log(b);
}
return s
}
const s = a()
var b = 3
s() // 2
複製代碼
這裏瞭解一下函數、變量提高
console.dir(foo) // foo(){}
function foo() {}
var foo = 5
/*
console.dir(foo) // undefined
var foo = 5
*/
------------------------------
var foo = 5
function foo() {}
console.dir(foo) // 5
複製代碼
從以上代碼結果能夠得出結論:
function fn1() {
var name = 'hi';
function fn2() {
console.log(name);
}
return fn2
}
fn1()() // hi
複製代碼
function makeAdder(x) {
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
複製代碼
這裏看個有趣的東西
function foo(){
let a={name:'me'}
let b={who:'isMe'}
let wm=new WeakMap()
function bar(){
console.log(a) // a被閉包保留
wm.set(b,1) // 弱引用b對象
return wm //wm被閉包保留
}
return bar
}
const wm=foo()()
console.dir(wm) // No properties 即爲空
-------------------------------------------
function foo(){
let a={name:'me'}
let wm=new WeakMap()
function bar(){
console.log(a)
wm.set(a,1)
return wm
}
return bar
}
const wm=foo()()
console.dir(wm) // 保存了對象a與其值1
複製代碼
先看一張圖
function foo(){
console.dir(this) // window ,嚴格下undefined
}
foo()
-----------------------------------------------
function foo(){
console.dir(this) //非嚴格Number對象,嚴格模式 5
}
foo.call(5)
複製代碼
嚴格與非嚴格模式下的this指向是不一樣的,非嚴格老是指向一個對象,嚴格模式能夠爲任意值
執行前
執行後
以上2圖能夠使用chrome開發工具來進行查看程序執行時的相關數據,能夠看到嚴格模式下簡單調用的函數內部的this指向undefined
在沒有明確調用者狀況下函數內部this指向window,嚴格模式下都爲undefined,除非綁定函數的this指向,纔會改變this
// 直接調用函數
function foo() {
console.dir(this) //window,嚴格下 undefined
function boo(){
console.dir(this) //window,嚴格下 undefined
}
boo()
}
----------------------------------------------
// 取出對象中的函數,再進行調用
const obj = {
foo: function foo() {
console.dir(this) //window,嚴格下 undefined
function boo() {
console.dir(this) //window,嚴格下 undefined
}
return boo
}
}
const foo = obj.foo
foo()()
----------------------------------------------
// 直接經過對象調用函數,再調用返回的函數,能夠看出this的指向隨調用對象改變
const obj = {
foo: function foo() {
console.dir(this) //obj,嚴格下 obj
function boo() {
console.dir(this) //window,嚴格下 undefined
}
return boo
}
}
const foo = obj.foo()
foo()
----------------------------------------------
// 基於回調函數也是如此
function foo(func) {
console.dir(this) // window ,嚴格下 undefined
func()
}
foo(function () {
console.dir(this) // window ,嚴格下 undefined
})
複製代碼
函數調用也就是在函數名後面加個(),表示調用,若是函數名前沒有加任何東西,那麼默認爲簡單調用,在嚴格與非嚴格環境下,簡單調用的函數內部this指向undefined與window,可是全局環境下的this永遠爲window
基於對象
當函數做爲對象的方法調用時,不受函數定義方式或者位置影響
// 函數this指向調用者對象
const obj = {
foo: function () {
console.dir(this) // obj1,嚴格下 obj1
function boo() {
console.dir(this) // window,嚴格下 undefined
}
boo()
return boo
}
}
const obj1 = {}
obj1.boo = obj.foo
obj1.boo()
----------------------------------------------
// 不一樣調用對象時,this指向調用者
const obj = {
foo: function () {
console.dir(this) // obj,嚴格下 obj
function boo() {
console.dir(this)
}
boo() // window,嚴格下 undefined
return boo
}
}
const obj1 = {}
obj1.boo = obj.foo()
obj1.boo() // obj1,嚴格下 obj1
----------------------------------------------
// this指向最近的調用者
const obj = {
name: 'obj',
obj1: {
name: 'obj1',
foo: function () {
console.dir(this.name) // obj1
}
}
}
obj.obj1.foo()
複製代碼
基於new關鍵字
// 基於new關鍵字調用的函數內部this指向實例
function foo() {
console.dir(this) // foo實例
console.log(this instanceof foo) //true
console.log(foo.prototype.isPrototypeOf(this)) //true
that = this
}
var that
const f = new foo()
console.log(that === f) // true
----------------------------------------------
// 嵌套函數內部this與調用函數所在環境的this無關
function foo() {
console.dir(this) // foo實例
function boo() {
console.dir(this) //window,嚴格下undefined
}
boo()
}
const f = new foo()
複製代碼
基於定時器與微任務
微任務中的簡單調用的函數this指向window嚴格下指向undefined,而定時器中的回調函數無論在嚴格仍是非嚴格環境下this永遠指向window,說明一點,調用window對象的方法時this指向window也就是全局對象,換句話說,簡單調用的函數若是屬於window自己自帶的方法那麼這個方法的this指向window
// 異步任務中簡單調用的函數都是進入隊列,最後由全局環境調用
const id = setInterval(function () {
console.dir(this) // window ,嚴格下 window
setTimeout(() => {
console.dir(this) // window ,嚴格下 window
clearInterval(id)
});
})
----------------------------------------------
new Promise(function (resolve, reject) {
console.dir(this) // window ,嚴格下 undefined
resolve()
}).then(function (res) {
console.dir(this) // window ,嚴格下 undefined
});
----------------------------------------------
(async function foo() {
function boo() {
console.dir(this) // window ,嚴格下 undefined
}
await boo()
console.dir(this) // window ,嚴格下 undefined
})()
----------------------------------------------
// 定時器的回調最終都會被做爲簡單函數被執行,定時器屬於window對象的方法
function foo(){
setTimeout(function (){
console.log(this) //window ,嚴格下window
})
}
foo.call(5)
----------------------------------------------
// 函數內部的this就是指向調用者,而且能夠看出簡單調用的回調函數中的this也指向window
const obj = {
foo(callback) {
callback()
console.log(this.foo === obj.foo) // true
console.log(this === obj) // true
}
}
obj.foo(function () {
console.log(this) //window ,嚴格下undefined
})
----------------------------------------------
// 經過arguments調用的回調函數中的this指向調用者,注意嚴格與非嚴格下的arguments對象有所不一樣
const obj = {
foo(callback) {
arguments[0]()
console.log(this.foo === obj.foo) // true
console.log(this === obj) // true
}
}
obj.foo(function () {
console.log(this) //arguments對象 ,嚴格下 arguments對象
})
複製代碼
es6引入的箭頭函數,是不具備this綁定,不過在其函數體中能夠使用this,而這個this指向的是箭頭函數當前所處的詞法環境中的this對象,能夠理解爲,this在箭頭函數中是透明的,箭頭函數包不住this,因此函數內部與外部的this爲同一值
// 能夠看出箭頭函數中的this就是其所在環境的this,箭頭函數沒法固定this,由其環境決定
const foo = () => {
console.dir(this) //window ,嚴格下仍是window
}
foo()
----------------------------------------------
// 可見對象中的this指向window,箭頭函數中的this指向對象中的this。因爲只有建立執行上下文才會綁定this指向,而除了全局上下文,只有函數做用域纔會建立上下文環境從而綁定this,建立對象不會綁定this,因此仍是全局this
const obj={
this:this,
foo:()=>{
console.dir(this) //window ,嚴格下 window
}
}
console.dir(obj.this) //window ,嚴格下 window
obj.foo()
---------------------------------------------
// 對象方法內部嵌套箭頭函數,則此箭頭函數的this屬於外部非箭頭函數this。當調用obj.foo時foo函數建立的執行上下文中的this綁定對象obj,而箭頭函數並不會綁定this,因此其this屬於foo下的this,即對象obj
const obj = {
foo: function () {
return () => {
console.dir(this) //obj ,嚴格下 obj
}
}
}
obj.foo()()
複製代碼
最簡單的方法經過apply、call、bind來給函數綁定this
// 經過變量保留父級this,進行對_this變量修改也就達到修改原this的效果
const obj = {
name: 'obj',
foo: function () {
let _this = this
function boo() {
_this.name = 'OBJ'
console.dir(obj.name) // OBJ
}
return boo
}
}
obj.foo()()
複製代碼
這3者的實現其實差很少,bind實現可能會有點不同,都要實現this的改變
Function.prototype.Apply = function (thisArg, args = Symbol.for('args')) {
console.dir(this) //this爲這個方法的調用者=>foo函數
const fn = Symbol('fn') //生成一個不重複的鍵
thisArg[fn] = this || window //把foo函數做爲傳入this的一個方法
args === Symbol.for('args')
? thisArg[fn]()
: thisArg[fn](...args) //調用這方法,傳參
delete thisArg[fn] //使用完刪除
}
var name = 'foo'
var age = 5
function foo(age,height) {
console.log(this.name) // obj
console.log(age) // 3
console.log(height) // null
}
const obj = {
name: 'obj',
age: 3
}
foo.Apply(obj,[obj.age,null])
複製代碼
基本思路同apply,就是傳參形式改變一下,這裏經過arguments獲取參數列表
Function.prototype.Call = function (thisArg) {
console.dir(this) //this爲這個方法的調用者=>foo函數
const fn = Symbol('fn') //生成一個不重複的鍵
thisArg[fn] = this || window //把foo函數做爲傳入this的一個方法
const args = Array.from(arguments).slice(1)
args.length ? thisArg[fn](...args) : thisArg[fn]() //調用這方法,傳參
delete thisArg[fn] //使用完刪除
}
複製代碼
bind函數要可以返回嚴格綁定this與參數後的函數,調用這個返回的函數時有可能還會傳入參數,那麼須要拼接參數
Function.prototype.Bind = function (thisArg) {
const fn = Symbol('fn') //生成一個不重複的鍵
thisArg[fn] = this || window //把foo函數做爲傳入this的一個方法
const f = thisArg[fn] // 負責一份函數
delete thisArg[fn] //刪除原來對象上的函數,可是保留了this指向
const args = Array.from(arguments).slice(1)
return function () {
const arg = args.concat(...arguments)
f(...arg)
}
}
var name = 'foo'
var age = 5
var height = 4
function foo(age, height) {
console.log(this.name) // obj
console.log(age) // 3
console.log(height) // 2
}
const obj = {
name: 'obj',
age: 3
}
foo.Bind(obj, obj.age)(2)
複製代碼
先看一張圖,有個大致架構
如下展現的是事件循環大體流程
如下爲主線程判斷邏輯
// test.html(主線程)
const w= new Worker('postMessage.js')
w.onmessage=function(e){
console.log(e.data);
}
w.postMessage('b') // b is cat
w.terminate() // 手動關閉子線程
----------------------------------------------
// postMessage.js(worker線程)
this.addEventListener('message', (e) => {
switch (e.data) {
case 'a': this.postMessage(e.data+' is tom')
break;
case 'b': this.postMessage(e.data + ' is cat')
break;
default: this.postMessage(e.data + " i don't know")
this.close() // 自身關閉
break;
}
})
複製代碼
模塊化的引入主要是用於解決命名衝突、代碼複用、代碼可讀性、依賴管理等
define(id?,dependencies?,factory)
定義模塊,id可選,爲定義模塊的標識,默認爲模塊文件名不包括後綴,dependencies可選,是當前模塊依賴的模塊路徑數組,factory爲工廠方法,初始化模塊的函數或者對象,若是爲函數將會只執行一次,若是是對象將做爲模塊的輸出require(dependencies,factory)
導入模塊,其中dependencies爲須要導入的模塊路徑數組,factory爲當模塊導入以後的回調函數,此函數的參數列表爲對應導入的模塊require.config({
baseUrl: "js/lib",
paths: {
"jquery": "jquery.min", //實際路徑爲js/lib/jquery.min.js
"underscore": "underscore.min",
}
})
複製代碼
define(id?,deps?,factory)
,id同AMD,deps通常不在其中寫依賴,而是在factory中在須要使用的時候引入模塊,factory函數接收3各參數,參數一require方法,用來內部引入模塊的時候調用,參數二exports是一個對象,用來向外部提供模塊接口,參數三module也是一個對象上面存儲了與當前模塊相關聯的一些屬性和方法seajs.use(deps,func)
加載模塊,deps爲引入到模塊路徑數組,func爲加載完成後的回調函數AMD、CMD的主要區別在於
AMD推崇依賴前置,在定義模塊的時候就要聲明其依賴的模塊 CMD推崇就近依賴,只有在用到某個模塊的時候再去require
module.exports
(不推薦exports)導出模塊對象,經過require(模塊路徑)加載模塊__dirname
表明當前模塊文件所在的文件夾路徑,__filename
表明當前模塊文件夾路徑+文件名pop()
尾部彈出一個元素push()
尾部插入一個元素shift()
頭部彈出一個元素unshift()
頭部插入一個元素sort([func])
對數組進行排序,func有2各參數,其返回值小於0,那麼參數1被排列到參數2以前,反之參數2排在參數1以前reverse()
原位反轉數組中的元素splice(pos,deleteCount,...item)
返回修改後的數組,從pos開始刪除deleteCount個元素,並在當前位置插入itemscopyWithin(pos[, start[, end]])
複製從start到end(不包括end)的元素,到pos開始的索引,返回改變後的數組,淺拷貝arr.fill(value[, start[, end]])
從start到end默認到數組最後一個位置,不包括end,填充val,返回填充後的數組其餘數組api不改變原數組
window.location===document.location
,其中的屬性都是可讀寫的,可是隻有修改href和hash纔有意義,href會從新定位到一個URL,hash會跳到當前頁面中的anchor名字的標記(若是有),並且頁面不會被從新加載// 這行代碼將會使當前頁面重定向到http://www.baidu.com
window.location.href = 'http://www.baidu.com'
----------------------------------------------
// 若是使用hash而且配合input輸入框,那麼當頁面刷新以後,鼠標將會自動聚焦到對應id的input輸入框,
<input type="text" id="target">
<script>
window.location.hash = '#target'
</script>
複製代碼
先看下其擁有的屬性
這裏補充一個origin屬性,返回URL協議+服務器名稱+端口號 (location.origin == location.protocol + '//' + location.host)
window.addEventListener("hashchange", funcRef);
// 或者
window.onhashchange = funcRef;
複製代碼
location方法
assign(url)
,經過調用window.location.assign
方法來打開指定url的新頁面window.location.assign('http://www.baidu.com')
在當前頁面打開百度,可回退replace(url)
,在當前頁面打開指定url,不可回退reload([Boolean])
,調用此方法將會從新加載當前頁面,若是參數爲false或者不填,則會以最優的方式從新加載頁面,可能從緩存中取資源,若是參數爲true則會從服務器從新請求加載資源window.navigator
對象包含有關瀏覽器的信息,能夠用它來查詢一些關於運行當前腳本的應用程序的相關信息document.write("瀏覽器的代碼名:" + navigator.appCodeName + "<br>");
document.write("瀏覽器的名稱:" + navigator.appName + "<br>");
document.write("當前瀏覽器的語言:" + navigator.browserLanguage + "<br>");
document.write("瀏覽器的平臺和版本信息:" + navigator.appVersion + "<br>");
document.write("瀏覽器中是否啓用 cookie :" + navigator.cookieEnabled + "<br>");
document.write("運行瀏覽器的操做系統平臺 :" + navigator.platform + "<br>");
複製代碼
navigator.appCodeName
只讀,任何瀏覽器中,老是返回 'Gecko'。該屬性僅僅是爲了保持兼容性。navigator.appName
只讀,返回瀏覽器的官方名稱。不要期望該屬性返回正確的值。navigator.appVersion
只讀,返回一個字符串,表示瀏覽器的版本。不要期望該屬性返回正確的值。navigator.platform
只讀,返回一個字符串,表示瀏覽器的所在系統平臺。navigator.product
只讀,返回當前瀏覽器的產品名稱(如,"Gecko")。navigator.userAgent
只讀,返回當前瀏覽器的用戶代理字符串(user agent string)以下在不一樣瀏覽器打印的信息
/*
chrome:
Mozilla/5.0
(Macintosh; Intel Mac OS X 10_12_6)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/61.0.3163.91 Safari/537.36
safari:
Mozilla/5.0
(Macintosh; Intel Mac OS X 10_12_6)
AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0
Safari/604.1.38
ios11劉海X:
Mozilla/5.0
(iPhone; CPU iPhone OS 11_0 like Mac OS X)
AppleWebKit/604.1.38 (KHTML, like Gecko)
Version/11.0 Mobile/15A372 Safari/604.1
ipad:
Mozilla/5.0
(iPad; CPU OS 9_1 like Mac OS X)
AppleWebKit/601.1.46 (KHTML, like Gecko)
Version/9.0 Mobile/13B143 Safari/601.1
galxy sansum:
Mozilla/5.0
(Linux; Android 5.0; SM-G900P Build/LRX21T)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/61.0.3163.91 Mobile Safari/537.36
安裝uc瀏覽器:
Mozilla/5.0
(Linux; U; Android 6.0.1; zh-CN; Mi Note 2 Build/MXB48T)
AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0
Chrome/40.0.2214.89 UCBrowser/11.4.9.941 Mobile Safari/537.36
winphone:
Mozilla/5.0
(Linux; Android 5.1.1; Nexus 6 Build/LYZ28E)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/61.0.3163.91 Mobile Safari/537.36
hybrid方法的可能:
Mozilla/5.0
(iPhone; CPU iPhone OS 11_0 like Mac OS X)
AppleWebKit/604.1.38 (KHTML, like Gecko)
Mobile/15A372 weibo/80011134
*/
複製代碼
window.XMLHttpRequest
構造器實例化一個網絡請求對象const XHR = new XMLHttpRequest()
XHR.open(method, url, [ async, [ user, [ password]]])
此方法用來發送一個請求,method爲請求方法,url爲請求地址,async爲boolean值默認爲true即便用異步請求,user和password在請求須要用戶和密碼的時候使用XHR.send(body)
參數爲發生請求主體內容,其格式能夠爲FormData、ArrayBuffer、Document、序列化字符串,在收到響應後,響應的數據會自動填充XHR對象的屬性XHR.setRequestHeader(header,value)
設置請求頭的類型與值,當以post方式發起請求就用設置XHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
此請求頭,值可更改XHR.onreadystatechange = function () { }
XHR.responseText
屬性爲這次響應的數據,爲字符串,多是JSON格式須要JSON.parse解析XHR.responseXML
屬性爲xml形式的數據,能夠經過XHR.responseType = 'document'
和XHR.overrideMimeType('text/xml')
來解析爲XMLXHR.withCredentials
屬性設置爲boolean值,經過此屬性來設置是否使用cookies、authorization等憑證字段XHR.timeout
經過此屬性來設置請求超時時間XHR.ontimeout
經過此屬性來設置請求超時的回調函數,函數的參數爲事件對象XHR.abort()
此方法用來終止網絡請求XHR.getAllResponseHeaders()
此方法用來獲取全部的響應頭XHR.getResponseHeader(name)
此方法用來獲取指定的響應頭XHR.addEventListener(eventname,callback)
方法添加對應的事件監聽,其回調函數接收一個事件對象參數簡單的發起一次請求
// 最簡單的發起一個請求
const XHR = new XMLHttpRequest()
XHR.open('get','http://127.0.0.1:3000/test?key=value')
XHR.send()
XHR.addEventListener('load',(e)=>{
// 服務端返回的是查詢參數
console.log(XHR.response) // {"key":"value"}
})
複製代碼
基於XMLHttpRequest封裝一個請求方法
// 發送的數據
const data = {
name: 'tom'
}
// 請求配置
const config = {
type: "post",
url: "http://127.0.0.1:3000/test",
data: data,
dataType: 'application/json',
success: function (res) {
console.log(res);
},
error: function (e) {
console.log(e);
}
}
// 請求構造器
function Ajax(conf) {
this.type = conf.type || 'get'
this.url = conf.url || ''
this.data = conf.data || {}
this.dataType = conf.dataType || ''
this.success = conf.success || null
this.error = conf.error || null
}
// send方法
Ajax.prototype.send = function () {
if (this.url === '') return
const XHR = new XMLHttpRequest()
XHR.addEventListener('load', () => {
if (XHR.status >= 200 && XHR.status < 300 || XHR.status == 304) {
typeof this.success === 'function' && this.success(XHR.response)
}
})
XHR.addEventListener('error', (e) => {
typeof this.error === 'function' && this.error(e)
})
if (this.type.toLowerCase() === 'get') {
XHR.open('get', this.url)
XHR.send(null)
} else {
XHR.open(this.type, this.url)
XHR.setRequestHeader('Content-Type', this.dataType || 'application/x-www-form-urlencoded')
let data = this.data
if (this.dataType === 'application/json') {
data = JSON.stringify(this.data)
}
XHR.send(data)
}
}
// 發送請求
const ajax = new Ajax(config).send()
複製代碼
因爲網絡請求模塊封裝較繁瑣,這裏就簡單封裝了一下,僅供參考(。^▽^)
// fetch方法返回一個Promise對象,可用then方法接收結果,用catch方法捕獲異常,同Promise使用
// 配置對象具體配置
const config = {
method: 'GET', // 請求方法
headers: { // 頭信息
'user-agent': 'Mozilla/4.0 MDN Example',
'content-type': 'application/json'
},
body: JSON.stringify({ // 請求的 body 信息,Blob, FormData 等
data: 1
}),
mode: 'cors', // 請求的模式,cors、 no-cors 或 same-origin
credentials: 'include', // omit、same-origin 或 include。爲了在當前域名內自動發送 cookie, 必須提供這個選項
cache: 'no-cache', // default 、 no-store 、 reload 、 no-cache 、 force-cache 或者 only-if-cached
redirect: 'follow', // 可用的 redirect 模式: follow (自動重定向), error (若是產生重定向將自動終止而且拋出一個錯誤), 或者 manual (手動處理重定向).
referrer: 'no-referrer', // no-referrer、client或一個 URL。默認是 client。
referrerPolicy: 'no-referrer', // 指定 referer HTTP頭
integrity: 'sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=', // 包括請求的 subresource integrity 值
}
// 發起請求
fetch('http://biadu.com' [, config])
複製代碼
then的回調函數接受一個Response對象參數,其對象擁有9個屬性,8個方法
9個屬性
8個方法
WebSocket(url[, protocols])
建立實例,參數1 對方絕對url其url以ws://
或者wss://(加密)
開頭,參數2 protocols是單協議或者包含協議的字符串數組// 必須傳入絕對URL,能夠是任何網站
const s = new WebSocket('ws://www.baidu.com')
s.readyState // 0 創建鏈接 1 已經創建 2 正在關閉 3 鏈接已關閉或者沒有連接成功
s.send('hello') // 發送的數據必須是純文本
s.onopen = function () {}
s.onerror = function () {}
s.onmessage = function (event) {
// 當接收到消息時
console.log(event.data) // 數據是純字符
}
s.close() // 關閉鏈接
s.onclose = function (event) {
/*
* event.wasClean 是否明確的關閉
* event.code 服務器返回的數值狀態碼
* event.reason 字符串,服務器返回的消息
*/
}
複製代碼
簡單演示
const xhr = new XMLHttpRequest()
// 每秒發送一次短輪詢
const id = setInterval(() => {
xhr.open('GET', 'http://127.0.0.1:3000/test?key=value')
xhr.addEventListener('load', (e) => {
if (xhr.status == 200) {
// 處理數據
console.log(xhr.response)
// 若是不須要能夠關閉
clearInterval(id)
}
})
xhr.send()
}, 1000)
複製代碼
簡單演示
function ajax() {
const xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:3000/test?key=value');
xhr.addEventListener('load', (e) => {
if (xhr.status == 200) {
// 處理數據
console.log(xhr.response)
// 若是不須要能夠關閉
if (xhr.response != '') return
ajax()
}
})
xhr.send();
}
複製代碼
Connection:keep-alive
Connection:keep-alive
來決定使用,而是否輪詢,是根據服務端的處理方式來決定的,與客戶端沒有關係https://localhost:80/
和http://localhost:8080/
的Cookie是共享的,能夠經過domain設置域,path設置域下的共享路徑.baidu.com
,則a.baidu.com
可訪問其上級域名的cookie基本使用
localStorage.setItem("b", "isaac"); //設置b爲"isaac"
localStorage.getItem("b"); //獲取b的值,爲"isaac"
localStorage.key(0); //獲取第0個數據項的鍵名,此處即爲「b」
localStorage.removeItem("b"); //清除c的值
localStorage.clear(); //清除當前域名下的全部localStorage數據
複製代碼
window.addEventListener("storage", function(e){}
設置localStorage事件監聽,當存儲區域的內容發生改變時,將會調用回調sessionStorage.setItem(name, num); //存儲數據
sessionStorage.setItem('value2', 119);
sessionStorage.valueOf(); //獲取所有數據
sessionStorage.getItem(name); //獲取指定鍵名數據
sessionStorage.sessionData; //sessionStorage是js對象,也能夠使用key的方式來獲取值
sessionStorage.removeItem(name); //刪除指定鍵名數據
sessionStorage.clear();
複製代碼
來實現一下吧
// 前端準備
// 定義回調函數
function fn(arg) {
// arg爲服務端傳來的數據
console.log(`客戶端獲取的數據:${arg}`)
}
// 建立script標籤
const s = document.createElement('script')
// 給script標籤的src屬性賦值,值爲請求url,查詢參數callback,需與後端對應
// fn爲前端回調函數名
s.src = `http://127.0.0.1:3000/test?callback=fn`
// 向html添加此標籤,添加完成以後瀏覽器自動請求script的src對應的網址
document.getElementsByTagName('head')[0].appendChild(s);
// 等待瀏覽器收到響應以後,將會自動執行響應內容的代碼
----------------------------------------------
// 後端準備
// nestjs(ts)處理
@Controller('test') //api
export class TestController {
@Get() //get方式請求
//取url中的查詢參數,即?以後的鍵值對,鍵與值對應query對象參數的鍵與值
callback(@Query() query) {
// 返回的數據
const data = '我是服務端返回的數據';
// 取查詢參數,這裏的callback要與前端?以後的鍵名一致,fn即fn函數名
const fn = query.callback;
// 返回結果,格式:函數名(服務器的數據),注意這裏須要序列化成字符串,若是參數自己是字符串那麼要加引號,前端並不知道data是字符串
return `${fn}('${data}')`;
}
}
// express(js)處理,同上
router.get('/test', async (req, res) => {
const data = '我是服務器返回的數據'
// req.query爲查詢參數列表
const fn = req.query.callback
// 返回數據
res.send(`${fn}('${data}')`)
})
複製代碼
響應內容
Access-Control-Allow-Origin:*
便可開啓cors容許跨域請求,使用通配符*表示容許全部不一樣域的源訪問資源,也可單獨設置指定容許的源域名postMessage(data,origin[, source])
data爲發送的數據只能發送字符串信息,origin發送目標源,指定哪些窗口能接收到消息事件,若是origin設置爲*則表示無限制,source爲發送消息窗口的window對象引用,<!-- test.html -->
<iframe src="http://127.0.0.1:5501/postMessage.html"
name="postIframe" onload="messageLoad()"></iframe>
<script>
// 定義加載以後執行的函數,給postMessage.html發送數據
function messageLoad() {
const url = 'http://127.0.0.1:5501/postMessage.html'
window.postIframe.postMessage('給postMessage的數據', url)
}
// 用於監聽postMessage.html的回饋,執行回調
window.addEventListener('message', (event) => {
console.log(event.data);
})
</script>
----------------------------------------------
<!-- postMessage.html -->
<script>
// 監聽test.html發來的數據,延遲1秒返回數據
window.addEventListener('message', (event) => {
setTimeout(() => {
event.source.postMessage('給test的數據', event.origin)
},1000)
})
</script>
複製代碼
window.name
屬於全局屬性,在html中的iframe加載新頁面(能夠是跨域),經過iframe設置的src指向的源中更改name的值,同時主頁面中的name也隨之更改,可是須要給iframe中的window設置爲about:blank
或者同源頁面便可<!-- test.html -->
// 封裝應該用於獲取數據的函數
function foo(url, func) {
let isFirst = true
const ifr = document.createElement('iframe')
loadFunc = () => {
if (isFirst) {
// 設置爲同源
ifr.contentWindow.location = 'about:blank'
isFirst = false
} else {
func(ifr.contentWindow.name)
ifr.contentWindow.close()
document.body.removeChild(ifr)
}
}
ifr.src = url
ifr.style.display = 'none'
document.body.appendChild(ifr)
// 加載以後的回調
ifr.onload = loadFunc
}
foo(`http://127.0.0.1:5501/name.html`, (data) => {
console.log(data) //
})
----------------------------------------------
<!-- name.html -->
const obj = { name: "iframe" }
// 修改name的值,必須爲string類型
window.name = JSON.stringify(obj);
複製代碼
document.domain
的值對應當前頁面的域名http://a.baidu.com
與http://b.baidu.com
這2個子域名之間才能使用domain跨域,通常用於子域名之間的跨域訪問<!-- test.html -->
<script>
document.domain = 'baidu.com';
const ifr = document.createElement('iframe');
ifr.src = 'a.baidu.com/test.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 此處便可操做domain.html的document
ifr.onload = null;
};
</script>
----------------------------------------------
<!-- domain.html -->
<script>
// domain.html下設置爲與test.html中的domain一致
document.domain = 'baidu.com';
</script>
複製代碼
// nginx.conf配置
server {
listen 80; // 監聽端口
server_name www.baidu.com; // 匹配來源
location / { //匹配路徑
// 反向代理到http://127.0.0.1:3000
proxy_pass http://127.0.0.1:3000;
// 默認入口文件
index index.html index.htm index.jsp;
}
複製代碼
setTimeout(func|code, [delay], [arg1], [arg2], ...)
參數1爲想要執行的函數或代碼字符串,參數2爲延遲執行時間,單位毫秒默認0,參數3及以後的參數爲參數1爲函數時傳入的參數,調用以後會返回一個定時器idclearTimeout(id)
清除定時器來取消回調也就是說setTimeout是在上一次回調執行以後纔開啓的定時
setInterval(func|code, [delay], [arg1], [arg2], ...)
,參數列表同setTimeout,參數2爲每次循環時間因爲這2個api都屬於異步宏任務,在執行的時候都會進入任務隊列,若是隊列前的任務執行時間較長,那麼也會影響到定時器的執行時機
在瀏覽器中alert、confirm、prompt都會阻塞js主線程執行,直到彈窗消失,可是定時器還會繼續執行;定時器並不能達到0延遲,最小延遲限制在4ms
requestAnimationFrame(callback)
的參數就是每次渲染前須要執行的動畫更新函數,當瀏覽器將要重繪畫面時就會執行這個回調函數,這個回調函數接受一個參數,即從當前頁面加載以後到如今所通過的毫秒數document.getElementById("btn").onclick = function () {}
----------------------------------------------
<input type="button" onclick="alert('hi!');">
複製代碼
addEventListener(eventName,callback,isCapturing)
方法爲元素設置事件監聽器,參數1爲註冊事件名不帶on開頭的string類型,參數2爲觸發事件的回調函數,接受一個事件對象參數,參數3爲是否在捕獲階段觸發,默認爲falseremoveEventListener(eventName,callback,isCapturing)
方法移除指定事件名、回調、是否捕獲的事件,匿名回調沒法刪除<ul id="f">
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
<script>
const ul = document.querySelector('#f')
// 點擊li時觸發事件委託
ul.addEventListener('click',function foo(event){
// 處理元素爲父元素
console.dir(event.currentTarget) // ul#f
// 觸發元素爲子元素,event.target爲具體觸發對象
console.dir(event.target) // li
})
//--------------------------------------------
// 經過點擊添加子元素
ul.addEventListener('click',function foo(event){
const child = document.createElement('li')
child.innerText = '我是新增的子元素'
event.currentTarget.appendChild(child)
})
//--------------------------------------------
// 經過點擊刪除子元素
ul.addEventListener('click',function foo(event){
event.currentTarget.removeChild(event.target)
})
</script>
----------------------------------------------
<!-- 若是點擊span 想知道是哪一個li下面的元素 -->
<ul id="f">
<li>a</li>
<li>
<span>b</span>
</li>
<li>
<span>c</span>
</li>
</ul>
<script>
const ul = document.querySelector('#f')
ul.addEventListener('click', function foo(event) {
let target = event.target
// 一級級向上尋找直到找到知足條件的元素
while (target.nodeName.toLowerCase() !== 'li') {
target.target.parentNode
}
console.dir(target) // li
console.dir(target.parentNode === event.currentTarget) //true
})
</script>
複製代碼
以上總結可能沒有什麼順序,可是每章節都是針對性的講解,零散的知識點較多,但願看完這篇文章能擴展你的知識面,也許某方面講的不是很詳細,若是感興趣能夠找些針對性的文章進行深刻了解。
部份內容並不是原創,仍是要感謝前輩的總結,若是本文影響到您的利益,那麼還請事先告知,在寫本文時的初衷就是想給更多學習前端的小夥伴拓展知識,夯實基礎,共同進步,也爲了之後方便複習使用
總結不易,如需轉載請註明出處,感謝!
若是本文對你有所幫助,就請點個贊支持一下吧,讓更多人看到,你的支持就是我堅持寫做下去的動力,若是喜歡個人文章,那麼還請關注後續的文章吧~ ψ(`∇´)ψ
本人正在構建的GitHub 前端知識體系 項目,記錄整理的知識點,能夠一塊兒學習與分享!
部份內容非原創,若有須要,請原創做者聯繫本人添加連接!