1. 程式人生 > 實用技巧 >javascript陣列常用方法

javascript陣列常用方法

es6宣告變數的方法:var、function、let、const、import、class。

頂層物件的屬性與全域性變數掛鉤,被認為是JavaScript語言最大的設計敗筆之一。這樣的設計帶來了幾個很大的問題,首先是沒法在編譯時就報出變數未宣告的錯誤,只有執行時才能知道(因為全域性變數可能是頂層物件的屬性創造的,而屬性的創造是動態的);其次程式設計師很容易不知不覺地就創造了全域性變數;最後,頂層物件的屬性是到處可以讀寫的,這非常不利於模組化程式設計。另一方面,window物件有實體含義,值的是瀏覽器的視窗物件,頂層物件是一個有實體含義的物件也是不合適的。

var、function命令宣告的全域性變數依舊是頂層物件的屬性,let、const、class命令宣告的全域性變數不屬於頂層物件的屬性。也就是說,從es6開始,全域性變數將逐步與頂層物件的屬性脫鉤(進行剝離)。

獲取頂層物件

(typeof window !== 'undefined'
    ? window : 
        (typeof process === 'object' && 
        typeof require === 'function' && 
        typeof global === 'object')
        ? global : this)

function getGlobal(){
    if(typeof self !== 'undefined'){
        return self
    }
    if(typeof window !== 'undefined'){
        return window
    }
    if(typeof global !== 'undefined'){
        return global
    }
    throw new Error('unable to locate global object')
}

解構賦值報錯:等號右邊的值,要麼轉為物件以後不具備Iterator介面,要麼本身就不具備Iterator介面。事實上,只要某種資料結構具有Iterator介面,都可以採用陣列形式的解構賦值。

解構賦值允許指定預設值。es6內部使用嚴格相等運算子(===),判斷一個位置是否有值。只有當一個數組(物件中屬性)成員的值嚴格等於undefined,預設值才會生效。

物件的解構賦值是下面形式的簡寫。

let {foo:foo,bar:bar} = {foo:'aaa',bar:'bbb'}

也就是說,物件的解構賦值的內部機制是先找到同名屬性,然後再賦給對應的變數。真正被賦值的是後者,而不是前者。

let {foo:baz} = {foo:'aaa',bar:'bbb'}
baz //'aaa'
foo //error:foo is not defined  

前者是匹配模式。後者才是變數。

在模板字串(template string)中使用反引號,需要在其前面用反斜槓轉義。

使用模板字串表示多行字串,所有空格和縮排都會被保留在輸出之中。

ES6 提供了二進位制和八進位制數值的新的寫法,分別用字首0b(或0B)和0o(或0O)表示。

函式引數制定了預設值以後,函式的length屬性將返回沒有指定預設值的引數的個數。

一旦設定了引數的預設值,函式進行宣告初始化時,引數會形成一個單獨的作用域(context)。等初始化結束,這個作用於就會消失。這種語法行為,在不設定引數預設值時,是不會出現的。

var x = 1
function fn(x,y = x){
    console.log(y)
}
fn(2) //2
fn() //undefined

利用引數預設值,可以指定某個引數不得省略,如果省略就會丟擲錯誤。

function throwIfMiss(){
    throw new Error('Missing Parameter')
}
function fn(mustBeProvidedParam = throwIfMiss()){
    return mustBeProvidedParam
}
fn()

將引數預設值設為undefined,表明這個引數是可以省略的。

函式的length屬性,不包括rest引數(函式的多餘引數)。

es6規定,只要函式引數使用了預設值、解構賦值、擴充套件運算子,那麼函式就不能顯式設定為嚴格模式,否則報錯。有兩種方法可以規避這種限制:

第一種是設定全域性性的嚴格模式

'use strict';
function doSomething(a, b = a) {
  // code
}

第二種是把函式包在一個無引數立即執行函式裡面。

const doSomething = (function () {
  'use strict';
  return function(a,b = a) {
    // code 
  };
}());

箭頭函式有幾個使用注意點:

  • 函式體內的this物件,就是定義時所在的物件,而不是使用時所在的物件。
  • 不可以當做建構函式,也就是說,不可以使用new命令,否則會丟擲一個錯誤。
  • 不可以使用arguments物件,該物件在函式體內不存在。如果要使用,可以使用rest引數代替。 
  • 不可以使用yield命令,因此箭頭函式不能用作Generator函式。

Array.from()方法可將類陣列物件(array-like object)和可遍歷(iterable)的物件轉為真正的陣列。

Array.of()方法用於將一組值轉換為陣列。

物件中屬性名錶達式如果是一個物件,預設情況下會自動將物件轉為字串[object Object],而物件中只會有最後一個[object Object],前面的均會被覆蓋掉。

isPrototypeOf()、getPrototypeOf()、setPrototype()用法示例:

如果Symbol的引數是一個物件,就會呼叫該物件的toString方法,將其轉為字串,然後再生成一個symbol值。

Symbol例項屬性description,直接返回symbol的描述。

使用Set很容易實現並集(Union)、交集(Intersect)和差集(Difference)。

let s1 = new Set([1,2,3])
let s2 = new Set([3,4,5])

