国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

編寫高質(zhì)量JavaScript代碼之?dāng)?shù)組和字典

izhuhaodev / 635人閱讀

摘要:但實例化該構(gòu)造函數(shù)仍然得到的是的實例。或者,為了避免在所有查找屬性的地方都插入這段樣本代碼,我們可以將該模式抽象到的構(gòu)造函數(shù)中。該構(gòu)造函數(shù)封裝了所有在單一數(shù)據(jù)類型定義中編寫健壯字典的技術(shù)細(xì)節(jié)。

參考書籍:《Effective JavaScript》

數(shù)組和字典

對象是JavaScript中最萬能的數(shù)據(jù)結(jié)構(gòu)。取決于不同的環(huán)境,對象可以表示一個靈活的鍵值關(guān)聯(lián)記錄,一個繼承了方法的面向?qū)ο髷?shù)據(jù)抽象,一個密集或稀疏的數(shù)組,或一個散列表。

使用Object的直接實例構(gòu)造輕量級的字典

JavaScript對象的核心是一個字符串屬性名稱和屬性值的映射表。這使得使用對象實現(xiàn)字典易如反掌,因為字典就是可變長的字符串與值的映射集合。

JavaScript提供了枚舉一個對象屬性名的利器,for ... in循環(huán),但是其除了枚舉出對象“自身”的屬性外,還會枚舉出繼承過來的屬性。

如果我們創(chuàng)建一個自定義的字典并將其元素作為該字典對象自身的屬性。

function NaiveDict() { }

NaiveDict.prototype.count = function () {
    var i = 0;

    for (var name in this) { // counts every property
        i++;
    }

    return i;
};

NaiveDict.prototype.toString = function () {
    return "[object NaiveDict]";
};

var dict = new NaiveDict();

dict.alice = 34;
dict.bob = 24;
dict.chris = 62;

dict.count(); // 5

上述代碼的問題在于我們使用同一個對象來存儲NaiveDict數(shù)據(jù)結(jié)構(gòu)的固定屬性(count和toString)和特定字典的變化條目(alice、bob和chris)。因此,當(dāng)調(diào)用count來枚舉字典的所有屬性時,它會枚舉出所有的屬性(count、toString、alice、bob和chris),而不是僅僅枚舉出我們關(guān)心的條目。

一個相似的錯誤是使用數(shù)組類型來表示字典。

var dict = new Array();

dict.alice = 34;
dict.bob = 24;
dict.chris = 62;

dict.bob; // 24

上述代碼面對原型污染時很脆弱。原型污染指當(dāng)枚舉字典的條目時,原型對象中的屬性可能會導(dǎo)致出現(xiàn)一些不期望的屬性。例如,應(yīng)用程序中的其他庫可能決定增加一些便利的方法到Array.prototype中。

Array.prototype.first = function () {
    return this[0];
};

Array.prototype.last = function () {
    return this[this.length - 1];
};

var names = [];

for (var name in dict) {
    names.push(name);
}

names; // ["alice", "bob", "chris", "first", "last"]

這告訴我們將對象作為輕量級字典的首要原則是:應(yīng)該僅僅將Object的直接實例作為字典,而不是其子類(例如,NaiveDict),當(dāng)然也不是數(shù)組。

var dict = {};

dict.alice = 34;
dict.bob = 24;
dict.chris = 62;

var names = [];

for (var name in dict) {
    names.push(name);
}

names; // ["alice", "bob", "chris"]

當(dāng)然,這仍然不能保證對于原型污染時安全的,因為任何人仍然能增加屬性到Object.prototype中,但是通過使用Object的直接實例,我們可以將風(fēng)險僅僅局限于Object.prototype

提示:

使用對象字面量構(gòu)建輕量級字典。

輕量級字典應(yīng)該是Object.prototype的直接子類,以使for ... in循環(huán)免收原型污染。

使用null原型以防止原型污染

在ES5未發(fā)布之前,你可能會嘗試設(shè)置一個構(gòu)造函數(shù)的原型屬性為null或者undefined來創(chuàng)建一個空原型的新對象。

但實例化該構(gòu)造函數(shù)仍然得到的是Object的實例。

function C() {}
C.prototype = null;

