前端面試之JavaScript(一)

也到了本身快找工做的時候了,因此最近在複習前端的知識,這裏是本身對JavaScript的一些知識點的總結,之後會持續更新,分享本身的複習知識,有些是在不懂的時候參考大佬的講解。有些理解不到位的地方還請指正。祝本身找工做順利!!!javascript

一、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
}
複製代碼

二、函數柯里化

在Lambda演算(一套數理邏輯的形式系統,具體我也沒深刻研究過)中有個小技巧:假如一個函數只能收一個參數,那麼這個函數怎麼實現加法呢,由於高階函數是能夠當參數傳遞和返回值的,因此問題就簡化爲:寫一個只有一個參數的函數,而這個函數返回一個帶參數的函數,這樣就實現了能寫兩個參數的函數了——這就是所謂的柯里化(Currying,以邏輯學家Hsakell Curry命名),也能夠理解爲一種在處理函數過程當中的邏輯思惟方式。java

在計算機科學中,柯里化(Currying)是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數且返回結果的新函數的技術。git

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

複製代碼

三、原型、原型鏈

一、原型

原型是function的一個屬性,該屬性本質上是一個對象,它定義了構造函數構造出來的共有祖先,構造函數產生的實例對象能夠繼承該屬性的方法和屬性,當實例訪問某個屬性找不到就會順着原型鏈訪問該屬性。es6

二、原型鏈

有了原型,原型仍是一個對象,那麼這個名爲原型的對象天然還有本身的原型,這樣的原型上還有原型的結構就構成了原型鏈。github

原型鏈是是描述實例對象與構造函數的原型之間的關係,若是實例對象找不到某個屬性或者方法就會到構造函數的prototype上查找,若是仍是找不到就會訪問構造函數prototype屬性的__proto__屬性,直到null。面試

四、繼承

在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
    }
})
複製代碼

五、this

參考:juejin.im/post/5c96d0…json

一、什麼是this?

this是JavaScript中的一個關鍵字,被自動定義在全部函數的做用域中。**this是在運行的時候進行綁定,並非在編寫的時候進行綁定,它的上下文取決於函數調用時的各類條件。**this的綁定和函數的聲明位置無關,只取決於函數的調用方式。

當一個函數被調用的時候,會建立一個活動記錄(也成爲執行上下文)。這個記錄會包含函數在哪裏調用(調用棧)、函數調用的方法、傳入的參數等信息。this就是記錄中的一個屬性,會在函數執行的過程當中用到。

二、調用位置

調用位置指的是函數被調調用的位置而不是聲明的位置。

三、綁定規則

一、默認綁定

默認綁定的時候this指向window,默認綁定是指函數不帶任何修飾的函數引用進行調用。好比:

function foo() {
    console.log(this)
}
foo() // window
複製代碼

可是須要注意的是在嚴格模式下,默認綁定並不會指向window

二、隱式綁定

隱式綁定一般以對象做爲執行上下文調用。可是咱們須要明白一個道理:無論是在對象中聲明一個函數,仍是先定義再添加函數的引用,嚴格來叔這個函數都不屬於該對象。

隱式綁定規則會把函數調用中的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
複製代碼
一、隱式綁定丟失
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是很是常見的。

三、顯示綁定

  • call
  • apply
  • bind:bind會返回一個硬編碼的新函數,它會把參數設置爲this的上下文並調用原始函數。

若是把null或者undefined做爲this綁定的對象傳入其中,這些值會被忽略,其實是默認綁定。

四、new綁定

五、綁定優先級

new > call、apply、bind > 隱式綁定 > 默認綁定

六、防抖節流

節流(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)
    }
}
複製代碼

七、Promise

面試常見問題:

一、瞭解 Promise 嗎?

二、Promise 解決的痛點是什麼?

三、Promise 解決的痛點還有其餘方法能夠解決嗎?若是有,請列舉。

四、Promise 如何使用?

五、Promise 經常使用的方法有哪些?它們的做用是什麼?如何使用?

六、Promise 在事件循環中的執行過程是怎樣的?

七、Promise 的業界實現都有哪些?

八、能不能手寫一個 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
複製代碼

一、promise含義

promise是異步編程的一種解決方案,解決了回調地獄的問題。Promise是一個容器保存着某個將來纔會結束的事件的結果,也能夠說是一個對象從它能夠獲取異步操做的消息

