使用'new'关键字创建JavaScript对象

3

我正在学习JavaScript,对它的对象概念感到困惑。我以前是Java程序员,JavaScript没有类让我感到害怕。

从我目前所学的知识来看,在JavaScript中,函数可以替代类。你可以使用'new'关键字实例化函数,如下所示。

function person() {
    var name;
    this.tellYourName = function() {
        return 'My name is ' + name;
    }
    this.setName = function(newName) {
        name = newName;
    }
}

var person1 = new person();

person1.setName('Chris');
alert(person1.tellYourName());

在上面的代码中,我创建了一个名为'person1'的对象,该对象继承了person函数的属性。
那么我的问题是:person()是一个对象吗?我所说的对象是指person()是一个类还是一个已实例化的对象。
当我学习闭包时,我对这个概念感到困惑。以下是示例代码。
function closureTest(a) {
    return function(b) {
        return a + b;
    }
}

var test = closureTest(1);
alert(test(2));

我猜测closureTest函数作为方法而非类起作用。并且var test = closureTest(1);这一行没有使用'new'关键字。这意味着test变量将存储closureTest(1)的返回值。但是你会发现,test变量被用作closureTest的对象。这是怎么可能的?因为当我尝试下面的测试代码时:

function test(a) {
    return a;
}
var t = test(2);
alert(t);

按照我的期望,它打印出了测试函数的返回值。

我希望我的问题不会太令人困惑。我目前从Java转到JavaScript,以为它们几乎相同,但是这种想法是错误的。我阅读了几篇关于JavaScript中对象概念的讲座,但情况变得更加混乱。感谢您花时间阅读这篇文章,希望能从您那里得到一些东西 :)


5
忘掉你对Java的一切了解。Java和Javascript几乎没有任何共同之处;它们之所以这样命名,只是出于营销目的。 - user2357112
我本来想投票支持一个重复的问题,但是不小心按了回车键而不是退格键...然后选择错误了,不能再投票支持重复问题了。请将此问题作为 https://dev59.com/h3I-5IYBdhLWcg3w18V3 的重复问题关闭。 - Antti Haapala -- Слава Україні
@user2357112 我相信我应该这样做。谢谢你的话,我会记在心里的。 - Eugene Yu
在JavaScript中,“对象”更类似于Java中的HashMap。它只是一组键值对集合,但它们可以是任何类型(其他对象或甚至函数)。从技术上讲,您可以将Java中的对象视为相同的东西,除了事实上您预先声明了可以放入HashMap中的键以及值的类型。JavaScript确实具有内置语言功能来预先声明和创建“预定义”HashMap /对象,也称为类(可以看作是对象可以具有的键/值的声明)。 - Justin L.
@Antti Haapala,您发布的链接非常有帮助。我将关闭这个问题。 - Eugene Yu
@JustinL。是的,在函数中访问属性的可访问性类似于Java中的HashMap。谢谢 :) - Eugene Yu
5个回答

3
一些理论观察 :)
在理解JavaScript时,认为对象是类的实例化并不是很有帮助。JavaScript涉及原型继承,而不是经典继承。换句话说,对象从继承链上的其他对象继承属性,而不是从类中继承。
首先,在JavaScript中函数是对象。这意味着函数可以有方法。
其次,如果使用new前缀调用函数,则会创建一个新对象。此新对象将链接到函数的原型,但this将引用新对象。设计用于与new一起使用的函数称为构造函数
第三,有多种实现相同功能的方法。所以你可以这样做:
// The capital for the function indicates a constructor function
function Person(name) {
    this.name = name;
}
Person.prototype.tellYourName = function() {
    return 'My name is ' + this.name;
};
Person.prototype.changeName = function(newName) {
    this.name = newName;
};    
var person1 = new person("Chris"),
    random_string = person1.tellYourName(); // Chris

另外,您可以在不使用new的情况下实现相同的功能。

function person(name) {
    // myName is a private variable
    var myName = name; // This line is actually unnecessary if you use name throughout

    return {
        // Public methods
        tellYourName: function() {
            return 'My name is ' + myName;
        },
        setName: function(newName) {
            myName = newName;
        }
    }
}
var person1 = person("Chris"); // Note that `new` is not used

后者通常更受欢迎,因为它带来了数据隐藏。换句话说,访问属性name的唯一方法是使用公共方法。