var o = new C();
Object.getPrototypeOf(o) === null; // false
Object.getPrototypeOf(o) === Object.prototype; // true

ES5首先提供了標(biāo)準(zhǔn)方法來創(chuàng)建一個沒有原型的對象。

var o = Object.create(null);

Object.getPrototypeOf(o) === null; // true

一些不支持Object.create函數(shù)的舊的JavaScript環(huán)境可能支持另一種值得一提的方式。

var o = { __proto__: null };

o instanceof Object; // false (no-standard)

提示:

在ES5環(huán)境中,使用Object.create(null)創(chuàng)建的自由原型的空對象是不太容易被污染的。

在一些老的環(huán)境中,考慮使用{ __proto__: null }

但是注意__proto__既不標(biāo)準(zhǔn),已不是完全可移植的,并且可能在未來的JavaScript環(huán)境中去除。

絕不要使用“__proto__”名作為字典的key,因為一些環(huán)境將其作為特殊的屬性對待。

使用hasOwnProperty方法以避免原型污染

JavaScript的對象操作總是以繼承的方式工作,即使是一個空的對象字面量也繼承了Object.prototype的大量屬性。

var dict = {};

"alice" in dict; // false
"toString" in dict; // true

幸運的是,Object.prototype提供了hasOwnProperty方法,當(dāng)測試字典條目時它可以避免原型污染。

dict.hasOwnProperty("alice"); // false
dict.hasOwnProperty("toString"); // false

我們還可以通過在屬性查找時使用一個測試來防止其受污染的影響。

dict.hasOwnProperty("alice") ? dict.alice : undefined;

hasOwnProperty方法繼承自Object.prototype對象,但是如果在字典中存儲一個同為“hasOwnProperty”名稱的條目,那么原型中的hasOwnProperty方法不能再被獲取到。

dict.hasOwnProperty = 10;
dict.hasOwnProperty("alice"); // error: dict.hasOwnProperty is not a function

此時我們可以采用call方法,而不用將hasOwnProperty作為字典的方法來調(diào)用。

var hasOwn = Object.prototype.hasOwnProperty;
// 或者,var hasOwn = {}.hasOwnProperty;

hasOwn.call(dict, "alice");

為了避免在所有查找屬性的地方都插入這段樣本代碼,我們可以將該模式抽象到Dict的構(gòu)造函數(shù)中。該構(gòu)造函數(shù)封裝了所有在單一數(shù)據(jù)類型定義中編寫健壯字典的技術(shù)細(xì)節(jié)

function Dict(elements) {
    // allow an optional initial table
    this.elements = elements || {}; // simple Object
}

Dict.prototype.has = function (key) {
    // own property only
    return {}.hasOwnProperty.call(this.elements, key);
};

Dict.prototype.get = function (key) {
    // own property only
    return this.has(key) ? this.elements[key] : undefined;
};

Dict.prototype.set = function (key, val) {
    this.elements[key] = val;
};

Dict.prototype.remove = function (key) {
    delete this.elements[key];
};

var dict = new Dict({
    alice: 34,
    bob: 24,
    chris: 62
});

dict.has("alice"); // true
dict.get("bob"); // 24
dict.has("toString"); // false

上述代碼比使用JavaScript默認(rèn)的對象語法更健壯,而且也同樣方便使用。

在一些JavaScript的環(huán)境中,特殊的屬性名__proto__可能導(dǎo)致其自身的污染問題。

在某些環(huán)境中,__proto__屬性只是簡單地繼承自Object.prototype,因此空對象是真正的空對象。

var empty = Object.create(null);
"__proto__" in empty; // false (in some environments)

var hasOwn = {}.hasOwnProperty;
hasOwn.call(empty, "__proto__"); // false (in some environments)

在其他的環(huán)境中,只有in操作符輸入為true。

var empty = Object.create(null);
"__proto__" in empty; // true (in some environments)

var hasOwn = {}.hasOwnProperty;
hasOwn.call(empty, "__proto__"); // false (in some environments)

不幸的是,某些環(huán)境會因為存在一個實例屬性__proto__而永久地污染所有的對象。

var empty = Object.create(null);
"__proto__" in empty; // true (in some environments)

