1. 程式人生 > >js高階技巧總結之高階函式和防篡改物件

js高階技巧總結之高階函式和防篡改物件

高階函式

1.安全的型別檢測

        談到型別檢測,可能大家首先想到的就是typeof 或者 instanceof (檢測陣列Array.isArray(arr))等這些方式,但是這些方法都有自己的侷限性,比如說Safari(直至第四版)對正則使用typeof會返回function,instanceof必須要在同一個作用域下,還有現在瀏覽器開始支援原生JSON物件了(Douglas Crockford定義了一個全域性JSON物件),於是檢測物件是不是原生就又變得困難了 。

         大家可能想到了一個物件的建構函式名和作用域是無關的,於是就可以使用以下方式判斷(比如說陣列):

function isArray(obj){
    return Object.prototype.toString.call(obj) == "[object Array]";
}

        像這樣,可以判別建構函式名是否為 [object Array],[object Function],[object RegExp],[object JSON]來判定各種型別

2.作用域安全的建構函式

         建構函式就是一個使用New操作符呼叫的函式,只有使用New呼叫時,建構函式裡面的this物件才會指向例項,如果像如下呼叫:

function Student(name,age){
    this.name = name;
    this.age = age;
}
var Tom = Student('Tom',12);

訪問Tom.name是未定義的,這是因為此時的this指向了window,訪問window.name就會得到'Tom'。其實很多時候我們會忘掉使用New操作符去例項化一個物件,造成this晚繫結。如果想避免這種問題就需要使用如下構造方式:

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

當然,這樣雖然避免了this晚繫結的問題,但是鎖定了可以使用該建構函式的環境,當你想要使用構造i函式竊取模式的繼承且不使用原型鏈,那麼這個繼承就會無效,例如:

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

function Xueba(name,age,score){
    Student.call(this,name,age);
    this.score = score;
}

var Tom = new Xueba('Tom',12,99);
console.log(Tom.name);

發現name和age並沒有被繼承,因為Xueba並非Student的例項。要解決這個問題只需要讓Xueba成為Student的例項即可

Xueba.prototype = new Student();

3.惰性載入函式

        因為各個瀏覽器之間的差異,多數的js程式碼中包含了很多if語句,重複的執行大量的if語句是很耗費資源的,於是就有了解決方法:惰性載入。何為惰性載入呢,直白說就是在第一次執行的時候得到結果,以後再執行就直接呼叫第一次獲取的結果使用。惰性載入有兩種載入方式:在第一次呼叫的時執行,在頁面首次載入時執行,至於那種方法更合適,就要看你的具體需求而定了。舉個簡單的栗子:

 function getInnerText(element){
            if(typeof element.textContent == "String"){
                return element.textContent;
            } else {
                return element.innerText;
            }
        }

(請忽略這裡的判斷很少,就當他的if語句很多且每個if裡面的邏輯很多)每次呼叫都會進行一次判斷,再看看下面的程式碼

function getInnerText(element){
            if(typeof element.textContent == "String"){
                getInnerText = function(element){
                    return element.textContent;
                }
            } else {
                getInnerText = function(element){
                    return element.innerText;
                } 
            }
            return getInnerText(item);
        }

第一次呼叫以上兩者都會進行判斷,但是不同的是,第二種在第一次掉用後就不需要再次判斷了,直接返回結果。假設處於大量判斷的程式碼中,後者是不是就節省了資源(廢話一句,可能會有人說,這點資源對於人的感知來說壓根感覺不到,但是你想想如果有很多這樣類似的判定呢),另外一種寫法就是在宣告函式的時候就指定適當的函式

var element = document.getElementById("there");
var getInnerText = (function(){
    if(typeof element.textContent == "String"){
          return function(element){
                return element.textContent;
          };
     } else {
          return function(element){
                 return element.innerText;
           } 
     }
      return getInnerText(item);
})();
console.log(getInnerText(element));

4.函式繫結

              簡單來說就是在特定的環境中以指定引數呼叫另一個函式,先看個例子

var handler = {
    msg: "233",
    handleClick: function(event){
        console.log(this.msg);
    }
}

var btn = document.getElementById("my-btn");
btn.addEventListener('click',handler.handleClick,false);

