簡體   English   中英

Object.defineProperty 帶有構造函數和原型

[英]Object.defineProperty with constructor function and prototype

我剛剛發現了Object.defineProperty並且因為我最熟悉 C#,所以我想在構造函數中使用訪問器屬性,例如:

 function Base(id) { var _id = id; Object.defineProperty(this,"ID",{ get: function() { return _id; }, set: function(value) { _id = value; } }) } function Derived(id, name) { var _name = name; Base.call(this,id); Object.defineProperty(this,"Name",{ get: function() { return _name; }, set: function(value) { _name = value; } }) } Derived.prototype = Object.create(Base.prototype); Derived.constructor = Derived; var b = new Base(2); var d = new Derived(4,"Alexander"); console.log(b.ID); console.log(d.ID, d.Name); d.ID = 100; console.log(d.ID, d.Name);

這打印:

2
4 “亞歷山大”
100“亞歷山大”

但是我對此感到非常困惑,例如這個得分很高的答案鼓勵上述方法,而這個答案說它會占用所有內存,因為將為我實例化的每個對象重新創建函數。 它建議采用以下方法:

var Base = function(id){this.__id = id}
Player.prototype = {
   get ID(){
      return this.__id;
   },
   set ID(value){
      this.__id = value;
   }
}

var p = new Player();
p.ID = 2;
alert(p.ID); // 4

然而,這種方法也創建了另一個公共屬性__id ,這對我來說似乎不太理想(我的示例中的屬性是“特權”的,因為它在 javascript 中顯然是調用的,因此不需要額外的公共屬性)。

有人可以解釋一下哪種方法適合我嗎? 現在我完全迷失在 javascript 文檔叢林中。 我非常喜歡Object.defineProperty方法,因為我覺得代碼很干凈,我可以將它與繼承一起使用。 但是,如果確實為每個對象重新創建了函數,我可能需要考慮第二種方法嗎?

有人可以解釋一下哪種方法適合我嗎?

根本不要使用Object.defineProperty 您在這里絕對不需要屬性描述符,並且您的 getter 和 setter 沒有做任何特別的事情。 只需使用一個簡單的普通屬性。 它將比您關心的其他任何事情都更快、優化得更好。

function Base(id) {
    this.ID = id;
}

function Derived(id, name) {
    Base.call(this,id);
    this.Name = name;
}

Derived.prototype = Object.create(Base.prototype);
Derived.prototype.constructor = Derived;

如果確實為每個對象重新創建了函數,我可能需要考慮第二種方法嗎?

是的,這是真的,但可以忽略不計。 您不應該過早地進行微觀優化。 您將知道何時真正需要它,然后您仍然可以輕松地交換實現。 在此之前,請選擇干凈簡單的代碼。

我參加聚會可能有點晚了,但我想在這里指出一些小事。

首先,您在構造函數中定義自己的屬性,這對於實例特定屬性和不應與其他實例共享的屬性很好,但不適用於 getter、setter 和方法。 相反,您應該在函數原型上定義它們。

我明白你的問題是這樣的

你如何制作真正的私人財產?

這很簡單,並且適用於所有瀏覽器。 首先我們將詳細介紹Object.defineProperty,以及派生的Object.create、Object.defineProperties。

javascript對象定義的原型

現代 javascript 允許使用一些“語法糖”來聲明類,但每個自豪的開發人員都應該了解它的實際工作原理。

首先沒有課 Javascript 沒有類。 它有原型,而不是 類比類,所以在我天真固執的情況下,上過好幾個類才找到真相,反駁又反駁——一路追根究底。 沒有課。

you成為對象。 如果youyour prototype無法回答問題,那么您的prototypes´ prototype將被詢問,然后您的prototypes´ prototypes´ prototype將被詢問,然后我們繼續直到沒有更多的原型要問。

所有這些原型仍然是對象 這個算法說明了它:

// Friendly version
function askQuestion(askee, question) {
  do {
    if (askee.hasOwnProperty(question)) {
      return askee[question];
    }
  } while (askee = Object.getPrototypeOf(askee))
}