var hasOwn = {}.hasOwnProperty;
hasOwn.call(empty, "__proto__"); // true (in some environments)

這意味著,在不同的環(huán)境中,下面的代碼可能有不同的結(jié)果。

var dict = new Dict();
dict.has("__proto__"); // ?

為了達(dá)到最大的可移植性和安全性,我們只能為每個Dict方法的“__proto__”關(guān)鍵字增加一種特例。

function Dict(elements) {
    // allow an optional initial table
    this.elements = elements || {}; // simple Object
    this.hasSpecialProto = false; // has "__proto__" key?
    this.specialProto = undefined; // "__proto__" element
}

Dict.prototype.has = function (key) {
    if (key === "__proto__") {
        return this.hasSpecialProto;
    }
    // own property only
    return {}.hasOwnProperty.call(this.elements, key);
};

Dict.prototype.get = function (key) {
    if (key === "__proto__") {
        return this.specialProto;
    }
    // own property only
    return this.has(key) ? this.elements[key] : undefined;
};

Dict.prototype.set = function (key, val) {
    if (key === "__proto__") {
        this.hasSpecialProto = true;
        this.specialProto = val;
    } else {
        this.elements[key] = val;
    };
}
    

Dict.prototype.remove = function (key) {
    if (key === "__proto__") {
        this.hasSpecialProto = false;
        this.specialProto = undefined;
    } else {
        delete this.elements[key];
    }
};

var dict = new Dict();

dict.has("__proto__"); // false 

不管環(huán)境是否處理__proto__屬性,該實現(xiàn)保證是可工作的。

提示:

使用hasOwnProperty方法避免原型污染。

使用詞法作用域和call方法避免覆蓋hasOwnProperty方法。

考慮在封裝hasOwnProperty測試樣板代碼的類中實現(xiàn)字典操作。

使用字典類避免將“__proto__”作為key來使用。

使用數(shù)組而不要使用字典來存儲有序集合

直觀地說,一個JavaScript對象是一個無序的屬性集合。ECMAScript標(biāo)準(zhǔn)并為規(guī)定屬性存儲的任何特定順序,甚至對于枚舉對象也沒涉及。

這導(dǎo)致的問題是,for ... in循環(huán)會挑選一定的順序來枚舉對象的屬性。一個常見的錯誤是提供一個API,要求一個對象表示一個從字符串到值的有序映射,例如,創(chuàng)建一個有序的報表。

function report(highScores) {
    var result = "";
    var i = 1;

    for (var name in highScores) { // unpredictable order
        result += i + ". " + name + ": " + highScores[name] + "
";
        i++;
    }

    return result;
}

report([{ name: "Hank", points: 1110100 },
    { name: "Steve", points: 1064500 },
    { name: "Billy", points: 1050200 }]); // ?

由于不同的環(huán)境可以選擇以不同的順序來存儲和枚舉對象屬性,所以這個函數(shù)會導(dǎo)致產(chǎn)生不同的字符串,得到順序混亂的“最高分”報表。

如果你需要依賴一個數(shù)據(jù)結(jié)構(gòu)中的條目順序,請使用數(shù)組而不是字典。如果上述例子中的report函數(shù)的API使用一個對象數(shù)組而不是單個對象,那么它完全可以工作在任何JavaScript環(huán)境中。

function report(highScores) {
    var result = "";

    for (var i = 0, n = highScores.length; i < n; i++) { 
        var score = highScores[i];
        result += (i + 1) + ". " + score.name + ": " + score.points + "
";
    }

    return result;
}

report([{ name: "Hank", points: 1110100 },
    { name: "Steve", points: 1064500 },
    { name: "Billy", points: 1050200 }]); // 1. Hank: 1110100
2. Steve: 1064500
3. Billy: 1050200

一個微妙的順序依賴的典型例子是浮點型運算。假設(shè)有一個映射標(biāo)題和等級的電影字典。

var ratings = {
    "Good Will Hunting": 0.8,
    "Mystic River": 0.7,
    "21": 0.6,
    "Doubt": 0.9
};

var total = 0, count = 0;

for (var key in ratings) { // unpredictable order
    total += ratings[key];
    count++;
}

total /= count;
total; // ?

