Từ khóa mới của JavaScript có bị coi là có hại không? [đóng cửa]


568

Trong một câu hỏi khác , một người dùng đã chỉ ra rằng newtừ khóa này rất nguy hiểm khi sử dụng và đề xuất một giải pháp cho việc tạo đối tượng không sử dụng new. Tôi không tin đó là sự thật, chủ yếu là vì tôi đã sử dụng Prototype, Scriptacificent và các thư viện JavaScript tuyệt vời khác và tất cả mọi người trong số họ đã sử dụng newtừ khóa.

Mặc dù vậy, hôm qua tôi đã xem cuộc nói chuyện của Douglas Crockford tại nhà hát YUI và anh ấy đã nói chính xác điều đó, rằng anh ấy đã không sử dụng newtừ khóa nữa trong mã của mình ( Crockford trên JavaScript - Act III: Function the Ultimate - 50:23 phút ).

Có phải là 'xấu' khi sử dụng newtừ khóa? Những lợi thế và bất lợi của việc sử dụng nó là gì?


90
Nó không phải là "xấu" khi sử dụng từ khóa mới. Nhưng nếu bạn quên nó, bạn sẽ gọi hàm tạo đối tượng như một hàm thông thường. Nếu nhà xây dựng của bạn không kiểm tra bối cảnh thực thi của nó thì nó sẽ không nhận thấy rằng 'cái này' trỏ đến các đối tượng khác nhau (thông thường là đối tượng toàn cầu) thay vì đối tượng mới. Do đó, hàm tạo của bạn sẽ thêm các thuộc tính và phương thức vào đối tượng toàn cục (cửa sổ). Nếu bạn luôn kiểm tra rằng 'đây' là một thể hiện của đối tượng của bạn trong hàm đối tượng, thì bạn sẽ không bao giờ gặp phải vấn đề này.
Kristian B

5
Tôi không hiểu điều này. Một mặt doug không khuyến khích sử dụng new. Nhưng khi bạn nhìn vào thư viện YUI. Bạn phải sử dụng newở mọi nơi. Chẳng hạn như var myDataSource = new Y.DataSource.IO({source:"./myScript.php"}); .
aditya_gaur

2
@aditya_gaur Đó là bởi vì nếu bạn cần khởi tạo một đối tượng, bạn sẽ phải hack một initphương thức nếu bạn đang sử dụng Object.createphương pháp này và gọi nó sau. Dễ dàng hơn nhiều khi chỉ sử dụng newcả hai, thiết lập chuỗi nguyên mẫu và gọi một số mã khởi tạo.
Juan Mendes

67
Tôi không nghĩ rằng điều này nên được đóng lại. Vâng, nó có khả năng truyền cảm hứng cho một số nọc độc chống fan của Crockford nhưng chúng tôi đang nói về lời khuyên phổ biến để cơ bản tránh một tính năng ngôn ngữ chính ở đây. Cơ chế làm cho mọi đối tượng JQuery nặng gần như không có gì (liên quan đến bộ nhớ) liên quan đến việc sử dụng từ khóa 'mới'. Ưu tiên các phương thức xuất xưởng để liên tục gọi mới, nhưng không làm giảm đáng kể các tùy chọn kiến ​​trúc và tiềm năng hiệu suất của bạn bằng cách chỉ sử dụng các đối tượng bằng chữ. Họ có vị trí của họ và nhà xây dựng có vị trí của họ. Đó là lời khuyên thực tế và tệ hại.
Erik Reppen

2
TLDR: Sử dụng newkhông nguy hiểm. Bỏ qua newlà nguy hiểm, và do đó xấu . Nhưng trong ES5, bạn có thể sử dụng Chế độ nghiêm ngặt , bảo vệ bạn khỏi mối nguy hiểm đó và nhiều người khác.
jkdev

Câu trả lời:


603

Crockford đã làm rất nhiều để phổ biến các kỹ thuật JavaScript tốt. Lập trường đầy quan điểm của ông về các yếu tố chính của ngôn ngữ đã gây ra nhiều cuộc thảo luận hữu ích. Điều đó nói rằng, có quá nhiều người coi mỗi lời tuyên bố "xấu" hoặc "có hại" là phúc âm, từ chối nhìn xa hơn ý kiến ​​của một người. Nó có thể là một chút bực bội đôi khi.