特色:

  1. 對象狀態不受外界影響,只有pending(進行中)、fulfilled(已成功)和rejected(已失敗)三種狀態。
  2. 一旦狀態改變就不會再改變。只能從pending(進行中)到fulfilled(已成功)或pending(進行中)到reject(以失敗)。

二、基本語法

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);
});
複製代碼

三、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);
});
複製代碼

四、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方法的第二個參數。

五、promise.prototype.finally

finally方法用於執行無論最後狀態如何,都會執行的操做。

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
複製代碼

六、promise.all()

該方法用於將多個Promise實例包裝成一個新的Promise實例。

const p = Promise.all([p1, p2, p3])
複製代碼

Promise.all()接受一個數組,數組的值都是Promise對象,若是不是則會調用Promise.resolve()方法。(Promise.all方法的參數能夠不是數組,但必須具備 Iterator 接口,且返回的每一個成員都是 Promise 實例。)

p的狀態由p1p2p3決定,分紅兩種狀況。

(1)只有p1p2p3的狀態都變成fulfilledp的狀態纔會變成fulfilled,此時p1p2p3的返回值組成一個數組,傳遞給p的回調函數。

(2)只要p1p2p3之中有一個被rejectedp的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。

注意,若是做爲參數的 Promise 實例,本身定義了catch方法,那麼它一旦被rejected,並不會觸發Promise.all()catch方法。

七、promise.race()

Promise.race方法一樣是將多個 Promise 實例,包裝成一個新的 Promise 實例。

const p = Promise.race([p1, p2, p3])
複製代碼

上面代碼中,只要p1p2p3之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調函數。和Promise同樣,數組的值必須是promise對象,若是不是則會調用Promise.resolve()方法。

八、Promise.resolve()

Promise.resolve()方法能夠將現有的對象轉換爲Promise對象。

Promise.resolve('foo')
// 等同於
new Promise(function(resolve) {
    resolve('foo')
})
複製代碼

九、Promise.reject()

Promise.reject(reason)方法也會返回一個新的 Promise 實例,該實例的狀態爲rejected

let p = Promise.reject('foo')
// 等同於
new Promise((resolve, reject) => reject('foo'))
複製代碼

八、深拷貝

由於數組和對象都是引用值,因此當咱們直接使用=賦值,會是兩個對象的指針指向同一個空間,當咱們改變其中一個值的時候,另外一個對象也會受到影響。當咱們使用深拷貝從新開闢了一個內存空間,將該對象的指針指向新開闢的空間。

針對數組咱們可使用[...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
}
複製代碼

九、JavaScript事件循環

juejin.im/post/5bac87…

JavaScript將任務分爲同步任務和異步任務,在第一次執行的時候會將整個script代碼看做宏任務同步任務進入主線程,異步任務進入Event Table註冊,當知足條件異步任務的回調函數加入到Event Queue隊列中,當主線程空閒的時候,會從Event Queue取出對應的函數。宏任務(script、setTimeout)和微任務(Promise、process.nextTick)分別進入不一樣的Event Table,它們的執行順序不同,當主線程空閒的時候首先會清空微任務隊列,而後再拿出一個宏任務隊列的函數,而後再檢查微任務隊列,如此循環。

十、做用域、做用域鏈、執行上下文、預編譯

一、做用域

做用域是在運行時代碼中的特定變量的有效範圍。做用域決定了代碼區塊中變量和其餘資源的可見性。做用域內層能夠看見做用域外層,做用域外層不能看見做用域外層,因此做用域在不一樣做用域中聲明的變量不會形成污染和命名衝突。

  • 全局做用域

定義在最外層的函數和變量,未經聲明就賦值的變量,window的屬性。這裏須要注意的是var聲明的全局變量以及未經聲明就賦值的變量會掛載到window屬性上,可是var聲明的變量不能刪除,未經聲明的變量能夠刪除。

  • 函數做用域

當函數執行的時候就會在內部建立一個函數做用域,當函數執行完成就會銷燬該做用域。

  • 塊級做用域

在ES6以前是沒有塊級做用域的,ES6引入了let、const關鍵字就能夠建立塊級做用域。

二、做用域鏈

當在一個函數內部搜索一個變量的時候,若是該函數沒有聲明該變量,那麼就會順着代碼執行環境建立的做用域逐層向外搜索,一直搜索到全局做用域。

三、執行上下文

解釋階段:

  • 詞法分析
  • 語法分析
  • 做用域規則肯定()

