คุณสมบัติที่ซ่อนของ JavaScript? [ปิด]


312

"คุณสมบัติที่ซ่อนอยู่" ของ JavaScript อะไรที่คุณคิดว่าโปรแกรมเมอร์ทุกคนควรรู้

หลังจากที่ได้เห็นคุณภาพที่ยอดเยี่ยมของคำตอบสำหรับคำถามต่อไปนี้ฉันคิดว่ามันถึงเวลาถาม JavaScript แล้ว

แม้ว่า JavaScript จะเป็นภาษาฝั่งไคลเอ็นต์ที่สำคัญที่สุดในตอนนี้ (เพียงแค่ถาม Google) มันน่าประหลาดใจที่นักพัฒนาเว็บส่วนใหญ่น้อยชื่นชมความแรงของมัน


1
คุณไม่ได้หมายความว่า "เมื่อได้เห็นคะแนนตัวแทนและดูคำถามอื่น ๆ ที่ดึงดูดฉันคิดว่าฉันจะถามคำถามเดียวกันเพื่อกระตุ้นตัวเอง" ;-)
Bobby Jack

1
แน่นอนว่าคนมองโลกในแง่ร้าย :) ฉันถือว่าคำถามนี้เป็นปัญหาชุมชน นอกจากนี้หลังจากที่คุณได้รับคะแนนจำนวนหนึ่งแล้วทุกอย่างก็จะได้รับผลตอบแทนที่ลดลง
Allain Lalonde

1
ยุติธรรมพอ - ไม่เหมือนว่าคุณต้องการตัวแทน! ฉันเดาว่าฉันเพิ่งมีปัญหาใหญ่กับ C # หนึ่ง - ดูเหมือนว่าฉันจะไม่ชอบประเภทของคำถามที่เว็บไซต์นี้ตั้งใจ
บ๊อบบี้แจ็ค

3
ใช่อาจไม่ใช่ แต่ฉันพบความรู้ในคำตอบที่ยอดเยี่ยม ฉันคิดว่าคุณจะกดยากที่จะเปิดเผยโปรแกรมเมอร์ C # เฉลี่ยทั้งหมดในที่เดียวถ้าไม่ใช่เพื่อ SO มันต้องใช้เวลาหลายปีในการเล่นกับมันเพื่อหารายชื่อที่ชนะยากเหมือนกัน
Allain Lalonde

7
ฉันเขียน JavaScript อย่างมืออาชีพเป็นเวลา 10 ปีแล้วและฉันได้เรียนรู้สิ่งหนึ่งหรือสามอย่างจากหัวข้อนี้ ขอบคุณอลัน!
Andrew Hedges

คำตอบ:


373

คุณไม่จำเป็นต้องกำหนดพารามิเตอร์ใด ๆ สำหรับฟังก์ชั่น คุณสามารถใช้argumentsวัตถุคล้ายอาร์เรย์ของฟังก์ชัน

function sum() {
    var retval = 0;
    for (var i = 0, len = arguments.length; i < len; ++i) {
        retval += arguments[i];
    }
    return retval;
}

sum(1, 2, 3) // returns 6

117
น่าสังเกตว่าถึงแม้ว่าอาร์กิวเมนต์จะทำหน้าที่เหมือนอาร์เรย์ แต่มันไม่ใช่ javascript Array ที่แท้จริง แต่เป็นเพียงวัตถุเท่านั้น ดังนั้นคุณไม่สามารถเข้าร่วม (), pop (), push (), slice () และอื่น ๆ ได้ (คุณสามารถแปลงเป็นอาเรย์จริงได้หากคุณต้องการ: "var argArray = Array.prototype.slice.call (อาร์กิวเมนต์))")
Jacob Mattison

51
นอกจากนี้ยังเป็นที่น่าสังเกตว่าการเข้าถึงออบเจ็กต์อาร์กิวเมนต์นั้นค่อนข้างแพงตัวอย่างที่ดีที่สุดคือ Safari, Firefox และ Chrome nightlies ซึ่งการอ้างอิงargumentsวัตถุนั้นทำให้การเรียกใช้ฟังก์ชันช้าลงมากเช่น ถ้าอาร์กิวเมนต์ (false); จะทำร้ายความสมบูรณ์แบบ
olliej

48
ในหลอดเลือดดำเดียวกันอาร์กิวเมนต์มีคุณสมบัติ "callee" ซึ่งเป็นฟังก์ชันปัจจุบันเอง นี้ช่วยให้การเรียกซ้ำด้วยฟังก์ชั่นที่ไม่ระบุชื่อ cool!
Vincent Robert

4
@ นาธาน "f (x, y, z)" ดูดีกว่า "f ([x, y, z])"
Mark Cidade

16
@Vincent Robert: โปรดทราบว่าarguments.calleeกำลังเลิกใช้งาน
เคน

204

ฉันสามารถอ้างอิงส่วนใหญ่ของหนังสือยอดเยี่ยมของ Douglas Crockford ได้ JavaScript: The Good Partsส่วนดี

แต่ฉันจะเอาแค่อันเดียวสำหรับคุณใช้เสมอ===และ!==แทน==และ!=

alert('' == '0'); //false
alert(0 == ''); // true
alert(0 =='0'); // true

==ไม่ใช่สกรรมกริยา หากคุณใช้===มันจะให้เท็จสำหรับงบเหล่านี้ทั้งหมดตามที่คาดไว้


29
มันเป็นความอัปยศที่หลายคนคิดว่า Crockford รู้ดี จริงอยู่ที่คนมีสิทธิในการทำเครื่องหมายกับที่สุดของการวิพากษ์วิจารณ์ แต่ฉันหยุดสั้นของการให้สิ่งที่เขารับรองผ้าห่มเหมือน devs จำนวนมากเพื่อทำ ...
เจสันตอม่อ

21
ฉันเตือนของเจสันที่สอง หนังสือในตัวเองที่น่าสนใจมากและจะไม่ให้มากคำแนะนำที่ดี แต่ซีจะห่างไกลความเชื่อมั่นมากเกินไปว่าวิธีการของเขาทำสิ่งที่เป็นวิธีเดียวที่ถูกต้องทุกอย่างอื่นคือ "ข้อบกพร่อง" หากคุณต้องการตัวอย่างให้ดูคำตอบของเขาในกลุ่ม JSLint Yahoo
Zilk

30
ใช้ === แทนที่จะเป็น == เป็นคำแนะนำที่ดีถ้าคุณสับสนโดยการพิมพ์แบบไดนามิกและเพียงต้องการให้มันเป็น "จริงๆ" เท่ากับ พวกเราที่เข้าใจการพิมพ์แบบไดนามิกอาจยังคงใช้ == สำหรับสถานการณ์ที่เรารู้ว่าเราต้องการส่งเช่นใน 0 == '' หรือ 0 == '0'
thomasrutter

20
ดี == และ === ไม่เกี่ยวกับการพิมพ์แบบไดนามิก == ไม่พิมพ์ประเภทที่เป็นสัตว์ร้ายที่แตกต่างกัน ถ้าคุณรู้ว่าคุณต้องการที่จะส่งไปยังสตริง / หมายเลข / ฯลฯ จากนั้นคุณ shold ทำอย่างชัดเจน
Rene Saarsoo

15
ฉันคิดว่าส่วนที่น่ากลัวที่สุด==คือ'\n\t\r ' == 0=> true... : D
Shrikant Sharat

189

ฟังก์ชั่นเป็นพลเมืองชั้นหนึ่งใน JavaScript:

var passFunAndApply = function (fn,x,y,z) { return fn(x,y,z); };

var sum = function(x,y,z) {
  return x+y+z;
};

alert( passFunAndApply(sum,3,4,5) ); // 12

เทคนิคการเขียนโปรแกรมฟังก์ชั่นที่สามารถใช้ในการเขียนจาวาสคริปต์ที่สง่างาม

โดยเฉพาะอย่างยิ่งฟังก์ชั่นสามารถส่งผ่านเป็นพารามิเตอร์เช่นArray.filter ()ยอมรับการเรียกกลับ:

[1, 2, -1].filter(function(element, index, array) { return element > 0 });
// -> [1,2]

คุณสามารถประกาศฟังก์ชั่น "ส่วนตัว" ที่มีอยู่ภายในขอบเขตของฟังก์ชั่นเฉพาะ:

function PrintName() {
    var privateFunction = function() { return "Steve"; };
    return privateFunction();
}

3
มีสามวิธีในการสร้างฟังก์ชั่นใน javascript: function sum (x, y, z) {return (x + y + z); } และ var sum = new Function ("x", "y", "z", "return (x + y + z);"); เป็นวิธีอื่น
Marius