Việc sử dụng chức năng được cung cấp bởi newtừ khóa có một số lợi thế so với việc xây dựng từng đối tượng từ đầu:

  1. Di truyền nguyên mẫu . Mặc dù thường được xem xét với sự pha trộn giữa sự nghi ngờ và dè bỉu của những người quen với các ngôn ngữ OO dựa trên lớp, kỹ thuật kế thừa tự nhiên của JavaScript là một phương tiện sử dụng lại mã đơn giản và hiệu quả đáng ngạc nhiên. Và từ khóa mới là phương tiện chính (và chỉ có sẵn trên nhiều nền tảng) có nghĩa là sử dụng nó.
  2. Hiệu suất. Đây là tác dụng phụ của # 1: nếu tôi muốn thêm 10 phương thức cho mọi đối tượng tôi tạo, tôi có thể viết một hàm tạo tự gán từng phương thức cho từng đối tượng mới ... Hoặc, tôi có thể gán chúng cho chức năng tạo prototypevà sử dụng newđể dập tắt các đối tượng mới. Điều này không chỉ nhanh hơn (không cần mã cho mỗi và mọi phương thức trên nguyên mẫu), nó còn tránh việc tạo bóng cho từng đối tượng với các thuộc tính riêng cho từng phương thức. Trên các máy chậm hơn (hoặc đặc biệt là trình thông dịch JS chậm hơn) khi nhiều đối tượng đang được tạo, điều này có thể có nghĩa là tiết kiệm đáng kể thời gian và bộ nhớ.

Và vâng, newcó một nhược điểm rất quan trọng, được mô tả bằng các câu trả lời khác: nếu bạn quên sử dụng nó, mã của bạn sẽ bị hỏng mà không có cảnh báo. May mắn thay, nhược điểm đó dễ dàng được giảm thiểu - chỉ cần thêm một chút mã vào chính hàm:

function foo()
{
   // if user accidentally omits the new keyword, this will 
   // silently correct the problem...
   if ( !(this instanceof foo) )
      return new foo();

   // constructor logic follows...
}

Bây giờ bạn có thể có những lợi thế của newmà không phải lo lắng về các vấn đề gây ra do vô tình sử dụng sai. Bạn thậm chí có thể thêm một xác nhận vào kiểm tra nếu suy nghĩ về mã bị hỏng âm thầm làm phiền bạn. Hoặc, như một số nhận xét, sử dụng kiểm tra để giới thiệu ngoại lệ thời gian chạy:

if ( !(this instanceof arguments.callee) ) 
   throw new Error("Constructor called as a function");

(Lưu ý rằng đoạn mã này có thể tránh mã hóa cứng tên hàm xây dựng, vì không giống như ví dụ trước, nó không cần phải thực sự khởi tạo đối tượng - do đó, nó có thể được sao chép vào từng hàm mục tiêu mà không cần sửa đổi.)

John Resig đi sâu vào chi tiết về kỹ thuật này trong bài đăng Khởi tạo "Lớp" đơn giản của mình , cũng như bao gồm một phương tiện xây dựng hành vi này vào "các lớp" của bạn theo mặc định. Chắc chắn đáng đọc ... như là cuốn sách sắp ra mắt, Bí mật của Ninja JavaScript , mà thấy ẩn vàng trong việc này và nhiều người khác "có hại" đặc trưng của ngôn ngữ JavaScript (các chương trên withlà đặc biệt soi sáng cho chúng ta những người ban đầu bị sa thải tính năng không phù hợp này như một mánh lới quảng cáo).


96
if (! (this instanceof argument.callee)) throw Error ("Trình xây dựng được gọi là hàm"); // Chung hơn, không yêu cầu kiến ​​thức về tên của hàm tạo, làm cho người dùng sửa mã.
một số

5
Nếu bạn lo lắng về hiệu suất - và thực sự có lý do - thì đừng kiểm tra gì cả. Hãy nhớ sử dụng new, hoặc sử dụng chức năng bao bọc để ghi nhớ nó cho bạn. Tôi nghi ngờ rằng nếu bạn đang ở thời điểm quan trọng, bạn đã cạn kiệt các tối ưu hóa khác như ghi nhớ, do đó, các cuộc gọi sáng tạo của bạn sẽ được bản địa hóa bằng mọi cách ...
Shog9