浮點型算術(shù)運算的四舍五入會導(dǎo)致計算順序的微妙依賴。當(dāng)組合未定義順序的枚舉時,可能會導(dǎo)致循環(huán)不可預(yù)知。

事實證明,流行的JavaScript環(huán)境實際上使用不同的順序執(zhí)行這個循環(huán)。

一些環(huán)境根據(jù)加入對象的順序來枚舉對象的key

(0.8 + 0.7 + 0.6 + 0.9) / 4 // 0.75

其他環(huán)境總是先枚舉潛在的數(shù)組索引,然后才是其他key。例如,電影“21”的名字恰好是一個可行的數(shù)組索引。

(0.6 + 0.8 + 0.7 + 0.9) / 4 // 0.7499999999999999

這種情況下,更好的表示方式是在字典中使用整數(shù)值。

(8 + 7 + 6 + 9) / 4 / 10 // 0.75
(6 + 8 + 7 + 9) / 4 / 10 // 0.75

提示:

使用for ... in循環(huán)來枚舉對象屬性應(yīng)當(dāng)與順序無關(guān)。

如果聚集運算字典中的數(shù)據(jù),確保聚集操作與數(shù)據(jù)無關(guān)。

使用數(shù)組而不是字典來存儲有序集合。

絕不要在Object.prototype中增加可枚舉的屬性

for ... in循環(huán)非常便利,但它很容易受到原型污染的影響。例如,如果我們增加一個產(chǎn)生對象屬性名數(shù)組的allKeys方法。

Object.prototype.allKeys = function () {
    var result = [];

    for (var key in this) {
        result.push(key);
    }

    return result;
};

({ a: 1, b: 2, c: 3 }).allKeys(); // ["a", "b", "c", "allKeys"]

遺憾的是,該方法也污染了其自身。

更為友好的是將allKeys定義為一個函數(shù)而不是方法。

function allKeys(obj) {
    var result = [];

    for (var key in obj) {
        result.push(key);
    }

    return result;
}

如果你確實想在Object.prototype增加屬性,ES5提供了一種更加友好的機(jī)制。

Object.defineProperty方法可以定義一個對象的屬性并指定該屬性的元數(shù)據(jù)。

Object.defineProperty(Object.prototype, "allKeys", {
    value: function () {
        var result = [];

        for (var key in this) {
            result.push(key);
        }

        return result;
    },
    wirtable: true,
    enumerable: false,
    configurable: true
});

提示:

避免在Object.prototype中增加屬性。

考慮編寫一個函數(shù)代替Object.prototype方法。

如果你確實需要在Object.prototype中增加屬性,請使用ES5中的Object.defineProperty方法將它們定義為不可枚舉的屬性。

避免在枚舉期間修改對象

一個社交網(wǎng)絡(luò)有一組成員,每個成員有一個存儲其朋友信息的注冊列表。

function Member(name) {
    this.name = name;
    this.friends = [];
}

var a = new Member("Alice"),
    b = new Member("Bob"),
    c = new Member("Carol"),
    d = new Member("Dieter"),
    e = new Member("Eli"),
    f = new Member("Fatima");

a.friends.push(b);
b.friends.push(c);
c.friends.push(e);
d.friends.push(b);
e.friends.push(d, f);

搜索該網(wǎng)絡(luò)意味著需要遍歷該社交網(wǎng)絡(luò)圖。這通常通過工作集(work-set)來實現(xiàn)。工作集以單個根節(jié)點開始,然后添加發(fā)現(xiàn)的節(jié)點,移除訪問過的節(jié)點

Member.prototype.inNetwork = function (other) {
    var visited = {};
    var workset = {};

    workset[this.name] = this; // 工作集以單個根節(jié)點開始

    for (var name in workset) {
        var member = workset[name];
        delete workset[name]; // modified while enumerating 移除訪問過的節(jié)點
 
        if (name in visited) { // don"t revisit members
            continue;
        }
        visited[name] = member;

        if (member === other) { // found?
            return true;
        }

        member.friends.forEach(function (friend) { // 添加發(fā)現(xiàn)的節(jié)點
            workset[friend.name] = friend;
        });
    }

    return false;
};

不幸的是,在許多JavaScript環(huán)境中這段代碼根本不能工作。

a.inNetwork(f); // false