6
แนวคิดของฟังก์ชั่น - ตาม - ข้อมูลชนะคะแนนใหญ่อย่างแน่นอนในหนังสือของฉัน
35432 Jason

ฉันเพิ่งอัปเดตตัวอย่างเพื่อแสดงวิธีใช้ฟังก์ชัน "ส่วนตัว" ที่มีอยู่ภายในขอบเขตของฟังก์ชันเฉพาะเท่านั้น
Chris Pietschmann

new Function()มันชั่วร้ายเหมือนevalกัน ไม่ได้ใช้.
Nicolás

11
ไม่แน่ใจว่านี่เป็นคุณลักษณะที่ซ่อนอยู่ ... เหมือนคุณลักษณะหลัก
Claudiu

162

คุณสามารถใช้ตัวดำเนินการinเพื่อตรวจสอบว่ามีกุญแจอยู่ในวัตถุหรือไม่:

var x = 1;
var y = 3;
var list = {0:0, 1:0, 2:0};
x in list; //true
y in list; //false
1 in list; //true
y in {3:0, 4:0, 5:0}; //true

หากคุณพบว่าตัวอักษรวัตถุน่าเกลียดเกินไปคุณสามารถรวมเข้ากับคำแนะนำฟังก์ชันแบบไม่มีพารามิเตอร์ได้:

function list()
 { var x = {};
   for(var i=0; i < arguments.length; ++i) x[arguments[i]] = 0;
   return x
 }

 5 in list(1,2,3,4,5) //true

22
ไม่ฉลาดนักที่ตรวจสอบว่ามีคีย์อยู่หรือไม่และถ้ามีค่า x ในรายการ ใช้งานได้เพียงเพราะ x [1]! = null ไม่ใช่เพราะมีค่า 1
Armin Ronacher

1
ฉันไม่ได้ใช้เทคนิคในขณะที่ดังนั้นฉันลืมว่าฉันเคยใช้ตัวอักษรของวัตถุมาก่อน ขอบคุณสำหรับการแก้ไข
Mark Cidade

34
นอกจากนี้ระวัง: ผู้ปฏิบัติงานในเครื่องจะทดสอบโซ่ต้นแบบ! ถ้ามีคนใส่คุณสมบัติที่เรียกว่า '5' บน Object.prototype ตัวอย่างที่สองจะส่งกลับค่าจริงแม้ว่าคุณจะเรียกว่า '5 ในรายการ (1, 2, 3, 4)' ... คุณควรใช้ hasOwnProperty เมธอด: รายการ (1, 2, 3, 4) .hasOwnProperty (5) จะส่งคืนค่าเท็จแม้ว่า Object.prototype จะมีคุณสมบัติ '5'
Martijn

3
สำหรับวิธีแก้ปัญหาทั่วไปส่วนใหญ่ที่สามารถทดสอบว่า Object มีคุณสมบัติของตัวเองหรือไม่แม้ว่าจะมีชื่อว่า "hasOwnProperty" คุณจะต้องไปที่: Object.prototype.hasOwnProperty.call (object, name) ;
กริช Kowal

1
@Kris ไม่เว้นแต่จะมีคนเขียนทับ Object.prototype.hasOwnProperty;)
นิค

153

การกำหนดค่าเริ่มต้นให้กับตัวแปร

คุณสามารถใช้โลจิคัลหรือโอเปอเรเตอร์||ในนิพจน์การกำหนดค่าเพื่อให้ค่าเริ่มต้น

var a = b || c;

aตัวแปรจะได้รับค่าของcเฉพาะในกรณีที่bเป็นfalsy (ถ้าเป็นnull, false, undefined, 0,empty stringหรือNaN) มิฉะนั้นaจะได้รับค่าของbจะได้รับค่าของ

สิ่งนี้มักจะมีประโยชน์ในฟังก์ชั่นเมื่อคุณต้องการที่จะให้ค่าเริ่มต้นให้กับข้อโต้แย้งในกรณีที่ไม่ได้ให้:

function example(arg1) {
  arg1 || (arg1 = 'default value');
}

ตัวอย่าง IE fallback ในตัวจัดการเหตุการณ์:

function onClick(e) {
    e || (e = window.event);
}

คุณลักษณะด้านภาษาต่อไปนี้อยู่กับเรามาเป็นเวลานานการใช้งาน JavaScript ทั้งหมดสนับสนุนพวกเขา แต่พวกเขาไม่ได้เป็นส่วนหนึ่งของข้อกำหนดจนกระทั่ง ECMAScript 5th Edition :

debuggerคำสั่ง

อธิบายไว้ใน: § 12.15 คำสั่งดีบักเกอร์

คำสั่งนี้อนุญาตให้คุณใส่จุดสั่งหยุดแบบเป็นโปรแกรมในรหัสของคุณเพียงแค่:

// ...
debugger;
// ...

หากมีการดีบั๊กหรือมีการใช้งานมันจะทำให้มันแตกทันทีทันทีบนบรรทัดนั้น

มิเช่นนั้นถ้าตัวดีบั๊กไม่มีอยู่หรือใช้งานคำสั่งนี้จะไม่มีผลที่สังเกตได้

Multiline String ตัวอักษร

อธิบายไว้ใน: § 7.8.4 ตัวอักษรสตริง

var str = "This is a \
really, really \
long line!";

คุณต้องระวังเพราะตัวละครที่อยู่ถัดจาก\ ต้องเป็นตัวยุติบรรทัดหากคุณมีช่องว่างหลังจาก\ตัวอย่างรหัสจะมีลักษณะเหมือนกันSyntaxErrorทุกประการแต่จะเพิ่มขึ้น


28
ไม่ใช่ถ้ามันเป็นโมฆะหากถือว่าเป็นเท็จ a = 0 || 42; จะให้ 42. สิ่งนี้เทียบได้กับ Python หรือไม่ใช่ C # ผู้ประกอบการ หากคุณต้องการพฤติกรรม C # ให้ทำ = (b === ว่าง)? c: b;
Armin Ronacher

นอกจากนี้ยังทำงานใน Visual Studio เช่นกันถ้าคุณพัฒนาบน ASP.NET :)
ชาคริต

2
ฉันหวังว่าจะมีความเหมาะสม | | สำหรับไม่ได้กำหนดเท่านั้น วันนี้ฉันถูกกัดด้วย 0 เพราะฉันต้องการสร้างการจำลองของวิธีที่มากเกินไปเพื่อให้อาร์กิวเมนต์สุดท้ายเป็นตัวเลือกและจะใช้ค่าเริ่มต้นแทน
egaga

+1 เคล็ดลับนี้ถูกใช้โดยตัวอย่างข้อมูลเริ่มต้นของ Google Analytics `var _gaq = _gaq || []; `; มันป้องกันผู้ใช้ overzealous จากการเขียนทับงานของตนเอง
Yahel

2
ฉันไม่รู้เกี่ยวกับเทคนิคตัวอักษรสตริงหลายบรรทัด ขอบคุณมาก
Charlie Flowers

145

จาวาสคริปต์ไม่มีขอบเขตบล็อก (แต่มันปิดดังนั้นขอเรียกมันด้วยเหรอ?)

var x = 1;
{
   var x = 2;
}
alert(x); // outputs 2

3
นั่นเป็นสิ่งที่ดี มันเป็นข้อแตกต่างที่สำคัญมากจากภาษาซีส่วนใหญ่
Martin Clarke

9
คุณสามารถทำ "var tmp = function () {/ * block scope * /} ();" ไวยากรณ์น่าเกลียด แต่ใช้งานได้
Joeri Sebrechts

3
หรือคุณสามารถใช้ "ปล่อย" ถ้าเป็น Firefox เท่านั้น: stackoverflow.com/questions/61088/…
Eugene Yokota

10
หรือเพียงแค่: (function () {var x = 2;}) (); การแจ้งเตือน (typeof x); // undefined
Pim Jager

@Pim: JSLint พูดว่า: "ย้ายคำร้องขอไปยัง parens ที่มีฟังก์ชัน" พร้อมกับ "คาดว่าจะมีช่องว่างเดียวระหว่าง 'ฟังก์ชัน' และ '('.".
Hello71

144

คุณสามารถเข้าถึงคุณสมบัติของวัตถุได้ด้วย[]แทนที่จะเป็น.

สิ่งนี้อนุญาตให้คุณค้นหาคุณสมบัติที่ตรงกับตัวแปร

obj = {a:"test"};
var propname = "a";
var b = obj[propname];  // "test"