執行階段

  • 建立執行上下文
  • 執行函數代碼
  • 垃圾回收

JavaScript在解釋階段便會肯定做用域規則,可是執行上下文是在函數執行的前一刻。

執行上下文最明顯的就是this指向是在執行的時候肯定的。

區別:執行上下文在運行時肯定,隨時能夠改變;做用域在定義時就肯定,而且不會改變。同一做用域下,不一樣的調用會產生不一樣的執行上下文,從而產生不一樣的結果。

四、預編譯

  1. 建立AO對象
  2. 尋找形參和變量聲明
  3. 形參實參相統一
  4. 找函數聲明,函數名做爲屬性名,函數體做爲屬性值

五、閉包

當函數能夠記住並訪問所在的詞法做用域時,就產生了閉包,即便函數是在當前詞法做用域外執行。簡單講,閉包就是指有權訪問另外一個函數做用域中的變量的函數。

建立閉包:

  1. 在函數內部引用外部函數
let a = 1
function foo() {
  console.log(a)
}
function bar() {
  let a = 2
  foo()
}
bar() // 1
複製代碼
  1. 在函數內部返回函數
let a = 'window'
function foo() {
    let a = 'foo'
    return function() {
        console.log(a)
    }
}
let bar = foo()
bar() // foo
複製代碼

閉包的應用和缺陷

  1. 設計私有的方法和變量。
  2. 容易形成內存泄露。

十一、DOM事件

一、DOM事件級別

  1. DOM0:dom.onclick = function(){}
  2. DOM2:dom.addEventListenner('click',function(){})
  3. DOM3:dom.addEventListenner('keyup',fuction(){}),增長了事件類型

二、DOM事件模型

dom.addEventListenner('keyup',fuction(){}, false|true),第三個參數爲false表示在冒泡階段觸發,第三個參數爲true表示在捕獲階段觸發。先捕獲後冒泡。

  1. 捕獲:父元素到子元素,dom.addEventListenner('keyup',fuction(){}, true)
  2. 冒泡:子元素到父元素,dom.addEventListenner('keyup',fuction(){}, false)

事件委託就是基於事件冒泡的,當子元素觸發點擊事件會冒泡到父元素,而後經過e.target來判斷子元素。

三、DOM事件流

經過冒泡或者捕獲怎麼到達目標對象的階段

事件首先經過捕獲到達目標元素,再經過目標元素冒泡到window對象,即先捕獲後冒泡

四、Event常見對象

  • 阻止默認行爲:e.preventDefault()
  • 阻止冒泡:e.stopPropagation()
  • 阻止其餘綁定的事件的執行(事件響應優先級):e.stopImmediatePropagation()
  • e.target
  • e.currentTarget

五、自定義事件

參考: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})
複製代碼

十二、new

  1. 建立一個新對象
  2. 將該對象的__proto__屬性指向函數的prototype屬性
  3. 將this指向該對象
  4. 若是該函數沒有顯示的返回對象則返回建立的對象
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
}
複製代碼

1三、JavaScript數據類型

  • object:包括Function、Date、Array等
  • number:數值,NaN和自身不相等,可是能夠經過Object.is()來判斷
  • string:字符串
  • boolean:布爾
  • null:原型鏈的終點
  • undefined:表示變量聲明尚未被賦值
  • symbol:ES6新增,表示獨一無二的值

一、隱式轉換

二、顯示轉換

三、包裝類

語法:let str = new String('hello world')

當咱們聲明一個字符串變量的時候let str1 = 'hello',這是字面量的形式,而且是一個不可變的值。咱們訪問str1.length屬性、或其餘屬性的時候,就會把該變量轉換成爲一個String對象(這裏一般叫作包裝類),由於聲明的字符串沒有該屬性,只有轉換爲包裝類纔有。在JavaScript中會把字符串字面量轉化成String對象

四、null、undefined比較

null在數值轉換時被轉換爲0,undefined會被轉換爲NaN

  • nudefined

undefined只有一個值,即undefined。如下狀況會出現undefined:

  1. 定義變量,可是沒有初始化;
  2. 調用某個函數時,實參個數小於形參個數時,未實參化的形參在函數調用過程當中的值是undefined;
  3. 訪問對象沒有的屬性
  4. 函數默認的返回值
  5. 爲初始化的變量執行typeof
  6. 未聲明的變量執行typeof
  • null