事實上,ECMAScript對并發(fā)修改在不同JavaScript環(huán)境下的行為規(guī)定了:如果被枚舉的對象在枚舉期間添加了新的屬性,那么在枚舉期間并不能保證新添加的屬性能夠被訪問。也就是,如果我們修改了被枚舉的對象,則不能保證for ... in循環(huán)的行為是可預(yù)見的。

讓我們進(jìn)行另一種遍歷圖的嘗試。這次自己管理循環(huán)控制。當(dāng)我們使用循環(huán)時,應(yīng)該使用自己的字典抽象以避免原型污染。

function WorkSet() {
    this.entries = new Dict();
    this.count = 0;
}

Workset.prototype.isEmpty = function () {
    return this.count === 0;
};

WorkSet.prototype.add = function (key, val) {
    if (this.entries.has(key)) {
        return;
    }

    this.entries.set(key, val);
    this.count++;
};

WorkSet.prototype.get = function (key) {
    return this.entries.get(key);
};

WorkSet.prototype.remove = function (key) {
    if (!this.entries.has(key)) {
        return;
    }

    this.entries.remove(key);
    this.count--;
};

WorkSet.prototype.pick = function () {
    return this.entries.pick();
};

Dict.prototype.pick = function () {
    for (var key in this.elements) {
        if (this.has(key)) {
            return key;
        }
    }

    throw new Error("empty dictionary");
};

現(xiàn)在我們可以使用簡單的while循環(huán)來實現(xiàn)inNetwork方法。

Member.prototype.inNetwork = function (other) {
    var visited = {};
    var workset = new WorkSet();
    workset.add(this.name, this); // 工作集以單個根節(jié)點開始
    
    while (!workset.isEmpty()) {
        var name = workset.pick();
        var member = workset.get(name);
        workset.remove(name); // 移除訪問過的節(jié)點

        if (name in visited) { // don"t revisit members
            continue;
        }

        visited[name] = member;

        if (member === other) { // found?
            return true;
        }

        member.friends.forEach(function (friend) { // 添加發(fā)現(xiàn)的節(jié)點
            workset.add(friend.name, friend);
        });
    }

    return false;
};

pick方法是一個不確定性的例子。不確定性指的是一個操作并不能保證使用語言的語義產(chǎn)生一個單一的可預(yù)見的結(jié)果。這個不確定性來源于這樣一個事實:for ... in循環(huán)可能在不同的JavaScript環(huán)境中選擇不同的枚舉順序。

將工作條目存儲到數(shù)組中而不是集合中,則inNetwork方法將總是以完全相同的順序遍歷圖。

Member.prototype.inNetwork = function (other) {
    var visited = {};
    var worklist = [this]; // 工作集以單個根節(jié)點開始

    while (worklist.length > 0) {
        var member = worklist.pop(); // 移除訪問過的節(jié)點

        if (member.name in visited) { // don"t revisit
            continue;
        }
        visited[member.name] = member;
        
        if (member === other) { // found? 
            return true;
        }

        member.friends.forEach(function (friend) { // 添加發(fā)現(xiàn)的節(jié)點
            worklist.push(friend); // add to work-list
        });
    }

    return false;
};

提示:

當(dāng)使用for ... in循環(huán)枚舉一個對象的屬性時,確保不要修改該對象。

當(dāng)?shù)粋€對象時,如果該對象的內(nèi)容可能會在循環(huán)期間被改變,應(yīng)該使用while循環(huán)或經(jīng)典的for循環(huán)來代替for ... in循環(huán)。

為了在不斷變化的數(shù)據(jù)結(jié)構(gòu)中能夠預(yù)測枚舉,考慮使用一個有序的數(shù)據(jù)結(jié)構(gòu),例如數(shù)組,而不要使用字典對象。

數(shù)組迭代要優(yōu)先使用for循環(huán)而不是for...in循環(huán)
var scores = [98, 74, 85, 77, 93, 100, 89];
var total = 0;

for (var score in scores) {
    total += score;
}

var mean = total / scores.length;
mean; // ?

for ... in循環(huán)始終枚舉所有的key,即使是數(shù)組的索引屬性,對象屬性key始終是字符串。所以最終mean值為17636.571428571428。