คุณยังสามารถใช้สิ่งนี้เพื่อรับ / ตั้งค่าคุณสมบัติของวัตถุที่มีชื่อไม่ใช่ตัวระบุทางกฎหมาย

obj["class"] = "test";  // class is a reserved word; obj.class would be illegal.
obj["two words"] = "test2"; // using dot operator not possible with the space.

บางคนไม่รู้เรื่องนี้และจบลงด้วยการใช้eval ()เช่นนี้ซึ่งเป็นความคิดที่ไม่ดีจริงๆ :

var propname = "a";
var a = eval("obj." + propname);

นี่เป็นการยากที่จะอ่านและยากที่จะหาข้อผิดพลาดใน (ไม่สามารถใช้ jslint), ช้ากว่าในการดำเนินการและสามารถนำไปสู่การหาประโยชน์ XSS


eval เป็นสิ่งที่ชั่วร้าย แต่ไม่ค่อยมีความจำเป็น
Doug Domeny

ฉันไม่เคยใช้ eval และจดจำเมื่อฉันค้นพบสิ่งนี้ มันทำให้ฉันมีความสุขมาก

โดยสรุปคุณสมบัติของวัตถุสามารถเข้าถึงได้ผ่านสัญกรณ์แบบจุดและตัวห้อย
Russ Cam

9
เป็นที่น่าสนใจที่จะทราบว่าการอ้างอิงดอทนั้นเป็นน้ำตาลซินแทกซ์สำหรับ bracketref foo.barตามที่อยู่แล้วสเป็ค, foo["bar"]พฤติกรรมเช่นเดียวกับ โปรดทราบว่าทุกอย่างเป็นคุณสมบัติสตริง แม้ว่าคุณจะเข้าถึงอาเรarray[4]ย์ 4 จะถูกแปลงเป็นสตริง (อีกครั้งอย่างน้อยตามข้อกำหนด ECMAScript v3)
Claudiu

ฉันเดาว่าโปรแกรมเมอร์ JS ทุกคนควรรู้สิ่งนี้
Cem Kalyoncu

144

หากคุณเป็น Google สำหรับการอ้างอิง JavaScript ที่ดีในหัวข้อที่กำหนดให้ใส่คำหลัก "mdc" ในการสืบค้นของคุณและผลลัพธ์แรกของคุณจะมาจาก Mozilla Developer Center ฉันไม่ได้พกหนังสืออ้างอิงหรือออฟไลน์ใด ๆ ติดตัวไปด้วย ฉันมักจะใช้เคล็ดลับคำหลัก "mdc" เพื่อไปที่สิ่งที่ฉันกำลังมองหาโดยตรง ตัวอย่างเช่น:

Google: javascript เรียงลำดับอาร์เรย์ mdc
(ในกรณีส่วนใหญ่คุณอาจละเว้น "javascript")

ปรับปรุง: Mozilla ผู้พัฒนาศูนย์ได้รับการเปลี่ยนชื่อ Mozilla ผู้พัฒนาเครือข่าย "การ MDC" เคล็ดลับคำหลักที่ยังคงทำงานได้ แต่ไม่นานพอที่เราอาจจะต้องเริ่มใช้ "MDN" แทน


50
ว้าวทรัพยากรที่ดี ดีกว่า w3schools เส็งเคร็งในทันที ...
DisgruntledGoat

11
คุณไม่จำเป็นต้องใช้ Google หากคุณใช้ Firefox ให้พิมพ์ "array mdc" ลงในแถบที่อยู่แล้วกด Enter
Sasha Chedygov

2
ส่วนที่ดีที่สุดคือวิธีคำถามนี้แตกล้นอยู่บนหน้าแรกของผล :)
Jiaaro

5
proPO นี้: promotejs.comเป็นระดับรากหญ้า SEO ความคิดริเริ่มที่จะขับรถ MDC ผลขึ้นต่อไปในผลการค้นหาของ Google
Yahel

3
ตอนนี้เป็นศูนย์กลาง doc MDN ดังนั้น 'MDC' คำหลักที่ยังคงถูกต้อง :)
Aleadam

143

อาจเห็นได้ชัดเล็กน้อยสำหรับบางคน ...

ติดตั้งFirebugและใช้ console.log ("hello") ดีกว่าการใช้การเตือนแบบสุ่ม (); ซึ่งฉันจำได้ว่าทำไม่กี่ปี


12
อย่าลืมลบคำสั่งคอนโซลก่อนที่จะปล่อยรหัสของคุณให้ผู้อื่นที่อาจไม่ได้ติดตั้ง Firebug
Chris Noe

161
ฟังก์ชั่นบันทึก (msg) {if (คอนโซล) console.log (msg) การแจ้งเตือนอื่น (msg)}
Josh

4
ดียิ่งขึ้นนำหน้าคำสั่งบันทึกด้วย ';;;' จากนั้นย่อขนาดจัดการมันให้คุณ (อย่างน้อยการใช้โมดูล Perl ฉันมีคุณสมบัติที่และการเรียกร้องมันเป็นเรื่องธรรมดา.)
Kev

10
Josh: ไม่สามารถใช้งานได้เนื่องจากไม่ได้กำหนดคอนโซล คุณสามารถตรวจสอบ typeof console! == "undefined" หรือ window.console
Eli Gray

23
รวมถึง: if (typeof ('console') == 'undefined') {console = {log: function () {}}; } จากนั้นคุณสามารถใช้ console.log ต่อไปและไม่ทำอะไรเลย
gregmac

120

วิธีการส่วนตัว

วัตถุสามารถมีวิธีการส่วนตัว

function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;

    // A private method only visible from within this constructor
    function calcFullName() {
       return firstName + " " + lastName;    
    }

    // A public method available to everyone
    this.sayHello = function () {
        alert(calcFullName());
    }
}

//Usage:
var person1 = new Person("Bob", "Loblaw");
person1.sayHello();

// This fails since the method is not visible from this scope
alert(person1.calcFullName());

16
นั่นไม่ใช่ฟังก์ชั่นส่วนตัว - มันเป็นตัวแปรของฟังก์ชั่นในขอบเขตท้องถิ่นมากกว่า
Keith

6
จริง แต่โดยคำจำกัดความการดำเนินงานทั้งหมดฉันสามารถคิดได้ว่าเป็นวิธีการ มันเป็นบล็อคของรหัสที่มีชื่อที่สามารถเข้าถึงสถานะอินสแตนซ์และสามารถดูได้โดยอินสแตนซ์นั้นเท่านั้น คำจำกัดความของคุณเกี่ยวกับวิธีการส่วนตัวคืออะไร?
Allain Lalonde

14
@Zach แน่นอน! เป็นเรื่องง่ายหลังจากใช้เวลาหลายปีในการทำงานกับภาษา OO ที่ใช้ในชั้นเรียนเพื่อลืมว่าพวกเขาเป็นเพียงการดำเนินการตามแนวคิดของ OO แน่นอนว่าไลบรารีต่าง ๆ ที่พยายามยัด OO แบบกึ่งคลาสลงใน JS ก็ไม่ได้ช่วยอะไรเช่นกัน ...
Shog9

5
แค่สงสัยว่า person1 มีบล็อกของกฎหมายหรือไม่ ;-)
travis

4
+1 สำหรับการอ้างอิงการพัฒนาที่ถูกจับกุม
โดเมนอิก

99

ยังกล่าวถึงใน "Javascript: The Good Parts" ของ Crockford:

parseInt()อันตราย. หากคุณผ่านสตริงโดยไม่แจ้งให้ทราบถึงฐานที่ถูกต้องมันอาจส่งคืนหมายเลขที่ไม่คาดคิด ตัวอย่างเช่นparseInt('010')คืนค่า 8 ไม่ใช่ 10 การส่งผ่านฐานไปยัง parseInt ทำให้ทำงานได้อย่างถูกต้อง:

parseInt('010') // returns 8! (in FF3)
parseInt('010', 10); // returns 10 because we've informed it which base to work with.

13
เมื่อทำการตรวจสอบโค้ดให้มองหาสิ่งนี้เสมอ การเว้น ", 10" เป็นข้อผิดพลาดทั่วไปที่ไม่มีใครสังเกตเห็นได้ในการทดสอบส่วนใหญ่
Doug Domeny

ฉันถูกเผาโดยปัญหา radix เมื่อหลายปีก่อนและไม่เคยลืมสิ่งที่ต่อต้านได้ง่ายเช่นนี้ เป็นสิ่งที่ดีที่จะชี้ให้เห็นเพราะมันจะทำให้คุณสงสัยในขณะที่
JamesEggers

