Apa kata kunci 'baru' dalam JavaScript?

1744

Kata newkunci dalam JavaScript bisa sangat membingungkan ketika pertama kali ditemukan, karena orang cenderung berpikir bahwa JavaScript bukan bahasa pemrograman berorientasi objek.

  • Apa itu?
  • Masalah apa yang dipecahkan?
  • Kapan itu tepat dan kapan tidak?
Alon Gubkin
sumber
10
Juga, utas terkait - stackoverflow.com/questions/383402/…
Chetan Sastry
baca contoh-contoh ini orang pertama, developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Martian2049

Jawaban:

2145

Itu melakukan 5 hal:

  1. Itu menciptakan objek baru. Jenis objek ini hanyalah objek .
  2. Ini menetapkan objek baru ini internal, tidak dapat diakses, [[prototipe]] (yaitu __proto__ ) properti menjadi fungsi konstruktor eksternal, dapat diakses, prototipe objek (setiap fungsi objek secara otomatis memiliki prototipe properti).
  3. Itu membuat thistitik variabel ke objek yang baru dibuat.
  4. Itu mengeksekusi fungsi konstruktor, menggunakan objek yang baru dibuat setiap kali thisdisebutkan.
  5. Ini mengembalikan objek yang baru dibuat, kecuali fungsi konstruktor mengembalikan nullreferensi non- objek. Dalam hal ini, referensi objek itu dikembalikan sebagai gantinya.

Catatan: fungsi konstruktor merujuk ke fungsi setelah newkata kunci, seperti pada

new ConstructorFunction(arg1, arg2)

Setelah ini selesai, jika properti yang tidak ditentukan dari objek baru diminta, skrip akan memeriksa objek [[prototipe]] objek untuk properti sebagai gantinya. Ini adalah bagaimana Anda bisa mendapatkan sesuatu yang mirip dengan warisan kelas tradisional di JavaScript.

Bagian yang paling sulit tentang ini adalah titik nomor 2. Setiap objek (termasuk fungsi) memiliki properti internal ini disebut [[prototipe]] . Ini hanya dapat diatur pada waktu pembuatan objek, baik dengan yang baru , dengan Object.create , atau berdasarkan literal (fungsi default ke Function.prototype, angka ke Number.prototype, dll.). Itu hanya dapat dibaca dengan Object.getPrototypeOf (someObject) . Tidak ada cara lain untuk mengatur atau membaca nilai ini.

Fungsi, selain properti [[prototipe]] tersembunyi , juga memiliki properti yang disebut prototipe , dan inilah yang dapat Anda akses, dan modifikasi, untuk menyediakan properti dan metode yang diwarisi untuk objek yang Anda buat.


Berikut ini sebuah contoh:

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'

Ini seperti warisan kelas karena sekarang, benda apa pun yang Anda buat menggunakan new ObjMaker()juga akan tampak mewarisi properti 'b'.

Jika Anda menginginkan sesuatu seperti subclass, maka Anda melakukan ini:

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

Saya membaca satu ton sampah tentang hal ini sebelum akhirnya menemukan halaman ini , di mana ini dijelaskan dengan sangat baik dengan diagram yang bagus.

Daniel Howard
sumber
47
Hanya ingin menambahkan: Sebenarnya ada cara untuk mengakses [[prototipe]] internal, oleh __proto__. Namun ini tidak standar, dan hanya didukung oleh browser yang relatif baru (dan tidak semuanya). Ada cara standar yang muncul, yaitu Object.getPrototypeOf (obj), tetapi Ecmascript3.1, dan itu sendiri hanya didukung di browser baru - lagi. Secara umum disarankan untuk tidak menggunakan properti itu, hal-hal menjadi rumit dengan sangat cepat di dalam sana.
Blub
9
Pertanyaan: apa yang terjadi secara berbeda jika ObjMakerdidefinisikan sebagai fungsi yang mengembalikan nilai?
Jim Blackler
13
@LonelyPixel newada sehingga Anda tidak perlu menulis metode pabrik untuk membangun / menyalin fungsi / objek. Ini berarti, "Salin ini, membuatnya seperti 'kelas' induknya; lakukan secara efisien dan benar; dan simpan info warisan yang hanya dapat diakses oleh saya, JS, secara internal". Untuk melakukannya, ia memodifikasi bagian prototypedalam objek baru yang tidak dapat diakses yang tidak dapat diakses untuk mengenkapsulasi anggota yang diwariskan, meniru rantai pewarisan OO klasik (yang tidak dapat dimodifikasi runtime). Anda dapat mensimulasikan ini tanpa new, tetapi warisan akan dapat dimodifikasi runtime. Baik? Buruk? Terserah kamu.
Insinyur
11
titik kecil untuk menambahkan: panggilan ke konstruktor, ketika didahului oleh kata kunci baru, secara otomatis mengembalikan objek yang dibuat; tidak perlu mengembalikannya secara eksplisit dari dalam konstruktor.
charlie roberts
7
Ada catatan yang mengatakan Notice that this pattern is deprecated!. Apa pola up-to-date yang benar untuk mengatur prototipe kelas?
Tom Pažourek
400