65
Sử dụng arguments.calleeđể kiểm tra xem bạn đã được gọi với newcó thể không tốt lắm, vì arguments.calleekhông có sẵn trong chế độ nghiêm ngặt. Tốt hơn để sử dụng tên chức năng.
Sean McMillan

69
Nếu tôi viết sai một mã định danh, mã của tôi sẽ bị hỏng. Tôi không nên sử dụng định danh? Không sử dụng newbởi vì bạn có thể quên viết nó trong mã của bạn cũng giống như ví dụ của tôi.
Thomas Eding

16
Nếu bạn bật chế độ nghiêm ngặt, nếu bạn quên sử dụng mới, bạn sẽ gặp một ngoại lệ khi bạn cố gắng sử dụng chế độ này: yuiblog.com/blog/2010/12/14/strict-mode-is-asing-to-town Đó là dễ dàng hơn việc thêm một thể hiện kiểm tra vào mỗi hàm tạo.
stephenbez

182

Tôi vừa đọc một số phần của cuốn sách Crockfords "Javascript: The Good Parts". Tôi có cảm giác rằng anh ta coi mọi thứ đã từng cắn mình là có hại:

Giới thiệu về chuyển đổi rơi qua:

Tôi không bao giờ cho phép các trường hợp chuyển đổi rơi vào trường hợp tiếp theo. Tôi đã từng tìm thấy một lỗi trong mã của tôi gây ra bởi một sự cố ngoài ý muốn ngay lập tức sau khi đã có một bài phát biểu mạnh mẽ về lý do tại sao rơi qua đôi khi lại hữu ích. (trang 97, ISBN 976-0-596-51774-8)

Giới thiệu về ++ và -

Các toán tử ++ (tăng) và - (giảm) đã được biết là đóng góp cho mã xấu bằng cách khuyến khích sự khéo léo xuất thần. Chúng chỉ đứng thứ hai sau kiến ​​trúc bị lỗi trong việc kích hoạt virus và các mối đe dọa bảo mật khác. (trang 122)

Về mới:

Nếu bạn quên bao gồm tiền tố mới khi gọi hàm xây dựng, thì điều này sẽ không bị ràng buộc với đối tượng mới. Đáng buồn thay, điều này sẽ bị ràng buộc với đối tượng toàn cầu, vì vậy thay vì tăng đối tượng mới của bạn, bạn sẽ ghi đè các biến toàn cục. Điều đó thực sự tồi tệ. Không có cảnh báo biên dịch, và không có cảnh báo thời gian chạy. (trang 49)

Có nhiều hơn, nhưng tôi hy vọng bạn có được hình ảnh.

Câu trả lời của tôi cho câu hỏi của bạn: Không, nó không có hại. nhưng nếu bạn quên sử dụng nó khi bạn cần, bạn có thể gặp một số vấn đề. Nếu bạn đang phát triển trong một môi trường tốt, bạn nhận thấy điều đó.

Cập nhật

Khoảng một năm sau khi câu trả lời này được viết, phiên bản thứ 5 của ECMAScript đã được phát hành, với sự hỗ trợ cho chế độ nghiêm ngặt . Trong chế độ nghiêm ngặt, thiskhông còn bị ràng buộc với đối tượng toàn cầu mà là undefined.


3
Tôi hoàn toàn đồng ý. Giải pháp: Luôn ghi lại cách người dùng phải khởi tạo đối tượng của bạn. Sử dụng một ví dụ và người dùng có thể sẽ cắt / dán. Có các cấu trúc / tính năng trong mọi ngôn ngữ có thể bị sử dụng sai dẫn đến hành vi bất thường / bất ngờ. Nó không làm cho chúng có hại.
nicerobot

45
Có một quy ước để luôn bắt đầu các hàm tạo với một chữ cái viết hoa và tất cả các chức năng khác với một chữ cái viết thường.
một số

61
Tôi mới nhận ra rằng Crockford không coi WHILE có hại ... Tôi không biết mình đã tạo ra một vòng lặp nguyên bản bao nhiêu lần vì tôi đã quên tăng một biến ...
một số

