Javascript - 如何向对象构造函数添加属性

Javascript - how do you add properties to an object constructor function

本文关键字:构造函数 添加 属性 对象 Javascript      更新时间:2023-09-26

如何在 JavaScript 中向构造函数添加属性?例如。如果我有以下功能。

function Hotel(name)
{
   this.name = name;
};
var hotel1 = new Hotel('Park');

我可以添加一个"局部"变量,该变量可以在类中本地使用,就好像它是私有的一样,使用相同的符号使用关键字"this"。当然,它不会是私有的,因为创建的对象将能够正确使用它?

我可以做这样的事情吗?我是使用 this 关键字还是使用 var 关键字是哪一个?我在底部的函数构造函数上有示例 2

1. var numRooms = 40;
2. this.numRooms = 40;
3. numRooms : 40,
function Hotel(name)
{
   this.name = name;
   this.numRooms = 40;
};

我知道,如果我想要对象构造函数中的函数,我需要使用this词。正如我上面问的那样,这也适用于正常变量吗?

function Hotel(name)
{
   this.name = name;
   this.numRooms = 40;
   this.addNumRoomsPlusFive = function()
   {
       return this.numRooms + 5;
   }
};

您可以简单地将私有变量添加到构造函数中:

function Hotel(name) {
    var private = 'private';
    this.name = name;
};

但是,如果您在没有new运算符的情况下使用 Hotel 函数,则附加到this的所有属性和函数都将成为全局属性和函数。

function Hotel(name) {
    var private = 'private';
    this.name = name;
};
var hotel = Hotel('test');
console.log(name); // test
最好

在构造函数中返回一个对象:

function Hotel(name) {
    var 
        private_var = 'private',
        private_func = function() {
            // your code
        };
    retur {
        name: 'name',
        public_func: private_func
    }
};
var hotel = Hotel('test');
console.log(name); // undefined

因此,如果您使用Hotel构造函数而不使用运算符new则不会创建全局变量。仅当返回值是对象时,才可以执行此操作。否则,如果您尝试返回任何不是对象的内容,构造函数将继续其常规行为并返回 this

我可以添加一个"局部"变量,该变量可以在类中本地使用,就好像它是私有的一样,使用相同的符号使用关键字"this"。

是的,我们能:

// API implementation in the library
function Hotel(name) {
  // only our library code knows about the actual value
  const numRooms = 'privateNoRoomsVar';
  this.name = name;
  this[numRooms] = 40;
  this.addNumRoomsPlusFive = function() {
    return this[numRooms] + 5;
  }
};
// from the library user's perspective
const hotel = new Hotel('Transylvania');
console.log('rooms+5 =', hotel.addNumRoomsPlusFive());
console.log('hotel.numRooms =', hotel.numRooms); // undefined
// also, users don't have access to 'numRooms' variable so they can't use hotel[numRooms].

如果用户查看源代码并找出值privateNoRoomsVar,那么他们可能会滥用 API。
为此,我们需要使用symobls:

// API implementation in the library
function Hotel(name) {
  // no one can duplicate a symbol so the variable is really private
  const numRooms = Symbol();
  this.name = name;
  this[numRooms] = 40;
  this.addNumRoomsPlusFive = function() {
    return this[numRooms] + 5;
  }
};
// from the library user's perspective
const hotel = new Hotel('Transylvania');
console.log('rooms+5 =', hotel.addNumRoomsPlusFive());
console.log('hotel.numRooms =', hotel.numRooms); // undefined
// there is no way users will get access to the symbol object so the variable remains private.

<小时 />

#privateField,所有浏览器都支持私有类功能,因此我们不必再担心这一点。

// API implementation in the library
class Hotel {
  // private field
  #numRooms = 40;
  constructor(name) {
    this.name = name;
  }
  addNumRoomsPlusFive() {
    return this.#numRooms + 5;
  }
};
// from the library user's perspective
const hotel = new Hotel('Transylvania');
console.log('rooms+5 =', hotel.addNumRoomsPlusFive());
console.log('hotel.numRooms =', hotel.numRooms); // undefined
//console.log('hotel.numRooms =', hotel.#numRooms); // throws error

Javascript 历来从其他对象的原型创建对象。这是EMCA2015的结果,即您具有指定对象的类的独特语法。顺便说一句,如果您将鼠标悬停在该链接中的表格上,它会给出该功能实现的日期。