為什麼點選後顯示的是undefine而不是233 ?此時的handler裡的this指向了window,這裡我們可以用閉包來解決這個問題

var handler = {
   msg: "233",
   handleClick: function(event){
      console.log(this.msg);
   }
}

var btn = document.getElementById("my-btn");
btn.addEventListener('click',function(event){handler.handleClick(event);},false);

雖然問題解決了,但是有時候閉包會增大程式碼的理解以及除錯難度。ECMAScript5為所有函式定義了一個原生的bind()方法,它接受一個函式和一個環境,並返回一個在給定環境中呼叫給定函式的函式,並將所有引數傳遞過去,可能有一些繞口,我們來看一個例子

function bind(fn,context){
    return function(){
        return fn.apply(context,arguments);
    }
}

bind函式的作用就是在閉包中使用apply呼叫傳入的函式,並傳遞context物件和引數,當呼叫返回函式時,就會在給定環境中執行被傳入的函式並給出所有引數。使用方式如下

function bind(fn,context){
    return function(){
         return fn.apply(context,arguments);
     }
}
var handler = {
     msg: "233",
      handleClick: function(event){
             console.log(this.msg);
      }
}

var btn = document.getElementById("my-btn");
btn.addEventListener('click',bind(handler.handleClick,handler),false);

上面的bind函式只是為了大家方便理解才寫出來的,在ECMAScript5中無需我們定義就可直接使用,如下

var handler = {
   msg: "233",
   handleClick: function(event){
        console.log(this.msg);
   }
}

var btn = document.getElementById("my-btn");
btn.addEventListener('click',handler.handleClick.bind(handler),false);

此時被繫結的函式要比普通函式有更多的開銷,他們主要用於事件處理程式以及SetTimeout,setInterval等,請在必要時使用。

5.函式柯里化

          函式的柯里化和函式的繫結精密相關,使用方式也是一樣的,兩者的區別在於前者的函式被呼叫時,還需要傳入一些引數。bind()方法也實現了函式柯里化,例如

var handler = {
    msg: "233",
    handleClick: function(name,event){
       console.log(this.msg+":"+name+":"+event.type);
    }
}

var btn = document.getElementById("my-btn");
btn.addEventListener("click",handler.handleClick.bind(handler,"hello"),false);

防篡改物件

          js共享的本質一直讓我們有些頭疼,多人開發的專案,你一不小心就修改了別人的程式碼,甚至是用非相容重寫原生物件。當然你可以通過屬性的[[Configurable]],[[Writeable]]等特性改變屬性的行為,但是這裡還有更簡單的方法。

1.不可擴充套件物件(Object.preventExtensions(obj))

         一般的物件都是可以擴充套件的,也就是說任何物件都允許新增屬性和方法,但是使用Object.preventExtensions()方法可以改變這個行為

var person = {
    name: "Bob",
    age: 12
};
person.score= 99;
console.log(person);
Object.preventExtensions(person);
person.job = "student";
console.log(person);

你會發現呼叫了Object.preventExtensions()方法後,就不能新增屬性了,但是對於已經存在的屬性,我們依舊可以修改或者刪除

var person = {
    name: "Bob",
    age: 12
};
person.score= 99;
console.log(person);

Object.preventExtensions(person);
person.job = "student";
console.log(person);

person.age= 23;
console.log(person);

delete person.age;
console.log(person);

使用Object.isExtensible(person)可以判定person這個物件是否可以擴充套件(可擴充套件返回true,反之false)

2.密封物件(Object.seal(obj))

          ECMAScript5定義的第二個級別就是密封物件,密封物件不可擴充套件,已有成員的[[Configurable]]已經被設為false,這意味著不能刪除屬性和方法,但是依然能修改已有屬性,使用方式和第一個一樣。使用Object.isSealed()方法判斷物件是否密封了,被密封了的會返回true.注意,因為密封物件是不可擴充套件的,所以密封了的物件呼叫Object.isExtensible()會返回false

