Javascript 为什么将变量(对象范围之外)设置为对象方法会返回未定义的结果?

Javascript 为什么将变量(对象范围之外)设置为对象方法会返回未定义的结果?,javascript,oop,Javascript,Oop,下面的代码是从中复制和粘贴的。我研究了使用JavaScript进行OOP的一般问题。但是,我是一名初学者,对此代码段有以下问题: 行Person.prototype.gender=''的目的是什么?如果我取出它并运行代码,我会得到相同的结果 为什么调用genderTeller()会导致“未定义”警报?从初学者的角度来看,MDN的解释似乎有点单薄。这是一个范围问题吗 我仔细阅读了这篇文章,发现对于一个还不熟悉javascript的人来说,这确实是一件多么令人困惑的事情 关于#1,我认为这是对JIT

下面的代码是从中复制和粘贴的。我研究了使用JavaScript进行OOP的一般问题。但是,我是一名初学者,对此代码段有以下问题:

  • Person.prototype.gender=''的目的是什么?如果我取出它并运行代码,我会得到相同的结果

  • 为什么调用
    genderTeller()
    会导致“未定义”警报?从初学者的角度来看,MDN的解释似乎有点单薄。这是一个范围问题吗


  • 我仔细阅读了这篇文章,发现对于一个还不熟悉javascript的人来说,这确实是一件多么令人困惑的事情

    关于#1,我认为这是对JIT编译器的一个提示,“性别”是一个字符串。Javascript JIT编译器喜欢对象属性保持相同类型(在本例中为字符串)的情况。这篇文章没有描述这一点,或者这一行根本就在那里,这是愚蠢的。也许这是为了证明您可以在“新人”实例中重写原型属性

    关于#2,当您在中调用object.method()时,会自动调用方法,其中堆栈将“object”填充为“this”。但是,如果您执行类似var detachedmethod=curobject.method的操作,然后调用detachedmethod(),它不会以“this”绑定为curobject进行调用(相反,在方法体中,this==未定义。或者可能是“window”,我不确定:-)

    总而言之,这是一堆吹毛求疵的东西,对于javascript的日常使用来说并不重要,而且可以在您进行过程中进行学习

    Person.prototype.gender=''的目的是什么?如果我取出它并运行代码,我会得到相同的结果

    这似乎为
    gender
    属性建立了一个默认值。使用它,即使在不调用构造函数的情况下创建实例,也会设置属性:

    var person2 = Object.create(Person.prototype);
    console.log(person2.gender); // ""
    
    这在创建子类型时可能很有用:

    function Employee() {}
    Employee.prototype = Object.create(Person.prototype);
    
    console.log(new Employee().gender); // ""
    

    为什么调用
    genderTeller()
    会导致“未定义”警报

    MDN应该进一步解释它,特别是“函数上下文”部分。但是,
    this
    的值是在调用
    函数时确定的,而不是在何时何地定义它

    通过将
    person1.sayGender
    分配给
    genderTeller
    ,它与
    person1
    分离。因此,它不再是特定对象的“方法”

    相反,它作为一个常规函数调用,其值
    this
    默认为全局对象,在浏览器中为
    window

    window.gender = 'n/a';
    
    var genderTeller = person1.sayGender;
    genderTeller(); // logs: 'n/a'
    

    对于问题1,行

    Person.prototype.gender = '';
    
    为性别属性提供一个默认值,这样即使在创建新的
    Person
    对象时没有为其分配其他值,它也有一个未定义的默认值。
    未定义的
    之间存在差异,有时重要,有时不重要,但存在差异


    对于问题2,当您这样做时:

    var person1 = new Person('Male');
    var genderTeller = person1.sayGender;
    genderTeller(); // alerts undefined
    
    var genderTeller = person1.sayGender;
    
    您的变量
    genderTeller
    包含一个函数指针,并且只包含一个函数指针。它与您的
    person1
    对象没有关联。当您仅将其作为函数调用时,函数中的
    this
    指针将设置为全局对象(
    浏览器中的窗口
    )或
    未定义的
    (如果在严格模式下运行),因此因为
    this
    不是
    Person
    对象,
    this.gender
    将不包含适当的值,并且可能是
    未定义的


    在这个错误中有一个非常重要的教训。在javascript中,
    这个
    指针是根据调用方法的方式设置的。当您调用
    genderTeller()
    时,您只是在调用一个函数,因此与任何特定的
    对象都没有任何关联。
    this
    指针不会指向
    Person
    对象,因此对
    this.xxx
    的任何引用(假设它是
    Person
    对象)都不会起作用

    当您调用
    person1.sayGender()
    时,这是一种非常不同的调用同一函数的方法。因为您是通过对象引用调用它的,所以javascript会将
    this
    指针设置为
    person1
    对象,它就会工作


    这两种场景之间的差异很微妙,但在javascript中非常重要,因此我将尝试进一步解释

    创建
    person1
    对象后,该对象包含名为
    sayGender
    的属性。该属性包含一个值,该值指向sayGender操作的函数
    sayGender
    只是一个包含在属性中的函数(技术上是在
    person1
    对象的原型链中,但您可以将其视为该对象上的属性)

    执行此操作时:

    var person1 = new Person('Male');
    var genderTeller = person1.sayGender;
    genderTeller(); // alerts undefined
    
    var genderTeller = person1.sayGender;
    
    您正在创建一个名为
    genderTeller
    的新变量,该变量现在也包含指向同一函数的指针。但是,就像它在
    sayGender
    属性中一样,它只是一个函数。它与任何对象都没有固有的绑定。只有通过对象引用调用它时,它才会获得与对象的绑定(或者如果您使用
    .call()
    .apply()
    强制对象引用,但这超出了您在此处所做的操作)。当您仅调用

    genderTeller()
    
    您只是调用一个函数,它将没有与之关联的对象引用,因此当函数运行时,
    不会指向
    个人
    对象


    如上所述,可以强制对象引用。例如,您可以执行以下所有操作:

    var genderTeller = person1.sayGender;
    genderTeller.call(person1);
    
    // .apply() only differs from .call() in how 
    // arguments are passed to the function
    // since you have no arguments to sayGender() it looks the same as .call()
    var genderTeller = person1.sayGender;
    genderTeller.apply(person1);
    
    var genderTeller = person1.sayGender.bind(person1);
    genderTeller();
    
    而且,它会再次起作用,因为您强制与
    person1
    ob关联
    genderTeller();  // returns undefined
    gender = 'hi there';
    genderTeller();  // returns 'hi there'