非常有帮助。特别是大写字母表示构造函数部分解决了我的问题。但是这里还有更多的问题,第二段代码中的函数 person(name) 的对象是什么时候创建的?是在声明函数的那一行吗?还是在 var person1 = person("Chris"); 这一行? - Eugene Yu
我的意思是我用以下这段代码进行了测试: var person1 = person("Chris"); var person2 = person("Malcom"); 但看起来person1仍然保持名字Chris。这是否意味着将person分配给变量时隐藏了“new”关键字? - Eugene Yu
@EugeneYu 在代码行 var person1 = person("Chris"); 中创建了 person1 对象。你的代码行 var person2 = person("Malcolm"); 创建了另一个对象。当然,Person 函数本身也是一个对象,但不是同样的方式。关于你的第二个问题,没有隐藏的 new 关键字,但它确实会产生相同的结果。在第二个版本中,函数 person 返回一个对象 {...}。这就是括号 {...} 的作用 - 你用 [...] 包含数组,用 {...} 包含对象。所以你返回了一个具有两个方法的对象,但这些方法可以访问 myName - Nick
@EugeneYu 推荐阅读的最佳书籍是Douglas Crockford的《JavaScript: The Good Parts》。这本书相当简短,但你需要读60页关键内容大约十次才能真正理解它 :) - Nick
注意到了您的建议。谢谢您花时间帮助我。我一定会去看看这本书的。 - Eugene Yu

3
我理解你的困惑。Java和JavaScript唯一共同点是类似C语法和名称。除此之外,Java和JavaScript是两种非常不同的编程语言:
  1. Java是一种传统的面向对象编程语言。JavaScript是一种基于原型的面向对象编程语言。
  2. 在Java中,函数必须是类的方法。在JavaScript中,函数是头等公民。这使得JavaScript比Java更加强大。
  3. Java的祖先可以追溯到C/C++。JavaScript的祖先可以追溯到Self和Scheme。
与Java不同,JavaScript不仅是一种面向对象编程语言,还是一种函数式编程语言(虽然不如Haskell或OCaml那么函数化)。因此,在JavaScript中,函数扮演着重要角色。
JavaScript中的函数可用于:

链接1:创建对象构造函数。

链接2:作为变量传递给其他函数。

  1. 创建一个对象的模板(即像类一样运作)。
  2. 封装状态和行为(即像命名空间一样运作)。
  3. 共享功能并减少冗余(即像混入一样运作)。

将函数视为类

JavaScript没有类,因为它是一种原型对象导向的编程语言。不幸的是,JavaScript的原型性质被隐藏在构造函数后面,使其看起来更像Java。然而,这只会让Java程序员难以理解JavaScript中的继承。

例如,假设您在Java中有以下Rectangle类:

public class Rectangle {
    public int width;
    public int height;

    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public int area() {
        return this.width * this.height;
    }
}

在JavaScript中,您将以上内容编写为:
function Rectangle(width, height) {
    this.width = width;
    this.height = height;
}

Rectangle.prototype.area = function () {
    return this.width * this.height;
};

如果您不喜欢这种语法,那么您可以按照以下方式使其更像一个类:
function CLASS(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}

var Rectangle = CLASS({
    constructor: function (width, height) {
        this.width = width;
        this.height = height;
    },
    area: function () {
        return this.width * this.height;
    }
});

我写了一个名为augment的函数,它可能会让你的生活更轻松。

2

首先是关于 new 关键字的问题。可以这样理解:在 person 函数体内存储在 this 上的任何内容,在创建新实例时都会被传递。因此,new Person() 返回 this 和你在 this 上设置的任何函数。

对于你的第二个问题,可以将其视为一个返回另一个函数的函数。它返回的是一个函数,而不是一个对象。函数本质上也是一个对象。因此,当你调用 closuretest(1) 时,实际上返回的是

var test=  function(b) {
    return 1+b;
 }

现在,如果您调用test(2),它将返回1 + 2 = 3。

希望这有所帮助。


0

你上面展示的例子是两种不同的用法。在JavaScript中,每个东西都被视为一个对象。当你使用new关键字来创建一个函数时,你的函数对象被视为一个类,而用this引用的变量则是你的类属性,例如:

var Person = function(){
    this.name = null;
    this.age = 0;
};

这是一个被视为人的,它具有名称和年龄作为其属性,这些属性将在类Person的实例/对象中可用,并且对象是这样创建的:

var someone = new Person();
//someone.name and someone.age are valid then.

但是如果在函数内部不使用this,则不会将其创建为类,因此它就像您的示例2中显示的那样作为普通函数运行,返回另一个对象/函数可供使用。 要更清楚地了解这个问题,您应该阅读面向对象的JavaScript以及有关此主题的其他链接。


3
JavaScript 中没有类,把函数看作类会让你很快迷失方向。 - Antti Haapala -- Слава Україні
@Antti,你说得对,虽然没有类,但将它们视为类有助于更好地理解这个概念。 - guleria

0
当你在JavaScript中使用'new'关键字时,它会执行几个步骤。首先,它创建了一个具有提供的函数原型的新对象实例,在您的情况下是person。第二步是使用新创建的对象的上下文调用提供的函数。最后一步是返回一个新创建的对象。因此,对象创建的神奇之处在于new运算符中。 您可以在这里阅读有关它的内容

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接