【问题标题】:Impact of 'this' keyword in Chrome debuggerChrome 调试器中“this”关键字的影响
【发布时间】:2012-11-16 13:25:50
【问题描述】:

我注意到 Google Chrome 调试器的行为因 JS 对象的创建方式而异;

如果我创建一个这样的 js 对象;

var SonA = function(thename) {
               this.firstname = thename || "Graham";
               this.age = 31;
}

SonA.prototype = new Father();

然后 Chrome 的调试器不允许我深入查看原型。但是,它确实给了我实例变量名称和年龄的键值对。

但是,如果我省略了 this 关键字,我可以深入到原型,但我不会在调试器中显示实例变量。

//SonB doesn't use this keyword    
var SonB = function(thename) {
                  firstname = thename || "Graham";
                  age = 31;
           } 

SonB.prototype = new Father();
console.log(new SonA()); //this logs as: SonA {firstname: "graham", age: 31}
console.log(new SonB()); //this logs as as drill down object that shows the prototype

有谁知道这里发生了什么,为什么调试器的行为不同?下面的图片和 jsfiddle 可能会使问题更清晰易懂;

http://jsfiddle.net/7z8sp/1/

【问题讨论】:

标签: javascript google-chrome-devtools


【解决方案1】:

我看不出你想要达到什么目的。 为什么你可以“向下钻取”sonB中的原型,唯一的原因是构造函数没有设置任何属性,但它正在创建隐含全局变量。只需将此行添加到您的小提琴中:

console.log(age);//<--logs 31

它记录由sonB 构造函数设置的值。如果您想获得原型属性,您可以:删除孩子的属性,或者使用Object.getPrototypeOf() 方法。

澄清一下:这不是调试器固有的,而是 JS 获取对象的值和属性的方式。考虑以下几点:

var emptyObject = {};
console.log(emptyObject.someProperty);//logs undefined
Object.prototype.someProperty = 'YaY';
console.log(emptyObject.someProperty);//logs YaY
emptyObject.someProperty = function()
{
    return Object.getPrototypeOf(this).someProperty;
};
console.log(typeof emptyObject.someProperty);//function
console.log(emptyObject.someProperty());//YaY again
delete(emptyObject.someProperty);//returns true
console.log(emptyObject.someProperty);//logs YaY.

这是什么意思:简单地说,如果你试图访问 any 对象(数组、对象、函数、很多)上的属性,JS 将首先检查是否该特定实例定义了该属性,如果没有,则 JS 在 prototype-chain 中升级。如果该原型没有请求的属性,则 JS 跳到下一个原型,以此类推。如果没有找到属性,undefined 将被返回。
因此有理由认为,如果您的构造函数设置了某些属性,JS 不会打扰原型并尽快返回属性。

同样的逻辑也适用于隐含的全局变量。如果变量缺少var 关键字,JS 会扫描作用域(当前函数、“父”函数,最后是全局作用域)以搜索该变量。如果找到,该变量将被使用或重新分配(取决于您在代码中使用它做什么)。如果没有找到变量,那么 JS 会为您创建一个。可悲的是,没有费心回到当前范围。结果:创建了一个全局变量。
在您的代码中,Father 构造函数为每个实例创建一个新的函数对象。这个函数依赖于一个闭包变量 (sirname) 和一个 global 变量 (firstname)。后者不是由SonA 设置的,因为该构造函数分配了一个新属性。但是,SonB 确实创建了SonASonB 将共享的全局变量。

FathergetName 成员函数对 sonB 起作用的唯一原因是,该方法也依赖于 范围扫描 和隐含的全局:

new SonB();//<-- constructor sets age and firstname globals
SonB.getName();//<-- ~= Father.getName.apply(SonB,arguments);
     ||
     ---> gets firstname global that was set in SonB constructor, uses that value

就这些了,把Father的getName方法重新定义为:

this.getName = function()
{//use this to point at calling context -> IE the instance on which the member function is being invoked
    return this.firstname + ' ' + surname;
};

【讨论】:

  • “你可以“深入”到sonB中的原型的唯一原因是因为构造函数没有设置任何属性”但我认为这是他的问题问。为什么一个允许你扩展对象,而另一个不允许。换句话说,他在问为什么他们使用两种不同的显示器来显示对象。
  • @user1689607: 答案很简单:因为他使用隐含的全局变量,因此是全局对象,而不是对象本身。我的提款机有点乱,但我会尽量澄清
  • 但是为什么这会对它们显示对象的方式产生影响呢?我无法重现它,所以我无法测试,但对象上实际属性的存在会使它可扩展,这似乎很奇怪。
  • @user1689607:大多数调试器显示一个变量,并且 - 如果该 var 是一个对象 - 显示一些绑定到该特定实例的属性。在第一种情况下,agefirstname 是属性(它们被分配给this),在第二种情况下,没有创建任何属性,因此除了原型
【解决方案2】:

使用Console.dir() 代替有效:http://jsfiddle.net/7z8sp/3/

【讨论】:

    【解决方案3】:

    使用 24.0.1312.5(官方版本 166104)测试版,我得到了这个:

    SonA
        age: 31
        firstname: "Graham"
        __proto__: Father
            getName: function () {
            __proto__: Object
    
    SonB
        __proto__: Father
            getName: function () {
            __proto__: Object
    

    这是正确的,因为没有this,您将在全局对象上分配agefirstname(在这种情况下为window)。在result iframe 的上下文中运行:

    > firstname
    "Graham"
    > age
    31
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-10
      • 2011-11-09
      • 1970-01-01
      • 2012-08-28
      • 1970-01-01
      • 2012-09-10
      相关资源
      最近更新 更多