Misalkan Anda memiliki fungsi ini:

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

Jika Anda menyebut ini sebagai fungsi mandiri seperti:

Foo();

Menjalankan fungsi ini akan menambahkan dua properti ke windowobjek ( Adan B). Itu menambahkannya ke windowkarena windowadalah objek yang disebut fungsi ketika Anda mengeksekusi seperti itu, dan thisdalam suatu fungsi adalah objek yang disebut fungsi. Setidaknya dalam Javascript.

Sekarang, sebut saja seperti ini dengan new:

var bar = new Foo();

Apa yang terjadi ketika Anda menambahkan newpanggilan fungsi adalah bahwa objek baru dibuat (hanya var bar = new Object()) dan bahwa di thisdalam fungsi menunjuk ke yang baru ObjectAnda buat, bukan ke objek yang disebut fungsi. Jadi barsekarang menjadi objek dengan properti Adan B. Fungsi apa pun bisa menjadi konstruktor, tidak selalu masuk akal.

JulianR
sumber
7
Tergantung pada konteks eksekusi. Dalam kasus saya (skrip Qt) itu hanya objek global.
Maxym
2
apakah ini akan menyebabkan lebih banyak penggunaan memori?
Jürgen Paul
2
karena jendela adalah objek yang disebut fungsi - harus: karena jendela adalah objek yang berisi fungsi.
Dávid Horváth
1
@ Taurus Di browser web, fungsi non-metode akan menjadi metode yang windowtersirat. Bahkan dalam penutupan, bahkan jika anonim. Namun, dalam contoh ini adalah doa metode sederhana di jendela: Foo();=> [default context].Foo();=> window.Foo();. Dalam ungkapan ini windowadalah konteks (bukan hanya penelepon , yang tidak masalah).
Dávid Horváth
1
@ Taurus Pada dasarnya ya. Namun dalam ECMA 6 dan 7 hal-hal lebih kompleks (lihat lambdas, kelas, dll).
Dávid Horváth
164

Selain jawaban Daniel Howard, berikut adalah apa yang newdilakukan (atau setidaknya tampaknya dilakukan):

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;
}

Sementara

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

setara dengan

var obj = new A(1, 2);
basilikum
sumber
73
Saya menemukan bahwa javascript lebih mudah dipahami daripada bahasa Inggris: v
damphat
Jawaban yang sangat bagus. Saya punya satu pertanyaan kecil: Bagaimana bisa mungkin untuk func.prototypemenjadi null? Bisakah Anda menjelaskan sedikit tentang itu?
Tom Pažourek
6
@tomp Anda dapat mengganti properti prototipe, hanya dengan menulis A.prototype = null;Dalam kasus itu new A()akan menghasilkan objek, itulah prototipe internal menunjuk ke Objectobjek: jsfiddle.net/Mk42Z
basilikum
2
Pemeriksaan typeof mungkin salah karena objek host dapat menghasilkan sesuatu yang berbeda dari "objek" atau "fungsi". Untuk menguji apakah sesuatu itu objek, saya lebih suka Object(ret) === ret.
Oriol
2
@Oriol terima kasih atas komentarnya. Memang benar apa yang Anda katakan dan tes yang sebenarnya harus dilakukan dengan cara yang lebih kuat. Namun, saya pikir untuk jawaban konseptual ini, typeoftes hanya membuatnya lebih mudah untuk memahami apa yang terjadi di balik layar.
basilikum
109

