关于new operator:javascript中的”new”关键字是什么?

What is the 'new' keyword in JavaScript?

javascript中的new关键字在第一次遇到时会非常混乱,因为人们往往认为javascript不是面向对象的编程语言。

  • 这是怎么一回事?
  • 它能解决什么问题?
  • 什么时候合适,什么时候不合适?


它可以做5件事:

  • 它创建一个新对象。这个对象的类型只是对象。
  • 它将这个新对象的内部的、不可访问的、[[prototype]](即,proto)属性设置为构造函数函数的外部的、可访问的原型对象(每个函数对象自动具有原型属性)。
  • 它使this变量指向新创建的对象。
  • 每当提到this时,它使用新创建的对象来执行构造器函数。
  • 它返回新创建的对象,除非构造函数函数返回非null对象引用。在这种情况下,将返回该对象引用。
  • 注:constructor函数是指new关键字后的函数,如

    1
    new ConstructorFunction(arg1, arg2)

    完成后,如果请求新对象的未定义属性,脚本将检查对象的[[原型]]对象的属性。这就是如何在JavaScript中获得类似于传统类继承的东西。

    最困难的部分是第2点。每个对象(包括函数)都有一个称为[[原型]]的内部属性。它只能在对象创建时设置,可以使用new、object.create或基于文本(函数默认为function.prototype、numbers to number.prototype等)。它只能用object.getPrototypeof(someObject)读取。没有其他方法可以设置或读取此值。

    函数除了隐藏的[[原型]]属性外,还有一个名为原型的属性,您可以通过它访问和修改,为所生成的对象提供继承的属性和方法。

    下面是一个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    ObjMaker = function() {this.a = 'first';};
    // ObjMaker is just a function, there's nothing special about it that makes
    // it a constructor.

    ObjMaker.prototype.b = 'second';
    // like all functions, ObjMaker has an accessible prototype property that
    // we can alter. I just added a property called 'b' to it. Like
    // all objects, ObjMaker also has an inaccessible [[prototype]] property
    // that we can't do anything with

    obj1 = new ObjMaker();
    // 3 things just happened.
    // A new, empty object was created called obj1.  At first obj1 was the same
    // as {}. The [[prototype]] property of obj1 was then set to the current
    // object value of the ObjMaker.prototype (if ObjMaker.prototype is later
    // assigned a new object value, obj1's [[prototype]] will not change, but you
    // can alter the properties of ObjMaker.prototype to add to both the
    // prototype and [[prototype]]). The ObjMaker function was executed, with
    // obj1 in place of this... so obj1.a was set to 'first'.

    obj1.a;
    // returns 'first'
    obj1.b;
    // obj1 doesn't have a property called 'b', so JavaScript checks
    // its [[prototype]]. Its [[prototype]] is the same as ObjMaker.prototype
    // ObjMaker.prototype has a property called 'b' with value 'second'
    // returns 'second'

    这类似于类继承,因为现在,使用new ObjMaker()创建的任何对象似乎也继承了"b"属性。

    如果您想要子类之类的东西,那么您可以这样做:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    SubObjMaker = function () {};
    SubObjMaker.prototype = new ObjMaker(); // note: this pattern is deprecated!
    // Because we used 'new', the [[prototype]] property of SubObjMaker.prototype
    // is now set to the object value of ObjMaker.prototype.
    // The modern way to do this is with Object.create(), which was added in ECMAScript 5:
    // SubObjMaker.prototype = Object.create(ObjMaker.prototype);

    SubObjMaker.prototype.c = 'third';  
    obj2 = new SubObjMaker();
    // [[prototype]] property of obj2 is now set to SubObjMaker.prototype
    // Remember that the [[prototype]] property of SubObjMaker.prototype
    // is ObjMaker.prototype. So now obj2 has a prototype chain!
    // obj2 ---> SubObjMaker.prototype ---> ObjMaker.prototype

    obj2.c;
    // returns 'third', from SubObjMaker.prototype

    obj2.b;
    // returns 'second', from ObjMaker.prototype

    obj2.a;
    // returns 'first', from SubObjMaker.prototype, because SubObjMaker.prototype
    // was created with the ObjMaker function, which assigned a for us

    在找到这一页之前,我读了很多关于这一主题的垃圾,在那里用漂亮的图表很好地解释了这一点。


    假设您具有以下功能:

    1
    2
    3
    4
    var Foo = function(){
      this.A = 1;
      this.B = 2;
    };

    如果您将其称为这样的独立函数:

    1
    Foo();

    执行此函数将向window对象(AB添加两个属性。它将它添加到window中,因为window是这样执行函数时调用函数的对象,而函数中的this是调用函数的对象。至少在javascript中。

    现在,用new这样称呼它:

    1
    var bar = new Foo();

    当您向函数调用中添加new时,会发生的情况是创建了一个新对象(只是var bar = new Object()),并且函数中的this指向您刚刚创建的新Object,而不是指向调用该函数的对象。因此,bar现在是一个具有AB属性的对象。任何函数都可以是一个构造函数,但它并不总是有意义的。


    除了Daniel Howard的回答之外,以下是new所做的(或者至少看起来是这样做的):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function New(func) {
        var res = {};
        if (func.prototype !== null) {
            res.__proto__ = func.prototype;
        }
        var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
        if ((typeof ret ==="object" || typeof ret ==="function") && ret !== null) {
            return ret;
        }
        return res;
    }

    同时

    1
    var obj = New(A, 1, 2);

    等于

    1
    var obj = new A(1, 2);


    让初学者更好地理解它

    在浏览器控制台中尝试以下代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function Foo() {
        return this;
    }

    var a = Foo();       //returns window object
    var b = new Foo();   //returns empty object of foo

    a instanceof Window;  // true
    a instanceof Foo;     // false

    b instanceof Window;  // false
    b instanceof Foo;     // true

    现在您可以阅读社区wiki答案:)


    so it's probably not for creating
    instances of object

    正是为了这个。您可以这样定义函数构造函数:

    1
    2
    3
    4
    5
    function Person(name) {
        this.name = name;
    }

    var john = new Person('John');

    然而,ecmascript的额外好处是,您可以使用.prototype属性进行扩展,因此我们可以做一些类似于…

    1
    Person.prototype.getName = function() { return this.name; }

    从这个构造函数创建的所有对象现在都将有一个getName,因为它们可以访问原型链。


    JavaScript是一种面向对象的编程语言,它完全用于创建实例。它是基于原型的,而不是基于类的,但这并不意味着它不是面向对象的。


    JavaScript是一种支持面向对象编程范式的动态编程语言,用于创建新的对象实例。

    对象不需要类-JavaScript是一种基于原型的语言。


    已经有一些非常好的答案了,但是我发布了一个新的答案来强调我对下面的案例三的观察,即当你在一个函数中有一个明确的返回语句时会发生什么,而这个函数是你正在执行的。请看以下案例:

    案例一:

    1
    2
    3
    4
    5
    6
    var Foo = function(){
      this.A = 1;
      this.B = 2;
    };
    console.log(Foo()); //prints undefined
    console.log(window.A); //prints 1

    上面是调用Foo所指向的匿名函数的简单例子。当调用此函数时,它返回undefined。由于没有显式的返回语句,所以javascript解释器在函数的末尾强制插入一条return undefined;语句。这里的窗口是调用对象(上下文this,它获取新的AB属性。

    案例二:

    1
    2
    3
    4
    5
    6
    7
    var Foo = function(){
      this.A = 1;
      this.B = 2;
    };
    var bar = new Foo();
    console.log(bar()); //illegal isn't pointing to a function but an object
    console.log(bar.A); //prints 1

    在这里,看到new关键字的javascript解释器创建一个新的对象,作为Foo指向的匿名函数的调用对象(上下文this)。在这种情况下,AB成为新创建对象(代替窗口对象)的属性。由于没有任何显式的返回语句,所以javascript解释器强制插入一个返回语句,以返回由于使用new关键字而创建的新对象。

    III案:

    1
    2
    3
    4
    5
    6
    7
    8
    var Foo = function(){
      this.A = 1;
      this.B = 2;
      return {C:20,D:30};
    };
    var bar = new Foo();
    console.log(bar.C);//prints 20
    console.log(bar.A); //prints undefined. bar is not pointing to the object which got created due to new keyword.

    在这里,看到new关键字的javascript解释器再次创建了一个新的对象,作为Foo指向匿名函数的调用对象(上下文this)。同样,AB成为新创建对象的属性。但是这次您有一个显式的返回语句,这样JavaScript解释器就不会自己做任何事情。

    在第三种情况下需要注意的是,由于new关键字而创建的对象从雷达上丢失了。bar实际上指向一个完全不同的对象,而不是由new关键字创建的javascript解释器。


    有时代码比文字容易:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var func1 = function (x) { this.x = x; }                    // used with 'new' only
    var func2 = function (x) { var z={}; z.x = x; return z; }   // used both ways
    func1.prototype.y = 11;
    func2.prototype.y = 12;

    A1 = new func1(1);      // has A1.x  AND  A1.y
    A2 =     func1(1);      // undefined ('this' refers to 'window')
    B1 = new func2(2);      // has B1.x  ONLY
    B2 =     func2(2);      // has B2.x  ONLY

    对我来说,只要我不是原型,我就使用func2样式,因为它在函数内部和外部都提供了更多的灵活性。


    new关键字用于创建新的对象实例。是的,JavaScript是一种动态编程语言,它支持面向对象的编程范式。关于对象命名的约定是,对于应该由新关键字实例化的对象,始终使用大写字母。

    1
    obj = new Element();

    new关键字更改运行函数的上下文,并返回指向该上下文的指针。

    如果不使用new关键字,则运行函数Vehicle()的上下文与调用Vehicle函数的上下文相同。this关键字将引用相同的上下文。使用new Vehicle()时,会创建一个新的上下文,因此函数内的关键字this引用新的上下文。您得到的回报是新创建的上下文。


    好吧,每个SI的javascript在不同平台之间可能有很大的不同,因为它始终是原始规范ecmascript的实现。

    在任何情况下,与实现无关,所有遵循ECMAScript规范的JavaScript实现都将为您提供一种面向对象的语言。根据ES标准:

    ECMAScript is an object-oriented programming language for
    performing computations and manipulating computational objects
    within a host environment.

    现在我们已经同意了JavaScript是ECMAScript的一个实现,因此它是一种面向对象的语言。在任何面向对象的语言中,new操作的定义都表示,此类关键字用于从某个类型的类(包括匿名类型,如c)创建对象实例。

    在ecmascript中,我们不使用类,正如您可以从规范中看到的那样:

    ECMAScript does not use classes such as those in C++, Smalltalk, or Java. Instead objects may be created in various ways including via
    a literal notation or via constructors which create objects and then execute code that initializes all or part of them by assigning initial
    values to their properties. Each constructor is a function that has a
    property named ―
    prototype ‖ that is used to implement prototype - based inheritance and shared properties. Objects are created by
    using constructors in new expressions; for example, new
    Date(2009,11) creates a new Date object. Invoking a constructor
    without using new has consequences that depend on the constructor.
    For example, Date() produces a string representation of the
    current date and time rather than an object.


    总结:

    在javascript中使用new关键字从构造函数函数创建对象。new关键字必须放在构造函数函数调用之前,并将执行以下操作:

  • 创建新对象
  • 将此对象的原型设置为构造函数函数的原型属性
  • this关键字绑定到新创建的对象并执行构造函数函数
  • 返回新创建的对象
  • 例子:

    1
    2
    3
    4
    5
    6
    7
    8
    function Dog (age) {
      this.age = age;
    }

    const doggie = new Dog(12);

    console.log(doggie);
    console.log(doggie.__proto__ === Dog.prototype) // true

    具体发生了什么:

  • const doggie说:我们需要内存来声明变量。
  • 赋值运算符=说:我们将用=后面的表达式初始化这个变量。
  • 表达为new Dog(12)。JS引擎看到新的关键字,创建一个新的对象,并将原型设置为dog.prototype。
  • 构造器函数是在this值设置为新对象的情况下执行的。在这个步骤中,年龄被分配给新创建的doggie对象。
  • 新创建的对象将返回并分配给变量doggie。

  • new关键字使用函数作为构造函数创建对象实例。例如:

    1
    2
    3
    4
    5
    var Foo = function() {};
    Foo.prototype.bar = 'bar';

    var foo = new Foo();
    foo instanceof Foo; // true

    实例继承自构造函数函数的prototype。所以举个例子…

    1
    foo.bar; // 'bar'