ECMAScript 6(無聊)

為了說明“現代 javascript”的語法,我將離題:

 class Tortoise extends Turtle { constructor() { while (this.__proto__) { this.__proto__ = Object.getPrototypeOf(this.__proto__); } } #privateProperty = "This isn't a comment" get __proto__() { return Object.getPrototypeOf(this); } set __proto__(to) { Object.setPrototypeOf(this, to); } }

這不適用於所有瀏覽器。 它也不適用於任何瀏覽器。 這只是為了顯示語法。

有人聲稱,上述內容只是老式 javascript 之上的語法糖,但是當涉及到擴展本機對象時,它懷疑它的作用遠不止表面上的那么簡單。

即使“現代 javascript”(ECMAScript 6)允許我們編寫上述類,您也應該了解它

此外,現代 javascript 與 Internet Explorer 11 的頑固相結合,迫使我們使用babel ,這是一個令人難以置信的強大工具,它非常先進和靈活,以至於這種工具甚至存在的可能性純屬巧合無限不可能。

該工具的存在本身就證明上帝的存在,對此我痛心地說,也證明上帝不存在。


跆拳道?? 這里那里那里

彈出引擎蓋

不要在構造函數中創建 REUSABLE 屬性。 許多實例使用的函數不應在構造函數中賦值。

真實版

function Base(id) {
  // GOOD
  this.id = id;

  // GOOD, because we need to create a NEW array for each
  this.tags = [];

  // Okay, but could also just be in the prototype
  this.numberOfInteractions = 0;

  // BAD
  this.didInteract = function() {
    this.numberOfInteractions++;
  }
}

糖衣

class Base {
  constructor(id) {
    // GOOD
    this.id = id;

    // GOOD, because we need to create a NEW array for each
    this.tags = [];

    // Okay, but could also just be in the prototype
    this.numberOfInteractions = 0;

    // BAD
    this.didInteract = function() {
      this.numberOfInteractions++;
    }
  }
}

改進的真實版本

function Base(id) {
  this.id = id;
  this.tags = [];
}
Base.prototype.numberOfInteractions = 0;
Base.prototype.didInteract = function() {
  this.numberOfInteractions++;
}

改進的糖衣版本

如果你堅持用糖,並且在寫完代碼之后你想寫更多的代碼行和一些額外的勞動,你可以安裝 babel 並像下面這樣編寫你的代碼。

它會生成稍大和稍慢的腳本文件——除非你真的不需要支持所有瀏覽器。

class Base {

  constructor(id) {
    this.id = id;
    this.tags = [];
  }

  numberOfInteractions = 0;

  didInteract() {
    this.numberOfInteractions++;
  }

}

繼承ABC

EcmaScript 中的繼承非常簡單,一旦你真正理解了它!

TLDR:如果你想讓上面的類擴展另一個類,你可以這樣做:

Object.setPrototypeOf(Base.prototype, Parent.prototype);

這應該適用於任何地方。 它本質上是Base.prototype.__proto__ = Parent.prototype

Base.prototype vs實例原型

javascript 中的所有對象都有一個實例原型 它是為對象屬性搜索“默認值”的實例原型

function MyConstructor() { /* example function or "class" */ }

上面的語句創建了一個名為MyConstructor對象,它有一個實例原型,它是對Function.prototype的引用。 同時,它也是一個可以調用的函數。

這很重要:

MyConstructor instanceof Function;
// is TRUE because
Object.getPrototypeOf(MyConstructor) === Function.prototype

// this is NOT TRUE
MyConstructor.prototype === Function.prototype

因為這種細微的差別

var myInstance = new MyConstructor();

myInstance instanceof MyConstructor;
// this is TRUE because
Object.getPrototypeOf(myInstance) === MyInstance.prototype

現在, MyConstructor.prototype只是一個空對象(它有一個引用Object.prototype實例原型)。

訪問屬性

在javascript中,對象只有屬性。 它們沒有方法,但它們具有指向 function 的屬性。

當您嘗試訪問對象( Base實例)上的屬性時,引擎會像這樣查找該屬性:

檢查位置 說明
this.$HERE$ 在您當地的房產上
this.__proto__.$HERE$ __proto__是對Base.prototype的引用。
this.__proto__.__proto__.$HERE$ 這將是您的類原型。
this.__proto__.__proto__.__proto__.$HERE$ 這將是您的祖父類原型。
...然后我們繼續搜索原型鏈,直到沒有更多的原型。 搜索是使用Object.prototype.hasOwnProperty完成的,這意味着即使值是undefined ,搜索也會停止。

__proto__是一個魔法屬性,它已被棄用,取而代之的是Object.getPrototypeOf() 為簡潔起見,我使用它。

通過原型鏈找到的任何屬性,在返回給您之前都將綁定this

接下來是繼承和方法定義。 這里有兩種思想流派; 一個用Object.create創建的新對象覆蓋Base.prototype ,然后繼續創建方法:

// This works (but it overwrites the Base.prototype object)
Base.prototype = Object.create(ParentClass);

// Declare a method
Base.prototype.incrementMyValue = function() {
    this.myValue++;
}

// A default value
Base.prototype.myValue = 123

// A getter
Object.defineProperty(Base.prototype, 'getMyValue', {get: function() { 
  return myValue;
}});

在上面的代碼中,我想指出,當您訪問instance.myValue ,它將是未定義的,因此掃描原型鏈,您將獲得123

如果您第一次調用instance.incrementMyValue() ,原型鏈將被掃描並返回 123。然后您增加該值,並將其分配給您的實例

你開始於:

instance // no .myValue exists
instance.__proto__.myValue = 123

調用:

instance.incrementValue();

你最終得到:

instance.myValue = 124;
instance.__proto__.myValue = 123

默認值仍然存在,但它被實例本地myValue屬性覆蓋。

帶有繼承的老派類定義

這個類擁有一切:

  • 私人財產
  • 私有靜態屬性
  • 公共財產
  • 公共靜態屬性

看哪,“Charm”類,從我希望發布的 Charm.js 庫中借出想法:

var Derived = (function(){
  /* Wrapped in a function, so we keep things private*/

  /**
   * CONSTRUCTOR
   */
  function Derived(id, name) {
    Base.call(this, id);               // Calling the parent constructor
    private(this).name = name;         // Setting a TRUE private property
  }

  /**
   * PRIVATE STATIC PROPERTIES
   */
  var thisIsPrivateAndShared = 0;

  /**
   * PUBLIC STATIC PROPERTIES
   */
  Derived.thisIsPublicAndShared = 0;

  /**
   * PRIVATE NON-STATIC PROPERTIES THROUGH WeakMap
   */
  var secrets = new WeakMap();
  function private(for) {
    var private = secrets.get(for);
    if (!private) {
      private = {};
      secrets.set(for, private);
    }
    return private;
  }

  /**
   * Building the prototype
   */
  Derived.prototype = Object.create(

    /**
     * EXTEND Base.prototype (instead of Object.prototype)
     */
    Base.prototype,

    /**
     * Declare getters, setters, methods etc (or see below)
     */
    {
      /**
       * GETTERS AND SETTERS FOR THE PRIVATE PROPERTY 'name'
       */
      name: {
        get: function() {                // getter
          return private(this).name;
        },
        set: function(value) {           // setter
          private(this).name = value;
        }
      },

      /**
       * A PUBLIC METHOD
       */
      method: {value: function() {

      }},

      /**
       * A PUBLIC PROPERTY WITH A DEFAULT VALUE
       */
      age: {value: 42, writable: true}
    });

    /**
     * I am too lazy to write property descriptors,
     * unless I want a getter/setter/private properties,
     * so I do this:
     */
    Derived.prototype.lessWorkMethod = function() {
    };
  }
  return Derived;
})();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM