Javascript 中的原型和范围界定

Prototypes and scoping in Javascript

var Bar = function(a, b) {

    this.b = b - a;
    this.m2 = function() {
        return this.a / b;
    }

}

var Foo = function(a, b) {
    this.a = a;
    this.b = b;
    Bar.prototype.a = a + b;
    Bar.prototype.b = a - b;
    Bar.prototype.m1 = Bar.prototype.m2;
    Bar.prototype.m2 = function() {
        return this.a * a + this.b * b;
    }
}

o1 = new Bar(1, 5);
o2 = new Foo(2, 3);

o4 = new Foo(3, 2);

r1 = o1.m1();

console.log(r1)
console.log(o1.a)
console.log(o1.b)
r1 的值似乎是基于从 o2 Foo 对象的实例化中提出的原型方法,而不是 o4 Foo 对象。但是,删除 o4 Foo 对象似乎会阻止整个工作。同样,删除 o2 也会阻止它工作。

我觉得正确的行为是通过 o4 修改对象 Bar 的原型,这意味着通过执行 o1.m1() 得到的值 return 将从 o4 Bar 原型派生。所以,

return this.a * 3 + this.b * 2;

然而事实并非如此。正确的解决方案 returns 来自: return this.a * 2 + this.b * 3;

这似乎很违反直觉。什么属性在这里起作用?为什么解决方案(即 r1 == 22)依赖于 o2 和 o4 的实例化?

编辑:r1 应该是 22。我想弄清楚我需要走什么路才能达到这个数字。

调用new Bar(1, 5)后,o1.b的值将是4o1.m2 的值将是那个函数,但这并不重要;那是一条红鲱鱼。

调用new Foo(2, 3)后:

  • Bar.prototype.a 将是 5
  • Bar.prototype.b 将是 -1(这无关紧要)
  • Bar.prototype.m1 将是 undefined(无所谓)
  • Bar.prototype.m2 将是一个有效的函数:

    function() {
        return this.a * 2 + this.b * 3;
    }
    

ab(与 this.athis.b 不同)在该函数中是 "frozen in time",它们永远不会是任何东西除了 23.

调用new Foo(3, 2)后:

  • Bar.prototype.a 将是 5
  • Bar.prototype.b 将是 1(无关紧要)
  • Bar.prototype.m1 将是上面的 "m2" 函数
  • Bar.prototype.m2 将是相同的功能,除了本地 a 为 3 和 b 为 2(无关紧要)

所以当 o1.m1() 被调用时,我们是 运行 一个将使用以下值的函数:

  • this.a 将是 5,因为这是 Bar 原型的最近设置值 属性 "a"
  • this.b 将是 4,因为这是第一步中创建的 Bar 的 实例 上的 "b" 属性 的值和实例属性影子原型属性

因此在函数中

function() {
  return this.a * 2 + this.b * 3;
}

根据这些值,我们得到了 5 * 2 + 4 * 3,那就是 22

该问题调查了 JavaScript 的多种行为:

  • 对象阴影继承属性的局部属性,无法访问原型链中保存的渲染值,
  • 嵌套函数创建的闭包使用创建内部函数时存在的外部函数变量和参数值:嵌套函数的词法记录链对于创建它们的外部函数的每次调用都是不同的(内部功能)。
  • this 函数内部的行为和价值。

var Bar = function(a, b) {

    this.b = b - a;
    this.m2 = function() {
        return this.a / b;
    }

}
// every Bar object has a local property b, and method m2
// these are not inherited
// Bar.prototype values for b and m2 will be ignored

var Foo = function(a, b) {
    this.a = a;
    this.b = b;
    Bar.prototype.a = a + b;
    Bar.prototype.b = a - b;
    Bar.prototype.m1 = Bar.prototype.m2;
    Bar.prototype.m2 = function() {
        return this.a * a + this.b * b;
    }
}
// Foo is (nonsense) code that sets Foo object properties a and b
// These are not used by Bar object methods where "this" refers to the Bar object.

// Setting Bar.prototype.b has no affect as discussed under Bar.

// Although Bar.prototype.m2 is never inherited by Bar objects, it is
// a valid place to store a function value. Initially it is undefined.

// After calling Foo once, Bar.prototype.m1 is undefined.
// After calling Foo twice, Bar.prototype.m1 is
//
//    function() { return this.a * a + this.b * b}
//
// Where this.a was calculated in the most recent call to Foo,
// set on Bar.prototype, and inherited by Bar objects,
// this.b is a local property of the Bar object, and
//  a and b are the parameter values of the call to Foo which created
// the anonymous function object currently stored in  Bar.prototype.m1.


o1 = new Bar(1, 5);
//  at this point o1.b is set to 4 and does not change
//  o1.a is not in the inheritance chain and is undefined
//

o2 = new Foo(2, 3);
// at this point o1.a is inherited as 5 and o1.m1 is undefined

o4 = new Foo(3, 2);
// at this point o1.a is again inherited as 5 (the sum of 3+2)
// and o1.m1 is defined

r1 = o1.m1();
//   uses o1.a=5, o1.b=4 and the values of parameter variables a and b
//   held in the closure created by the nested m2 function inside Foo.
//
// the method function is shuffled down before arriving at Bar.prototype.m1,
// where it captures the a and b parameters of the second to last call to Foo,
// which in this example is a=2, b=3

// So the expected value of o1.m1() is
//  o1.a * a + o1.b * b
// =  5  * 2 + 4 * 3
// = 22

console.log(r1)
console.log(o1.a)
console.log(o1.b)