JS的進階技巧


前言

你真的了解JS嗎,看完全篇,你可能對人生產生疑問。

typeof

typeof運算符,把類型信息當做字符串返回。

//正則表達式 是個什么 ?

typeof /s/     // object

//null

typeof null   // object

正則表達式並不是一個‘function’,而是一個object。在大多數語言中,null 代表的是一個空指針(0x00),但是在js中,null為一個object。

instanceof

instanceof運算符,用來測試一個對象在其原型鏈中是否存在一個構造函數:prototype

//語法 object instanceof constructor 

function Person(){};
var p =new Person();
p instanceof Person;  //true

[] instanceof window.frames[0].Array  // false

因為 Array.prototype !== window.frames[0].Array.prototype ,因此,我們必須使用Array.isArray(obj)或者Object.prototype.toString.call(obj) === "[object Array]" 來判斷obj是否為數組。

Object.prototype.toString

根據上面提到的,可以使用該方法獲取對象類型的字符串。

//call的使用可以看博主前面的文章。(使用apply亦可)

var toString = Object.prototype.toString;

toString.call(new Date); // [object Date]
toString.call(new String); // [object String]
toString.call(Math); // [object Math]
toString.call(/s/); // [object RegExp]
toString.call([]); // [object Array]
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]

作用域安全與構造函數

構造函數:使用new調用的函數,當使用new調用時,構造函數內的this會指向新創建的對象實例。

function Dog(name, age){
    this.name = name;
    this.age = age;
}

let dog = new Dog("柴犬", 5);

dog.name // 柴犬

如果我們沒有使用new又會如何?

let dog = Dog("柴犬", 5);

dog.name // undefined
window.name // 柴犬

這是因為在沒有使用new關鍵字時,this在當前情況被解析成window,所以屬性就被分配到window上了。

function Dog(name, age){
    if(this instanceof Dog){
        this.name = name;
        this.age = age;     
    }else{
        return new Dog(name, age);
    }
}

let dog1 = new Person("柴犬", 5);
dog1.name // 柴犬

let dog2 = Dog("柯基犬", 20);
dog2 .name // 柯基犬

使用上面的方法,就可以再不使用new的情況下,正常構造函數。

惰性載入函數

一個函數如下:

function foo(){
    if(a != b){
        console.log('111') //返回結果1
    }else{
        console.log('222') //返回結果2
    }
}

ab是不變的,那么無論執行多少次,結果都是不變的,但是每一次都要執行if判斷語句,這樣就造成了資源浪費

而惰性載入函數,便可以解決這個問題。

function foo(){
    if(a != b){
        foo = function(){
            console.log('111')
        }
    }else{
        foo = function(){
            console.log('222')
        }
    }
    return foo();
}
var foo = (function foo(){
    if(a != b){
        return function(){
            console.log('111')
        }
    }else{
        return function(){
            console.log('222')
        }
    }
})();

如上函數所示:第一次執行后便會對foo進行賦值,覆蓋之前的函數,所以再次執行,便不會在執行if判斷語句。

fun.bind(thisarg[,arg1[,arg2[,....]]])綁定函數

thisarg:當綁定函數被調用時,該參數會作為原函數運行時的this指向。當使用new時,該參數無效。

arg:當綁定時,這些參數將傳遞給被綁定的方法。

例子:

let person = {
   name: 'addone',
   click: function(e){
       console.log(this.name)
   }
}

let btn = document.getElementById('btn');
EventUtil.addHandle(btn, 'click', person.click);

這里創建了一個person對象,然后將person.click方法分配給DOM,但是當你按按鈕時,會打印undefied,原因是this指向了DOM而不是person

解決方法,當時是使用綁定函數了:

 EventUtil.addHandle(btn, 'click', person.click.bind(person));

函數柯里化

柯里化是把接受多個參數的函數轉變成接受單一參數的函數。

//簡單例子,方便你明白柯里化
function add(num1, num2){
    return num1 + num2;
}
function curryAdd(num2){
    return add(1, num2);
}
add(2, 3) // 5
curryAdd(2) // 3

下面是柯里化函數的通用方法:

