js常見函式總結(一)

錦魚發表於2019-02-15

[TOC]

編寫一個方法,求一個字串的長度

    function getbytes(str){
        var len = str.length;
        var bytes = len;
        for (var i=0;i<len;i++){
            if(str.charCodeAt(i)>255)bytes++
        }
        return bytes;
    }
複製程式碼

如何統計字串“aaaabbbccccddfgh”

var str = 'aaaabbbccccddfgh';
dealStr(str);
function dealStr(str) {
    var obj = {};
    for(var i=0;i<str.length;i++){
        var v = str.charAt(i);
        if(obj[v] && obj[v].value === v){
            ++obj[v].count
        }else{
            obj[v] = {
                count:1,
                value:v
            }
        }
    }
    return obj;
}
var obj = dealStr(str);
for(key in obj){
    console.log(obj[key].value + '=' +obj[key].count)
}
複製程式碼

深拷貝

    //deepClone函式
    function deepClone(obj){
        let objClone = Array.isArray(obj)?[]:{};
        if(obj && typeof obj === 'object'){
            for(key in obj){
                if(obj.hasOwnProperty(key)){
                    //判斷obj子元素是否為物件,如果是,遞迴複製
                    if(obj[key] && typeof obj[key] == 'object'){
                        objClone[key] = deepClone(obj[key]);
                    }else{
                        objClone[key] = obj[key];
                    }
                }
            }
        }
    }
    
複製程式碼

寫一個function,清除字串前後的空格(相容所有瀏覽器)

function trim(str) {
    if(str && typeof str == 'string'){
        return str.replace(/^\s+|\s+$/g,"");
    }
}
複製程式碼

請說出阻止事件冒泡的方法

function stopBubble(e) {
    var evt = e||window.event;
    evt.stopPropagation ? evt.stopPropagation() : (evt.cancelBubble = true);
}
複製程式碼

以下兩個函式會返回相同的結果嗎?為什麼

function foo1() {
    return {
        bar:'hello'
    }
}
function foo2() {
    return
    {
        bar:'hello'
    }
}
//第一個返回一個物件
//第二個返回undefine 因為第二個 return 後面沒內容,分號自動載入 return 後面
複製程式碼

判斷一個數字是否是整數

//es6
Number.isInteger()
//非es6
function isInteger(x) {
    return (x^0) === x;
}
複製程式碼

寫一個sum方法,在使用任意語法呼叫時,都可以正常工作

//方法一
function sum(x) {
    if(arguments.length === 2){
        return arguments[0] + arguments[1];
    }else{
        return function (y) {
            return x+y
        }
    }
}
//方法二
function sum2(x,y) {
    if(y !== undefined){
        return x+y;
    }else{
        return function (y) {
            x+y;
        }
    }
}
複製程式碼

下面的程式碼將輸出什麼內容到控制檯?

var myObject = {
    foo:'bar',
    func:function(){
        var self = this;
        console.log("outer func: this.foo = " + this.foo);
        console.log("outer func: self.foo =" + self.foo);
        (function(){
            console.log("inner func: this.foo="+this.foo);
            console.log("inner func: self.foo="+self.foo);
        }())
    }
}
//輸出
outer func: this.foo = bar
outer func:self.foo = bar
inner func: this.foo = undefined
inner func: self.foo = bar
複製程式碼

下面的程式碼將輸出什麼?閉包在這裡能起什麼作用?

for(var i=0;i<5;i++){
    (function(){
       setTimeout(function(){
        console.log(i)
       },i*1000) 
    }())
}
//輸出  5 5 5 5 5
//原因:在迴圈中執行的每個函式將先整個迴圈完成之後執行,因此,將會引用儲存在i中的最後一個值,那就是5

//閉包可以為每次迭代建立一個唯一的作用域,儲存作用域內的迴圈變數,如下程式碼會按預期輸出0、1、2、3、4到控制檯
for(var i=0;i<5;i++){
    (function(x){
        setTimeout(function(){
            console.log(x)
        },x*1000) 
    })(i)
}
複製程式碼

一下程式碼行將輸出什麼到控制檯

console.log("0 || 1 ="+(0 || 1));
console.log("1 || 2 ="+(1 || 2));
console.log("0 && 1 ="+(0 && 1));
console.log("1 && 2 ="+(1 && 2));
//輸出
//0 || 1 = 1;
//1 || 2 = 1;
//0 && 1 = 0;
//1 && 2 = 2 
複製程式碼

