(1)_toString
function _toString (val) {
return val == null
? ''
: typeof val === 'object'
? JSON.stringify(val, null, 2)
: String(val)
}
這個函數(shù)還是比較好理解的,如果傳入的值為空,則返回空字符串;否則,如果傳入的是對象,則將其變?yōu)?JSON 的數(shù)據(jù)格式(強調(diào):JSON 是字符串!);否則以字符串的形式輸出。
這里面,我們需要學(xué)習(xí)的是 JSON.stringify(val, null, 2) 這個方法,其中的 2 表示縮進為 2 個空格,其中的第二個 null 參數(shù)表示序列化,如果為 null 則表示所有屬性都被序列化。
(2) toNumber
function toNumber (val) {
var n = parseFloat(val);
return isNaN(n) ? val : n
}
該函數(shù)表示將傳入的值轉(zhuǎn)化為數(shù)值,如果傳入的不是數(shù)字,則返回這個值。這里面,我們需要了解到 parseFloat 這個函數(shù),返回它遇到的第一個不是數(shù)字的前面的所有數(shù)字。
比如說:parseFloat('123ab'),返回的是 123;parseFloat('ab123') 則返回 NaN 。
所以,這也是為什么這里使用了 isNaN() 這個函數(shù)。當(dāng)然,在 ES6 的規(guī)范中,建議還是使用 Number.isNaN() 和 Number.parseFloat() 更好。
(3)makeMap
function makeMap (
str, // 字符串
expectsLowerCase // 轉(zhuǎn)換為小寫
) {
var map = Object.create(null);
var list = str.split(',');
for (var i = 0; i < list.length; i++) {
map[list[i]] = true;
}
return expectsLowerCase
? function (val) { return map[val.toLowerCase()]; }
: function (val) { return map[val]; }
}
// 檢查 tag 標(biāo)簽是否為內(nèi)置的標(biāo)簽
var isBuiltInTag = makeMap('slot,component', true);
關(guān)于這個函數(shù),官方的說法是:創(chuàng)建一個 map 并返回一個函數(shù)以確定是否該鍵存在于這個 map 中。至少目前,我們看不出它的作用來,后面用到了再說吧。
其中我們需要學(xué)習(xí)到 Object.create() 這個方法是干什么的,它和 new Object() 有什么區(qū)別。Object.create() 這個方法的用處就是創(chuàng)建一個對象,它可以繼承指定的原型。比如說,我有一個對象:
Object.create(null, {
a: {
value: 1
}
}
它等價于:
const obj = {
a: 1
}
記住,這是錯誤的!因為我們這里設(shè)置的第一個參數(shù)為 null,那么這個對象是沒有原型的!也就是說,它沒有對象原型上的方法。如果要等價的話,那么我們的參數(shù)要設(shè)置為 {}。
Object.create({}, {
a: {
value: 1
}
}
這樣,才是等價的。
在這里,設(shè)置為 null 的意思就是說,創(chuàng)建一個沒有原型的對象。
(4)remove$1
function remove$1 (arr, item) {
if (arr.length) {
var index = arr.indexOf(item);
if (index > -1) {
return arr.splice(index, 1)
}
}
}
這個函數(shù)的意思是,從數(shù)組中移除掉一個值。這個函數(shù)很典型。首先,如果它是一個數(shù)組,那么則找到想要的這個值第一次出現(xiàn)時的索引值;如果數(shù)組中存在這個值,那么就使用 splice() 這個方法將其移除并返回。
我們來測試一下這個方法:
remove$1([1, 2, 3], 2)
// 返回 [2],原數(shù)組變?yōu)閇1, 3]
remove$1([1, 2, 3, 2], 2)
// 返回 [2],原數(shù)組變?yōu)閇1, 3, 2]
這個函數(shù)我們平時也是可以用到的,但是這個函數(shù)呢,只能返回第一個被查詢到的值。這是 indexOf() 這個函數(shù)的局限性,記住這一點。
(5)hasOwnProperty
var hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn (obj, key) {
return hasOwnProperty.call(obj, key)
}
這個函數(shù)的作用是檢查該屬性是否是這個對象所擁有的。說的更加直白一點就是,對象是否有這個鍵(key),我們來測試一下:
const obj = {
a: 1,
b: 2
};
console.log(hasOwn(obj, 'c')); // false
console.log(hasOwn(obj, 'a')); // true
這個 obj 中是沒有 c 這個鍵的。這個是被封裝起來的函數(shù),平時如果單獨使用的話,則可以直接調(diào)用 Object.prototype.hasOwnProperty() 這個方法的。
(6)isPrimitive
function isPrimitive (value) {
return typeof value === 'string' || typeof value === 'number'
}
該函數(shù)是判斷該值是否為原始值。
原始值有六種類型,分別為 String、Number、Boolean、Symbol、Null 和 Undefined。
當(dāng)然這里只判斷了該值是字符串還是數(shù)值類型。
(7)cached
function cached (fn) {
var cache = Object.create(null);
return (function cachedFn (str) {
var hit = cache[str];
return hit || (cache[str] = fn(str))
})
}
這個方法的作用是創(chuàng)建一個純函數(shù)的緩存版本。單獨看,肯定是看不懂這個函數(shù)想表達(dá)一個什么意思。來,我們接著往下看:
(1)駝峰化以連字符分隔的字符串。
var camelizeRE = /-(\w)/g;
var camelize = cached(function (str) {
return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; })
});
(2)大寫字符串。
var capitalize = cached(function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
});
(3)連接駝峰命名的字符串。
var hyphenateRE = /([^-])([A-Z])/g;
var hyphenate = cached(function (str) {
return str
.replace(hyphenateRE, '$1-$2')
.replace(hyphenateRE, '$1-$2')
.toLowerCase()
});
我們先不看上面三個代碼,我們先來試著理解 cache() 這個函數(shù)。這個函數(shù)是干什么的?在 JavaScript 中,有一個很經(jīng)典的函數(shù)寫法叫做 “緩存策略”,它是用來加速我們的數(shù)據(jù)的讀取速度的。
它的實現(xiàn)原理:創(chuàng)建一個對象,將我們所寫的東西保存到這個對象中,如果說我們后面再次用到了這個東西,那么 JavaScript 就不需要再計算一遍了,直接將這個對象中我們需要的東西提取出來就好了。
這是提高執(zhí)行速度的一種十分有效的辦法,可以作為了解,很多的庫都會用到它。
(8)bind$1
function bind$1 (fn, ctx) {
function boundFn (a) {
var l = arguments.length;
return l
? l > 1
? fn.apply(ctx, arguments)
: fn.call(ctx, a)
: fn.call(ctx)
}
// 記錄原始的 fn 長度
boundFn._length = fn.length;
return boundFn
}
該函數(shù)的作用與原生的 bind() 方法一致,但官方說,這個函數(shù)比原生要快。其中的 ctx 參數(shù)的意思是 context,即上下文。其實也就是我們常使用的 this 指定的上下文環(huán)境。
(9)toArray
function toArray (list, start) {
start = start || 0;
var i = list.length - start;
var ret = new Array(i);
while (i--) {
ret[i] = list[i + start];
}
return ret
}
該方法將一個類數(shù)組對象轉(zhuǎn)換成真正的數(shù)組對象。其中的 list 表示類數(shù)組對象,而 start 表示起始的索引值。當(dāng)然,我們的 ES6 中的 Array.from() 也可以轉(zhuǎn)換,可是它不能指定起始的位置。
我們來測試一下:
console.log(toArray({
0: 'a',
1: 'b',
2: 'c',
length: 3
}, 1));
// ["b", "c"]
當(dāng)然了,其實我們使用 ES6 同樣可以達(dá)到這個效果。我來寫一個能達(dá)到同樣效果的函數(shù)吧:
function toArray(list, start = 0) {
const realArray = Array.from(list);
return realArray.slice(start);
}
不僅如此,我還可以設(shè)置結(jié)束的位置:
function toArray(list, start = 0, end = list.length) {
const realArray = Array.from(list);
return realArray.slice(start, end);
}
好了,這個函數(shù)就到此結(jié)束啦。
(10)extend
function extend (to, _from) {
for (var key in _from) {
to[key] = _from[key];
}
return to
}
該函數(shù)的作用是:將屬性混合到目標(biāo)對象中。說的直白一點就是拷貝對象的屬性。
比如我這里有一個空對象,還有另一個對象,將另一個對象中的屬性拷貝到這個空對象中去:
const a = {};
const b = {
a: 1,
b: 2
};
extend(a, b);
console.log(a); // {a: 1, b: 2}