4
ทำไมไม่ใช้Math.floorหรือNumber? 10 === Math.floor("010"); 10 === Number("010");ลอย:42 === Math.floor("42.69"); 42.69 === Number("42.69");
ใครสักคน

1
@Infinity หากไม่ใช่คำตอบที่โพสต์แล้วคุณควร ฉันไม่รู้ว่ามันง่ายอย่างนี้เพื่อลบล้างการทำงานของฟังก์ชันในตัว แน่นอนว่าควรทำให้ดูอย่างใกล้ชิดยิ่งขึ้นในแพ็คเกจรหัสใด ๆ ที่พวกเขายืมจากเว็บไซต์อื่น ๆ parseIntฟังก์ชั่นที่ไม่เป็นอันตรายนั้นสามารถทำสิ่งที่ไม่เป็นอันตรายได้อย่างง่ายดาย
Bob-the-Destroyer

6
@ อินฟินิตี้: สิ่งที่เกี่ยวกับการกำหนด fn ใหม่เพื่อเน้น 'ข้อผิดพลาดการเข้ารหัส'? __parseInt = parseInt; parseInt = function (str, base) { if (!base) throw new Error(69, "All your base belong to us"); return __parseInt(str, base); }
JBRWilkinson

97

ฟังก์ชั่นเป็นวัตถุจึงสามารถมีคุณสมบัติ

fn = function (x) {
   // ...
}

fn.foo = 1;

fn.next = function (y) {
  //
}

13
นี่เป็นเคล็ดลับที่มีประโยชน์มาก ตัวอย่างเช่นคุณสามารถตั้งค่าเริ่มต้นเป็นคุณสมบัติของฟังก์ชั่น ตัวอย่างเช่น: myfunc.delay = 100; จากนั้นผู้ใช้สามารถเปลี่ยนค่าเริ่มต้นและการเรียกใช้ฟังก์ชันทั้งหมดจะใช้ค่าเริ่มต้นใหม่ ตัวอย่างเช่น: myfunc.delay = 200; MyFunc ();
BarelyFitz

มีประโยชน์ ... และอันตราย!
palswim

ดูเลอะเทอะทำไมใช้สิ่งนี้แทนตัวแปร?
instantsetsuna

1
@instantsetsuna: ทำไมมีตัวแปรอีกตัวแยกกัน ตามปกตินี้จะเดือดลงไปที่ "ใช้เมื่อเหมาะสม / มีประโยชน์" ;-)
VolkerK

91

ฉันต้องบอกว่าฟังก์ชั่นการทำงานด้วยตนเอง

(function() { alert("hi there");})();

เนื่องจาก Javascript ไม่มีขอบเขตบล็อกคุณสามารถใช้ฟังก์ชันการดำเนินการด้วยตนเองหากคุณต้องการกำหนดตัวแปรในเครื่อง:

(function() {
  var myvar = 2;
  alert(myvar);
})();

ที่นี่myvarจะไม่รบกวนหรือก่อให้เกิดมลพิษทั่วโลกขอบเขตและหายไปเมื่อฟังก์ชั่นยุติ


2
สิ่งนี้มีประโยชน์สำหรับอะไร? คุณได้รับผลลัพธ์เดียวกันจากการวางการแจ้งเตือนนอกฟังก์ชั่น
PotatoEngineer

7
มันไม่ได้เกี่ยวกับการแจ้งเตือนมันเกี่ยวกับการกำหนดและดำเนินการฟังก์ชั่นทั้งหมดในครั้งเดียว คุณสามารถให้ฟังก์ชันการดำเนินการด้วยตนเองคืนค่าและส่งผ่านฟังก์ชันเป็นพารามิเตอร์ไปยังฟังก์ชันอื่น
ScottKoon

5
@ พอลมันเป็นสิ่งที่ดีสำหรับการห่อหุ้ม
Mike Robinson

22
นอกจากนี้ยังดีสำหรับการกำหนดขอบเขตของบล็อก
Jim Hunziker

24
ใช่ฉันใส่.jsไฟล์ทั้งหมดของฉันไว้ในฟังก์ชั่นสั่งงานด้วยตนเองแบบไม่ระบุชื่อและแนบทุกสิ่งที่ฉันต้องการให้สามารถเข้าถึงได้ทั่วโลกไว้ในwindowวัตถุ ป้องกันไม่ให้มลพิษเนมสเปซทั่วโลก
cdmckay

83

รู้ว่าฟังก์ชั่นที่ต้องการคือพารามิเตอร์จำนวนเท่าใด

function add_nums(num1, num2, num3 ){
    return num1 + num2 + num3;
}
add_nums.length // 3 is the number of parameters expected.

รู้ว่าฟังก์ชั่นได้รับพารามิเตอร์จำนวนเท่าใด

function add_many_nums(){
    return arguments.length;
}    
add_many_nums(2,1,122,12,21,89); //returns 6

23
ไม่เคยรู้เกี่ยวกับส่วนแรก ดี!
mcjabberz

1
function.lengthในทำนองเดียวกันคุณสามารถหาวิธีการหลายข้อโต้แย้งฟังก์ชั่นที่มีการคาดหวังว่า
Xavi

6
@ Xavi ซึ่งเป็นส่วนที่ 1 ของคำตอบ
pramodc84

79

นี่คือสิ่งที่น่าสนใจ:

  • เปรียบเทียบNaNกับสิ่งที่ (แม้NaN) อยู่เสมอเท็จที่มี==, และ<>
  • NaN ไม่ใช่แทนจำนวน แต่ถ้าคุณถามหาประเภทมันจะคืนค่าจำนวนจริง
  • Array.sort สามารถใช้ฟังก์ชั่นเปรียบเทียบและเรียกใช้โดยไดรเวอร์ที่คล้ายกันอย่างรวดเร็ว
  • นิพจน์ปกติ "ค่าคงที่" สามารถรักษาสถานะเช่นเดียวกับสิ่งสุดท้ายที่พวกเขาจับคู่
  • บางรุ่นของ JavaScript ช่วยให้คุณสามารถเข้าถึง$0, $1, $2สมาชิกนิพจน์ทั่วไป
  • nullไม่เหมือนสิ่งอื่นใด มันเป็นค่าวัตถุบูลีน, undefinedหมายเลขสตริงหรือ เป็นบิตเช่น undefined"สำรอง" (หมายเหตุ: typeof null == "object")
  • ในบริบทนอกสุดthisให้ผลตอบแทนวัตถุ [Global] ที่ไม่สามารถระบุได้
  • การประกาศตัวแปรด้วยvarแทนที่จะพึ่งการประกาศอัตโนมัติของตัวแปรนั้นจะช่วยให้รันไทม์มีโอกาสที่แท้จริงในการเพิ่มประสิทธิภาพการเข้าถึงตัวแปรนั้น
  • สิ่งwithก่อสร้างจะทำลายการเพิ่มประสิทธิภาพเช่นนั้น
  • ชื่อตัวแปรสามารถมีอักขระ Unicode
  • นิพจน์ทั่วไปของ JavaScript ไม่ได้เป็นจริงตามปกติ มันขึ้นอยู่กับ regex ของ Perl และเป็นไปได้ที่จะสร้างนิพจน์ด้วย lookaheads ที่ใช้เวลานานมากในการประเมิน
  • breakบล็อกสามารถระบุและใช้เป็นเป้าหมายของ continueลูปสามารถระบุและใช้เป็นเป้าหมายของ
  • อาร์เรย์ไม่กระจัดกระจาย การตั้งค่าองค์ประกอบ 1000 undefinedของอาร์เรย์ที่ว่างเปล่ามิฉะนั้นควรกรอกด้วย (ขึ้นอยู่กับการใช้งาน)
  • if (new Boolean(false)) {...} จะดำเนินการ{...}บล็อก
  • เอ็นจิ้นนิพจน์ทั่วไปของ Javascript นั้นใช้งานเฉพาะ: เช่นมันเป็นไปได้ที่จะเขียนนิพจน์ปกติ "ไม่พกพา"

[อัพเดทเล็กน้อยเพื่อตอบสนองต่อความคิดเห็นที่ดี; โปรดดูความคิดเห็น]


5
จริง ๆ แล้ว null เป็นวัตถุ (พิเศษ) typeof nullส่งคืน "วัตถุ"
Ates Goral

4
คุณสามารถรับวัตถุ [Global] จากที่ใดก็ได้เช่นนี้: var glb = function () {return this; } ();
Zilk