function curry(fn){
    var args = Array.prototype.slice.call(arguments, 1);
    return function(){
        let innerArgs = Array.prototype.slice.call(arguments);
        let finalArgs = args.concat(innerArgs);
        return fn.apply(null, finalArgs);
    }
}

Array.prototype.slice.call(arguments, 1)來獲取第一個參數后的所有參數。在函數中,同樣調用 Array.prototype.slice.call(arguments)innerArgs存放所有的參數, 然后用contact將內部外部參數組合起來,用apply傳遞函數。

function add(num1, num2){
    return num1 + num2;
}
let curryAdd1 = curry(add, 1);
curryAdd1(2); // 3

let curryAdd2 = curry(add, 1, 2);
curryAdd2(); // 3

不可擴展對象

默認情況下對象都是可擴展的,無論是擴展屬性或是方法。

let dog = { name: '柴犬' };
dog.age = 5;

如第二行,我們為dog擴展了age屬性。

使用Object.preventExtensions()可以阻止擴展行為。

let dog = { name: '柴犬' };
Object.preventExtensions(dog);
dog.age = 20;

dog.age // undefined

還可以使用 Object.isExtensible()來判斷對象是否支持擴展。

let dog = { name: 'addone' };
Object.isExtensible(dog); // true

Object.preventExtensions(dog);
Object.isExtensible(dog); // false。

密封的對象

密封后的對象不可擴展,且不能刪除屬性和方法。

使用Object.seal()來進行密封。

let dog = { name: '柴犬' };
Object.seal(dog);

dog.age = 20;
delete dog.name;

dog.age // undefined
dog.name // 柴犬

當然也有Object.isSealed()來判斷是否密封

let dog = { name: '柴犬' };
Object.isExtensible(dog); // true
Object.isSealed(dog); // false

Object.seal(dog);
Object.isExtensible(dog); // false
Object.isSealed(dog); // true

凍結對象

凍結對象為防篡改級別最高的,密封,且不能修改。

使用Object.freeze()來進行凍結。

let dog= { name: '柴犬' };
Object.freeze(dog);

dog.age = 20;
delete dog.name;
dog.name = '吉娃娃'

dog.age // undefined
dog.name // 柴犬

當然也有Object.isFrozen()來判斷是否凍結

let dog = { name: '柴犬' };
Object.isExtensible(dog); // true
Object.isSealed(dog); // false
Object.isFrozen(dog); // false

Object.freeze(dog);
Object.isExtensible(dog); // false
Object.isSealed(dog); // true
Object.isFrozen(dog); // true

數組分塊

瀏覽器對長時間運行的腳本進行了制約,如果運行時間超過特定時間或者特定長度,便不會繼續執行。

如果發現某個循環占用了大量的時間,那么就要面對下面兩個問題:

1.該循環是否必須同步完成?

2.數據是否必須按順序完成?

如果是否,那么我們可以使用一種叫做數組分塊的技術。基本思路是為要處理的項目創建一個列隊,然后使用定時取出一個要處理的項目進行處理,以此類推。

function chunk(array, process, context){
    setTimeout(function(){
        // 取出下一個項目進行處理
        let item = array.shift();
        process.call(item);
        
        if(array.length > 0){
            setTimeout(arguments.callee, 100);
        }
    }, 100)
}

這里設置三個參數,要處理的數組,處理的函數,運行該函數的環境。

節流函數

節流函數目的是為了防止某些操作執行的太快,比如onresize,touch等事件。這種高頻率的操作可能會使瀏覽器崩潰,為了避免這種情況,可以采用節流的方式。

function throttle(method, context){
    clearTimeout(method.tId);
    method.tId = setTimeout(function(){
        method.call(context);
    }, 100)
}

這里接收兩個參數,要執行的函數,和執行的環境。執行時先clear之前的定時器,然后將當前定時器賦值給方法的tId,之后調用call來確定函數的執行環境。

function resizeDiv(){
    let div = document.getElementById('div');
    div.style.height = div.offsetWidth + "px";
}

window.onresize = function(){
    throttle(resizeDiv);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM