經過代理進行對象訪問控制

對象屬性的get、set

爲了建立對象私有變量,咱們會在構造函數中經過定義的getter、setter函數閉包訪問和修改私有屬性。javascript

function Man() {
  let name = "liu";
  this.getName = () => {
    console.log("getname");
    return name;
  };
  this.setName = name => {
    console.log("setname");
    name = name;
  };
}
let man = new Man();
let name = man.getName();
console.log(name);
man.setName("zhang");
複製代碼

使用get和set關鍵字在對象字面量中定義getter、setter。java

const Man = {
  _name: "liu",
  get name() {
    console.log("getname");
    return this._name;
  },
  set name(name) {
    console.log("setname");
    this._name = name;
  }
};
let name = Man.name;
console.log(name);
Man.name = "zhang";
console.log(Man._name);
複製代碼

能夠看出經過get和set關鍵字定義出的getter、setter更像是屬性而不是方法。 使用get和set關鍵字在class中定義getter、setter,方法相似。數組

class Man {
  constructor() {
    this._name = "liu";
  }
  get name() {
    console.log("getname");
    return this._name;
  }
  set name(name) {
    console.log("setname");
    this._name = name;
  }
}
const man = new Man();
let name = man.name;
console.log(name);
man.name = "zhang";
console.log(man._name);
複製代碼

回到第一個構造函數,咱們想改寫它的getter、setter,能夠像訪問屬性同樣訪問name私有屬性,而get和set關鍵字只能在對象和class中定義,因此還須要藉助於Object.defineProperty()方法,在傳入的參數中定義getter、setter。閉包

function Man() {
  let _name = "liu";
  Object.defineProperty(this, "name", {
    get: () => {
      console.log("getname");
      return _name;
    },
    set: name => {
      console.log("setname");
      _name = name;
    }
  });
}
const man = new Man();
let name = man.name;
console.log(name);
man.name = "zhang";
console.log(man.name);
複製代碼

Object.defineProperty中傳入的參數this將指向man,並綁定傳入的name屬性,而name屬性的特性由後面的對象配置。這其中就包含了經過get和set關鍵字定義出的getter、setter。當咱們訪問和修改name屬性時將調用get和set對應的函數。app

經過setter能夠對傳入值進行校驗函數

function Man() {
  let _age = 20;
  Object.defineProperty(this, "age", {
    get: () => {
      console.log("getage");
      return _age;
    },
    set: age => {
      console.log("set_age");
      if (!Number.isInteger(age)) {
        throw new TypeError("age should be number!!");
      } else {
        _age = age;
      }
    }
  });
}
const man = new Man();
let age = man.age;
console.log(age);
man.age = 10;
console.log(man.age);
man.age = "zhang";
複製代碼

當age爲整數時能夠成功賦值,若是是小數或其餘數據類型將拋出錯誤提示性能

整個對象的get、set

以上能夠看作對對象的某個屬性進行代理操做,當咱們要對對象的每一個屬性進行代理時,挨個屬性代理將行不通,咱們須要使用Proxy構造函數對對象進行代理,代理操做最終將做用於源對象。測試

let man = { name: "liu" };
man = new Proxy(man, {
  get: (target, key) => {
    console.log("getProperty");
    return key in target ? target[key] : "Don't have";
  },
  set: (target, key, value) => {
    console.log("setProperty");
    target[key] = value;
    return true;
  }
});
let name = man.name;
console.log(name);
man.name = "zhang";
name = man.name;
console.log(name);
複製代碼

以上代碼經過Proxy構造函數建立出man對象的代理對象並從新賦值給man標識符(最初的man對象最爲代理對象的目標對象將保持活躍,相似閉包),Proxy構造函數中傳入需代理的man對象以及配置對象,配置對象中定義了每一個屬性的getter、setter,須要注意set方法中咱們須要返回Boolean值,明確是否設置成功。ui

固然數組也是對象,因此Proxy也能夠代理數組,咱們能夠經過代理數組擴展原生數組功能,如JavaScript中數組索引不能爲負數,經過代理數組能夠解決。this

function array(array) {
  if (!Array.isArray(array)) {
    throw new TypeError("unexpected type !");
  }
  return new Proxy(array, {
    get: (target, index) => {
      console.log("get");
      index = +index;
      return target[index > 0 ? index : target.length + index];
    },
    set: (target, index, value) => {
      console.log("set");
      index = +index;
      return (target[index > 0 ? index : target.length + index] = value);
    }
  });
}
const mans = ["liu", "zhang", "li"];
let woman = "liu";
const man = array(mans);
console.log(man[-1]);
man[-1] = "wang";
console.log(mans);
複製代碼

咱們在get和set方法中先將傳入的索引index經過一元操做符「+」轉換爲數值,再經過判斷index是否大於零決定索引index仍是target.length + index。

除了代理對象咱們也能夠代理函數,當咱們須要測試函數性能時,可以使用代理函數計算執行時長。

function isPrime(number) {
  if (number < 2) {
    return false;
  }
  for (let i = 2; i < number; i++) {
    if (number % i === 0) {
      return false;
    }
  }
  return true;
}

isPrime = new Proxy(isPrime, {
  apply: (target, thisArg, argumentList) => {
    console.time("isPrime");
    const result = target.apply(thisArg, argumentList);
    console.timeEnd("isPrime");
    return result;
  }
});
console.log(isPrime(11373212));
複製代碼

咱們先定義了用於計算傳入的數字是否爲素數的isPrime函數,以後經過Proxy構造函數傳入isPrime函數獲得新的isPrime代理函數,其中傳入的配置對象設置了apply方法該方法在代理函數被執行時執行,該方法將默認傳入目標函數、當前函數執行上下文以及參數列表,以後經過applay將當前函數執行上下文綁定到目標函數並傳入參數執行。執行先後經過設置計時器(timer)打印執行時間。

相關文章
相關標籤/搜索