2
วัตถุทั่วโลกใน javascript ในเบราว์เซอร์เป็นวัตถุหน้าต่าง เมื่ออยู่ในขอบเขตส่วนกลางที่ทำ: window.a == a;
Pim Jager

8
"อาร์เรย์ไม่กระจัดกระจาย" ขึ้นอยู่กับการนำไปใช้ หากคุณตั้งค่าเป็น [1,000] และดูที่ [999] ใช่แล้วมันคือundefinedแต่นั่นเป็นเพียงค่าเริ่มต้นที่คุณได้รับเมื่อค้นหาดัชนีที่ไม่มีอยู่ หากคุณตรวจสอบ [2000] นั่นอาจเป็นundefinedเช่นนั้น แต่นั่นไม่ได้หมายความว่าคุณได้จัดสรรหน่วยความจำแล้ว ใน IE8 อาร์เรย์บางตัวมีความหนาแน่นสูงและบางตัวมีความเบาบางขึ้นอยู่กับว่าเครื่องยนต์ JScript รู้สึกอย่างไรในเวลานั้น อ่านเพิ่มเติมได้ที่นี่: blogs.msdn.com/jscript/archive/2008/04/08/…
Chris Nielsen

2
@Ates และ @SF: typeof ส่งคืน "object" สำหรับช่วงของประเภทที่แตกต่างกัน แต่เมื่อคุณรู้ว่ามันทำงานอย่างไรและประเภทใดระบุว่าเป็น "วัตถุ" อย่างน้อยก็น่าเชื่อถือและสอดคล้องกันในการใช้งาน
thomasrutter

77

ฉันรู้ว่าฉันมางานปาร์ตี้ช้า แต่ฉันไม่อยากจะเชื่อเลยว่า+ประโยชน์ของผู้ปฏิบัติงานไม่ได้ถูกกล่าวถึงนอกเหนือไปจาก "แปลงอะไรเป็นตัวเลข" บางทีนั่นอาจจะเป็นวิธีที่ซ่อนคุณสมบัติได้ดี?

// Quick hex to dec conversion:
+"0xFF";              // -> 255

// Get a timestamp for now, the equivalent of `new Date().getTime()`:
+new Date();

// Safer parsing than parseFloat()/parseInt()
parseInt("1,000");    // -> 1, not 1000
+"1,000";             // -> NaN, much better for testing user input
parseInt("010");      // -> 8, because of the octal literal prefix
+"010";               // -> 10, `Number()` doesn't parse octal literals 

// A use case for this would be rare, but still useful in cases
// for shortening something like if (someVar === null) someVar = 0;
+null;                // -> 0;

// Boolean to integer
+true;                // -> 1;
+false;               // -> 0;

// Other useful tidbits:
+"1e10";              // -> 10000000000
+"1e-4";              // -> 0.0001
+"-12";               // -> -12

แน่นอนคุณสามารถทำสิ่งนี้ทั้งหมดโดยใช้Number()แทน แต่+ผู้ประกอบการสวยกว่ามาก!

นอกจากนี้คุณยังสามารถกำหนดค่าที่ส่งคืนเป็นตัวเลขสำหรับวัตถุโดยการแทนที่valueOf()วิธีของต้นแบบ การแปลงตัวเลขใด ๆ ที่ทำกับวัตถุนั้นจะไม่ส่งผลNaNแต่จะส่งคืนค่าของvalueOf()วิธีการ:

var rnd = {
    "valueOf": function () { return Math.floor(Math.random()*1000); }
};
+rnd;               // -> 442;
+rnd;               // -> 727;
+rnd;               // -> 718;

คุณสามารถทำได้เพียงแค่0xFFฯลฯ +"0xFF"จำเป็นที่จะต้องไม่มี
nyuszika7h

9
@ Nyuszika7H: คุณพลาดจุดนี้ไปแล้วนั่นเป็นการบังคับแบบดั้งเดิมและวัตถุให้เป็นตัวเลข แน่นอนคุณสามารถเพียงแค่เขียน0xFFมากแบบเดียวกับที่คุณสามารถเขียนแทน1 +trueฉันขอแนะนำให้คุณใช้+("0x"+somevar)เป็นทางเลือกparseInt(somevar, 16)หากคุณต้องการ
Andy E

75

" วิธีการขยายใน JavaScript " ผ่านคุณสมบัติต้นแบบ

Array.prototype.contains = function(value) {  
    for (var i = 0; i < this.length; i++) {  
        if (this[i] == value) return true;  
    }  
    return false;  
}

นี้จะเพิ่มcontainsวิธีการไปยังArrayวัตถุทั้งหมด คุณสามารถเรียกวิธีนี้โดยใช้ไวยากรณ์นี้

var stringArray = ["foo", "bar", "foobar"];
stringArray.contains("foobar");

18
โดยทั่วไปถือว่าเป็นความคิดที่ไม่ดีเพราะรหัสอื่น ๆ (ไม่ใช่ของคุณ) อาจทำให้สมมติฐานเกี่ยวกับวัตถุ Array
Chris Noe