3.凍結物件(Object.freeze(obj))

         最嚴格的就是凍結物件,凍結物件既不可擴充套件,又是密封,且[[Writeable]]特性定位false,凍結了的物件只可讀,使用方式和第一個一樣。使用Object.isFrozen()可以檢測凍結物件,凍結了的物件返回true, 注意凍結物件既是不可擴充套件也是密封的,可以呼叫Object.isSealed()和Object.isExtensible()方法

注意:凍結一個物件,只是一種淺凍結,對於物件裡的特殊元素,比如陣列,物件等依然可以操作。例如

const teacher = Object.freeze({
    name: "Bob",
    age: 21,
    grade: [2,2,2,3,4]
})
teacher.name = "www";
teacher.grade.push(6);//物件的淺凍結
console.log(teacher);

grade依然可以新增元素。如果你想所有凍結(深凍結),可以參考以下方式

var constantize = function(obj){
    Object.freeze(obj);
    Object.keys(obj).forEach((key,i)=>{
        if(typeof obj[key] === 'object'){
            constantize(obj[key]);
        }
    });
}
const teacher2 = {
    name: "Bob",
    age: 21,
    grade: [2,2,2,3,4]
}
constantize(teacher2);
teacher2.name = "www";
teacher2.grade.push(6);//有些會報錯,有些回忽略
console.log(teacher2);

相關推薦

js高階技巧總結高階函式篡改物件

高階函式 1.安全的型別檢測         談到型別檢測,可能大家首先想到的就是typeof 或者 instanceof (檢測陣列Array.isArray(arr))等這些方式,但是這些方法都有自己的侷限性,比如說Safari(直至第四版)對正則使用typeof

數獨高階技巧入門三——Fish