5
Tương tự với ++, -. Họ diễn đạt chính xác những gì tôi dự định bằng ngôn ngữ rõ ràng nhất có thể. Tôi yêu! chuyển qua ngã có thể rõ ràng với một số người, nhưng tôi mệt mỏi về những điều đó. Tôi sử dụng chúng khi chúng rõ ràng rõ ràng hơn (không sử dụng cách chơi chữ, không thể cưỡng lại).
PEZ

30
Câu trả lời của tôi cho Crockford: Lập trình rất khó, hãy đi mua sắm. Sheesh!
Jason Jackson

91

Javascript là ngôn ngữ động có hàng trăm cách để gây rối trong đó ngôn ngữ khác sẽ ngăn bạn.

Tránh một tính năng ngôn ngữ cơ bản như newtrên cơ sở rằng bạn có thể gây rối cũng giống như tháo giày mới sáng bóng của bạn trước khi đi qua một bãi mìn chỉ trong trường hợp bạn có thể khiến đôi giày của mình bị bùn.

Tôi sử dụng một quy ước trong đó các tên hàm bắt đầu bằng một chữ cái viết thường và 'các hàm' thực sự là các định nghĩa lớp bắt đầu bằng một chữ cái viết hoa. Kết quả là một đầu mối trực quan thực sự khá hấp dẫn rằng 'cú pháp' sai: -

var o = MyClass();  // this is clearly wrong.

Trên đầu thói quen đặt tên tốt này giúp. Sau khi tất cả các hàm làm mọi việc và do đó nên có một động từ trong tên của nó trong khi các lớp đại diện cho các đối tượng và là danh từ và tính từ không có động từ.

var o = chair() // Executing chair is daft.
var o = createChair() // makes sense.

Thật thú vị khi cách tô màu cú pháp của SO đã diễn giải đoạn mã trên.


8
Vâng, tôi chỉ nghĩ giống như vậy về màu cú pháp.
BobbyShaftoe

4
Khi tôi quên gõ từ 'hàm'. Các từ nên được tránh bằng mọi giá. Một lần khác, tôi đã không sử dụng dấu ngoặc nhọn trong câu lệnh if / then đa dòng. Vì vậy, bây giờ tôi không sử dụng dấu ngoặc nhọn và chỉ viết các điều kiện một dòng.
Hal50000

"Điều này rõ ràng là sai" . Tại sao? Đối với các lớp học của tôi (đơn giản là làm if (! this instanceof MyClass) return new MyClass()) tôi thực sự thích newcú pháp -less. Tại sao? Bởi vì các hàm là chung hơn các hàm xây dựng . Rời khỏi newgiúp dễ dàng thay đổi hàm xây dựng thành hàm thông thường ... Hoặc ngược lại. Thêm vào newlàm cho mã cụ thể hơn nó cần phải được. Và do đó kém linh hoạt. So sánh với Java nơi khuyến nghị chấp nhận Listthay vì ArrayListvậy người gọi có thể chọn triển khai.
Stijn de Witt

41

Tôi là người mới sử dụng Javascript nên có lẽ tôi không quá kinh nghiệm trong việc cung cấp một quan điểm tốt cho vấn đề này. Tuy nhiên, tôi muốn chia sẻ quan điểm của tôi về điều "mới" này.

Tôi đến từ thế giới C #, nơi sử dụng từ khóa "mới" rất tự nhiên đến nỗi nó là mẫu thiết kế nhà máy trông rất kỳ lạ đối với tôi.

Khi tôi lần đầu tiên viết mã bằng Javascript, tôi không nhận ra rằng có từ khóa và mã "mới" giống như từ khóa trong mẫu YUI và tôi không mất nhiều thời gian để gặp thảm họa. Tôi mất dấu vết về những gì một dòng cụ thể được cho là sẽ làm khi nhìn lại mã tôi đã viết. Hỗn loạn hơn là tâm trí tôi không thể thực sự chuyển giữa các ranh giới đối tượng khi tôi "chạy khô" mã.

Sau đó, tôi tìm thấy từ khóa "mới" mà theo tôi, nó "tách biệt" mọi thứ. Với từ khóa mới, nó tạo ra mọi thứ. Nếu không có từ khóa mới, tôi biết tôi sẽ không nhầm lẫn nó với việc tạo ra mọi thứ trừ khi chức năng tôi đang gọi cho tôi manh mối mạnh mẽ về điều đó.

