javascript 中的私有成员安全吗?

Is a private member secure in javascript?

我正在学习 freecodecamp JavaScript oop 课程。该课程描述了在密码和银行账户等无法从外部更改的情况下,您应该如何在对象内部声明一个私有变量。对私有变量的唯一访问是通过可以访问私有变量的 public 方法。然后它给出了这个演示代码来说明这一点。

function Bird() {
  let hatchedEgg = 10; // private variable

  /* publicly available method that a bird object can use */
  this.getHatchedEggCount = function() { 
    return hatchedEgg;
  };
}
let ducky = new Bird();
ducky.getHatchedEggCount(); // returns 10

我不明白 hatchedEgg 变量如何被称为安全的。 作为 user/hacker,我所要做的就是猜测从函数 getHatchedEggCount() 返回的变量名。在这种情况下 hatchedEgg (这对黑客来说并不难)然后我可以使用这一行来修改私有变量;

Bird.hatchedEgg = 20;
console.log(Bird) // returns { [Function: Bird] hatchedEgg : 20 }

我是不是漏掉了什么?

//-------------- Clarification Edit

显然问题出在 freecodecamp 控制台(一个错误),它正在更改私有变量的值。 如果你愿意,你可以尝试将上面的代码粘贴到这个 link 中,你会看到。 freecodeConsoleLink

直接调用 ducky.hatchedEgg(我猜你的意思是这个而不是 Bird.hatchedEgg)不会给你任何结果(准确地说是 undefined)。这就是示例的重点,即私有变量不能直接从外部访问。它们是通过一种方法访问的,而这种方法又 be/could 不会暴露在外面。安全来自于此。

Update 我又重新阅读了这个问题,你正在做的是:你将 属性 hatchedEgg 附加到 Bird 并为其设置一个值。

Bird.hatchedEgg = 20

但是,此 hatchedEgg 与函数中最初定义的 let hatchedEgg = 10; 不同。

ducky.getHatchedEggCount(); 仍然会给你 10。Bird.hatchedEgg 会给你 20,因为你将 属性 hatchedEgg 附加到对象 Bird 与函数 Bird.

中声明的 hatchedEgg 不同

另外,functions first-class 对象,您可以 add/remove 它的属性,就像您对任何其他对象所做的那样。但是,在最后的post中,我有意试图区分function Bird和object Bird,把事情说清楚。这就像在说,array 也是 objects。但是你在谈话中保持一种区分,以使事情简单化。无论如何,更新这个我认为会导致某种混乱。

我认为您对正在创建的变量以及如何访问每个变量有点困惑。

您从函数开始:

function Bird() {
  let hatchedEgg = 10;

  this.getHatchedEggCount = function () {
    return hatchedEgg;
  };
}

此函数没有任何属性。

调用此函数时,它只是将 getHatchedEggCount 属性 附加到 this 对象。 getHatchedEggCount 属性 本身就是一个函数,它引用 Bird 函数 运行s.

时创建的 hatchedEgg 变量

因此我们可以这样做:

// We call the 'Bird' function.
// As we are using the 'new' keyword, we create an empty context for the function.
// The empty context is referenced using the 'this' keyword inside the 'Bird' function.
// We assign 'getHatchedEggCount' as a method on the 'this' empty object
// We then return the 'this' object
// The 'ducky' variable now points to the returned 'this' object
// Therefore the 'ducky' variable is an object with a 'getHatchedEggCount' method
let ducky = new Bird();

并且控制台日志记录给出以下输出:

console.log(ducky);
// Output: Bird { getHatchedEggCount: [Function] }

console.log(ducky.hatchedEgg);
// Output: undefined

console.log(ducky.getHatchedEggCount());
// Output: 10

这是意料之中的,因为 ducky 是一个只有一个 属性 的对象 - getHatchedEggCount 方法。

但最重要的是,请注意当我们记录以下内容时会发生什么:

console.log(Bird);
// Output: [Function: Bird]

console.log(Bird.hatchedEgg);
// Output: undefined

console.log(Bird.getHatchedEggCount);
// Output: undefined

我们看到Bird函数其实是一个空对象。它没有属性(我们自己分配的),因此 Bird.hatchedEgg 不存在!

So why can we access the hatchedEgg variable when calling ducky.getHatchedEggCount()? This is due to how closures work, but that is off topic. Needless to say, the hatchedEgg variable is ONLY accessible to the getHatchedEggCount function.

现在,如果我们说:

    Bird.hatchedEgg = 20;

我们正在为 Bird 函数添加一个全新的 属性。所以我们可以控制台日志:

console.log(Bird);
// Output: [Function: Bird] { hatchedEgg: 20 }

console.log(Bird.hatchedEgg);
// Output: 20

我们在 Bird 函数中添加了一个新的 hatchedEgg 属性,以前不存在!

但是请注意,这 属性 与 getHatchedEggCount 函数使用的 hatchedEgg 变量不同。

原来的hatchedEgg变量是在Bird函数内部创建的,可以在函数体内访问,但不是属性 OF Bird 函数对象,因此无法通过 Bird.hatchedEgg 访问。你看出区别了吗?

例如,运行这个代码:

function Bird() {
  let hatchedEgg = 10;

  this.getHatchedEggCount = function () {
    return hatchedEgg;
  };
}

Bird.hatchedEgg = 20;
console.log(Bird.hatchedEgg);
// Output: 20

let ducky = new Bird();
console.log(ducky.getHatchedEggCount());
// Output: 10

你明白为什么值为10hatchedEgg变量仍然无法访问了吗?它与 Ducky.hatchedEgg.

不是同一个变量

如freecodecamp主题中所述,它称为闭包。只需使用非常琐碎的编程概念这样想:

Variables declared inside a function are scoped to that function, there is no way to access those from outside the function. This is not only true for JavaScript, for all other languages.

现在至于为什么我们可以从外部获取该作用域变量的值是闭包的魔法。由于题目没有提到"closure"这个词,更不用说问了,这里就不细说闭包了。

至于你说的黑客可以用来破解它的代码,行

Bird.hatchedEgg = 20;

仅在 对象 Bird 中创建一个 属性。这里的另一个基本概念 - 函数 Bird 也是 JavaScript 中的第一个 class 对象,这意味着它可以像任何其他对象一样具有属性和功能。 Bird.hatchedEgg指的是Bird对象的属性,而Bird函数内部声明的变量(带let hatchedEgg = 10;)是函数的局部变量,两者是截然不同的。如果该行要更改在函数内声明的局部变量的值,您应该在控制台日志中得到相同的结果 ducky.getHatchedEggCount(),但您不会。