Câu hỏi Từ khóa 'mới' trong JavaScript là gì?


Các new từ khóa trong JavaScript có thể khá khó hiểu khi nó lần đầu tiên gặp phải, vì mọi người có xu hướng nghĩ rằng JavaScript không phải là ngôn ngữ lập trình hướng đối tượng.

  • Nó là gì?
  • Nó giải quyết vấn đề gì?
  • Khi nào nó thích hợp và khi nào thì không?

1559
2017-10-29 21:32


gốc


Ngoài ra, chủ đề liên quan - stackoverflow.com/questions/383402/… - Chetan Sastry


Các câu trả lời:


Nó có 5 điều:

  1. Nó tạo ra một đối tượng mới. Loại đối tượng này chỉ đơn giản là vật.
  2. Nó đặt nội bộ của đối tượng mới này, không thể tiếp cận, [[nguyên mẫu]] (I E. __proto__) thuộc tính là hàm bên ngoài của hàm tạo, có thể truy cập, nguyên mẫu đối tượng (mỗi đối tượng hàm tự động có một nguyên mẫu bất động sản).
  3. Nó làm cho this điểm biến đối tượng mới được tạo.
  4. Nó thực thi hàm constructor, sử dụng đối tượng mới được tạo ra bất cứ khi nào this được đề cập.
  5. Nó trả về đối tượng mới được tạo ra, trừ khi hàm constructor trả về một non-null tham chiếu đối tượng. Trong trường hợp này, tham chiếu đối tượng đó được trả về thay thế.

Chú thích: hàm xây dựng đề cập hàm sau new từ khóa, như trong

new ConstructorFunction(arg1, arg2)

Khi điều này được thực hiện, nếu một thuộc tính không xác định của đối tượng mới được yêu cầu, kịch bản lệnh sẽ kiểm tra đối tượng [[nguyên mẫu]] đối tượng cho thuộc tính thay thế. Đây là cách bạn có thể nhận được một cái gì đó tương tự như thừa kế lớp truyền thống trong JavaScript.

Phần khó nhất về điều này là số điểm 2. Mỗi đối tượng (bao gồm cả các hàm) có thuộc tính bên trong này được gọi là [[nguyên mẫu]]. Nó có thể chỉ có được đặt ở thời gian tạo đối tượng, với Mới, với Object.create, hoặc dựa trên chữ (các hàm mặc định là Function.prototype, các số thành Number.prototype, vv). Nó chỉ có thể được đọc với Object.getPrototypeOf (someObject). Có Không cách khác để đặt hoặc đọc giá trị này.

Chức năng, ngoài các ẩn [[nguyên mẫu]] tài sản, cũng có một tài sản được gọi là nguyên mẫuvà đó là điều này mà bạn có thể truy cập và sửa đổi, để cung cấp các thuộc tính và phương thức được kế thừa cho các đối tượng mà bạn thực hiện.


Đây là một ví dụ:

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'

Nó giống như thừa kế lớp vì bây giờ, bất kỳ đối tượng nào bạn sử dụng new ObjMaker() cũng sẽ xuất hiện để thừa hưởng thuộc tính 'b'.

Nếu bạn muốn một cái gì đó giống như một phân lớp, sau đó bạn làm điều này:

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

Tôi đọc một đống rác về chủ đề này trước khi cuối cùng tìm ra trang này, nơi điều này được giải thích rất tốt với các sơ đồ đẹp.


1961
2017-10-29 22:22



Chỉ muốn thêm: Trên thực tế, có một cách để truy cập [[prototype]] bên trong, bởi __proto__. Tuy nhiên, điều này là không chuẩn và chỉ được hỗ trợ bởi các trình duyệt tương đối mới (và không phải tất cả các trình duyệt này). Có một cách tiêu chuẩn hóa sắp tới, cụ thể là Object.getPrototypeOf (obj), nhưng nó là Ecmascript3.1, và chính nó chỉ được hỗ trợ trên các trình duyệt mới - một lần nữa. Nó thường được khuyến khích để không sử dụng tài sản đó mặc dù, công cụ được phức tạp thực sự nhanh chóng bên trong đó. - Blub
Câu hỏi: Điều gì xảy ra khác nếu ObjMaker được định nghĩa là hàm trả về một giá trị? - Jim Blackler
@LonelyPixel new tồn tại để bạn không phải viết các phương thức nhà máy để xây dựng / sao chép các hàm / đối tượng. Nó có nghĩa là, "Sao chép này, làm cho nó giống như lớp cha mẹ của nó '; làm như vậy một cách hiệu quả và chính xác; và lưu trữ thông tin thừa kế mà chỉ có thể truy cập với tôi, JS, nội bộ". Để làm như vậy, nó sửa đổi nội bộ không thể tiếp cận khác prototype của đối tượng mới để gói gọn các thành viên được thừa kế, bắt chước các chuỗi kế thừa OO cổ điển (không phải là thời gian chạy có thể sửa đổi). Bạn có thể mô phỏng điều này mà không cần new, nhưng thừa kế sẽ có thể thay đổi thời gian chạy. Tốt chứ? Xấu? Tùy thuộc vào bạn. - Arcane Engineer
một điểm nhỏ cần thêm: một cuộc gọi đến một hàm tạo, khi bắt đầu bằng từ khóa mới, tự động trả về đối tượng đã tạo; không cần phải trả lại một cách rõ ràng từ bên trong hàm tạo. - charlie roberts
Có một lưu ý rằng Notice that this pattern is deprecated!. Mẫu up-to-date chính xác để thiết lập nguyên mẫu của một lớp là gì? - Tom Pažourek


Giả sử bạn có hàm này:

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

Nếu bạn gọi đây là một hàm độc lập như sau:

Foo();

Thực thi hàm này sẽ thêm hai thuộc tính vào window vật (A và B). Nó thêm nó vào window bởi vì window là đối tượng được gọi là hàm khi bạn thực thi nó như vậy, và this trong một hàm là đối tượng được gọi là hàm. Trong Javascript ít nhất.

Bây giờ, gọi nó như thế này với new:

var bar = new Foo();

Điều gì sẽ xảy ra khi bạn thêm new để gọi hàm là một đối tượng mới được tạo (chỉ var bar = new Object()) và rằng this bên trong hàm trỏ đến điểm mới Object bạn vừa tạo, thay vì đối tượng gọi là hàm. Vì thế bar bây giờ là một đối tượng với các thuộc tính A và B. Bất kỳ hàm nào cũng có thể là một hàm tạo, nó không phải lúc nào cũng có ý nghĩa.


359
2018-06-20 23:46



Phụ thuộc vào ngữ cảnh thực thi. Trong trường hợp của tôi (Qt scripting) nó chỉ là một đối tượng toàn cục. - Maxym
điều này sẽ gây ra việc sử dụng bộ nhớ nhiều hơn? - Jürgen Paul
vì cửa sổ là đối tượng gọi là hàm - phải là: vì cửa sổ là đối tượng chứa đựng chức năng. - Dávid Horváth
@Taurus Trong một trình duyệt web, một hàm phi phương thức sẽ là một phương thức window ngầm. Ngay cả trong một đóng cửa, ngay cả khi ẩn danh. Tuy nhiên, trong ví dụ nó là một lời gọi phương thức đơn giản trên cửa sổ: Foo(); => [default context].Foo(); => window.Foo();. Trong biểu thức này window là bối cảnh (không chỉ người gọi, không quan trọng). - Dávid Horváth
@Taurus Basicly yes. Tuy nhiên trong ECMA 6 và 7 điều phức tạp hơn (xem lambdas, lớp học, vv). - Dávid Horváth


Ngoài câu trả lời của Daniel Howard, đây là những gì new (hoặc ít nhất dường như làm):

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

Trong khi

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

tương đương với

var obj = new A(1, 2);

146
2018-05-27 09:23