Bagi pemula untuk memahaminya dengan lebih baik

coba kode berikut di konsol browser.

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

Sekarang Anda dapat membaca jawaban komunitas wiki :)

Anulal S
sumber
4
Jawaban yang bagus. Juga - meninggalkan return this;menghasilkan output yang sama.
Nelu
37

jadi mungkin bukan untuk membuat instance objek

Ini digunakan tepat untuk itu. Anda mendefinisikan konstruktor fungsi seperti:

function Person(name) {
    this.name = name;
}

var john = new Person('John');

Namun manfaat tambahan yang dimiliki ECMAScript adalah Anda dapat memperluas dengan .prototypeproperti, sehingga kami dapat melakukan sesuatu seperti ...

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

Semua objek yang dibuat dari konstruktor ini sekarang akan memiliki getNamekarena rantai prototipe yang mereka akses.

meder omuraliev
sumber
6
fungsi konstruktor digunakan seperti kelas, tidak ada classkata kunci tetapi Anda dapat melakukan hal yang sama.
meder omuraliev
Ada kindof adalah kata kunci kelas - kelas dicadangkan untuk digunakan di masa depan
Greg
11
Kebetulan yang ini mengapa Anda menggunakan .className tidak .class untuk mengatur kelas CSS
Greg
23
Itu harus dikapitalisasi Orang oleh konvensi.
eomeroff
27

JavaScript adalah bahasa pemrograman berorientasi objek dan digunakan tepat untuk membuat instance. Ini berbasis prototipe, bukan berbasis kelas, tetapi itu tidak berarti bahwa itu tidak berorientasi objek.

Michael
sumber
6
Saya suka mengatakan bahwa JavaScript tampaknya lebih berorientasi objek daripada semua bahasa berbasis kelas itu. Dalam JavaScript semua yang Anda tulis segera menjadi objek, tetapi dalam bahasa berbasis kelas, Anda pertama-tama menulis deklarasi dan hanya kemudian Anda membuat instance (objek) kelas tertentu. Dan prototipe JavaScript tampaknya samar-samar mengingatkan semua hal VTABLE untuk bahasa berbasis kelas.
JustAMartin
14

Javascript adalah bahasa pemrograman dinamis yang mendukung paradigma pemrograman berorientasi objek, dan digunakan untuk membuat instance objek baru.

Kelas tidak diperlukan untuk objek - Javascript adalah bahasa berbasis prototipe .

Greg
sumber
12

Sudah ada beberapa jawaban yang sangat bagus tetapi saya memposting yang baru untuk menekankan pengamatan saya pada kasus III di bawah ini tentang apa yang terjadi ketika Anda memiliki pernyataan pengembalian eksplisit dalam fungsi yang sedang Anda newlakukan. Lihat kasus-kasus di bawah ini:

Kasus I :

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

Di atas adalah kasus sederhana memanggil fungsi anonim yang ditunjuk oleh Foo. Ketika Anda memanggil fungsi ini, ia kembali undefined. Karena tidak ada pernyataan pengembalian yang eksplisit maka penerjemah JavaScript dengan paksa memasukkan return undefined;pernyataan di akhir fungsi. Di sini jendela adalah objek doa (kontekstual this) yang mendapat properti baru Adan B.

Kasus II :

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

Di sini, penerjemah JavaScript yang melihat newkata kunci membuat objek baru yang bertindak sebagai objek doa (kontekstual this) dari fungsi anonim yang ditunjukkan oleh Foo. Dalam hal ini Adan Bmenjadi properti pada objek yang baru dibuat (sebagai pengganti objek jendela). Karena Anda tidak memiliki pernyataan pengembalian yang eksplisit, maka penerjemah JavaScript dengan paksa memasukkan pernyataan kembali untuk mengembalikan objek baru yang dibuat karena penggunaan newkata kunci.

Kasus III :

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.

