Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 色先锋影音资源,一区二区三区久久,国产精品久久久视频

          整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          JavaScript 工具函數(shù)大全「新」



          一線大廠筆試題靈感來源

          目錄:

          • 第一部分:數(shù)組
          • 第二部分:函數(shù)
          • 第三部分:字符串
          • 第四部分:對象
          • 第五部分:數(shù)字
          • 第六部分:瀏覽器操作及其它

          篩選自以下兩篇文章:

          《127 Helpful JavaScript Snippets You Can Learn in 30 Seconds or Less》

          《30 seconds of code》

          https://github.com/30-seconds/30-seconds-of-code

          原本只想篩選下上面的那篇文章,在精簡掉了部分多余且無用的工具函數(shù)后,感覺不夠。于是順藤摸瓜,找到了原地址: 30 seconds of code

          然后將所有代碼段都看了遍,篩選了以下一百多段代碼片段,并加入了部分自己的理解。

          另外,本文工具函數(shù)的命名非常值得借鑒。


          1. 第一部分:數(shù)組

          1. all:布爾全等判斷

          const all = (arr, fn = Boolean) => arr.every(fn);
          
          all([4, 2, 3], x => x > 1); // true
          all([1, 2, 3]); // true
          復(fù)制代碼

          2. allEqual:檢查數(shù)組各項相等

          const allEqual = arr => arr.every(val => val === arr[0]);
          
          allEqual([1, 2, 3, 4, 5, 6]); // false
          allEqual([1, 1, 1, 1]); // true
          復(fù)制代碼

          3. approximatelyEqual:約等于

          const approximatelyEqual = (v1, v2, epsilon = 0.001) => Math.abs(v1 - v2) < epsilon;
          
          approximatelyEqual(Math.PI / 2.0, 1.5708); // true
          復(fù)制代碼

          4. arrayToCSV:數(shù)組轉(zhuǎn)CSV格式(帶空格的字符串)

          
          const arrayToCSV = (arr, delimiter = ',') =>
            arr.map(v => v.map(x => `"${x}"`).join(delimiter)).join('\n');
            
          arrayToCSV([['a', 'b'], ['c', 'd']]); // '"a","b"\n"c","d"'
          arrayToCSV([['a', 'b'], ['c', 'd']], ';'); // '"a";"b"\n"c";"d"'
          復(fù)制代碼

          5. arrayToHtmlList:數(shù)組轉(zhuǎn)li列表

          此代碼段將數(shù)組的元素轉(zhuǎn)換為<li>標(biāo)簽,并將其附加到給定ID的列表中。

          const arrayToHtmlList = (arr, listID) =>
            (el => (
              (el = document.querySelector('#' + listID)),
              (el.innerHTML += arr.map(item => `<li>${item}</li>`).join(''))
            ))();
            
          arrayToHtmlList(['item 1', 'item 2'], 'myListID');
          復(fù)制代碼

          6. average:平均數(shù)

          const average = (...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length;
          average(...[1, 2, 3]); // 2
          average(1, 2, 3); // 2
          復(fù)制代碼

          7. averageBy:數(shù)組對象屬性平均數(shù)

          此代碼段將獲取數(shù)組對象屬性的平均值

          const averageBy = (arr, fn) =>
            arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0) /
            arr.length;
            
          averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], o => o.n); // 5
          averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], 'n'); // 5
          復(fù)制代碼

          8. bifurcate:拆分斷言后的數(shù)組

          可以根據(jù)每個元素返回的值,使用reduce()和push() 將元素添加到第二次參數(shù)fn中 。

          const bifurcate = (arr, filter) =>
            arr.reduce((acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc), [[], []]);
          bifurcate(['beep', 'boop', 'foo', 'bar'], [true, true, false, true]); 
          // [ ['beep', 'boop', 'bar'], ['foo'] ]
          復(fù)制代碼

          9. castArray:其它類型轉(zhuǎn)數(shù)組

          const castArray = val => (Array.isArray(val) ? val : [val]);
          
          castArray('foo'); // ['foo']
          castArray([1]); // [1]
          castArray(1); // [1]
          復(fù)制代碼

          10. compact:去除數(shù)組中的無效/無用值

          const compact = arr => arr.filter(Boolean);
          
          compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34]); 
          // [ 1, 2, 3, 'a', 's', 34 ]
          復(fù)制代碼

          11. countOccurrences:檢測數(shù)值出現(xiàn)次數(shù)

          const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0);
          countOccurrences([1, 1, 2, 1, 2, 3], 1); // 3
          復(fù)制代碼

          12. deepFlatten:遞歸扁平化數(shù)組

          const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)));
          
          deepFlatten([1, [2], [[3], 4], 5]); // [1,2,3,4,5]
          復(fù)制代碼

          13. difference:尋找差異(并返回第一個數(shù)組獨有的)

          此代碼段查找兩個數(shù)組之間的差異,并返回第一個數(shù)組獨有的。

          
          const difference = (a, b) => {
            const s = new Set(b);
            return a.filter(x => !s.has(x));
          };
          
          difference([1, 2, 3], [1, 2, 4]); // [3]
          復(fù)制代碼

          14. differenceBy:先執(zhí)行再尋找差異

          在將給定函數(shù)應(yīng)用于兩個列表的每個元素之后,此方法返回兩個數(shù)組之間的差異。

          const differenceBy = (a, b, fn) => {
            const s = new Set(b.map(fn));
            return a.filter(x => !s.has(fn(x)));
          };
          
          differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [1.2]
          differenceBy([{ x: 2 }, { x: 1 }], [{ x: 1 }], v => v.x); // [ { x: 2 } ]
          復(fù)制代碼

          15. dropWhile:刪除不符合條件的值

          此代碼段從數(shù)組頂部開始刪除元素,直到傳遞的函數(shù)返回為true。

          const dropWhile = (arr, func) => {
            while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1);
            return arr;
          };
          
          dropWhile([1, 2, 3, 4], n => n >= 3); // [3,4]
          復(fù)制代碼

          16. flatten:指定深度扁平化數(shù)組

          此代碼段第二參數(shù)可指定深度。

          const flatten = (arr, depth = 1) =>
            arr.reduce((a, v) => a.concat(depth > 1 && Array.isArray(v) ? flatten(v, depth - 1) : v), []);
          
          flatten([1, [2], 3, 4]); // [1, 2, 3, 4]
          flatten([1, [2, [3, [4, 5], 6], 7], 8], 2); // [1, 2, 3, [4, 5], 6, 7, 8]
          復(fù)制代碼

          17. indexOfAll:返回數(shù)組中某值的所有索引

          此代碼段可用于獲取數(shù)組中某個值的所有索引,如果此值中未包含該值,則返回一個空數(shù)組。

          const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []);
          
          indexOfAll([1, 2, 3, 1, 2, 3], 1); // [0,3]
          indexOfAll([1, 2, 3], 4); // []
          復(fù)制代碼

          18. intersection:兩數(shù)組的交集

          
          const intersection = (a, b) => {
            const s = new Set(b);
            return a.filter(x => s.has(x));
          };
          
          intersection([1, 2, 3], [4, 3, 2]); // [2, 3]
          復(fù)制代碼

          19. intersectionWith:兩數(shù)組都符合條件的交集

          此片段可用于在對兩個數(shù)組的每個元素執(zhí)行了函數(shù)之后,返回兩個數(shù)組中存在的元素列表。

          
          const intersectionBy = (a, b, fn) => {
            const s = new Set(b.map(fn));
            return a.filter(x => s.has(fn(x)));
          };
          
          intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [2.1]
          復(fù)制代碼

          20. intersectionWith:先比較后返回交集

          const intersectionWith = (a, b, comp) => a.filter(x => b.findIndex(y => comp(x, y)) !== -1);
          
          intersectionWith([1, 1.2, 1.5, 3, 0], [1.9, 3, 0, 3.9], (a, b) => Math.round(a) === Math.round(b)); // [1.5, 3, 0]
          復(fù)制代碼

          21. minN:返回指定長度的升序數(shù)組

          const minN = (arr, n = 1) => [...arr].sort((a, b) => a - b).slice(0, n);
          
          minN([1, 2, 3]); // [1]
          minN([1, 2, 3], 2); // [1,2]
          復(fù)制代碼

          22. negate:根據(jù)條件反向篩選

          
          const negate = func => (...args) => !func(...args);
          
          [1, 2, 3, 4, 5, 6].filter(negate(n => n % 2 === 0)); // [ 1, 3, 5 ]
          復(fù)制代碼

          23. randomIntArrayInRange:生成兩數(shù)之間指定長度的隨機數(shù)組

          const randomIntArrayInRange = (min, max, n = 1) =>
            Array.from({ length: n }, () => Math.floor(Math.random() * (max - min + 1)) + min);
            
          randomIntArrayInRange(12, 35, 10); // [ 34, 14, 27, 17, 30, 27, 20, 26, 21, 14 ]
          復(fù)制代碼

          24. sample:在指定數(shù)組中獲取隨機數(shù)

          const sample = arr => arr[Math.floor(Math.random() * arr.length)];
          
          sample([3, 7, 9, 11]); // 9
          復(fù)制代碼

          25. sampleSize:在指定數(shù)組中獲取指定長度的隨機數(shù)

          此代碼段可用于從數(shù)組中獲取指定長度的隨機數(shù),直至窮盡數(shù)組。 使用Fisher-Yates算法對數(shù)組中的元素進行隨機選擇。

          const sampleSize = ([...arr], n = 1) => {
            let m = arr.length;
            while (m) {
              const i = Math.floor(Math.random() * m--);
              [arr[m], arr[i]] = [arr[i], arr[m]];
            }
            return arr.slice(0, n);
          };
          
          sampleSize([1, 2, 3], 2); // [3,1]
          sampleSize([1, 2, 3], 4); // [2,3,1]
          復(fù)制代碼

          26. shuffle:“洗牌” 數(shù)組

          此代碼段使用Fisher-Yates算法隨機排序數(shù)組的元素。

          
          const shuffle = ([...arr]) => {
            let m = arr.length;
            while (m) {
              const i = Math.floor(Math.random() * m--);
              [arr[m], arr[i]] = [arr[i], arr[m]];
            }
            return arr;
          };
          
          const foo = [1, 2, 3];
          shuffle(foo); // [2, 3, 1], foo = [1, 2, 3]
          復(fù)制代碼

          27. nest:根據(jù)parent_id生成樹結(jié)構(gòu)(阿里一面真題)

          根據(jù)每項的parent_id,生成具體樹形結(jié)構(gòu)的對象。

          const nest = (items, id = null, link = 'parent_id') =>
            items
              .filter(item => item[link] === id)
              .map(item => ({ ...item, children: nest(items, item.id) }));
          復(fù)制代碼

          用法:

          const comments = [
            { id: 1, parent_id: null },
            { id: 2, parent_id: 1 },
            { id: 3, parent_id: 1 },
            { id: 4, parent_id: 2 },
            { id: 5, parent_id: 4 }
          ];
          const nestedComments = nest(comments); // [{ id: 1, parent_id: null, children: [...] }]
          復(fù)制代碼


          強烈建議去理解這個的實現(xiàn),因為這是我親身遇到的阿里一面真題:


          2. 第二部分:函數(shù)

          1. attempt:捕獲函數(shù)運行異常

          該代碼段執(zhí)行一個函數(shù),返回結(jié)果或捕獲的錯誤對象。

          onst attempt = (fn, ...args) => {
            try {
              return fn(...args);
            } catch (e) {
              return e instanceof Error ? e : new Error(e);
            }
          };
          var elements = attempt(function(selector) {
            return document.querySelectorAll(selector);
          }, '>_>');
          if (elements instanceof Error) elements = []; // elements = []
          復(fù)制代碼

          2. defer:推遲執(zhí)行

          此代碼段延遲了函數(shù)的執(zhí)行,直到清除了當(dāng)前調(diào)用堆棧。

          const defer = (fn, ...args) => setTimeout(fn, 1, ...args);
          
          defer(console.log, 'a'), console.log('b'); // logs 'b' then 'a'
          復(fù)制代碼

          3. runPromisesInSeries:運行多個Promises

          const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve());
          const delay = d => new Promise(r => setTimeout(r, d));
          
          runPromisesInSeries([() => delay(1000), () => delay(2000)]);
          //依次執(zhí)行每個Promises ,總共需要3秒鐘才能完成
          復(fù)制代碼

          4. timeTaken:計算函數(shù)執(zhí)行時間

          
          const timeTaken = callback => {
            console.time('timeTaken');
            const r = callback();
            console.timeEnd('timeTaken');
            return r;
          };
          
          timeTaken(() => Math.pow(2, 10)); // 1024, (logged): timeTaken: 0.02099609375ms
          復(fù)制代碼

          5. createEventHub:簡單的發(fā)布/訂閱模式

          創(chuàng)建一個發(fā)布/訂閱(發(fā)布-訂閱)事件集線,有emit,on和off方法。

          1. 使用Object.create(null)創(chuàng)建一個空的hub對象。
          2. emit,根據(jù)event參數(shù)解析處理程序數(shù)組,然后.forEach()通過傳入數(shù)據(jù)作為參數(shù)來運行每個處理程序。
          3. on,為事件創(chuàng)建一個數(shù)組(若不存在則為空數(shù)組),然后.push()將處理程序添加到該數(shù)組。
          4. off,用.findIndex()在事件數(shù)組中查找處理程序的索引,并使用.splice()刪除。
          const createEventHub = () => ({
            hub: Object.create(null),
            emit(event, data) {
              (this.hub[event] || []).forEach(handler => handler(data));
            },
            on(event, handler) {
              if (!this.hub[event]) this.hub[event] = [];
              this.hub[event].push(handler);
            },
            off(event, handler) {
              const i = (this.hub[event] || []).findIndex(h => h === handler);
              if (i > -1) this.hub[event].splice(i, 1);
              if (this.hub[event].length === 0) delete this.hub[event];
            }
          });
          復(fù)制代碼

          用法:

          const handler = data => console.log(data);
          const hub = createEventHub();
          let increment = 0;
          
          // 訂閱,監(jiān)聽不同事件
          hub.on('message', handler);
          hub.on('message', () => console.log('Message event fired'));
          hub.on('increment', () => increment++);
          
          // 發(fā)布:發(fā)出事件以調(diào)用所有訂閱給它們的處理程序,并將數(shù)據(jù)作為參數(shù)傳遞給它們
          hub.emit('message', 'hello world'); // 打印 'hello world' 和 'Message event fired'
          hub.emit('message', { hello: 'world' }); // 打印 對象 和 'Message event fired'
          hub.emit('increment'); // increment = 1
          
          // 停止訂閱
          hub.off('message', handler);
          復(fù)制代碼

          6. memoize:緩存函數(shù)

          通過實例化一個Map對象來創(chuàng)建一個空的緩存。

          通過檢查輸入值的函數(shù)輸出是否已緩存,返回存儲一個參數(shù)的函數(shù),該參數(shù)將被提供給已記憶的函數(shù);如果沒有,則存儲并返回它。

          const memoize = fn => {
            const cache = new Map();
            const cached = function(val) {
              return cache.has(val) ? cache.get(val) : cache.set(val, fn.call(this, val)) && cache.get(val);
            };
            cached.cache = cache;
            return cached;
          };
          復(fù)制代碼

          Ps: 這個版本可能不是很清晰,還有Vue源碼版的:

          /**
           * Create a cached version of a pure function.
           */
          export function cached<F: Function> (fn: F): F {
            const cache = Object.create(null)
            return (function cachedFn (str: string) {
              const hit = cache[str]
              return hit || (cache[str] = fn(str))
            }: any)
          }
          復(fù)制代碼

          7. once:只調(diào)用一次的函數(shù)

          const once = fn => {
            let called = false
            return function () {
              if (!called) {
                called = true
                fn.apply(this, arguments)
              }
            }
          };
          復(fù)制代碼

          用法:

          const startApp = function(event) {
            console.log(this, event); // document.body, MouseEvent
          };
          document.body.addEventListener('click', once(startApp)); // 只執(zhí)行一次startApp
          復(fù)制代碼

          8. flattenObject:以鍵的路徑扁平化對象

          使用遞歸。

          1. 利用Object.keys(obj)聯(lián)合Array.prototype.reduce(),以每片葉子節(jié)點轉(zhuǎn)換為扁平的路徑節(jié)點。
          2. 如果鍵的值是一個對象,則函數(shù)使用調(diào)用適當(dāng)?shù)淖陨韕refix以創(chuàng)建路徑Object.assign()。
          3. 否則,它將適當(dāng)?shù)那熬Y鍵值對添加到累加器對象。
          4. prefix除非您希望每個鍵都有一個前綴,否則應(yīng)始終省略第二個參數(shù)。
          const flattenObject = (obj, prefix = '') =>
            Object.keys(obj).reduce((acc, k) => {
              const pre = prefix.length ? prefix + '.' : '';
              if (typeof obj[k] === 'object') Object.assign(acc, flattenObject(obj[k], pre + k));
              else acc[pre + k] = obj[k];
              return acc;
            }, {});
            
          flattenObject({ a: { b: { c: 1 } }, d: 1 }); // { 'a.b.c': 1, d: 1 }
          復(fù)制代碼

          9. unflattenObject:以鍵的路徑展開對象

          與上面的相反,展開對象。

          const unflattenObject = obj =>
            Object.keys(obj).reduce((acc, k) => {
              if (k.indexOf('.') !== -1) {
                const keys = k.split('.');
                Object.assign(
                  acc,
                  JSON.parse(
                    '{' +
                      keys.map((v, i) => (i !== keys.length - 1 ? `"${v}":{` : `"${v}":`)).join('') +
                      obj[k] +
                      '}'.repeat(keys.length)
                  )
                );
              } else acc[k] = obj[k];
              return acc;
            }, {});
            
          unflattenObject({ 'a.b.c': 1, d: 1 }); // { a: { b: { c: 1 } }, d: 1 }
          復(fù)制代碼

          這個的用途,在做Tree組件或復(fù)雜表單時取值非常舒服。

          3. 第三部分:字符串

          1.byteSize:返回字符串的字節(jié)長度

          const byteSize = str => new Blob([str]).size;
          
          byteSize(''); // 4
          byteSize('Hello World'); // 11
          復(fù)制代碼

          2. capitalize:首字母大寫

          const capitalize = ([first, ...rest]) =>
            first.toUpperCase() + rest.join('');
            
          capitalize('fooBar'); // 'FooBar'
          capitalize('fooBar', true); // 'Foobar'
          復(fù)制代碼

          3. capitalizeEveryWord:每個單詞首字母大寫

          const capitalizeEveryWord = str => str.replace(/\b[a-z]/g, char => char.toUpperCase());
          
          capitalizeEveryWord('hello world!'); // 'Hello World!'
          復(fù)制代碼

          4. decapitalize:首字母小寫

          const decapitalize = ([first, ...rest]) =>
            first.toLowerCase() + rest.join('')
          
          decapitalize('FooBar'); // 'fooBar'
          decapitalize('FooBar'); // 'fooBar'
          復(fù)制代碼

          5. luhnCheck:銀行卡號碼校驗(luhn算法)

          Luhn算法的實現(xiàn),用于驗證各種標(biāo)識號,例如信用卡號,IMEI號,國家提供商標(biāo)識號等。

          與String.prototype.split('')結(jié)合使用,以獲取數(shù)字數(shù)組。獲得最后一個數(shù)字。實施luhn算法。如果被整除,則返回,否則返回。

          const luhnCheck = num => {
            let arr = (num + '')
              .split('')
              .reverse()
              .map(x => parseInt(x));
            let lastDigit = arr.splice(0, 1)[0];
            let sum = arr.reduce((acc, val, i) => (i % 2 !== 0 ? acc + val : acc + ((val * 2) % 9) || 9), 0);
            sum += lastDigit;
            return sum % 10 === 0;
          };
          復(fù)制代碼

          用例:

          luhnCheck('4485275742308327'); // true
          luhnCheck(6011329933655299); //  false
          luhnCheck(123456789); // false
          復(fù)制代碼

          補充:銀行卡號碼的校驗規(guī)則

          關(guān)于luhn算法,可以參考以下文章:

          銀行卡號碼校驗算法(Luhn算法,又叫模10算法)

          銀行卡號碼的校驗采用Luhn算法,校驗過程大致如下:

          1. 從右到左給卡號字符串編號,最右邊第一位是1,最右邊第二位是2,最右邊第三位是3….
          2. 從右向左遍歷,對每一位字符t執(zhí)行第三個步驟,并將每一位的計算結(jié)果相加得到一個數(shù)s。
          3. 對每一位的計算規(guī)則:如果這一位是奇數(shù)位,則返回t本身,如果是偶數(shù)位,則先將t乘以2得到一個數(shù)n,如果n是一位數(shù)(小于10),直接返回n,否則將n的個位數(shù)和十位數(shù)相加返回。
          4. 如果s能夠整除10,則此號碼有效,否則號碼無效。

          因為最終的結(jié)果會對10取余來判斷是否能夠整除10,所以又叫做模10算法。

          當(dāng)然,還是庫比較香: bankcardinfo



          6. splitLines:將多行字符串拆分為行數(shù)組。

          使用String.prototype.split()和正則表達式匹配換行符并創(chuàng)建一個數(shù)組。

          const splitLines = str => str.split(/\r?\n/);
          
          splitLines('This\nis a\nmultiline\nstring.\n'); // ['This', 'is a', 'multiline', 'string.' , '']
          復(fù)制代碼

          7. stripHTMLTags:刪除字符串中的HTMl標(biāo)簽

          從字符串中刪除HTML / XML標(biāo)簽。

          使用正則表達式從字符串中刪除HTML / XML 標(biāo)記。

          const stripHTMLTags = str => str.replace(/<[^>]*>/g, '');
          
          stripHTMLTags('<p><em>lorem</em> <strong>ipsum</strong></p>'); // 'lorem ipsum'
          復(fù)制代碼

          4. 第四部分:對象

          1. dayOfYear:當(dāng)前日期天數(shù)

          const dayOfYear = date =>
            Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 1000 / 60 / 60 / 24);
          
          dayOfYear(new Date()); // 285
          復(fù)制代碼

          2. forOwn:迭代屬性并執(zhí)行回調(diào)

          const forOwn = (obj, fn) => Object.keys(obj).forEach(key => fn(obj[key], key, obj));
          forOwn({ foo: 'bar', a: 1 }, v => console.log(v)); // 'bar', 1
          復(fù)制代碼

          3. Get Time From Date:返回當(dāng)前24小時制時間的字符串

          const getColonTimeFromDate = date => date.toTimeString().slice(0, 8);
          
          getColonTimeFromDate(new Date()); // "08:38:00"
          復(fù)制代碼

          4. Get Days Between Dates:返回日期間的天數(shù)

          const getDaysDiffBetweenDates = (dateInitial, dateFinal) =>
            (dateFinal - dateInitial) / (1000 * 3600 * 24);
            
          getDaysDiffBetweenDates(new Date('2019-01-01'), new Date('2019-10-14')); // 286
          復(fù)制代碼

          5. is:檢查值是否為特定類型。

          const is = (type, val) => ![, null].includes(val) && val.constructor === type;
          
          is(Array, [1]); // true
          is(ArrayBuffer, new ArrayBuffer()); // true
          is(Map, new Map()); // true
          is(RegExp, /./g); // true
          is(Set, new Set()); // true
          is(WeakMap, new WeakMap()); // true
          is(WeakSet, new WeakSet()); // true
          is(String, ''); // true
          is(String, new String('')); // true
          is(Number, 1); // true
          is(Number, new Number(1)); // true
          is(Boolean, true); // true
          is(Boolean, new Boolean(true)); // true
          復(fù)制代碼

          6. isAfterDate:檢查是否在某日期后

          const isAfterDate = (dateA, dateB) => dateA > dateB;
          
          isAfterDate(new Date(2010, 10, 21), new Date(2010, 10, 20)); // true
          復(fù)制代碼

          7. isBeforeDate:檢查是否在某日期前

          const isBeforeDate = (dateA, dateB) => dateA < dateB;
          
          isBeforeDate(new Date(2010, 10, 20), new Date(2010, 10, 21)); // true
          復(fù)制代碼

          8 tomorrow:獲取明天的字符串格式時間

          
          const tomorrow = () => {
            let t = new Date();
            t.setDate(t.getDate() + 1);
            return t.toISOString().split('T')[0];
          };
          
          tomorrow(); // 2019-10-15 (如果明天是2019-10-15)
          復(fù)制代碼

          9. equals:全等判斷

          在兩個變量之間進行深度比較以確定它們是否全等。

          此代碼段精簡的核心在于Array.prototype.every()的使用。

          const equals = (a, b) => {
            if (a === b) return true;
            if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
            if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')) return a === b;
            if (a.prototype !== b.prototype) return false;
            let keys = Object.keys(a);
            if (keys.length !== Object.keys(b).length) return false;
            return keys.every(k => equals(a[k], b[k]));
          };
          復(fù)制代碼

          用法:

          equals({ a: [2, { e: 3 }], b: [4], c: 'foo' }, { a: [2, { e: 3 }], b: [4], c: 'foo' }); // true
          復(fù)制代碼

          5. 第五部分:數(shù)字

          1. randomIntegerInRange:生成指定范圍的隨機整數(shù)

          const randomIntegerInRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
          
          randomIntegerInRange(0, 5); // 3
          復(fù)制代碼

          2. randomNumberInRange:生成指定范圍的隨機小數(shù)

          const randomNumberInRange = (min, max) => Math.random() * (max - min) + min;
          
          randomNumberInRange(2, 10); // 6.0211363285087005
          復(fù)制代碼

          3. round:四舍五入到指定位數(shù)

          const round = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`);
          
          round(1.005, 2); // 1.01
          復(fù)制代碼

          4. sum:計算數(shù)組或多個數(shù)字的總和

          
          const sum = (...arr) => [...arr].reduce((acc, val) => acc + val, 0);
          
          sum(1, 2, 3, 4); // 10
          sum(...[1, 2, 3, 4]); // 10
          復(fù)制代碼

          5. toCurrency:簡單的貨幣單位轉(zhuǎn)換

          const toCurrency = (n, curr, LanguageFormat = undefined) =>
            Intl.NumberFormat(LanguageFormat, { style: 'currency', currency: curr }).format(n);
            
          toCurrency(123456.789, 'EUR'); // €123,456.79
          toCurrency(123456.789, 'USD', 'en-us'); // $123,456.79  
          toCurrency(123456.789, 'USD', 'fa'); // ??????????
          toCurrency(322342436423.2435, 'JPY'); // ¥322,342,436,423 
          復(fù)制代碼

          6. 第六部分:瀏覽器操作及其它

          1. bottomVisible:檢查頁面底部是否可見

          const bottomVisible = () =>
            document.documentElement.clientHeight + window.scrollY >=
            (document.documentElement.scrollHeight || document.documentElement.clientHeight);
          
          bottomVisible(); // true
          復(fù)制代碼

          2. Create Directory:檢查創(chuàng)建目錄

          此代碼段調(diào)用fs模塊的existsSync()檢查目錄是否存在,如果不存在,則mkdirSync()創(chuàng)建該目錄。

          const fs = require('fs');
          const createDirIfNotExists = dir => (!fs.existsSync(dir) ? fs.mkdirSync(dir) : undefined);
          createDirIfNotExists('test'); 
          復(fù)制代碼

          3. currentURL:返回當(dāng)前鏈接url

          const currentURL = () => window.location.href;
          
          currentURL(); // 'https://juejin.im'
          復(fù)制代碼

          4. distance:返回兩點間的距離

          該代碼段通過計算歐幾里得距離來返回兩點之間的距離。

          const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0);
          
          distance(1, 1, 2, 3); // 2.23606797749979
          復(fù)制代碼

          5. elementContains:檢查是否包含子元素

          此代碼段檢查父元素是否包含子元素。

          const elementContains = (parent, child) => parent !== child && parent.contains(child);
          
          elementContains(document.querySelector('head'), document.querySelector('title')); // true
          elementContains(document.querySelector('body'), document.querySelector('body')); // false
          復(fù)制代碼

          6. getStyle:返回指定元素的生效樣式

          const getStyle = (el, ruleName) => getComputedStyle(el)[ruleName];
          
          getStyle(document.querySelector('p'), 'font-size'); // '16px'
          復(fù)制代碼

          7. getType:返回值或變量的類型名

          const getType = v =>
            v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name.toLowerCase();
            
          getType(new Set([1, 2, 3])); // 'set'
          getType([1, 2, 3]); // 'array'
          復(fù)制代碼

          8. hasClass:校驗指定元素的類名

          const hasClass = (el, className) => el.classList.contains(className);
          hasClass(document.querySelector('p.special'), 'special'); // true
          復(fù)制代碼

          9. hide:隱藏所有的指定標(biāo)簽

          const hide = (...el) => [...el].forEach(e => (e.style.display = 'none'));
          
          hide(document.querySelectorAll('img')); // 隱藏所有<img>標(biāo)簽
          復(fù)制代碼

          10. httpsRedirect:HTTP 跳轉(zhuǎn) HTTPS

          const httpsRedirect = () => {
            if (location.protocol !== 'https:') location.replace('https://' + location.href.split('//')[1]);
          };
          
          httpsRedirect(); // 若在`http://www.baidu.com`, 則跳轉(zhuǎn)到`https://www.baidu.com`
          復(fù)制代碼

          11. insertAfter:在指定元素之后插入新元素

          const insertAfter = (el, htmlString) => el.insertAdjacentHTML('afterend', htmlString);
          
          // <div id="myId">...</div> <p>after</p>
          insertAfter(document.getElementById('myId'), '<p>after</p>'); 
          復(fù)制代碼

          12. insertBefore:在指定元素之前插入新元素

          const insertBefore = (el, htmlString) => el.insertAdjacentHTML('beforebegin', htmlString);
          
          insertBefore(document.getElementById('myId'), '<p>before</p>'); // <p>before</p> <div id="myId">...</div>
          復(fù)制代碼

          13. isBrowser:檢查是否為瀏覽器環(huán)境

          此代碼段可用于確定當(dāng)前運行時環(huán)境是否為瀏覽器。這有助于避免在服務(wù)器(節(jié)點)上運行前端模塊時出錯。

          const isBrowser = () => ![typeof window, typeof document].includes('undefined');
          
          isBrowser(); // true (browser)
          isBrowser(); // false (Node)
          復(fù)制代碼

          14. isBrowserTab:檢查當(dāng)前標(biāo)簽頁是否活動

          const isBrowserTabFocused = () => !document.hidden;
          
          isBrowserTabFocused(); // true
          復(fù)制代碼

          15. nodeListToArray:轉(zhuǎn)換nodeList為數(shù)組

          const nodeListToArray = nodeList => [...nodeList];
          
          nodeListToArray(document.childNodes); // [ <!DOCTYPE html>, html ]
          復(fù)制代碼

          16. Random Hexadecimal Color Code:隨機十六進制顏色

          
          const randomHexColorCode = () => {
            let n = (Math.random() * 0xfffff * 1000000).toString(16);
            return '#' + n.slice(0, 6);
          };
          
          randomHexColorCode(); // "#e34155"
          復(fù)制代碼

          17. scrollToTop:平滑滾動至頂部

          該代碼段可用于平滑滾動到當(dāng)前頁面的頂部。

          const scrollToTop = () => {
            const c = document.documentElement.scrollTop || document.body.scrollTop;
            if (c > 0) {
              window.requestAnimationFrame(scrollToTop);
              window.scrollTo(0, c - c / 8);
            }
          };
          
          scrollToTop();
          復(fù)制代碼

          18. smoothScroll:滾動到指定元素區(qū)域

          該代碼段可將指定元素平滑滾動到瀏覽器窗口的可見區(qū)域。

          const smoothScroll = element =>
            document.querySelector(element).scrollIntoView({
              behavior: 'smooth'
            });
            
          smoothScroll('#fooBar'); 
          smoothScroll('.fooBar'); 
          復(fù)制代碼

          19. detectDeviceType:檢測移動/PC設(shè)備

          const detectDeviceType = () =>
            /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
              ? 'Mobile'
              : 'Desktop';
          復(fù)制代碼

          20. getScrollPosition:返回當(dāng)前的滾動位置

          默認參數(shù)為window ,pageXOffset(pageYOffset)為第一選擇,沒有則用scrollLeft(scrollTop)

          const getScrollPosition = (el = window) => ({
            x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft,
            y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop
          });
          
          getScrollPosition(); // {x: 0, y: 200}
          復(fù)制代碼

          21. size:獲取不同類型變量的字節(jié)長度

          這個的實現(xiàn)非常巧妙,利用Blob類文件對象的特性,獲取對象的長度。

          另外,多重三元運算符,是真香。

          const size = val =>
            Array.isArray(val)
              ? val.length
              : val && typeof val === 'object'
              ? val.size || val.length || Object.keys(val).length
              : typeof val === 'string'
              ? new Blob([val]).size
              : 0;
          
          size([1, 2, 3, 4, 5]); // 5
          size('size'); // 4
          size({ one: 1, two: 2, three: 3 }); // 3
          
          復(fù)制代碼

          22. escapeHTML:轉(zhuǎn)義HTML

          當(dāng)然是用來防XSS攻擊啦。

          const escapeHTML = str =>
            str.replace(
              /[&<>'"]/g,
              tag =>
                ({
                  '&': '&',
                  '<': '<',
                  '>': '>',
                  "'": ''',
                  '"': '"'
                }[tag] || tag)
            );
          
          escapeHTML('<a href="#">Me & you</a>'); // '<a href="#">Me & you</a>'
          復(fù)制代碼



          ?? 看完三件事

          如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:

          1. 點贊,讓更多的人也能看到這篇內(nèi)容(收藏不點贊,都是耍流氓 -_-)
          2. 關(guān)注公眾號「前端勸退師」,不定期分享原創(chuàng)知識。
          3. 也看看其它文章


          原鏈接:https://juejin.im/post/5da1a04ae51d45783d6122bf

          階函數(shù)

          高階函數(shù)可以接收函數(shù)作為參數(shù),同時也可以返回一個新的函數(shù)。

          高階函數(shù)之所以高階,是因為高階函數(shù)的參數(shù)和返回值對象可以是函數(shù),這超越了普通函數(shù)處理的數(shù)據(jù)類型,例如字符串(strings)、數(shù)字(numbers)、布爾值(booleans)等。

          JavaScript 中,函數(shù)的應(yīng)用場景很豐富:

          • 作為變量存儲
          • 在數(shù)組中使用
          • 作為對象屬性(即方法)
          • 作為參數(shù)傳遞
          • 作為其他函數(shù)的返回值

          理解高階函數(shù)的關(guān)鍵在于,函數(shù)即數(shù)據(jù)

          數(shù)據(jù)是函數(shù)運作的基本

          數(shù)據(jù):字符串(Strings)

          sayHi = (name) => `Hi, ${name}!`;
          result = sayHi('User');
          console.log(result); // 'Hi, User!'
          

          數(shù)據(jù):數(shù)字(Numbers)

          double = (x) => x * 2;
          result = double(4);
          console.log(result); // 8
          

          數(shù)據(jù):布爾值(Booleans)

          getClearance = (allowed) => allowed ? 'Access granted' : 'Access denied';
          result1 = getClearance(true);
          result2 = getClearance(false);
          console.log(result1); // 'Access granted'
          console.log(result2); // 'Access denied'
          

          數(shù)據(jù):對象(Objects)

          getFirstName = (obj) => obj.firstName;
          result = getFirstName({ 
           firstName: 'Yazeed'
          });
          console.log(result); // 'Yazeed'
          

          數(shù)據(jù):數(shù)組(Arrays)

          len = (array) => array.length;
          result = len([1, 2, 3]);
          console.log(result); // 3
          

          在所有的主流語言中,以上這五種數(shù)據(jù)類型被稱為 “頭等對象”(原文:first-class citizen, https://www.wikiwand.com/en/First-class_citizen)。

          為什么是“頭等”呢?因為這五種數(shù)據(jù)類型既可以作為參數(shù)傳遞,又可以存儲在變量或者數(shù)組中,還可以作為變量用于計算,是數(shù)據(jù)的基本形式。

          函數(shù)也是數(shù)據(jù)

          函數(shù)作為參數(shù)

          isEven = (num) => num % 2 === 0;
          result = [1, 2, 3, 4].filter(isEven);
          console.log(result); // [2, 4]
          

          請觀察 filter 函數(shù)是如何使用 isEven 函數(shù)來判斷要保留哪些內(nèi)容的。這里的 isEven 是一個函數(shù),作為參數(shù)傳入了 filter 函數(shù)中。

          filter 函數(shù)每次在做判斷的時候都會調(diào)用 isEven 函數(shù),用 isEven 函數(shù)返回的布爾值來決定當(dāng)前數(shù)值的去留。

          函數(shù)作為返回值

          add = (x) => (y) => x + y;
          

          add 函數(shù)需要兩個參數(shù),但不需要它們倆同時傳入,第一次傳參傳入 x 就會返還一個新函數(shù),這個函數(shù)需要傳入 y 參數(shù)。

          能夠這樣操作的基礎(chǔ)在于 JavaScript 語言允許函數(shù)本身作為返回值存在,就像函數(shù)可以返回字符串(strings)、數(shù)字(numbers)、布爾值(booleans)等,JS 函數(shù)還可以返回另一個函數(shù)。

          當(dāng)然,我們也可以使用“雙重調(diào)用”的方式,一次性提供 x 和 y 兩個參數(shù):

          result = add(10)(20);
          console.log(result); // 30
          

          或者分兩次調(diào)用,先傳參數(shù) x,再傳參數(shù) y:

          add10 = add(10);
          result = add10(20);
          console.log(result); // 30
          

          在上面這個例子中, add10 函數(shù)是第一次調(diào)用 add 函數(shù)的返回值,可以嘗試用 console.log把結(jié)果打出來觀察一下。

          add10 函數(shù)會接收 y 參數(shù),然后返回 x + y 值。一旦 y 值到位,函數(shù)會立馬進行運算并返回結(jié)果。

          可重復(fù)利用性

          高階函數(shù)的魅力在于它的可重復(fù)利用性,如果不是高階函數(shù),map、filter、reduce 等強大的數(shù)組函數(shù)就不可能存在。

          假設(shè)我們有一組用戶,如下所示,然后我們要對該數(shù)組進行操作。

          users = [
           {
           name: 'Yazeed',
           age: 25
           },
           {
           name: 'Sam',
           age: 30
           },
           {
           name: 'Bill',
           age: 20
           }
          ];
          

          Map

          沒有高階函數(shù)的話,我們必須回到 for 循環(huán)的懷抱才能實現(xiàn) map 函數(shù)的操作。

          getName = (user) => user.name;
          usernames = [];
          for (let i = 0; i < users.length; i++) {
           const name = getName(users[i]);
           usernames.push(name);
          }
          console.log(usernames);
          // ["Yazeed", "Sam", "Bill"]
          

          用 map 函數(shù)就簡單多啦!

          usernames = users.map(getName);
          console.log(usernames);
          // ["Yazeed", "Sam", "Bill"]
          

          Filter

          在沒有高階函數(shù)的情況下,必須要用 for 循環(huán)來實現(xiàn) filter 函數(shù)的功能。

          startsWithB = (string) => string.toLowerCase().startsWith('b');
          namesStartingWithB = [];
          for (let i = 0; i < users.length; i++) {
           if (startsWithB(users[i].name)) {
           namesStartingWithB.push(users[i]);
           }
          }
          console.log(namesStartingWithB);
          // [{ "name": "Bill", "age": 20 }]
          


          用 filter 函數(shù)就簡單多啦!

          namesStartingWithB = users.filter((user) => startsWithB(user.name));
          console.log(namesStartingWithB);
          // [{ "name": "Bill", "age": 20 }]
          

          Reduce

          reduce 函數(shù)也是的……沒有高階函數(shù)的話,很多高端操作都是無法實現(xiàn)的!

          total = 0;
          for (let i = 0; i < users.length; i++) {
           total += users[i].age;
          }
          console.log(total);
          // 75
          


          那這樣是不是簡單多啦?

          totalAge = users.reduce((total, user) => user.age + total, 0);
          console.log(totalAge);
          // 75
          


          總結(jié)

          • 字符串(strings)、數(shù)字(numbers)、布爾值(booleans)、數(shù)組(arrays)、對象(objects)可以作為變量(variables)、數(shù)組(arrays)、屬性( properties)或者方法(methods)存儲起來。
          • JavaScript 語言中,函數(shù)也是像數(shù)據(jù)一樣同等對待的。
          • 因此函數(shù)可以作為另外一個函數(shù)的參數(shù)或者返回值使用,這樣的做法叫高階函數(shù)
          • map、filter、 reduce 等函數(shù)就是高階函數(shù)的最佳代表,它們讓數(shù)組的處理(改變,搜索,相加等)變得簡單不少!

          版權(quán)

          原文鏈接:https://mp.weixin.qq.com/s/JAsy4TVv3tzL0q8RxXBf7Q

          作者:jingruzhang

          . this 適合你嗎?

          我看到許多文章在介紹 JavaScript 的 this 時都會假設(shè)你學(xué)過某種面向?qū)ο蟮木幊陶Z言,比如 Java、C++ 或 Python 等。但這篇文章面向的讀者是那些不知道 this 是什么的人。我盡量不用任何術(shù)語來解釋 this 是什么,以及 this 的用法。

          也許你一直不敢解開 this 的秘密,因為它看起來挺奇怪也挺嚇人的。或許你只在 StackOverflow 說你需要用它的時候(比如在 React 里實現(xiàn)某個功能)才會使用。

          在深入介紹 this 之前,我們首先需要理解函數(shù)式編程和面向?qū)ο缶幊讨g的區(qū)別。

          2. 函數(shù)式編程 vs 面向?qū)ο缶幊?/strong>

          你可能不知道,JavaScript 同時擁有面向?qū)ο蠛秃瘮?shù)式的結(jié)構(gòu),所以你可以自己選擇用哪種風(fēng)格,或者兩者都用。

          我在很早以前使用 JavaScript 時就喜歡函數(shù)式編程,而且會像躲避瘟疫一樣避開面向?qū)ο缶幊蹋驗槲也焕斫饷嫦驅(qū)ο笾械年P(guān)鍵字,比如 this。我不知道為什么要用 this。似乎沒有它我也可以做好所有的工作。

          而且我是對的。

          在某種意義上 。也許你可以只專注于一種結(jié)構(gòu)并且完全忽略另一種,但這樣你只能是一個 JavaScript 開發(fā)者。為了解釋函數(shù)式和面向?qū)ο笾g的區(qū)別,下面我們通過一個數(shù)組來舉例說明,數(shù)組的內(nèi)容是 Facebook 的好友列表。

          假設(shè)你要做一個 Web 應(yīng)用,當(dāng)用戶使用 Facebook 登錄你的 Web 應(yīng)用時,需要顯示他們的 Facebook 的好友信息。你需要訪問 Facebook 并獲得用戶的好友數(shù)據(jù)。這些數(shù)據(jù)可能是 firstName、lastName、username、numFriends、friendData、birthday 和 lastTenPosts 等信息。

          const data = [
           {
           firstName: 'Bob',
           lastName: 'Ross',
           username: 'bob.ross', 
           numFriends: 125,
           birthday: '2/23/1985',
           lastTenPosts: ['What a nice day', 'I love Kanye West', ...],
           },
           ...
          ]
          

          假設(shè)上述數(shù)據(jù)是你通過 Facebook API 獲得的。現(xiàn)在需要將其轉(zhuǎn)換成方便你的項目使用的格式。我們假設(shè)你想顯示的好友信息如下:

          • 姓名,格式為`${firstName} ${lastName}`
          • 三篇隨機文章
          • 距離生日的天數(shù)


          3. 函數(shù)式方式

          函數(shù)式的方式就是將整個數(shù)組或者數(shù)組中的某個元素傳遞給某個函數(shù),然后返回你需要的信息:

          const fullNames = getFullNames(data)
          // ['Ross, Bob', 'Smith, Joanna', ...]
          

          首先我們有 Facebook API 返回的原始數(shù)據(jù)。為了將其轉(zhuǎn)換成需要的格式,首先要將數(shù)據(jù)傳遞給一個函數(shù),函數(shù)的輸出是(或者包含)經(jīng)過修改的數(shù)據(jù),這些數(shù)據(jù)可以在應(yīng)用中向用戶展示。

          我們可以用類似的方法獲得隨機三篇文章,并且計算距離好友生日的天數(shù)。

          函數(shù)式的方式是:將原始數(shù)據(jù)傳遞給一個函數(shù)或者多個函數(shù),獲得對你的項目有用的數(shù)據(jù)格式。

          4. 面向?qū)ο蟮姆绞?/strong>

          對于編程初學(xué)者和 JavaScript 初學(xué)者,面向?qū)ο蟮母拍羁赡苡悬c難以理解。其思想是,我們要將每個好友變成一個對象,這個對象能夠生成你一切開發(fā)者需要的東西。

          你可以創(chuàng)建一個對象,這個對象對應(yīng)于某個好友,它有 fullName 屬性,還有兩個函數(shù) getThreeRandomPosts 和 getDaysUntilBirthday。

          function initializeFriend(data) {
           return {
           fullName: `${data.firstName} ${data.lastName}`,
           getThreeRandomPosts: function() {
           // get three random posts from data.lastTenPosts
           },
           getDaysUntilBirthday: function() {
           // use data.birthday to get the num days until birthday
           }
           };
          }
          const objectFriends = data.map(initializeFriend)
          objectFriends[0].getThreeRandomPosts() 
          // Gets three of Bob Ross's posts
          

          面向?qū)ο蟮姆绞骄褪菫閿?shù)據(jù)創(chuàng)建對象,每個對象都有自己的狀態(tài),并且包含必要的信息,能夠生成需要的數(shù)據(jù)。

          5. 這跟 this 有什么關(guān)系?

          你也許從來沒想過要寫上面的 initializeFriend 代碼,而且你也許認為,這種代碼可能會很有用。但你也注意到,這并不是真正的面向?qū)ο蟆?/p>

          其原因就是,上面例子中的 getThreeRandomPosts 或 getdaysUntilBirtyday 能夠正常工作的原因其實是閉包。因為使用了閉包,它們在 initializeFriend 返回之后依然能訪問 data。關(guān)于閉包的更多信息可以看看這篇文章:作用域和閉包(https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch5.md)。

          還有一個方法該怎么處理?我們假設(shè)這個方法叫做 greeting。注意方法(與 JavaScript 的對象有關(guān)的方法)其實只是一個屬性,只不過屬性值是函數(shù)而已。我們想在 greeting 中實現(xiàn)以下功能:

          function initializeFriend(data) {
           return {
           fullName: `${data.firstName} ${data.lastName}`,
           getThreeRandomPosts: function() {
           // get three random posts from data.lastTenPosts
           },
           getDaysUntilBirthday: function() {
           // use data.birthday to get the num days until birthday
           },
           greeting: function() {
           return `Hello, this is ${fullName}'s data!`
           }
           };
          }
          

          這樣能正常工作嗎?

          不能!

          我們新建的對象能夠訪問 initializeFriend 中的一切變量,但不能訪問這個對象本身的屬性或方法。當(dāng)然你會問,

          難道不能在 greeting 中直接用 data.firstName 和 data.lastName 嗎?

          當(dāng)然可以。但要是想在 greeting 中加入距離好友生日的天數(shù)怎么辦?我們最好還是有辦法在 greeting 中調(diào)用 getDaysUntilBirthday。

          這時輪到 this 出場了!



          6. 終于——this 是什么

          this 在不同的環(huán)境中可以指代不同的東西。默認的全局環(huán)境中 this 指代的是全局對象(在瀏覽器中 this 是 window 對象),這沒什么太大的用途。而在 this 的規(guī)則中具有實用性的是這一條:

          如果在對象的方法中使用 this,而該方法在該對象的上下文中調(diào)用,那么 this 指代該對象本身。

          你會說“在該對象的上下文中調(diào)用”……是啥意思?

          別著急,我們一會兒就說。

          所以,如果我們想從 greeting 中調(diào)用 getDaysUntilBirtyday 我們只需要寫 this.getDaysUntilBirthday,因為此時的 this 就是對象本身。

          附注:不要在全局作用域的普通函數(shù)或另一個函數(shù)的作用域中使用 this!this 是個面向?qū)ο蟮臇|西,它只在對象的上下文(或類的上下文)中有意義。

          我們利用 this 來重寫 initializeFriend:

          function initializeFriend(data) {
           return {
           lastTenPosts: data.lastTenPosts,
           birthday: data.birthday, 
           fullName: `${data.firstName} ${data.lastName}`,
           getThreeRandomPosts: function() {
           // get three random posts from this.lastTenPosts
           },
           getDaysUntilBirthday: function() {
           // use this.birthday to get the num days until birthday
           },
           greeting: function() {
           const numDays = this.getDaysUntilBirthday() 
           return `Hello, this is ${this.fullName}'s data! It is ${numDays} until ${this.fullName}'s birthday!`
           }
           };
          }
          

          現(xiàn)在,在 initializeFriend 執(zhí)行結(jié)束后,該對象需要的一切都位于對象本身的作用域之內(nèi)了。我們的方法不需要再依賴于閉包,它們只會用到對象本身包含的信息。

          好吧,這是 this 的用法之一,但你說過 this 在不同的上下文中有不同的含義。那是什么意思?為什么不一定會指向?qū)ο笞约海?/p>

          有時候,你需要將 this 指向某個特定的東西。一種情況就是事件處理函數(shù)。比如我們希望在用戶點擊好友時打開好友的 Facebook 首頁。我們會給對象添加下面的 onClick 方法:

          function initializeFriend(data) {
           return {
           lastTenPosts: data.lastTenPosts,
           birthday: data.birthday,
           username: data.username, 
           fullName: `${data.firstName} ${data.lastName}`,
           getThreeRandomPosts: function() {
           // get three random posts from this.lastTenPosts
           },
           getDaysUntilBirthday: function() {
           // use this.birthday to get the num days until birthday
           },
           greeting: function() {
           const numDays = this.getDaysUntilBirthday() 
           return `Hello, this is ${this.fullName}'s data! It is ${numDays} until ${this.fullName}'s birthday!`
           },
           onFriendClick: function() {
           window.open(`https://facebook.com/${this.username}`)
           }
           };
          }
          

          注意我們在對象中添加了 username 屬性,這樣 onFriendClick 就能訪問它,從而在新窗口中打開該好友的 Facebook 首頁。現(xiàn)在只需要編寫 HTML:

          <button id="Bob_Ross">
           <!-- A bunch of info associated with Bob Ross -->
          </button> 
          

          還有 JavaScript:

          const bobRossObj = initializeFriend(data[0])
          const bobRossDOMEl = document.getElementById('Bob_Ross')
          bobRossDOMEl.addEventListener("onclick", bobRossObj.onFriendClick)
          

          在上述代碼中,我們給 Bob Ross 創(chuàng)建了一個對象。然后我們拿到了 Bob Ross 對應(yīng)的 DOM 元素。然后執(zhí)行 onFriendClick 方法來打開 Bob 的 Facebook 主頁。似乎沒問題,對吧?

          有問題!

          哪里出錯了?

          注意我們調(diào)用 onclick 處理程序的代碼是 bobRossObj.onFriendClick。看到問題了嗎?要是寫成這樣的話能看出來嗎?

          bobRossDOMEl.addEventListener("onclick", function() {
           window.open(`https://facebook.com/${this.username}`)
          })
          

          現(xiàn)在看到問題了嗎?如果把事件處理程序?qū)懗?bobRossObj.onFriendClick,實際上是把 bobRossObj.onFriendClick 上保存的函數(shù)拿出來,然后作為參數(shù)傳遞。它不再“依附”在 bobRossObj 上,也就是說,this 不再指向 bobRossObj。它實際指向全局對象,也就是說 this.username 不存在。似乎我們沒什么辦法了。

          輪到綁定上場了!



          7. 明確綁定 this

          我們需要明確地將 this 綁定到 bobRossObj 上。我們可以通過 bind 實現(xiàn):

          const bobRossObj = initializeFriend(data[0])
          const bobRossDOMEl = document.getElementById('Bob_Ross')
          bobRossObj.onFriendClick = bobRossObj.onFriendClick.bind(bobRossObj)
          bobRossDOMEl.addEventListener("onclick", bobRossObj.onFriendClick)
          

          之前,this 是按照默認的規(guī)則設(shè)置的。但使用 bind 之后,我們明確地將 bobRossObj.onFriendClick 中的 this 的值設(shè)置為 bobRossObj 對象本身。

          到此為止,我們看到了為什么要使用 this,以及為什么要明確地綁定 this。最后我們來介紹一下,this 實際上是箭頭函數(shù)。

          8. 箭頭函數(shù)

          你也許注意到了箭頭函數(shù)最近很流行。人們喜歡箭頭函數(shù),因為很簡潔、很優(yōu)雅。而且你還知道箭頭函數(shù)和普通函數(shù)有點區(qū)別,盡管不太清楚具體區(qū)別是什么。

          簡而言之,兩者的區(qū)別在于:

          在定義箭頭函數(shù)時,不管 this 指向誰,箭頭函數(shù)內(nèi)部的 this 永遠指向同一個東西。

          嗯……這貌似沒什么用……似乎跟普通函數(shù)的行為一樣啊?

          我們通過 initializeFriend 舉例說明。假設(shè)我們想添加一個名為 greeting 的函數(shù):

          function initializeFriend(data) {
           return {
           lastTenPosts: data.lastTenPosts,
           birthday: data.birthday,
           username: data.username, 
           fullName: `${data.firstName} ${data.lastName}`,
           getThreeRandomPosts: function() {
           // get three random posts from this.lastTenPosts
           },
           getDaysUntilBirthday: function() {
           // use this.birthday to get the num days until birthday
           },
           greeting: function() {
           function getLastPost() {
           return this.lastTenPosts[0]
           }
           const lastPost = getLastPost() 
           return `Hello, this is ${this.fullName}'s data!
           ${this.fullName}'s last post was ${lastPost}.`
           },
           onFriendClick: function() {
           window.open(`https://facebook.com/${this.username}`)
           }
           };
          }
          

          這樣能運行嗎?如果不能,怎樣修改才能運行?

          答案是不能。因為 getLastPost 沒有在對象的上下文中調(diào)用,因此getLastPost 中的 this 按照默認規(guī)則指向了全局對象。

          你說沒有“在對象的上下文中調(diào)用”……難道它不是從 initializeFriend 返回的內(nèi)部調(diào)用的嗎?如果這還不叫“在對象的上下文中調(diào)用”,那我就不知道什么才算了。

          我知道“在對象的上下文中調(diào)用”這個術(shù)語很模糊。也許,判斷函數(shù)是否“在對象的上下文中調(diào)用”的好方法就是檢查一遍函數(shù)的調(diào)用過程,看看是否有個對象“依附”到了函數(shù)上。

          我們來檢查下執(zhí)行 bobRossObj.onFriendClick() 時的情況。“給我對象 bobRossObj,找到其中的 onFriendClick 然后調(diào)用該屬性對應(yīng)的函數(shù)”。

          我們同樣檢查下執(zhí)行 getLastPost() 時的情況。“給我名為 getLastPost 的函數(shù)然后執(zhí)行。”看到了嗎?我們根本沒有提到對象。

          好了,這里有個難題來測試你的理解程度。假設(shè)有個函數(shù)名為 functionCaller,它的功能就是調(diào)用一個函數(shù):

          functionCaller(fn) {
           fn()
          }
          

          如果調(diào)用 functionCaller(bobRossObj.onFriendClick) 會怎樣?你會認為 onFriendClick 是“在對象的上下文中調(diào)用”的嗎?this.username有定義嗎?

          我們來檢查一遍:“給我 bobRosObj 對象然后查找其屬性 onFriendClick。取出其中的值(這個值碰巧是個函數(shù)),然后將它傳遞給 functionCaller,取名為 fn。然后,執(zhí)行名為 fn 的函數(shù)。”注意該函數(shù)在調(diào)用之前已經(jīng)從 bobRossObj 對象上“脫離”了,因此并不是“在對象的上下文中調(diào)用”的,所以 this.username 沒有定義。

          這時可以用箭頭函數(shù)解決這個問題:

          function initializeFriend(data) {
           return {
           lastTenPosts: data.lastTenPosts,
           birthday: data.birthday,
           username: data.username, 
           fullName: `${data.firstName} ${data.lastName}`,
           getThreeRandomPosts: function() {
           // get three random posts from this.lastTenPosts
           },
           getDaysUntilBirthday: function() {
           // use this.birthday to get the num days until birthday
           },
           greeting: function() {
           const getLastPost = () => {
           return this.lastTenPosts[0]
           }
           const lastPost = getLastPost() 
           return `Hello, this is ${this.fullName}'s data!
           ${this.fullName}'s last post was ${lastPost}.`
           },
           onFriendClick: function() {
           window.open(`https://facebook.com/${this.username}`)
           }
           };
          }
          

          上述代碼的規(guī)則是:

          在定義箭頭函數(shù)時,不管 this 指向誰,箭頭函數(shù)內(nèi)部的 this 永遠指向同一個東西。

          箭頭函數(shù)是在 greeting 中定義的。我們知道,在 greeting 內(nèi)部的 this 指向?qū)ο蟊旧怼R虼耍^函數(shù)內(nèi)部的 this 也指向?qū)ο蟊旧恚@正是我們需要的結(jié)果。

          9. 結(jié)論

          this 有時很不好理解,但它對于開發(fā) JavaScript 應(yīng)用非常有用。本文當(dāng)然沒能介紹 this 的所有方面。一些沒有涉及到的話題包括:

          • call 和 apply;
          • 使用 new 時 this 會怎樣;
          • 在 ES6 的 class 中 this 會怎樣。

          我建議你首先問問自己在這些情況下的 this,然后在瀏覽器中執(zhí)行代碼來檢驗?zāi)愕慕Y(jié)果。

          原文地址:https://mp.weixin.qq.com/s/L9eac6rzkSE_JqxXyg3FQw


          主站蜘蛛池模板: 国产精品免费视频一区| 高清国产精品人妻一区二区| 成人在线视频一区| 久久99国产精一区二区三区| 精品国产高清自在线一区二区三区 | 一区二区三区免费视频播放器| 午夜福利av无码一区二区| jizz免费一区二区三区| 内射一区二区精品视频在线观看| 99精品国产高清一区二区麻豆 | 国产乱码精品一区三上| 国产精品视频分类一区| 亚洲国产情侣一区二区三区| 高清国产AV一区二区三区| 韩国福利一区二区三区高清视频| 日韩av片无码一区二区三区不卡| 影院成人区精品一区二区婷婷丽春院影视 | 成人精品一区二区户外勾搭野战 | 波多野结衣在线观看一区二区三区 | 中文乱码人妻系列一区二区| 久久久精品人妻一区二区三区四| 精品久久一区二区| 无码少妇一区二区三区浪潮AV | 国内精品一区二区三区最新| 天美传媒一区二区三区| 国内精品视频一区二区八戒| 波多野结衣中文字幕一区二区三区| 波多野结衣中文字幕一区二区三区 | 日韩经典精品无码一区| 一区二区国产精品| 少妇无码AV无码一区| 国产AV午夜精品一区二区入口| 亚洲AV乱码一区二区三区林ゆな | 国产成人无码精品一区在线观看| 国产无码一区二区在线| 少妇精品无码一区二区三区 | 韩日午夜在线资源一区二区 | 一区二区三区午夜| 国产一区二区精品| 欧美人妻一区黄a片| 鲁大师成人一区二区三区|