Chẳng hạn, với var bar=foo();tôi không có manh mối nào là thanh có thể là gì .... Nó có phải là giá trị trả về hay nó là một đối tượng mới được tạo? Nhưng với var bar = new foo();tôi biết chắc chắn thanh là một đối tượng.


3
Đồng ý và tôi tin rằng mô hình nhà máy nên tuân theo quy ước đặt tên như makeFoo ()
pluckyglen

3
+1 - sự hiện diện của 'mới' đưa ra tuyên bố rõ ràng hơn về ý định.
belugabob

4
Phản hồi này khá kỳ lạ, khi hầu hết mọi thứ trong JS là một đối tượng. Tại sao bạn cần biết chắc chắn rằng một chức năng là một đối tượng khi tất cả các chức năng là các đối tượng?
Joshua Ramirez

@JoshuaRamirez Vấn đề không phải là như vậy typeof new foo() == "object". Nó newtrả về một thể hiện foovà bạn biết bạn có thể gọi foo.bar()foo.bang(). Tuy nhiên, điều đó có thể dễ dàng được giảm nhẹ bằng cách sử dụng @return của JsDoc Không phải tôi đang ủng hộ mã thủ tục (tránh từ này new)
Juan Mendes

1
@JuanMendes Hmm ... Bài đăng của bạn cho tôi thấy rằng bạn thích mới vì từ khóa rõ ràng trong cơ sở mã của bạn. Tôi có thể đào nó. Đó là lý do tại sao tôi sử dụng mô hình mô-đun. Tôi sẽ có một hàm gọi là createdFoo hoặc NewFoo hoặc MakeFoo, không quan trọng miễn là nó rõ ràng. Bên trong đó tôi khai báo các biến được sử dụng như các bao đóng bên trong một đối tượng bằng chữ được trả về từ hàm. Đối tượng đó thực sự là đối tượng của bạn và chức năng chỉ là một chức năng xây dựng.
Joshua Ramirez

39

Một trường hợp khác cho cái mới là cái mà tôi gọi là Pooh Coding . Winnie the Pooh theo bụng của mình. Tôi nói đi với ngôn ngữ bạn đang sử dụng, không chống lại nó.

Rất có thể là những người duy trì ngôn ngữ sẽ tối ưu hóa ngôn ngữ cho các thành ngữ mà họ cố gắng khuyến khích. Nếu họ đặt một từ khóa mới vào ngôn ngữ, có lẽ họ nghĩ nó có ý nghĩa rõ ràng khi tạo một thể hiện mới.

Mã được viết theo ý định của ngôn ngữ sẽ tăng hiệu quả với mỗi lần phát hành. Và mã tránh các cấu trúc chính của ngôn ngữ sẽ bị ảnh hưởng theo thời gian.

EDIT: Và điều này vượt xa hiệu suất. Tôi không thể đếm số lần tôi đã nghe (hoặc nói) "tại sao họ lại làm thế ?" khi tìm mã tìm lạ. Nó thường chỉ ra rằng tại thời điểm mã được viết, có một số lý do "tốt" cho nó. Theo Tao của ngôn ngữ là bảo hiểm tốt nhất của bạn vì không có mã của bạn bị chế giễu vài năm kể từ bây giờ.


3
+1 cho liên kết Mã hóa Pooh - bây giờ tôi cần tìm lý do để thực hiện điều này trong các cuộc trò chuyện của mình ...
Shog9

CƯỜI LỚN! Tôi có nhiều năm kinh nghiệm trong lĩnh vực cụ thể đó và tôi có thể đảm bảo với bạn rằng bạn sẽ không gặp khó khăn gì. Họ thường gọi tôi là Pooh tại robowiki.net. =)
PEZ

1
Không biết bạn có bao giờ thấy nhận xét này không, nhưng liên kết Mã hóa Pooh đã chết.
Calvin

Cảm ơn cho những người đứng đầu lên. Chúng tôi đã di chuyển robowiki.net sang wiki mới vào tuần trước và nội dung cũ hiện không thể truy cập được. Tôi sẽ nhanh chóng làm cho các liên kết cũ hoạt động.
PEZ