39
โดยทั่วไปถือว่าเป็นความคิดที่ไม่ดีในการตั้งสมมติฐานเกี่ยวกับวัตถุ Array :(
เปลือกตาไม่มีตัวตน

Uhmmmm .. javascript 1.6 อาร์เรย์พิเศษ? indexOf? เสียงระฆังใด?
เบรอตง

2
@Breton: มันไม่ใช่สิ่งที่เฉพาะเจาะจงสำหรับคลาส Array มันเป็นเพียงตัวอย่าง ฉันใช้สิ่งนี้เพื่อขยายวันที่ใหม่ (). toString (); วิธีการอนุญาตให้ใช้สตริงหน้ากาก วัตถุใด ๆ ที่สามารถขยายได้และทุกกรณีก็จะได้รับวิธีการใหม่
Esteban Küber

1
@Mathias: นี่ไม่เกี่ยวกับ DOM
dolmen

60

หากต้องการลบคุณสมบัติอย่างถูกต้องจากวัตถุคุณควรลบคุณสมบัติแทนการตั้งค่าเป็นไม่ได้กำหนด :

var obj = { prop1: 42, prop2: 43 };

obj.prop2 = undefined;

for (var key in obj) {
    ...

คุณสมบัติprop2จะยังคงเป็นส่วนหนึ่งของการทำซ้ำ ถ้าคุณต้องการกำจัดprop2อย่างสมบูรณ์คุณควรทำ:

delete obj.prop2;

คุณสมบัติprop2จะไม่ปรากฏเมื่อคุณวนซ้ำผ่านคุณสมบัติ


3
โปรดทราบว่าคำสั่งลบจะไม่ได้โดยไม่ต้อง quirks เฉพาะเบราว์เซอร์ เช่นนี้จะล้มเหลวด้วยข้อผิดพลาดใหญ่ถ้าคุณลองใน IE และวัตถุไม่ใช่วัตถุ JS พื้นเมือง (แม้ว่าจะลบคุณสมบัติที่คุณเพิ่มด้วยตัวเอง) มันไม่ได้มีไว้สำหรับการลบตัวแปรเช่นเดียวกับในการลบ myvar; แต่ฉันคิดว่ามันใช้ได้กับเบราว์เซอร์บางตัว รหัสในคำตอบข้างต้นดูเหมือนว่าปลอดภัย
thomasrutter

โดยวิธีการที่ไม่ได้กำหนดสามารถเป็นตัวแปรได้เช่นกัน! ลองใช้ var undefined = "something"
Johann Philipp Strathausen

57

with.

มันไม่ค่อยได้ใช้และตรงไปตรงมาไม่ค่อยมีประโยชน์ ... แต่ในสถานการณ์ที่ จำกัด มันมีประโยชน์

ตัวอย่างเช่น: ตัวอักษรของวัตถุค่อนข้างมีประโยชน์สำหรับการตั้งค่าคุณสมบัติบนวัตถุใหม่ได้อย่างรวดเร็ว แต่ถ้าคุณต้องการเปลี่ยนคุณสมบัติครึ่งหนึ่งบนวัตถุที่มีอยู่

var user = 
{
   fname: 'Rocket', 
   mname: 'Aloysus',
   lname: 'Squirrel', 
   city: 'Fresno', 
   state: 'California'
};

// ...

with (user)
{
   mname = 'J';
   city = 'Frostbite Falls';
   state = 'Minnesota';
}

ชี้อลันพายุเห็นว่าเรื่องนี้อาจเป็นอันตรายได้ค่อนข้าง: ถ้าวัตถุที่ใช้เป็นบริบทที่ไม่ได้มีหนึ่งในคุณสมบัติที่ถูกมอบหมายให้ไปก็จะได้รับการแก้ไขในขอบเขตด้านนอกอาจจะสร้างหรือเขียนทับตัวแปรทั่วโลก สิ่งนี้เป็นอันตรายอย่างยิ่งหากคุณคุ้นเคยกับการเขียนโค้ดเพื่อทำงานกับออบเจกต์ซึ่งคุณสมบัติที่มีค่าดีฟอลต์หรือค่าว่างจะไม่ถูกกำหนด:

var user = 
{
   fname: "John",
// mname definition skipped - no middle name
   lname: "Doe"
};

with (user)
{
   mname = "Q"; // creates / modifies global variable "mname"
}

ดังนั้นจึงเป็นความคิดที่ดีที่จะหลีกเลี่ยงการใช้withคำสั่งสำหรับการมอบหมายดังกล่าว

ดูเพิ่มเติมที่: มีการใช้งานที่ถูกต้องสำหรับคำสั่ง "กับ" ของ JavaScript หรือไม่?


29
ภูมิปัญญาดั้งเดิมที่มีกฎหมายควรหลีกเลี่ยง หากวัตถุผู้ใช้ไม่มีคุณสมบัติอย่างใดอย่างหนึ่งที่คุณกล่าวถึงตัวแปรภายนอกด้วยขอบเขตหลอกของบล็อกจะถูกปรับเปลี่ยน ด้วยวิธีนี้ข้อบกพร่องอยู่ ข้อมูลเพิ่มเติมที่yuiblog.com/blog/2006/04/11/with-statement-considered-harmful
Alan Storm

1
Shog การคัดค้านไม่ได้เกี่ยวกับตัวแปรที่สะกดผิดพวกเขาเกี่ยวกับการดูบล็อกของรหัสและสามารถพูดด้วยความมั่นใจในสิ่งที่บรรทัดใด ๆ ในบล็อกนั้นทำ เนื่องจากวัตถุ Javascript เป็นแบบไดนามิกดังนั้นคุณจึงไม่สามารถพูดได้อย่างแน่นอนกับคุณสมบัติ / สมาชิกที่มีในเวลาใด ๆ
Alan Storm

2
สาธุ - ถ้าฉันเห็นคำสั่ง "กับ" ใน JS ที่ฉันพบฉันจะกำจัดมันและถามนักพัฒนาที่เขียนมันขึ้นมาเพื่อให้แน่ใจว่าเขารู้ว่าทำไมมันจึงไม่ใช่สิ่งที่ดีที่จะใช้ ... "คุณสมบัติที่ซ่อนอยู่?" เพิ่มเติมเช่น "คุณสมบัติที่น่ารังเกียจ"
35432 Jason

1
ลองพิจารณา chain ที่มีความซับซ้อนมากขึ้น abcd "with (abc) {d.foo = bar;} มีประสิทธิภาพและไม่เกิดข้อผิดพลาดได้โดยง่ายกุญแจสำคัญคือการกำจัดรากหนึ่งระดับขึ้นไปและสะกดชื่อตัวแปรผิดหรือไม่คุณกำลังแนะนำบั๊ก ถ้าคุณทำที่ใดก็ตามที่คุณทำมันโดยไม่คำนึงถึง "กับ".
annakata

4
Douglas Crockford กล่าวเมื่อเร็ว ๆ นี้ว่า "with" เป็นหนึ่งในส่วนที่แย่ที่สุดของ JavaScript ใน. NET Rocks! พอดคาสต์
แกน

51

วิธีการ (หรือฟังก์ชั่น) สามารถเรียกบนวัตถุที่ไม่ได้เป็นประเภทที่พวกเขาได้รับการออกแบบมาเพื่อทำงานกับ นี่เป็นวิธีที่ยอดเยี่ยมในการเรียกใช้เนทีฟ (เร็ว) บนวัตถุที่กำหนดเอง

var listNodes = document.getElementsByTagName('a');
listNodes.sort(function(a, b){ ... });

รหัสนี้ขัดข้องเพราะlistNodesไม่ใช่Array

Array.prototype.sort.apply(listNodes, [function(a, b){ ... }]);

รหัสงานนี้เพราะlistNodesกำหนดพออาร์เรย์เช่นคุณสมบัติ (ความยาว, [] ผู้ดำเนินการ) sort()ที่จะใช้โดย


43

Prototypal ที่สืบทอด (นิยมโดย Douglas Crockford) ปฏิวัติวิธีที่คุณคิดเกี่ยวกับสิ่งต่างๆมากมายใน Javascript อย่างสมบูรณ์

Object.beget = (function(Function){
    return function(Object){
        Function.prototype = Object;
        return new Function;
    }
})(function(){});

มันเป็นนักฆ่า! น่าเสียดายที่แทบไม่มีใครใช้มัน

จะช่วยให้คุณ "รับ" อินสแตนซ์ใหม่ของวัตถุใด ๆ ขยายพวกเขาในขณะที่ยังคงเชื่อมโยงมรดกสืบทอด (สด) ต้นแบบไปยังคุณสมบัติอื่น ๆ ของพวกเขา ตัวอย่าง:

var A = {
  foo : 'greetings'
};  
var B = Object.beget(A);

alert(B.foo);     // 'greetings'

// changes and additionns to A are reflected in B
A.foo = 'hello';
alert(B.foo);     // 'hello'

A.bar = 'world';
alert(B.bar);     // 'world'


// ...but not the other way around
B.foo = 'wazzap';
alert(A.foo);     // 'hello'

B.bar = 'universe';
alert(A.bar);     // 'world'

42

บางคนอาจเรียกสิ่งนี้ว่าเรื่องรสชาติ แต่:

aWizz = wizz || "default";
// same as: if (wizz) { aWizz = wizz; } else { aWizz = "default"; }

ผู้ประกอบการ trinary สามารถถูกผูกมัดให้ทำหน้าที่เหมือน Scheme's (cond ... ):

(cond (predicate  (action  ...))
      (predicate2 (action2 ...))
      (#t         default ))

สามารถเขียนเป็น ...

predicate  ? action( ... ) :
predicate2 ? action2( ... ) :
             default;

นี่คือ "การทำงาน" มากเนื่องจากมันจะแยกรหัสของคุณโดยไม่มีผลข้างเคียง ดังนั้นแทนที่จะ:

if (predicate) {
  foo = "one";
} else if (predicate2) {
  foo = "two";
} else {
  foo = "default";
}

คุณสามารถเขียน:

foo = predicate  ? "one" :
      predicate2 ? "two" :
                   "default";

ใช้งานได้ดีกับการเรียกซ้ำอีกด้วย :)


ฉันชอบไวยากรณ์ของคำกริยาที่คุณให้ ฉันไม่เคยคิดจะผูกมัดแบบนั้น เรียบร้อย
Allain Lalonde

2
เอ่อ ... จาวาสคริปต์มีคำสั่ง switch () :-)
staticsan

ฉันไม่ได้เป็นแฟนตัวยงของงบสวิตช์ - พวกเขาเป็นสิ่งประดิษฐ์ของ C ไม่ใช่การเขียนโปรแกรมการทำงาน ในตัวอย่างของฉันคำสั่ง switch ยังคงต้องการคำสั่งแยกกันสามคำทั้งหมดเริ่มต้นด้วย "foo =" - การทำซ้ำที่ไม่จำเป็นอย่างชัดเจน
Andrey Fedorov

14
ฉันหนึ่งยินดีต้อนรับผู้ประกอบการที่ประกอบไปด้วย
thomasrutter

8
ในการอ่านอีกครั้งฉันต้องการชี้ให้เห็นว่านี่ไม่ใช่ "การทำให้โค้ดดูเหมือนภาษาอื่น" แต่จริงๆแล้วทำให้ความหมายของรหัสนั้นง่ายขึ้น: เมื่อคุณพยายามที่จะพูดว่า "ตั้งค่า foo เป็นหนึ่งในสาม สิ่งต่าง ๆ "นั่นคือคำสั่งที่ควรเริ่มต้นด้วย" foo = ... "ไม่ใช่" ถ้า "
Andrey Fedorov

41

ตัวเลขก็เป็นวัตถุ ดังนั้นคุณสามารถทำสิ่งดีๆเช่น:

// convert to base 2
(5).toString(2) // returns "101"

// provide built in iteration
Number.prototype.times = function(funct){
  if(typeof funct === 'function') {
    for(var i = 0;i < Math.floor(this);i++) {
      funct(i);
    }
  }
  return this;
}


(5).times(function(i){
  string += i+" ";
});
// string now equals "0 1 2 3 4 "

var x = 1000;

x.times(function(i){
  document.body.innerHTML += '<p>paragraph #'+i+'</p>';
});
// adds 1000 parapraphs to the document

พระเจ้าช่วย! ฉันไม่รู้เกี่ยวกับ toring (radix) ...
Ates Goral

1
การนำไปปฏิบัติtimesนั้นไม่มีประสิทธิภาพ: Math.floorถูกเรียกใช้ทุกครั้งแทนที่จะเป็นเพียงครั้งเดียว
dolmen

33

วิธีการเกี่ยวกับการปิดใน JavaScript (คล้ายกับวิธีที่ไม่ระบุชื่อใน C # v2.0 +) คุณสามารถสร้างฟังก์ชั่นที่สร้างฟังก์ชั่นหรือ "แสดงออก"

ตัวอย่างของการปิด :

//Takes a function that filters numbers and calls the function on 
//it to build up a list of numbers that satisfy the function.
function filter(filterFunction, numbers)
{
  var filteredNumbers = [];

  for (var index = 0; index < numbers.length; index++)
  {
    if (filterFunction(numbers[index]) == true)
    {
      filteredNumbers.push(numbers[index]);
    }
  }
  return filteredNumbers;
}

//Creates a function (closure) that will remember the value "lowerBound" 
//that gets passed in and keep a copy of it.
function buildGreaterThanFunction(lowerBound)
{
  return function (numberToCheck) {
    return (numberToCheck > lowerBound) ? true : false;
  };
}

var numbers = [1, 15, 20, 4, 11, 9, 77, 102, 6];

var greaterThan7 = buildGreaterThanFunction(7);
var greaterThan15 = buildGreaterThanFunction(15);

numbers = filter(greaterThan7, numbers);
alert('Greater Than 7: ' + numbers);

numbers = filter(greaterThan15, numbers);
alert('Greater Than 15: ' + numbers);

1
ฉันไม่แน่ใจ แต่สามารถส่งคืน (numberToCheck> lowerBound) ได้ไหม ถูกผิด; ก็กลายเป็นผลตอบแทน (numberToCheck> lowerBound); แค่พยายามที่จะเพิ่มความเข้าใจของฉัน ...
davidsleeps

4
ผมว่าฟังก์ชั่นที่ไม่ระบุชื่อใน C # เทียบเท่าของการปิดไม่วิธีอื่น ๆ :)
vava

11
ฟังก์ชั่นการปิดและไม่ระบุชื่อแยกต่างหากแนวคิดที่แตกต่าง ฟังก์ชั่นนั้นสามารถสร้างได้โดยไม่ต้องตั้งชื่อว่ามีฟังก์ชั่นนิรนาม ตัวแปรในขอบเขต 'สร้าง' เชื่อมโยงกับฟังก์ชันที่สร้างขึ้นเป็นการปิด ในระยะสั้นการปิดเป็นเหมือนตัวแปรทั่วโลกที่ซ่อนอยู่
slebetman

1
นั่นเป็นเรื่องจริง เฉพาะเมื่อวิธีการที่ไม่ระบุชื่อใช้ประโยชน์จากตัวแปรจากขอบเขตการสร้างมันคล้ายกับการปิด ฉันได้อัปเดตภาษาอังกฤษในคำตอบ มันยังมีบางสิ่งที่ต้องการ แต่ฉันหลงทางในภาษาอังกฤษที่ถูกต้อง
ไทเลอร์

2
ฉันไม่คิดว่านี่เป็นตัวอย่างที่ดีที่สุดหรือง่ายที่สุดที่จะเข้าใจว่าการปิดคืออะไร แค่พูด. จุดปิดคือแม้เมื่อตัวแปรหลายตัวปรากฏว่า 'ไม่อยู่ในขอบเขต' พวกเขายังคงสามารถใช้งานได้กับฟังก์ชั่นที่กำหนดไว้ภายในขอบเขตนั้น ในตัวอย่างด้านบนนั่นหมายความว่าตัวแปร lowerBound ยังคงสามารถเข้าถึงได้โดยฟังก์ชันภายในที่ไม่ระบุชื่อแม้ว่าฟังก์ชันภายนอก outer จะสร้าง buildGreaterThanFunction แล้วก็ตาม
thomasrutter

32

นอกจากนี้คุณยังสามารถขยาย (สืบทอด) คลาสและแทนที่คุณสมบัติ / เมธอดโดยใช้ต้นแบบ chain spoon16 ที่กล่าวถึง

ในตัวอย่างต่อไปนี้เราสร้างคลาส Pet และกำหนดคุณสมบัติบางอย่าง นอกจากนี้เรายังแทนที่. toString () วิธีการสืบทอดจาก Object

หลังจากนี้เราสร้างคลาส Dog ซึ่งขยาย Pet และแทนที่เมธอด. toString ()อีกครั้งเพื่อเปลี่ยนพฤติกรรม (polymorphism) นอกจากนี้เรายังเพิ่มคุณสมบัติอื่น ๆ ลงในคลาสย่อย

หลังจากนี้เราตรวจสอบห่วงโซ่การสืบทอดเพื่ออวดว่าสุนัขยังคงเป็นสุนัขประเภท, ประเภทสัตว์เลี้ยงและประเภทวัตถุ

// Defines a Pet class constructor 
function Pet(name) 
{
    this.getName = function() { return name; };
    this.setName = function(newName) { name = newName; };
}

// Adds the Pet.toString() function for all Pet objects
Pet.prototype.toString = function() 
{
    return 'This pets name is: ' + this.getName();
};
// end of class Pet

// Define Dog class constructor (Dog : Pet) 
function Dog(name, breed) 
{
    // think Dog : base(name) 
    Pet.call(this, name);
    this.getBreed = function() { return breed; };
}

// this makes Dog.prototype inherit from Pet.prototype
Dog.prototype = new Pet();

// Currently Pet.prototype.constructor
// points to Pet. We want our Dog instances'
// constructor to point to Dog.
Dog.prototype.constructor = Dog;

// Now we override Pet.prototype.toString
Dog.prototype.toString = function() 
{
    return 'This dogs name is: ' + this.getName() + 
        ', and its breed is: ' + this.getBreed();
};
// end of class Dog

var parrotty = new Pet('Parrotty the Parrot');
var dog = new Dog('Buddy', 'Great Dane');
// test the new toString()
alert(parrotty);
alert(dog);

// Testing instanceof (similar to the `is` operator)
alert('Is dog instance of Dog? ' + (dog instanceof Dog)); //true
alert('Is dog instance of Pet? ' + (dog instanceof Pet)); //true
alert('Is dog instance of Object? ' + (dog instanceof Object)); //true

คำตอบทั้งสองสำหรับคำถามนี้คือรหัสที่ดัดแปลงจากบทความ MSDN ที่ยอดเยี่ยมโดย Ray Djajadinata


31

คุณอาจจับข้อยกเว้นขึ้นอยู่กับประเภทของพวกเขา ยกมาจากMDC :

try {
   myroutine(); // may throw three exceptions
} catch (e if e instanceof TypeError) {
   // statements to handle TypeError exceptions
} catch (e if e instanceof RangeError) {
   // statements to handle RangeError exceptions
} catch (e if e instanceof EvalError) {
   // statements to handle EvalError exceptions
} catch (e) {
   // statements to handle any unspecified exceptions
   logMyErrors(e); // pass exception object to error handler
}

หมายเหตุ: ส่วนคำสั่ง catch แบบมีเงื่อนไขเป็นส่วนขยายของ Netscape (และ Mozilla / Firefox) ที่ไม่ได้เป็นส่วนหนึ่งของข้อมูลจำเพาะ ECMAScript และไม่สามารถเชื่อถือได้ยกเว้นบนเบราว์เซอร์บางตัว


29
ฉันไม่สามารถช่วยได้: จับ (ฉันถ้าคุณสามารถ)
Ates Goral

6
อ่านบันทึกย่อจากหน้า MDC ที่คุณอ้างถึง: ส่วนคำสั่ง catch แบบ catch เงื่อนไขเป็นส่วนขยายของ Netscape (และ Mozilla / Firefox) ที่ไม่ได้เป็นส่วนหนึ่งของข้อกำหนด ECMAScript และไม่สามารถใช้งานได้ยกเว้นเบราว์เซอร์บางตัว
46432 Jason Jason S

31

ปิดส่วนหัวของฉัน ...

ฟังก์ชั่น

arguments.callee หมายถึงฟังก์ชั่นที่เป็นโฮสต์ของตัวแปร "arguments" ดังนั้นมันสามารถใช้เพื่อเรียกคืนฟังก์ชั่นที่ไม่ระบุชื่อ:

var recurse = function() {
  if (condition) arguments.callee(); //calls recurse() again
}

มีประโยชน์ถ้าคุณต้องการทำสิ่งนี้:

//do something to all array items within an array recursively
myArray.forEach(function(item) {
  if (item instanceof Array) item.forEach(arguments.callee)
  else {/*...*/}
})

วัตถุ

สิ่งที่น่าสนใจเกี่ยวกับสมาชิกวัตถุ: พวกเขาสามารถมีสตริงเป็นชื่อของพวกเขา:

//these are normal object members
var obj = {
  a : function() {},
  b : function() {}
}
//but we can do this too
var rules = {
  ".layout .widget" : function(element) {},
  "a[href]" : function(element) {}
}
/* 
this snippet searches the page for elements that
match the CSS selectors and applies the respective function to them:
*/
for (var item in rules) {
  var elements = document.querySelectorAll(rules[item]);
  for (var e, i = 0; e = elements[i++];) rules[item](e);
}

เงื่อนไข

String.splitสามารถใช้นิพจน์ทั่วไปเป็นพารามิเตอร์ได้:

"hello world   with  spaces".split(/\s+/g);
//returns an array: ["hello", "world", "with", "spaces"]

String.replaceสามารถใช้นิพจน์ปกติเป็นพารามิเตอร์การค้นหาและฟังก์ชั่นเป็นพารามิเตอร์ทดแทน:

var i = 1;
"foo bar baz ".replace(/\s+/g, function() {return i++});
//returns "foo1bar2baz3"

สิ่งที่คุณพูดถึง ... พวกมันนำไปใช้กับเบราว์เซอร์ทั้งหมดหรือไม่?
cllpse

4
ไม่ฉันค่อนข้างมั่นใจว่าโมเสคขาดส่วนใหญ่
jsight

2
คุณสมบัติจาวาสคริปต์นั้นใช้งานได้ในเบราว์เซอร์หลัก ๆ (IE6 / 7, FF2 / 3, Opera 9+, Safari2 / 3 และ Chrome) document.querySelectorAll ยังไม่รองรับในทุกเบราว์เซอร์ (เป็นรุ่น W3C ของ JQuery's $ () และ Prototype's $$ ())
Leo

6
arguments.calleeเลิกใช้แล้วและจะโยนและยกเว้นใน ECMAScript 5
Hello71

ไม่จริงเลย คีย์วัตถุไม่สามารถ (หรือไม่ควร) ใช้สตริง "hasOwnProperty" เป็นชื่อเนื่องจากจะแทนที่เมธอด built in
Breton

29

คุณสามารถใช้วัตถุแทนสวิตช์ได้เกือบตลอดเวลา

function getInnerText(o){
    return o === null? null : {
        string: o,
        array: o.map(getInnerText).join(""),
        object:getInnerText(o["childNodes"])
    }[typeis(o)];
}

อัปเดต: หากคุณกังวลเกี่ยวกับกรณีที่ประเมินล่วงหน้าว่าไม่มีประสิทธิภาพ (เพราะเหตุใดคุณจึงกังวลเกี่ยวกับประสิทธิภาพในช่วงแรกของการออกแบบโปรแกรม) คุณสามารถทำสิ่งนี้ได้:

function getInnerText(o){
    return o === null? null : {
        string: function() { return o;},
        array: function() { return o.map(getInnerText).join(""); },
        object: function () { return getInnerText(o["childNodes"]; ) }
    }[typeis(o)]();
}

นี่เป็นงานพิมพ์ (หรืออ่าน) ที่ยากกว่าสวิตช์หรือวัตถุ แต่จะรักษาประโยชน์ของการใช้วัตถุแทนสวิตช์โดยมีรายละเอียดในส่วนความเห็นด้านล่าง สไตล์นี้ยังทำให้ตรงไปตรงมามากขึ้นในการหมุนออกเป็น "คลาส" ที่เหมาะสมเมื่อมันโตพอ

update2: ด้วยส่วนขยายไวยากรณ์ที่เสนอสำหรับ ES.next สิ่งนี้จะกลายเป็น

let getInnerText = o -> ({
    string: o -> o,
    array: o -> o.map(getInnerText).join(""),
    object: o -> getInnerText(o["childNodes"])
}[ typeis o ] || (->null) )(o);

3
นั่นเป็นวิธีที่ Python ได้รับโดยปราศจากคำสั่ง switch
outis

2
ปัญหาคือมันประเมินทุกกรณี
Kornel

@ porneL นี้เป็นความจริง แต่ก็มอบผลประโยชน์บางอย่าง: มันสะอาดกว่าในเชิงตรรกะ: กรณีต่างๆเป็นสตริงที่ค้นหาใน hashtable ไม่ใช่การแสดงออกว่าแต่ละคนจะต้องได้รับการประเมินความเท่าเทียมกันจนกว่าจะได้ผลจริง ดังนั้นในขณะที่มีการประเมิน "ค่า" มากขึ้นจะมีการประเมิน "คีย์" น้อยลง วัตถุสามารถสร้างแบบไดนามิกและปรับเปลี่ยนเพื่อความยืดหยุ่นในภายหลังสะท้อนให้เห็นสำหรับการพิมพ์ UI หรือสร้างเอกสารและแม้กระทั่งแทนที่ด้วยฟังก์ชั่น "ค้นหา" แบบไดนามิกซึ่งดีกว่ามีกรณีคัดลอก / วาง ไม่มีความสับสนเกี่ยวกับการหยุดพักการตกหล่นหรือค่าเริ่มต้น สามารถต่อเนื่อง JSON ...
Breton

@ porneL ใช่แล้วและอีกครั้งสำหรับสิ่งที่ขยายขนาดได้อย่างง่ายดายวัตถุสามารถหมุนออกไปสู่การกำหนดค่าภายนอกหรือไฟล์ข้อมูลการเปลี่ยนแปลงค่อนข้างตรงไปตรงมามากกว่าที่มีคำสั่งเปลี่ยน - แต่เล็กน้อยถ้าออกแบบด้วยวัตถุในใจเพื่อเริ่มต้น กับ
Breton

ฉันรู้ว่านี่เป็นรายการล่าช้า แต่ถ้าคุณไม่มีตรรกะการตรวจสอบประเภทกำหนดเองบางอย่างอาร์เรย์จะเคยทำงานกับตัวอย่างของคุณเมื่อใด var arr = []; typeof arr; // object
keeganwatkins

25

ตรวจสอบให้แน่ใจว่าใช้เมธอดhasOwnPropertyเมื่อวนซ้ำผ่านคุณสมบัติของวัตถุ:

for (p in anObject) {
    if (anObject.hasOwnProperty(p)) {
        //Do stuff with p here
    }
}

สิ่งนี้ถูกทำเพื่อให้คุณเข้าถึงคุณสมบัติโดยตรงของanObjectเท่านั้นและไม่ใช้คุณสมบัติที่อยู่ในสายโซ่ต้นแบบ


23

ตัวแปรส่วนตัวพร้อม Public Interface

มันใช้เคล็ดลับเล็กน้อยอย่างประณีตพร้อมนิยามฟังก์ชั่นการโทรด้วยตนเอง ทุกอย่างภายในวัตถุที่ส่งคืนจะพร้อมใช้งานในส่วนต่อประสานสาธารณะในขณะที่ทุกอย่างเป็นส่วนตัว

var test = function () {
    //private members
    var x = 1;
    var y = function () {
        return x * 2;
    };
    //public interface
    return {
        setx : function (newx) {
            x = newx;
        },
        gety : function () {
            return y();
        }
    }
}();

assert(undefined == test.x);
assert(undefined == test.y);
assert(2 == test.gety());
test.setx(5);
assert(10 == test.gety());

1
สิ่งนี้เรียกว่ารูปแบบโมดูลตามที่ขนานนามว่าโดย Eric Miraglia ที่yuiblog.com/blog/2007/06/12/module-pattern ฉันคิดว่าชื่อนั้นทำให้เข้าใจผิดควรเรียกว่า Singleton Pattern หรืออะไรทำนองนั้น ฉันอาจเพิ่มวิธีสาธารณะที่สามารถเรียกวิธีสาธารณะอื่น ๆ โดยใช้วัตถุ 'นี้' ฉันใช้รูปแบบนี้ตลอดเวลาในรหัสของฉันเพื่อจัดระเบียบและทำความสะอาด
mikeycgto
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.