術語Fish代表了一組工作原理相同的關於特定候選數的解題技巧(Fish技巧直接產生自數獨規則——每個單元內的數字都不能重複),Fish家族成員包括“體型”從小到大的X-Wing、Swordfish、 Jellyfish、Squirmbag、 Whale以及Leviathan(後三者在人工解題過程

數獨高階技巧入門二:簡單的單數鏈結構——雙強鏈

著作權 images div urb apt 藍色 刪除 作者 技巧 ?如果在數獨盤勢中,我們找到關於某個候選數的兩條強鏈,且這兩條強鏈的一側在同一單元(行、列、宮)內,稱為base,另一側有共同作用格,稱為cover。根據前篇《鏈及其簡單應用》中的定義,base側兩端

算法總結 反轉單向雙向鏈表

while turn double logs pub pan != package port 分別實現反轉單向和雙向鏈表的函數 看代碼: package TT; public class Test88 { public class Node{

python技巧總結set、日誌、rsa加密

value obj 字符 config fas bject union rom ase 一、日誌模塊logging模塊調用 1、日誌模塊使用原理 #!/usr/bin/python # -*- coding:utf-8 -*- import logging # 方式一

js跨域請求jsonp原理運用

1、js請求後端服務時,域名不同或域名相同埠不同都是跨域; 2、無論哪個瀏覽器js都不能跨域請求後端服務,解決辦法為jsonp;jsonp不是新技術,只是一個解決方案;即js不請求後端服務而是跨域請求js,即跨域載入js檔案,而這個js檔案由伺服器端返回。 3、js中可以在cookie中取出

跟老齊學Python私有函式專有方法

在任何語言中,都會規定某些物件(屬性、方法、函式、類等)只能夠在某個範圍內訪問,出了這個範圍就不能訪問了。這是“公”、“私”之分。此外,還會專門為某些特殊的東西指定一些特殊表示,比如類的名字就不能用class,def等,這就是保留字。除了保留字,python中還為類的名字做了某些特殊準備,就是“專有

OSI模型中物理層的通訊形式總結模擬傳輸數字傳輸(二)

模擬傳輸與數字傳輸[檢視定義] 1.模擬傳輸系統 背景 儘管模擬傳輸劣於數字傳輸(傳輸過程中,模擬傳輸容易受干擾,訊號易衰減,安全性也不高),但由於採用模擬傳輸技術的電話網在計算機網路出現以前就已運行了近一個世紀,因此世界各地幾乎都有這種電話網,雖然數字傳輸和數字網是今後網路

python全棧開發匿名函式遞迴函式

python全棧開發,匿名函式,遞迴函式 匿名函式 lambda函式也叫匿名函式,即函式沒有具體的名稱。是為了解決一些功能很簡單需求而設計的一句話函式。如下: #這段程式碼defcalc(n):returnn**nprint(calc(10))#換成匿名函式calc =lambdan:n

js正則表示式match函式

功能:使用正則表示式模式對字串執行查詢,並將包含查詢的結果作為陣列返回  函式格式:stringObj.match(rgExp) stringObj為字串必選 rgExp為正則表示式必選項  返回值:如果能匹配則返回結果陣列,如果不能匹配返回null  使用方法:&nb

Kotlin頂層函式屬性

今天一起來看看Kotlin中的頂層函式和屬性。 遇到的問題 我們都知道,Java中,所有的程式碼都是依託於類而存在,我們所謂的函式作為類的方法,我們所謂的屬性作為類的屬性。但是在有些情況下,我們發現有些方法可能不是屬於某一個特定類,有些屬性也不是屬於某一個特定的類。所以我們就建立了很多的Java

C++ primer plus書--C++函式C語言字串, 結構體, string

函式和C風格字串 要將C風格字串作為引數傳遞給函式, 表示字串的方式有三種: 1.char陣列 2.用""擴起來的字串常量 3.被設定為字串地址的char指標 來看一個例子: // c風格字串例子 #include "iostream" using namespace std

TensorFlow筆記(4)——優化手寫數字識別模型代價函式擬合

前言 上篇筆記我們利用MNIST資料集訓練了一個手寫數字識別的模型,但是準確率非常的低,維持在91%左右,我們可以嘗試著將準確率提高到96%以上,在實驗之前我們需要先了解一些基本的概念,本篇文章可能會有些枯燥,因為大多都是理論知識。 本文重點 啟用函式 代價函式 擬合 什麼是啟用函式

怎麼取檔案版本號 WindowsAPIGetFileVersionInfo函式VerQueryValue函式

VS_FIXEDFILEINFO結構包含了檔案的版本資訊: typedef struct tagVS_FIXEDFILEINFO { DWORD dwSignature; //包含的值是0xFEEF04BD DWORD dwStrucVersion; //該結構

js使用技巧2(json字串json物件的切換)

JSON字串: var str1 = '{ "name": "cxh", "sex": "man" }';  JSON物件: var str2 = { "name": "cxh", "sex": "man" }; 1、將json字串轉化為json物件 (1)var obj = ev

買菜總結五--補品高檔食材

燕窩雪蛤老山參西洋參冬蟲夏草犀黃藕粉葛根 (一種叫“白葛根”,另一種叫“紅葛根”)鹿茸虎骨銀耳蓮子百合鴿子蛋 (動物人蔘)藏紅花 高檔食材:水中:魚:鮑魚 (最好的就是日本的青森網鮑、南非網鮑和澳洲網鮑)魚翅 (膠原蛋白以及脯胺酸;鯊魚;好的是天九翅,其次海虎翅和金鉤翅)鱘龍

Kotlin字面函式函式表示式

這一節涉及的東西比較雜,各種函式和表示式,零零散散 package net.edaibu.kotlintest.FunctionAndLambda /** * @author geqipeng * * @date 2017/6/1 * * @time 16:39

opencv日常學習approxPolyDP函式boundingRect函式說明

opencv中利用函式approxPolyDP來對指定的點集進行逼近,其逼近的精度是可設定的 對應的函式為: void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilo

深度神經網路損失函式啟用函式

1.損失函式和啟用函式簡介 通過前面深度神經網路之前向傳播演算法和深度神經網路之反向傳播演算法的學習,我們能夠了解到損失函式是用來評估模型的預測值與真實值之間的差異程度。另外損失函式也是神經網路中優化的目標函式,神經網路訓練或者優化的過程就是最小化損失函式的過程

Python私有函式專有方法

在任何語言中,都會規定某些物件(屬性、方法、函式、類等)只能夠在某個範圍內訪問,出了這個範圍就不能訪問了。這是“公”、“私”之分。此外,還會專門為某些特殊的東西指定一些特殊表示,比如類的名字就不能用class,def等,這就是保留字。除了保留字,python中還為類的名字