24

Tôi đã viết một bài về cách giảm thiểu vấn đề gọi hàm tạo mà không có từ khóa mới.
Nó chủ yếu là mô phạm, nhưng nó cho thấy cách bạn có thể tạo các hàm tạo hoạt động cùng hoặc không newvà không yêu cầu bạn thêm mã soạn sẵn để kiểm tra thistrong mọi hàm tạo.

http://js-bits.blogspot.com/2010/08/constructor-without-USE-new.html

Đây là ý chính của kỹ thuật:

/**
 * Wraps the passed in constructor so it works with
 * or without the new keyword
 * @param {Function} realCtor The constructor function.
 *    Note that this is going to be wrapped
 *    and should not be used directly 
 */
function ctor(realCtor){
  // This is going to be the actual constructor
  return function wrapperCtor(){
    var obj; // object that will be created
    if (this instanceof wrapperCtor) {
      // Called with new
      obj = this;
    } else {
      // Called without new. Create an empty object of the
      // correct type without running that constructor
      surrogateCtor.prototype = wrapperCtor.prototype;
      obj = new surrogateCtor();
    }
    // Call the real constructor function
    realCtor.apply(obj, arguments);
    return obj;
  }

  function surrogateCtor() {}
}

Đây là cách sử dụng nó:

// Create our point constructor
Point = ctor(function(x,y){
  this.x = x;
  this.y = y;
});

// This is good
var pt = new Point(20,30);
// This is OK also
var pt2 = Point(20,30);

6
tránh arguments.calleelà tuyệt vời!
Gregg Lind

2
surrogateConstructornên surrogateCtor(hoặc ngược lại).
David Conrad

Tôi đã duy trì một bộ sưu tập các tiện ích lấy cảm hứng từ Crockford tương tự, và thấy rằng nó luôn khiến tôi phải chạy đi tìm các triển khai khi tôi bắt đầu một dự án mới. Và điều này kết thúc một phần cơ bản của lập trình: khởi tạo đối tượng. Tất cả công việc này, chỉ để kích hoạt mã khởi tạo haphazard? Thành thật, tôi đánh giá cao sự khéo léo của trình bao bọc này, nhưng nó dường như chỉ gây ra sự mã hóa bất cẩn.
Joe Coder

1
@joecoder Tôi đã đề cập đến điều này chỉ nhằm mục đích mô phạm, tôi không đăng ký kiểu mã hóa hoang tưởng này. Tuy nhiên, nếu tôi đang viết một thư viện lớp, thì có, tôi sẽ thêm tính năng này theo cách minh bạch cho người gọi
Juan Mendes

22

Lý do đằng sau không sử dụng từ khóa mới, rất đơn giản:

Bằng cách không sử dụng nó, bạn tránh được cạm bẫy khi vô tình bỏ qua nó. Mẫu xây dựng mà YUI sử dụng, là một ví dụ về cách bạn có thể tránh hoàn toàn từ khóa mới "

var foo = function () {
    var pub= { };
    return pub;
}
var bar = foo();

Ngoài ra, bạn có thể như vậy:

function foo() { }
var bar = new foo();

Nhưng bằng cách đó, bạn sẽ gặp rủi ro khi ai đó quên sử dụng từ khóa mớiđiều này toán tử toàn là fubar. AFAIK không có lợi thế để làm điều này (ngoài việc bạn đã quen với nó).

Vào cuối ngày: Đó là về phòng thủ. Bạn có thể sử dụng tuyên bố mới? Đúng. Nó làm cho mã của bạn nguy hiểm hơn? Đúng.

Nếu bạn đã từng viết C ++, thì giống như đặt con trỏ thành NULL sau khi bạn xóa chúng.


6
Không. Với "new foo ()", một số thuộc tính được đặt trên đối tượng được trả về, như hàm tạo.
một số

34
Vì vậy, chỉ cần làm rõ điều này: bạn không nên sử dụng Mới mới vì bạn có thể quên nó? Bạn đang dùa?
Bombe