Tôi thấy rằng javascript dễ hiểu hơn tiếng anh: v - damphat
Câu trả lời tuyệt vời. Tôi có một câu hỏi nhỏ: Làm sao có thể func.prototype được null? Bạn có thể giải thích một chút về điều đó không? - Tom Pažourek
@tomp bạn có thể ghi đè thuộc tính nguyên mẫu, bằng cách viết đơn giản A.prototype = null; Trong trường hợp đó new A() sẽ dẫn đến đối tượng, đó là nguyên mẫu nội bộ trỏ đến Object vật: jsfiddle.net/Mk42Z - basilikum
Việc kiểm tra typeof có thể sai vì một đối tượng host có thể tạo ra một thứ khác với "đối tượng" hoặc "hàm". Để kiểm tra nếu một cái gì đó là một đối tượng, tôi thích Object(ret) === ret. - Oriol
@ Oriol cảm ơn bạn đã bình luận. Đó là sự thật những gì bạn nói và bất kỳ thử nghiệm thực tế nên được thực hiện theo cách mạnh mẽ hơn. Tuy nhiên, tôi nghĩ về câu trả lời khái niệm này, typeof kiểm tra chỉ giúp dễ hiểu hơn những gì đang diễn ra đằng sau hậu trường. - basilikum


Để người mới bắt đầu hiểu nó tốt hơn

hãy thử mã sau trong bảng điều khiển trình duyệt.

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

Bây giờ bạn có thể đọc câu trả lời của cộng đồng wiki :)


85
2017-10-29 21:34



Câu trả lời tốt. Ngoài ra - bỏ đi return this; mang lại cùng một đầu ra. - Nelu
Sẽ được tốt đẹp để chỉ ra lý do tại sao điều này đang xảy ra. - Florian Leitgeb


vì vậy có thể không phải để tạo   trường hợp của đối tượng

Nó được sử dụng chính xác cho điều đó. Bạn định nghĩa một hàm tạo như sau:

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

var john = new Person('John');

Tuy nhiên, lợi ích bổ sung mà ECMAScript có là bạn có thể mở rộng với .prototype tài sản, vì vậy chúng tôi có thể làm điều gì đó như ...

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

Tất cả các đối tượng được tạo từ constructor này sẽ có một getName vì chuỗi nguyên mẫu mà họ có quyền truy cập.


33
2017-10-29 21:36



các hàm tạo hàm được sử dụng như các lớp, không có class nhưng bạn có thể làm điều tương tự. - meder omuraliev
Có kindof là một lớp từ khóa - lớp được dành riêng cho sử dụng trong tương lai - Greg
Ngẫu nhiên đó là lý do tại sao bạn sử dụng .className không .class để thiết lập một lớp CSS - Greg
Nó nên được viết hoa Người theo quy ước. - eomeroff


JavaScript  một ngôn ngữ lập trình hướng đối tượng và nó được sử dụng chính xác để tạo các cá thể. Nó dựa trên nguyên mẫu, chứ không phải dựa trên lớp, nhưng điều đó không có nghĩa là nó không hướng đối tượng.


26
2017-10-29 21:37



Tôi thích nói rằng JavaScript dường như có nhiều hướng đối tượng hơn tất cả các ngôn ngữ dựa trên lớp đó. Trong JavaScript mọi thứ bạn viết ngay lập tức sẽ trở thành một đối tượng, nhưng trong các ngôn ngữ dựa trên lớp, trước tiên bạn viết khai báo và chỉ sau này bạn mới tạo ra các cá thể (các đối tượng) cụ thể của các lớp. Và nguyên mẫu JavaScript dường như mơ hồ nhắc nhở tất cả những thứ VTABLE cho các ngôn ngữ dựa trên lớp. - JustAMartin


Javascript là một ngôn ngữ lập trình động hỗ trợ mô hình lập trình hướng đối tượng, và nó được sử dụng để tạo ra các cá thể đối tượng mới.

Các lớp không cần thiết cho các đối tượng - Javascript là một dựa trên nguyên mẫu ngôn ngữ.


13
2018-05-16 07:21





đôi khi mã dễ hơn các từ:

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

đối với tôi, miễn là tôi không phải là nguyên mẫu, tôi sử dụng kiểu func2 vì nó mang lại cho tôi sự linh hoạt hơn một chút bên trong và bên ngoài hàm.


3
2017-10-29 21:38



B1 = new func2(2);  <- Tại sao điều này sẽ không có B1.y  ? - sunny_dev
@sunny_dev Tôi không phải là chuyên gia JS, nhưng có lẽ vì func2trả về trực tiếp một giá trị (đối tượng z), thay vì làm việc / trả về với các giá trị bên trong (this) - Eagle