由 new 运算符创建的 javascript 对象或多或少是一个关联数组的组合(你用 let avar={}; 做的(,它可以访问它定义的函数级范围。数组的是其属性。根据其创建者的说法,Javascript被创建为一种易于使用的程序语言,没有类型层次结构。它实现这一点的方法之一是或多或少地将其映射类型视为等效于面向对象的编程语言描述的原型对象。

在 2022 年添加属性

function AProtoype(arg1, arg2, arg3){
    //this defines a property
    this.pa=arg1;
    /* unicorns in this section */
    let x = 1;
    /*    
    a getter which has the same syntax as a property
    but returns x from the scope which it references and
    not the object.
    */
    get getx() => x;
}
let object = new AProtoype(2,3,4);

等效于以下代码,用于数据访问,而不是继承和类型。new 运算符还会在用于这些目的的对象上设置变量。

function NewObject(arg1, arg2, arg3){
   let prototype = {};
   /*dragons in this section, as you are not using the this keyword to accomplish things*/
   prototype.pa = arg1;
   Object.defineProperty(prototype, "getx", {get:()=>x});
   return prototype;
}
//If you do this instead of using the new operator it is an anti-pattern.
//And like all anti-patterns: "But it works!"
let object = NewObject(2,3,4);

相关属性定义方法在某种意义上早在2010年、2011年就得到了支持。我没有当时的当代消息来源来确认你是否可以完成我正在做的事情,你只有在所有其他方法都失败并且需要在 Internet Explorer 9 上运行时才想要。如果所有其他方法都失败了,您可能需要阅读 Object.create 的文档,这也很有趣,因为或多或少地提供了一个 API 来创建新对象。

现在,为了享受有趣的时光和恐怖,您还可以定义一个返回 this 的函数,并使用该函数的等效绑定获取对象。当它是全局范围内的对象,并且您重命名该对象的属性时,恐怖就会到来;因为Javascript将通过愉快地写下它发现的任何内容来解决名称冲突,如果可以的话。然后,您可以使用它来重新实现原型模式,javascripts new 运算符是在概念上构建的,为了科学。

当您在 Javascript 中使用"构造函数"时,使用 this 关键字在实例上定义的任何属性都将变为公共属性。这是不可避免的,因为Javascript对象没有私有属性的概念 - 如果存在,可以直接作为object.property访问。

例如,如果您尝试按照以下代码片段进行操作,在 Java 或 C# 中使用私有变量模拟典型的 getter/setter 模式(请注意,即使这有效,这不是惯用的 Javascript(:

function MyObject(privateVar) {
  this.privateVar = privateVar;
}
MyObject.prototype.getVar = function() {
  return this.privateVar;
};
MyObject.prototype.setVar = function(newVal) {
  this.privateVar = newVal;
};

然后,虽然您确实可以使用 getter 和 setter 来按预期执行,但您也可以直接访问和设置私有变量!示范:

function MyObject(privateVar) {
  this.privateVar = privateVar;
}
MyObject.prototype.getVar = function() {
  return this.privateVar;
};
MyObject.prototype.setVar = function(newVal) {
  this.privateVar = newVal;
};
var obj = new MyObject(1);
// using public getter/setter
console.log(obj.getVar()); // 1
obj.setVar(2);
console.log(obj.getVar()); // 2
// using private variable directly - not intended to work
console.log(obj.privateVar); // 2
obj.privateVar = 3;
console.log(obj.getVar()); // 3 (using public API to get it to show that the direct update to the private variable also affects the intended public methods)

虽然有一种方法可以模仿私有变量的影响。它们实际上不是对象属性 - 因为,正如我刚刚演示的那样,这些属性本质上是公共的 - 但可以通过以下方式模拟:

  • 根本不使用"构造函数",而是碰巧返回对象的常规函数。无论如何,这就是构造函数真正做的所有事情 - JS中的区别只是语法上的,当你调用函数时,你不需要使用new关键字。(尽管您仍然可以,但如果您真的愿意 - 任何返回对象的函数都可以使用 new 调用,并且行为方式与不使用对象相同,尽管性能可能会受到一些影响,因为该函数随后会构造一个全新的对象并将其丢弃。请参阅 MDN 了解这些陈述的理由,特别是第 4 步。
  • 在此函数中,使用常规变量作为私有变量。通过简单的范围规则,这个变量将完全无法从外部访问,但你仍然可以让返回的对象通过闭包的"魔术"保留对它的访问权限。

这是上面翻译成此过程的getter/setter示例,以及它工作的演示。(不过,我赶紧再次补充,这在Javascript中不会被视为惯用代码。

function makeObjectWithPrivateVar(privateVar) {
  function getPrivateVar() {
    return privateVar;
  }
  function setPrivateVar(newVal) {
    privateVar = newVal;
  }
  return { getPrivateVar, setPrivateVar };
}
var obj = makeObjectWithPrivateVar(1);
// getter
console.log(obj.getPrivateVar()); // 1
// setter
obj.setPrivateVar(2);
// getter again to observe the change
console.log(obj.getPrivateVar()); // 2
// but how could we access the private var directly??
// answer, we can't
console.log(obj.privateVar); // undefined
console.log(privateVar); // ReferenceError, privateVar is not in scope!

最后请注意,在现代 Javascript 中很少使用这种风格的基于函数的构造函数,因为 class 关键字可以更轻松地模仿传统的基于类的语言,如 Java,如果你真的愿意的话。特别是,更新的浏览器直接支持私有属性(您只需要在属性名称前面加上#(,因此将初始代码片段转换为类并使用此功能即可正常工作:

class MyObject {
  #privateVar
  constructor(privateVar) {
    this.#privateVar = privateVar;
  }
  
  getVar() {
    return this.#privateVar;
  }
  
  setVar(newVal) {
    this.#privateVar = newVal;
  }
}

var obj = new MyObject(1);
// using public getter/setter
console.log(obj.getVar()); // 1
obj.setVar(2);
console.log(obj.getVar()); // 2
// using private variable directly - now doesn't work
console.log(obj.privateVar); // undefined, it doesn't exist
// console.log(obj.#privateVar); // error as it's explicitly private, uncomment to see error message

通常使用闭包执行:

var Hotel = (function() {
      var numrooms=40; // some kind of private static variable
      return function(name) { // constructor
         this.numrooms = numrooms;
         this.name = name;
      };
}());
var instance = new Hotel("myname");