16
@Bombe: trong các ngôn ngữ khác mà quên "mới" sẽ dẫn đến lỗi. Trong Javascript, nó chỉ tiếp tục vận chuyển. Bạn có thể quên, và không bao giờ nhận ra. Và chỉ đơn giản là nhìn vào mã lỗi sẽ không rõ ràng ở tất cả những gì đang xảy ra.
Kent Fredric

3
@Greg: Tôi không thấy kỹ thuật đầu tiên cho phép sử dụng chuỗi nguyên mẫu - nghĩa đen của đối tượng là tuyệt vời, nhưng việc loại bỏ hiệu suất và các lợi thế khác được cung cấp bởi các nguyên mẫu có vẻ hơi ngớ ngẩn.
Shog9

5
@Bombe - Bạn nên sử dụng "mới" bởi vì bạn (và bất kỳ ai sử dụng mã của bạn) sẽ không bao giờ mắc lỗi? Bạn đang dùa?
Greg Dean

20

Tôi nghĩ "mới" thêm sự rõ ràng vào mã. Và sự rõ ràng là giá trị mọi thứ. Thật tốt khi biết có những cạm bẫy, nhưng tránh chúng bằng cách tránh sự rõ ràng dường như không phải là cách dành cho tôi.


16

Trường hợp 1: newkhông bắt buộc và nên tránh

var str = new String('asd');  // type: object
var str = String('asd');      // type: string

var num = new Number(12);     // type: object
var num = Number(12);         // type: number

Trường hợp 2: newlà bắt buộc, nếu không bạn sẽ gặp lỗi

new Date().getFullYear();     // correct, returns the current year, i.e. 2010
Date().getFullYear();         // invalid, returns an error

5
Cần lưu ý rằng trong trường hợp 1, gọi hàm tạo là hàm chỉ có ý nghĩa nếu bạn đang có ý định chuyển đổi loại (và không biết liệu đối tượng thân tàu có phương thức chuyển đổi không, chẳng hạn như toString()). Trong tất cả các trường hợp khác, sử dụng chữ. Không String('asd') , nhưng đơn giản 'asd', và không Number(12) , nhưng đơn giản 12.
PointedEars

1
@PointedEars Một ngoại lệ cho quy tắc này sẽ là Array: Array(5).fill(0) rõ ràng dễ đọc hơn [undefined, undefined, undefined, undefined, undefined].fill(0)(var arr = []).length = 5; arr.fill(0);
yyny

@YoYoYonnY Câu lệnh của tôi được đưa ra trong ngữ cảnh của trường hợp 1 của câu trả lời, trong đó đề cập đến việc sử dụng newvới các hàm tạo cho các loại đối tượng tương ứng với các kiểu nguyên thủy . Nghĩa đen mà bạn đang đề xuất không tương đương với Arraycuộc gọi (thử 0 in …) và phần còn lại là lỗi cú pháp :) Nếu không, bạn đã đúng, mặc dù cũng cần lưu ý rằng bạn Array(5)có thể mơ hồ nếu bạn xem xét việc triển khai không tuân thủ (và do đó một mô phỏng Array.prototype.fill(…) ).
PointedEars

12

Dưới đây là tóm tắt ngắn gọn nhất mà tôi có thể đưa ra về hai lý lẽ mạnh nhất cho và không sử dụng new toán tử:

Lập luận phải đối new

  1. Các hàm được thiết kế để được khởi tạo như các đối tượng sử dụng newtoán tử có thể có các hiệu ứng tai hại nếu chúng được gọi không chính xác như các hàm bình thường. Mã của một hàm trong trường hợp như vậy sẽ được thực thi trong phạm vi mà hàm được gọi, thay vì trong phạm vi của một đối tượng cục bộ như dự định. Điều này có thể khiến các biến và thuộc tính toàn cầu bị ghi đè lên với hậu quả tai hại.
  2. Cuối cùng, viết function Func(), sau đó gọi Func.prototype và thêm nội dung vào nó để bạn có thể gọi new Func()để xây dựng đối tượng của mình có vẻ xấu đối với một số lập trình viên, những người muốn sử dụng một kiểu kế thừa đối tượng khác vì lý do kiến ​​trúc và phong cách.

Để biết thêm về lập luận này, hãy xem cuốn sách tuyệt vời và ngắn gọn của Douglas Crockford Javascript: The Good Parts. Trong thực tế, kiểm tra xem nó ra.