Di sini, lagi, penerjemah JavaScript yang melihat newkata kunci membuat objek baru yang bertindak sebagai objek doa (kontekstual this) dari fungsi anonim yang ditunjukkan oleh Foo. Sekali lagi, Adan Bmenjadi properti pada objek yang baru dibuat. Tetapi kali ini Anda memiliki pernyataan pengembalian yang eksplisit sehingga penerjemah JavaScript tidak akan melakukan apa pun sendiri.

Hal yang perlu diperhatikan dalam kasus III adalah bahwa objek yang dibuat karena newkata kunci hilang dari radar Anda. barsebenarnya menunjuk ke objek yang sama sekali berbeda yang bukan yang dibuat penerjemah JavaScript karena newkata kunci.

Mengutip David Flanagan dari JavaScripit: The Definitive Guide (Edisi ke-6), Ch. 4, Halaman # 62:

Saat ekspresi pembuatan objek dievaluasi, JavaScript pertama-tama membuat objek kosong baru, seperti yang dibuat oleh penginisialisasi objek {}. Selanjutnya, ia memanggil fungsi yang ditentukan dengan argumen yang ditentukan, meneruskan objek baru sebagai nilai dari kata kunci ini. Fungsi kemudian dapat menggunakan ini untuk menginisialisasi properti dari objek yang baru dibuat. Fungsi yang ditulis untuk digunakan sebagai konstruktor tidak mengembalikan nilai, dan nilai ekspresi pembuatan objek adalah objek yang baru dibuat dan diinisialisasi. Jika konstruktor mengembalikan nilai objek, nilai itu menjadi nilai ekspresi penciptaan objek dan objek yang baru dibuat dibuang.

--- Info Tambahan ---

Fungsi yang digunakan dalam cuplikan kode dari kasus di atas memiliki nama khusus di dunia JS seperti di bawah ini:

Kasus I dan II - Fungsi konstruktor

Kasus III - Fungsi pabrik. Fungsi pabrik tidak boleh digunakan dengan newkata kunci yang telah saya lakukan untuk menjelaskan konsep di utas saat ini.

Anda dapat membaca tentang perbedaan di antara mereka di utas ini .

RBT
sumber
kasus Anda 3, adalah pengamatan gr8
appu
5

terkadang kode lebih mudah daripada kata-kata:

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

bagi saya, selama saya tidak membuat prototipe, saya menggunakan gaya func2 karena memberi saya sedikit lebih banyak fleksibilitas di dalam dan di luar fungsi.

rsbkk
sumber
3
B1 = new func2(2); <- Kenapa ini tidak ada B1.y ?
sunny_dev
@sunny_dev Saya bukan ahli JS, tapi mungkin karena func2 mengembalikan secara langsung nilai (objek z), alih-alih bekerja / kembali dengan nilai-nilai internal (ini)
Eagle
5

Kata newkunci mengubah konteks di mana fungsi sedang dijalankan dan mengembalikan pointer ke konteks itu.

Ketika Anda tidak menggunakan newkata kunci, konteks di mana fungsi Vehicle()berjalan adalah konteks yang sama dari mana Anda memanggil Vehiclefungsi. Kata thiskunci akan merujuk pada konteks yang sama. Saat Anda menggunakan new Vehicle(), konteks baru dibuat sehingga kata kunci thisdi dalam fungsi merujuk ke konteks baru. Apa yang Anda dapatkan sebagai balasan adalah konteks yang baru dibuat.

Juzer Ali
sumber
Itu jawaban yang sangat mendalam dalam hal ruang lingkup. Gr8 selain jawabannya.
appu
3

Kata newkunci untuk membuat instance objek baru. Dan ya, javascript adalah bahasa pemrograman dinamis, yang mendukung paradigma pemrograman berorientasi objek. Konvensi tentang penamaan objek adalah, selalu gunakan huruf kapital untuk objek yang seharusnya dipakai oleh kata kunci baru.

obj = new Element();
erenon
sumber
2

Ringkasan:

Kata newkunci digunakan dalam javascript untuk membuat objek dari fungsi konstruktor. Kata newkunci harus ditempatkan sebelum panggilan fungsi konstruktor dan akan melakukan hal-hal berikut:

  1. Menciptakan objek baru
  2. Menetapkan prototipe objek ini ke properti prototipe fungsi konstruktor
  3. Mengikat thiskata kunci ke objek yang baru dibuat dan menjalankan fungsi konstruktor
  4. Mengembalikan objek yang baru dibuat