以下程式碼將輸出什麼?

var a={},b={key:'b'},c={key:'c'};
a[b] = 123;
a[c] = 456;
console.log(a[b]);
//輸出 456 
//當設定物件屬性時,JavaScript 會隱式地將[]內的變數轉換成字串,在這種情況下,由於b和c都是物件,
因此它們都將被轉換為[object Object]。結果就是,a[b]和a[c]均相當於a["[object Object]"],
並可以互換使用。因此,設定或引用a[c]和設定或引用a[b]完全相同。
複製程式碼

防抖

防抖是將多次執行操作轉變成一次

/**
 * underscore 防抖函式,返回函式連續呼叫時,空閒時間必須大於或等於 wait,func 才會執行
 *
 * @param  {function} func        回撥函式
 * @param  {number}   wait        表示時間視窗的間隔
 * @param  {boolean}  immediate   設定為ture時,是否立即呼叫函式
 * @return {function}             返回客戶呼叫函式
 */
_.debounce = function(func, wait, immediate) {
    var timeout, args, context, timestamp, result;

    var later = function() {
      // 現在和上一次時間戳比較
      var last = _.now() - timestamp;
      // 如果當前間隔時間少於設定時間且大於0就重新設定定時器
      if (last < wait && last >= 0) {
        timeout = setTimeout(later, wait - last);
      } else {
        // 否則的話就是時間到了執行回撥函式
        timeout = null;
        if (!immediate) {
          result = func.apply(context, args);
          if (!timeout) context = args = null;
        }
      }
    };

    return function() {
      context = this;
      args = arguments;
      // 獲得時間戳
      timestamp = _.now();
      // 如果定時器不存在且立即執行函式
      var callNow = immediate && !timeout;
      // 如果定時器不存在就建立一個
      if (!timeout) timeout = setTimeout(later, wait);
      if (callNow) {
        // 如果需要立即執行函式的話 通過 apply 執行
        result = func.apply(context, args);
        context = args = null;
      }

      return result;
    };
  };
複製程式碼

節流

節流是將多次執行變成每隔一段時間執行

/**
 * underscore 節流函式,返回函式連續呼叫時,func 執行頻率限定為 次 / wait
 *
 * @param  {function}   func      回撥函式
 * @param  {number}     wait      表示時間視窗的間隔
 * @param  {object}     options   如果想忽略開始函式的的呼叫,傳入{leading: false}。
 *                                如果想忽略結尾函式的呼叫,傳入{trailing: false}
 *                                兩者不能共存,否則函式不能執行
 * @return {function}             返回客戶呼叫函式   
 */
_.throttle = function(func, wait, options) {
    var context, args, result;
    var timeout = null;
    // 之前的時間戳
    var previous = 0;
    // 如果 options 沒傳則設為空物件
    if (!options) options = {};
    // 定時器回撥函式
    var later = function() {
      // 如果設定了 leading,就將 previous 設為 0
      // 用於下面函式的第一個 if 判斷
      previous = options.leading === false ? 0 : _.now();
      // 置空一是為了防止記憶體洩漏,二是為了下面的定時器判斷
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };
    return function() {
      // 獲得當前時間戳
      var now = _.now();
      // 首次進入前者肯定為 true
	  // 如果需要第一次不執行函式
	  // 就將上次時間戳設為當前的
      // 這樣在接下來計算 remaining 的值時會大於0
      if (!previous && options.leading === false) previous = now;
      // 計算剩餘時間
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      // 如果當前呼叫已經大於上次呼叫時間 + wait
      // 或者使用者手動調了時間
 	  // 如果設定了 trailing,只會進入這個條件
	  // 如果沒有設定 leading,那麼第一次會進入這個條件
	  // 還有一點,你可能會覺得開啟了定時器那麼應該不會進入這個 if 條件了
	  // 其實還是會進入的,因為定時器的延時
	  // 並不是準確的時間,很可能你設定了2秒
	  // 但是他需要2.2秒才觸發,這時候就會進入這個條件
      if (remaining <= 0 || remaining > wait) {
        // 如果存在定時器就清理掉否則會呼叫二次回撥
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        // 判斷是否設定了定時器和 trailing
	    // 沒有的話就開啟一個定時器
        // 並且不能不能同時設定 leading 和 trailing
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  };
複製程式碼

相關文章