迭代數(shù)組內(nèi)容的正確方法是使用傳統(tǒng)的for循環(huán)。

var scores = [98, 74, 85, 77, 93, 100, 89];
var total = 0;

for (var i = 0, n = scores.length; i < n; i++) {
    total += scores[i];
}

var mean = total / scores.length;
mean; // 88

提示:

迭代數(shù)組的索引屬性應(yīng)當(dāng)總是使用for循環(huán)而不是for ... in循環(huán)。

考慮在循環(huán)之前將數(shù)組的長度存儲在一個局部變量中以避免重新計算數(shù)組長度。

迭代方法優(yōu)于循環(huán)

JavaScript的for循環(huán)相當(dāng)簡潔。但是搞清楚終止條件是一個累贅。

for (var i = 0; i <= n; i++) { ... } // extra end iteration
for (var i = 1; i < n; i++) { ... } // missing first iteration
for (var i = n; i >= 0; i--) { ... } // extra start iteration
for (var i = n - 1; i > 0; i--) { ... } // missing last iteration

ES5為最常用的一些模式提供了便利的方法。

Array.prototype.forEach是其中最簡單的一個。

for (var i = 0, n = players.length; i < n; i++) {
    players[i].score++;
}

// 可用以下代碼替代上面的循環(huán)
players.forEach(function (p) {
    p.score++;
});

另一種常見的模式是對數(shù)組的每個元素進(jìn)行一些操作后建立一個新的數(shù)組。

var trimmed = [];

for (var i = 0, n = input.length; i < n; i++) {
    trimmed.push(input[i].trim());
}

// 可用以下代碼替代上面的循環(huán)
var trimmed = [];

input.forEach(function (s) {
    trimmed.push(s.trim());
});

通過現(xiàn)有的數(shù)組建立一個新的數(shù)組的模式是如此的普遍,所以ES5引入了Array.prototype.map方法使該模式更簡單、更優(yōu)雅。

var trimmed = input.map(function (s) {
    return s.trim();
});

另一個種常見的模式是計算一個新的數(shù)組,該數(shù)組只包含現(xiàn)有數(shù)組的一些元素。Array.prototype.filter使其變得很簡便。

listings.filter(function (listing) {
    return listing.price >= min && listing.price <= max;
});

我們可以定義自己的迭代抽象。例如,提取出滿足謂詞的數(shù)組的前幾個元素。

function takeWhile(a, pred) {
    var result = [];

    for (var i = 0, n = a.length; i < n; i++) {
        if (!pred(a[i], i)) {
            break;
        }

        result[i] = a[i];
    }

    return result;
}

var prefix = takeWhile([1, 2, 4, 8, 16, 32], function (n) {
    return n < 10;
}); // [1, 2, 4, 8]

我們也可以將takeWhile函數(shù)添加到Array.prototype中使其作為一個方法(前參閱前面關(guān)于對類似Array.prototype的標(biāo)準(zhǔn)原型添加猴子補(bǔ)丁的影響的討論)。

Array.prototype.takeWhile = function (pred) {
    var result = [];

    for (var i = 0, n = this.length; i < n; i++) {
        if (!pred(this[i], i)) {
            break;
        }

        result[i] = this[i];
    }

    return result;
};

var prefix = [1, 2, 4, 8, 16, 32].takeWhile(function (n) {
    return n < 10;
}); // [1, 2, 4, 8]

循環(huán)只有一點優(yōu)于迭代函數(shù),那就是前者有控制流操作,如break和continue。舉例來說,使用forEach方法來實現(xiàn)takeWhile函數(shù)將是一個尷尬的嘗試。

function takeWhile(a, pred) {
    var result = [];

    a.forEach(function (x, i) {
        if (!pred(x)) {
            // ?
        }

        result[i] = x;
    });

    return result;
}

我們可以使用一個內(nèi)部異常來提前終止該循環(huán),但是這既尷尬有效率低下。

function takeWhile(a, pred) {
    var result = [];
    var earlyExit = {}; // unique value signaling loop break

    try {
        a.forEach(function (x, i) {
            if (!pred(x)) {
                throw earlyExit;
            }

            result[i] = x;
        });
    } catch (e) {
        if (e !== earlyExit) { // only catch earlyExit
            throw e;
        }
    }
   
    return result;
}

