一天一個js知識

gujianxin發表於2019-01-04

原型繼承和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=this
    const args=[...arguments].slice(1)
    //返回一個函式
    return F(){
        //最後來說通過 new 的方式,在之前的章節中我們學習過如何判斷 //this,對於 new 的情況來說,不會被任何方式改變 //this,所以對於這種情況我們需要忽略傳入的 this
        if(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是可選引數,不傳預設window
    context.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 || window
  context.fn = this
  let result
  // 處理引數和 call 有區別
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
}
複製程式碼

this指向

對於this指向很多人都會混淆,其實很多網上把它說負複雜了看以下場景:

function foo(){
    console.log(this)
}
foo()
let num=1
const obj={
    num:1,
    foo:foo
}
obj.foo()
const c=new foo()
複製程式碼

傳一張最近看的小冊的老哥畫的圖,賊穩

一天一個js知識

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:陣列去重

let
    r,
    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]
複製程式碼

相關文章