null也只有一個值,可是當咱們執行typeof null的時候,會返回object。咱們能夠理解爲null是一個空指針對象,尚未保存對象。如下幾種狀況會使用出現null:

  1. 手動設置爲null,好比在釋放變量的時候
  2. 未獲取到DOM節點
  3. 原型鏈頂端
  4. 在正則捕獲的時候,若是沒有捕獲到結果,默認也是null

五、判斷數據類型

  • typeof

不能區別null、對象、數組、正則表達式等

  • instanceof

是基於原型鏈操做的:A instanceof B,判斷A的原型鏈上有沒有B的原型

  • Object.prototype.toString.call()

比較好的方法,可是IE6/7/8中 Object.prototype.toString.apply(null)返回「[object Object]」。

  • constructor

1四、對象

一、語法

對象聲明可使用字面量形式和構造函數形式

let obj = {}
let obj1 = new Object()
複製代碼

這兩種方法生成的對象是同樣的,區別在於字面量形式能夠添加多個鍵值對、構造函數形式只能逐個添加。

二、內置對象

JavaScript還有一些對象子類型,一般被稱爲內置對象。

  • String
  • Number
  • Boolean
  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Error

三、內容

對象中的值一般不會存儲在對象內部,一般狀況下,存儲在對象容器內部的是這些屬性的名稱,它們就像指針同樣,指向這些值的真正存儲位置

一、訪問對象值的方法

let obj = {
    a:1
}
obj.a
obj['a']
複製代碼

obj.a,的語法被稱爲屬性訪問,obj['a']的方法被稱爲鍵訪問,它們在大都數狀況下是能夠互換的,區別在於.a要符合命名的規範性,['a']能夠接受任意的UTF-8/Unicode字符做爲屬性名。好比"super-Fun!",這時候就不可使用屬性訪問了。

注意:在對象中屬性名永遠都是字符串,若是不是者會被轉換爲字符串。

二、可計算的屬性名

let a = "foo"
let obj = {
  [a + '1']: 'hello',
  [a + '2']: 'hello2'
}
複製代碼

四、對象常見的方法

一、屬性描述符

一、查看屬性描述符: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,是否可枚舉

二、設置屬性描述符:Object.defineProperty(obj, props)

語法:

Object.defineProperty(myObj, 'b', {
  value:2,
  writable: false,
  configurable: true,
  enumerable: true
})
複製代碼

因此咱們經過設置writable,configurable爲false來設置一個對象常量。

二、不變性

一、經過設置writable,configurable爲false來設置一個對象常量。

二、禁止拓展:Object.preventzectensions(obj)

語法:

let myObj1 = {
  a: 1
}
Object.preventExtensions(myObj1)
myObj1.b = 2
myObj1.b // undefined
複製代碼

三、密封:Object.seal(obj)

實際上這個方法會調用Object.preventzectensions(obj)方法,並將現有屬性的configurable設爲false,因此密封以後既不能添加新的屬性,也不能刪除和配置現有屬性

四、凍結:Object.freeze(obj)

這個方法會調用Object.seal()方法,並將現有屬性的writable設爲false,故既不能添加新的屬性,也不能刪除、配置、修改現有屬性

三、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() // 通知全部訂閱者
  }
})
複製代碼

四、存在性

一、in:檢查對象及原型鏈

二、hasOwnProperty()

五、其餘常見方法

  • Object.keys(),返回全部可枚舉屬性
  • Object.values(),返回全部可枚舉屬性的值
  • Object.entries(),返回全部可枚舉屬性的鍵和值
  • Object.getOwnPropertyNames,返回全部屬性,無論是否可枚舉

1五、數組

一、類數組

具備length屬性,能夠經過數字下標訪問元素,如arguments、獲取的DOM節點。Array.from(arguments)能夠將一個類數組轉化爲數組

二、數組常見方法

  • push\pop:在數組尾部添加刪除元素
  • unshift\shift:在數組頭部添加刪除元素
  • concat:合併數組
  • join:
  • slice:切片數組,返回一個新的數組
  • splice:刪除、修改、增長數組元素
  • sort:排序,sort((a,b)=>{return a - b})

三、去重

// es6最簡單的方式
[...new Set(arr)]

function unique(arr) {
    let list = [...arr]
    let res = []
    list.forEach(item => {
        if(!res.include(item)) {
            res.push(item)
        }
    })
    return res
}
複製代碼
相關文章
相關標籤/搜索