此外,ES5的數(shù)組方法some和every可以用于提前終止循環(huán)。

some方法返回一個布爾值表示其回調(diào)對數(shù)組的任何一個元素是否返回了一個真值。

[1, 10, 100].some(function (x) {
    return x > 5;
}); // true

[1, 10, 100].some(function (x) {
    return x < 0;
}); // false

every方法返回一個布爾值表示其回調(diào)是否對數(shù)組的所有元素返回了一個真值。

[1, 2, 3, 4, 5].every(function (x) {
    return x > 0;
}); // true

[1, 2, 3, 4, 5].some(function (x) {
    return x < 3;
}); // false

這兩個方法都是短路循環(huán)(short-circuiting)。如果對some方法的回調(diào)一旦產(chǎn)生了一個真值,則some方法會直接返回,不會執(zhí)行其余的元素。相似的,every方法的回調(diào)一旦產(chǎn)生了假值,則會立即返回。

可以使用every實現(xiàn)takeWhile函數(shù)。

function takeWhile(a, pred) {
    var result = [];

    a.every(function(x, i) {
        if (!pred(x)) {
            return false; // break
        }

        result[i] = x;
        return true; // continue
    });

    return result;
}

var arr = [1, 2, 4, 8, 16, 32]; // arr數(shù)組里的元素須從小到大排序
var prefix = takeWhile(arr, function (n) {
    return n < 10;
}); // [1, 2, 4, 8]

提示:

使用迭代方法(如Array.prototype.forEachArray.prototype.map)替代for循環(huán)使得代碼更可讀,并且避免了重復(fù)循環(huán)控制邏輯。

使用自定義的迭代函數(shù)來抽象未被標(biāo)準(zhǔn)庫支持的常見循環(huán)模式。

在需要提前終止循環(huán)的情況下,仍然推薦使用傳統(tǒng)的循環(huán)。另外,some和every方法也可用于提前退出。

在類數(shù)組對象上復(fù)用通用的數(shù)組方法

Array.prototype中的標(biāo)準(zhǔn)方法被設(shè)計成其他對象可復(fù)用的方法,即使這些對象并沒有繼承Array。

例如,函數(shù)的arguments對象沒有繼承Array.prototype,但是我們可以提取出forEach方法對象的引用并使用call方法來遍歷每一個參數(shù)。

function highlight() {
    [].forEach.call(arguments, function (widget) {
        widget.setBackground("yellow");
    });
}

在Web平臺,DOM(Document Object Model)的NodeList類是另一個類數(shù)組對象的實例。

數(shù)組對象的基本契約總共有兩個簡單的規(guī)則:

具有一個范圍在0到22^32 - 1的整型length屬性。

length屬性大于該對象的最大索引。

這就是一個對象需要實現(xiàn)的與Array.prototype中任一方法兼容的所有行為。

一個簡單的對象字面量可以用來創(chuàng)建一個類數(shù)組對象。

var arrayLike = {
    0: "a",
    1: "b",
    2: "c",
    length: 3,
};
var result = Array.prototype.map.call(arrayLike, function (s) {
    return s.toUpperCase();
}); // ["A", "B, "C"]

字符串也表現(xiàn)為不可變的數(shù)組,因為它們是可索引的,并且其長度也可以通過length屬性獲取。

var result = Array.prototype.map.call("abc", function (s) {
    return s.toUpperCase();
}); // ["A", "B, "C"]

模擬JavaScript數(shù)組的所有行為很精妙,這要歸功于數(shù)組行為的兩個方面。

將length屬性值設(shè)為小于n的值會自動地刪除索引值大于或等于n的所有屬性。

增加一個索引值為n(大于或等于length屬性值)的屬性會自動地設(shè)置length屬性為n + 1。

幸運的是,對于使用Array.prototype中的方法,這兩條規(guī)則都不是必須的,因為在增加或刪除索引屬性的時候它們都會強(qiáng)制地更新length屬性。

var arrayLike = {
    0: "a",
    1: "b",
    2: "c",
    length: 3,
};
Array.prototype.pop.call(arrayLike);
arrayLike; // { 0: "a", 1: "b", length: 2 }