let union = new Set([...s1,...s2])
let intersect = new Set([...s1].filter(i => s2.has(i)))
let difference = new Set([...s1].filter(i => !s2.has(i)))

Proxy建構函式:let proxy = new Proxy(target,handler);

target引數表示所要攔截的目標物件,handler引數也是一個物件。

下面是 Proxy 支援的攔截操作一覽,一共 13 種。

  • get(target, propKey, receiver):攔截物件屬性的讀取,比如proxy.fooproxy['foo']

  • set(target, propKey, value, receiver):攔截物件屬性的設定,比如proxy.foo = vproxy['foo'] = v,返回一個布林值。

  • has(target, propKey):攔截propKey in proxy的操作,返回一個布林值。

  • deleteProperty(target, propKey):攔截delete proxy[propKey]的操作,返回一個布林值。

  • ownKeys(target):攔截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in迴圈,返回一個數組。該方法返回目標物件所有自身的屬性的屬性名,而Object.keys()的返回結果僅包括目標物件自身的可遍歷屬性。

  • getOwnPropertyDescriptor(target, propKey):攔截Object.getOwnPropertyDescriptor(proxy, propKey),返回屬性的描述物件。

  • defineProperty(target, propKey, propDesc):攔截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一個布林值。

  • preventExtensions(target):攔截Object.preventExtensions(proxy),返回一個布林值。

  • getPrototypeOf(target):攔截Object.getPrototypeOf(proxy),返回一個物件。

  • isExtensible(target):攔截Object.isExtensible(proxy),返回一個布林值。

  • setPrototypeOf(target, proto):攔截Object.setPrototypeOf(proxy, proto),返回一個布林值。如果目標物件是函式,那麼還有兩種額外操作可以攔截。

  • apply(target, object, args):攔截 Proxy 例項作為函式呼叫的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)

  • construct(target, args):攔截 Proxy 例項作為建構函式呼叫的操作,比如new proxy(...args)

Reflect物件一共有 13 個靜態方法。

  • Reflect.apply(target, thisArg, args)用於繫結this物件後執行給定函式

  • Reflect.construct(target, args)提供了一種不使用new,來呼叫建構函式的方法。

  • Reflect.get(target, name, receiver)

  • Reflect.set(target, name, value, receiver)

  • Reflect.defineProperty(target, name, desc)用來為物件定義屬性

  • Reflect.deleteProperty(target, name)用於刪除物件的屬性

  • Reflect.has(target, name)方法對應name in obj裡面的in運算子。

  • Reflect.ownKeys(target) 用於返回物件的所有屬性,基本等同於Object.getOwnPropertyNamesObject.getOwnPropertySymbols之和。

  • Reflect.isExtensible(target) 返回一個布林值,表示當前物件是否可擴充套件。

  • Reflect.preventExtensions(target)用於讓一個物件變為不可擴充套件。

  • Reflect.getOwnPropertyDescriptor(target, name)用於得到指定屬性的描述物件,

  • Reflect.getPrototypeOf(target)用於讀取物件的__proto__屬性

  • Reflect.setPrototypeOf(target, prototype)用於設定目標物件的原型(prototype)

promise

例項的狀態都變成fulfilled,或者其中有一個變為rejected,才會呼叫Promise.all方法後面的回撥函式。

一個例項率先改變狀態,Promise.race()的狀態就跟著改變。那個率先改變的 Promise 例項的返回值,就傳遞給Promise.rece()的回撥函式

Promise.allSettled()方法只有等到所有這些引數例項都返回結果,不管是fulfilled還是rejected,包裝例項才會結束。

Promise.any()只要引數例項有一個變成fulfilled狀態,包裝例項就會變成fulfilled狀態;如果所有引數例項都變成rejected狀態,包裝例項就會變成rejected狀態。

Promise.try()

原生具備Iterator介面的資料結構

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函式的arguments物件
  • NodeList物件

模擬迭代器

類相當於例項的原型,所有在類中定義的方法都會被例項繼承。如果在一個方法前,加上static關鍵字,表示該方法不會被例項繼承,而是直接通過類呼叫,這就成為靜態方法。

如果靜態方法中包含this關鍵字,這個this指的是類,而不是例項。

class Foo{
    static bar(){
        this.baz()
    }
    static baz(){
        console.log('hello')
    }
    baz(){
        console.log('world')
    }
}
Foo.bar() // hello  

提案:私有屬性、方法,前面加#

new.target 屬性

new是從建構函式生成例項物件的命令。ES6 為new命令引入了一個new.target屬性,該屬性一般用在建構函式之中,返回new命令作用於的那個建構函式。如果建構函式不是通過new命令或Reflect.construct()呼叫的,new.target會返回undefined,因此這個屬性可以用來確定建構函式是怎麼呼叫的

需要注意的是,子類繼承父類時,new.target會返回子類,利用這個特點,可以寫出不能獨立使用,必須繼承後才能使用的類。

class Father{
    constructor(){
        if(new.target === Father){
            throw new Error('本類不能例項化')
        }
    }
}
class Son extends Father{
    constructor(x,y){
        super()
        this.x = x;
        this.y = y
    }
}

  

未完待續...