JavaScript闭包和类实现

时间:2013-08-30 00:30:01

标签: javascript closures

我编写了以下代码来理解JavaScript中的实现和闭包类。

1

function Person(name) {
    this.Name = name;
    console.log("Person Fired!!");

    (function EchoMyName(temp) {
        console.log("EchoMyName - " + temp);
    }("Self Called"));
    EchoMyName("Called by Person!");
}
Person.prototype.CallMe = function(){
    console.log("Call Me Fired!");
    EchoMyName("Called by CallMe!");
}

在我实例化Person时,我得到以下输出 -

Person Fired!!
EchoMyName - Self Called
ReferenceError: EchoMyName is not defined

但它就在那里,我在打电话之前定义了它?为什么会出错?

2

此外,当我修改代码以删除以前的错误并在某人身上调用CallMe时,似乎它无法访问EchoMyName

function Person(name) {
    this.Name = name;
    console.log("Person Fired!!");

    function EchoMyName(temp) {
        console.log("EchoMyName - " + temp);
    };
}
Person.prototype.CallMe = function(){
    console.log("Call Me Fired!");
    EchoMyName("Called by CallMe!");
}

拨打:

new Person().CallMe();

输出

Person Fired!!
Call Me Fired!
ReferenceError: EchoMyName is not defined

更新#1

感谢所有答案。我在Douglas Crockford的网站上找到了这个链接: http://javascript.crockford.com/private.html

它提供了对JavaScript中隐藏信息的非常清晰的理解。

3 个答案:

答案 0 :(得分:3)

代码段#1

由于以下代码而发生引用错误:EchoMyName("Called by Person!");

EchoMyName是一个自调用函数,位于名为Person的JavaScript构造函数的范围内。一旦它被自我调用,它就不再存在。如果我们使用另一种编程语言,我们可以对匿名对象进行类比。

在JavaScript中,函数实际上是对象。声明函数时,您正在创建Function对象的实例。

以下是一些声明功能的不同方法。

//function statement
function x (a, b) { return a + b;  }

//function expression, this will store an anonymous function into a variable
var y = function (a, b) { return a + b;  };

//Function constructor 
var z = new Function ("a", "b", "return a + b;"); // avoid this form, it will prevent certain optimizations from the browser's JS engine because of the strings (this is just an example)

代码段#2

在JavaScript函数中声明变量或函数时,它仅限于函数的范围。构造函数也是如此。

您需要声明一个函数表达式并将其存储在Person构造函数的属性中。

下面的小例子。

function Person(name) {
    this.Name = name;
    console.log("1 Person Fired!!");

    // notice that function is stored in this.EchoMyName
    this.EchoMyName = function(temp) {  
       console.log("2 EchoMyName - " + temp);
    };

    console.log("3 Still in Person!")
}

Person p = new Person("Bob");
p.EchoMyName("Hello World!");

输出:

1 Person Fired!!
3 Still in Person!
2 EchoMyName - Hello World!

原型

在JavaScript中,没有像许多其他面向对象语言(C ++,C#,Java)那样的传统继承。你所拥有的是对象链。

如果我们在示例中使用您的代码,Person构造函数将在一个名为 prototype 的属性中存储一个阴影对象。 Person的每个实例都将引用相同的原型。此外,原型的原型是Object。

我邀请您阅读Mozilla开发者网络中的文章Inheritance and the prototype chain以获取更多信息。

答案 1 :(得分:2)

(function EchoMyName(temp) {
    console.log("EchoMyName - " + temp);
}("Self Called"));

这是函数表达式,而不是函数定义;名称EchoMyName仅指函数本身内部的函数,因此以后不能使用它。 (在IE的JScript中除外,这是错误的。)

你的第二个问题源于这样一个事实:你所拥有的不是一个阶级;这是一个功能。可以在JavaScript中的函数内声明一个函数,并且它将作用于该函数,就像常规变量一样(但是可以在函数的任何位置使用它)。原型只是一个对象,并且无法访问构造函数的局部变量。

总结一下,在ECMAScript 5中:

  • 只有三个范围:
    • 全局
    • 功能
    • catch
  • 在命名函数表达式中,该名称只能由该函数访问
  • 对于常规变量,范围没有例外;原型上的函数应该使用this,如果他们需要目标的东西(如果他们不需要,为什么他们在原型上?)
  • 如果没有括号,函数表达式看起来像一个函数声明,它在同一作用域中就像一个变量,但是被“提升”,因此无论它在函数中出现的位置如何,它都表现得好像它出现在开头

答案 2 :(得分:1)

  

但它就在那里,我在打电话之前定义了它?为什么会出错?

不同之处在于如何评估function定义。

在第1段中,由于围绕它的括号,EchoMyNamefunction expression。允许立即调用它们,但只能在它们自己的正文中引用它们。

在第二天,EchoMyName取而代之的是function declaration。这些都受hoisting约束,允许他们在自己的身体之外引用他们的名字。

  

似乎无法访问EchoMyName

这是因为EchoMyName绑定到Person内的范围。除此之外,EchoMyName不存在。

您需要通过EchoMyNamethisPerson.prototype附加到实例,以便CallMe可以访问它:

this.EchoMyName = function EchoMyName(temp) {
    console.log("EchoMyName - " + temp);
};
Person.prototype.EchoMyName = function EchoMyName(temp) {
    console.log("EchoMyName - " + temp);
};
Person.prototype.CallMe = function(){
    console.log("Call Me Fired!");
    this.EchoMyName("Called by CallMe!");
};

或者,如果您希望保留EchoMyName作用域以使其无法被其他代码调用,则还需要在同一作用域CallMe内定义function。但是,这不一定是Person

var Person = (function () {
    function Person(name) {
        this.Name = name;
        console.log("Person Fired!!");
    }

    function EchoMyName(temp) {
        console.log("EchoMyName - " + temp);
    }

    Person.prototype.CallMe = function () {
        console.log("Call Me Fired!");
        EchoMyName("Called by CallMe!");
    };

    return Person;
})();

参考:Revealing Module Pattern