Contoh:

function Dog (age) {
  this.age = age;
}

const doggie = new Dog(12);

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

Apa yang sebenarnya terjadi:

  1. const doggie mengatakan: Kita membutuhkan memori untuk mendeklarasikan variabel.
  2. Operator tugas =mengatakan: Kami akan menginisialisasi variabel ini dengan ekspresi setelah=
  3. Ungkapannya adalah new Dog(12). Mesin JS melihat kata kunci baru, membuat objek baru dan menetapkan prototipe ke Dog.prototype
  4. Fungsi konstruktor dijalankan dengan thisnilai yang ditetapkan ke objek baru. Pada langkah ini adalah tempat usia ditetapkan untuk objek doggie yang baru dibuat.
  5. Objek yang baru dibuat dikembalikan dan ditugaskan ke doggie variabel.
Willem van der Veen
sumber
1

Kata newkunci membuat instance objek yang menggunakan fungsi sebagai konstruktor. Contohnya:

var Foo = function() {};
Foo.prototype.bar = 'bar';

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

Contoh mewarisi dari prototypefungsi konstruktor. Jadi diberikan contoh di atas ...

foo.bar; // 'bar'
kelopak mata
sumber
2
Kata kunci baru pada dasarnya mengaitkan fungsi sebagai konstruktor; Anda tidak perlu mengembalikan apa pun. Anda bisa melakukannya: function foo (x) {this.bar = x; } var obj = new foo (10); alert (obj.bar);
reko_t
Anda tidak perlu mengembalikan objek dari fungsi konstruktor kecuali jika Anda ingin, untuk suatu tujuan. Misalnya, jika Anda harus mengembalikan instance objek tertentu alih-alih membuat objek baru setiap kali (untuk alasan apa pun). Namun, dalam contoh Anda, itu sama sekali tidak perlu.
Chetan Sastry
Ya, itu contohnya. Anda dapat mengembalikan objek. Ada banyak pola yang digunakan dalam skenario ini, saya berikan satu sebagai "misalnya", maka kata-kata saya "misalnya".
kelopak mata
1

Yah JavaScript per si bisa sangat berbeda dari platform ke platform karena selalu merupakan implementasi dari spesifikasi asli EcmaScript.

Bagaimanapun, terlepas dari implementasi, semua implementasi JavaScript yang mengikuti spesifikasi EcmaScript benar, akan memberi Anda Bahasa Berorientasi Objek. Menurut standar ES:

ECMAScript adalah bahasa pemrograman berorientasi objek untuk melakukan komputasi dan memanipulasi objek komputasi dalam lingkungan host.

Jadi sekarang kita telah sepakat bahwa JavaScript adalah implementasi dari EcmaScript dan oleh karena itu bahasa berorientasi objek. Definisi newoperasi dalam bahasa berorientasi objek apa pun, mengatakan bahwa kata kunci tersebut digunakan untuk membuat turunan objek dari kelas jenis tertentu (termasuk jenis anonim, dalam kasus seperti C #).

Di EcmaScript kami tidak menggunakan kelas, karena Anda dapat membaca dari spesifikasi:

ECMAScript tidak menggunakan kelas-kelas seperti yang ada di C ++, Smalltalk, atau Java. Sebaliknya objek dapat dibuat dengan berbagai cara termasuk melalui notasi literal atau melalui konstruktor yang membuat objek dan kemudian mengeksekusi kode yang menginisialisasi semua atau sebagian dari mereka dengan menetapkan nilai awal ke properti mereka. Setiap konstruktor adalah fungsi yang memiliki properti bernama - prototipe ‖ yang digunakan untuk mengimplementasikan warisan berbasis prototipe dan properti bersama. Objek dibuat dengan
menggunakan konstruktor dalam ekspresi baru; misalnya, Date baru (2009,11) membuat objek Date baru. Meminta konstruktor tanpa menggunakan yang baru memiliki konsekuensi yang bergantung pada konstruktor. Misalnya, Date () menghasilkan representasi string dari tanggal dan waktu saat ini daripada objek.

João Pinho
sumber