Lập luận ủng hộ new

  1. Sử dụng newtoán tử cùng với gán nguyên mẫu là nhanh chóng.
  2. Những thứ về việc vô tình chạy mã của hàm xây dựng trong không gian tên toàn cục có thể dễ dàng bị ngăn chặn nếu bạn luôn bao gồm một chút mã trong các hàm tạo của bạn để kiểm tra xem chúng có được gọi chính xác không, và trong trường hợp chúng không được gọi , xử lý cuộc gọi phù hợp như mong muốn.

Xem bài đăng của John Resig để biết giải thích đơn giản về kỹ thuật này và để được giải thích sâu hơn về mô hình thừa kế mà ông ủng hộ.


Một bản cập nhật về các đối số chống lại: # 1 có thể được giảm thiểu bằng cách sử dụng 'use strict';chế độ (hoặc phương thức trong liên kết của bạn) # 2 Có đường đồng bộ trong ES6 , tuy nhiên hành vi vẫn giống như trước.
ninMonkey

9

Tôi đồng ý với pez và một số ở đây.

Đối với tôi có vẻ rõ ràng rằng "mới" là việc tạo đối tượng tự mô tả, trong đó mẫu YUI mà Greg Dean mô tả là hoàn toàn bị che khuất .

Khả năng ai đó có thể viết var bar = foo;hoặc var bar = baz();nơi baz không phải là một đối tượng tạo ra phương pháp dường như xa nguy hiểm hơn.


8

Tôi nghĩ rằng mới là xấu xa, không phải vì nếu bạn quên sử dụng nó do nhầm lẫn, nó có thể gây ra vấn đề mà vì nó làm hỏng chuỗi thừa kế, khiến ngôn ngữ khó hiểu hơn.

JavaScript là hướng đối tượng dựa trên nguyên mẫu. Do đó mọi đối tượng PHẢI được tạo từ một đối tượng khác như vậy var newObj=Object.create(oldObj). Ở đây oldObj được gọi là nguyên mẫu của newObj (do đó "dựa trên nguyên mẫu"). Điều này ngụ ý rằng nếu một tài sản không được tìm thấy trong newObj thì nó sẽ được tìm kiếm trong oldObj . Do đó, newObj theo mặc định sẽ là một đối tượng trống nhưng do chuỗi nguyên mẫu của nó, nó dường như có tất cả các giá trị của oldObj .

Mặt khác, nếu bạn làm thế var newObj=new oldObj(), nguyên mẫu của newObjoldObj.prototype , rất khó hiểu một cách không cần thiết.

Bí quyết là sử dụng

Object.create=function(proto){
  var F = function(){};
  F.prototype = proto;
  var instance = new F();
  return instance;
};

Chính bên trong chức năng này và chỉ ở đây mới được sử dụng. Sau này chỉ cần sử dụng phương thức Object.create () . Phương pháp giải quyết vấn đề nguyên mẫu.


7
Thành thật mà nói, tôi không dại gì về kỹ thuật này - nó không thêm bất cứ điều gì mới, và như câu trả lời của bạn minh họa, nó thậm chí có thể trở thành một cái nạng. IMHO, hiểu chuỗi nguyên mẫu và khởi tạo đối tượng là rất quan trọng để hiểu JavaScript ... Nhưng nếu việc này khiến bạn không thoải mái khi sử dụng chúng trực tiếp, thì sử dụng chức năng trợ giúp để chăm sóc một số chi tiết là tốt, miễn là bạn nhớ những gì bạn đang làm. FWIW: một biến thể (có phần hữu ích hơn) về điều này là một phần của phiên bản thứ 5 của ECMAScript. std, và đã có sẵn trong một số trình duyệt - vì vậy bạn nên cẩn thận không xác định lại một cách mù quáng!
Shog9

BTW: Tôi không chắc tại sao bạn tạo CW này, nhưng nếu bạn muốn đăng lại nó với các chỉnh sửa định dạng tôi đã thực hiện, hãy cẩn thận để tránh hộp kiểm đó ...
Shog9

2
Khó hiểu không cần thiết? var c = new Car()cũng giống như làmvar c = Object.create(Car.prototype); Car.call(c)
Juan Mendes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.