只有一個Array方法不是完全通用的,即數(shù)組連接方法concat。

function namesColumn() {
    return ["Names"].concat(arguments);
}
namesColumn("Alice", "Bob", "Chris"); // ["Names", { 0: "Alice", 1: "Bob", 2: "Chris" }]

為了使concat方法將一個類數(shù)組對象視為真正的數(shù)組,我們不得不自己轉(zhuǎn)換該數(shù)組。

function namesColumn() {
    return ["names"].concat([].slice.call(arguments));
}
namesColumn("Alice", "Bob", "Chris"); // ["Names", "Alice", "Bob", "Chris"]

提示:

對于類數(shù)組對象,通過提取方法對象并使用其call方法來復(fù)用通用的Array方法。

任意一個具有索引屬性和恰當(dāng)length屬性的對象都可以使用通用的Array方法。

數(shù)組字面量優(yōu)于數(shù)組構(gòu)造函數(shù)

字面量是一種表示數(shù)組的優(yōu)雅的方法。

var a = [1, 2, 3, 4, 5];

// 也可以使用數(shù)組構(gòu)造函數(shù)來替代
var a = new Array(1, 2, 3, 4, 5);

事實證明,Array構(gòu)造函數(shù)存在一些微妙的問題。

首先,你必須確保,沒有人重新包裝過Array類。

function f(Array) {
    return new Array(1, 2, 3, 4, 5);
}
f(String); // new String(1)

你還必須確保沒有人修改過全局的Array變量。

Array = String;
new Array(1, 2, 3, 4, 5); // new String(1)

如果使用單個數(shù)字來調(diào)用Array構(gòu)造函數(shù),效果完全不同。

var arr1 = [17]; // 創(chuàng)建一個元素只有17的數(shù)組,其長度屬性為1

var arr2 = new Array(17); // 創(chuàng)建一個沒有元素的數(shù)組,但其長度屬性為17

提示:

如果數(shù)組構(gòu)造函數(shù)的第一個參數(shù)是數(shù)字則數(shù)組的構(gòu)造函數(shù)行為是不同的。

使用數(shù)組字面量替代數(shù)組構(gòu)造函數(shù)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/94745.html

相關(guān)文章

  • V8引擎是如何工作?

    摘要:是開發(fā)的引擎它是開源的,而且是用編寫的。本文的目的是展示和理解如何工作,以便為客戶端或服務(wù)器端應(yīng)用程序生成優(yōu)化的代碼。將如何處理這種情況事實上,每當(dāng)構(gòu)造函數(shù)聲明一個屬性并跟蹤隱藏類的變化時,就會創(chuàng)建一個新的隱藏類。 V8是google開發(fā)的JavaScript引擎, 它是開源的 ,而且是用C++編寫的。它是用于客戶端(Google Chrome)和服務(wù)器端(node.js)JavaSc...

    不知名網(wǎng)友 評論0 收藏0
  • Python貓薦書系列五:Python高性能編程

    摘要:鋪墊已了,進(jìn)入今天的正題,貓薦書系列之五高性能編程本書適合已入門還想要進(jìn)階和提高的讀者閱讀。書中列舉了兩個慘痛的教訓(xùn)華爾街公司騎士資本由于軟件升級引入的錯誤,損失億美元公司小時全球中斷的嚴(yán)重事故。 showImg(https://segmentfault.com/img/bVbm92w?w=6720&h=4480); 稍微關(guān)心編程語言的使用趨勢的人都知道,最近幾年,國內(nèi)最火的兩種語言非...

    channg 評論0 收藏0
  • Python貓薦書系列五:Python高性能編程

    摘要:鋪墊已了,進(jìn)入今天的正題,貓薦書系列之五高性能編程本書適合已入門還想要進(jìn)階和提高的讀者閱讀。書中列舉了兩個慘痛的教訓(xùn)華爾街公司騎士資本由于軟件升級引入的錯誤,損失億美元公司小時全球中斷的嚴(yán)重事故。 showImg(https://segmentfault.com/img/bVbm92w?w=6720&h=4480); 稍微關(guān)心編程語言的使用趨勢的人都知道,最近幾年,國內(nèi)最火的兩種語言非...

    馬永翠 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<