2011-01-23 32 views
28

Đây là kịch bản:Làm thế nào để thay đổi "đối số"?

function runScripts() { 
    if (arguments.length === 0) return; 
    chrome.tabs.executeScript(null, { 
     file: arguments[0] 
    }, function() { 
     arguments.shift(); 
     runScripts.apply(null, arguments); 
    }); 
} 

Nó không làm việc vì arguments không phải là thực sự là một mảng, nó chỉ là mảng như thế nào. Vì vậy, làm thế nào tôi có thể "thay đổi" nó hoặc hack off các yếu tố đầu tiên để tôi có thể áp dụng chức năng này đệ quy?

Trả lời

19

Tôi giả sử bạn muốn tham chiếu gốcarguments, thay vì điều đó từ cuộc gọi lại bạn đang chuyển đến chrome.tabs.executeScript.

Nếu có, trước tiên bạn cần phải lưu bộ nhớ cache.

function runScripts() { 
    if (arguments.length === 0) return; 
    var args = []; 
    Array.prototype.push.apply(args, arguments); 

    chrome.tabs.executeScript(null, { 
     file: args.shift(); 
    }, function() { 
      // using the modified Array based on the original arguments object 
     runScripts.apply(null, args); 
    }); 
} 
+0

Eh ...? Tại sao bạn lại giả định điều đó? Mỗi phép lặp chỉ xử lý phần tử đầu tiên, và sau đó nó tự gọi nó với phần còn lại cho đến khi không còn phần tử nào. Tôi không thực sự chắc chắn về phiên bản của bạn. – mpen

+0

Oh..nevermind. Bởi vì chức năng nặc danh ... bạn nói đúng rồi, ngoại trừ nó vẫn không được phép chuyển qua phần tử đầu tiên .. nó được cho là vượt qua tất cả trừ cái đầu tiên. – mpen

+0

@Mark: Bạn có 2 chức năng. Có lẽ tôi sai, nhưng tôi đã giải thích câu hỏi của bạn khi muốn truyền vào 'arguments' từ' runScripts() '* vào * callback mà bạn đang chuyển tới' executeScript'. Nó không phải là một giả định kỳ lạ để làm vì bạn đang tham chiếu 'arguments [0]' * bên ngoài * gọi lại, sau đó cố gắng '.shift()' * bên trong * gọi lại. – user113716

31
var params = Array.prototype.slice.call(arguments); 
params.shift(); 

Bạn có thể xem chi tiết blog post giải thích chi tiết hơn.

+0

Tôi cũng đã xem qua bài đăng trên blog đó, nhưng nếu tôi chạy 'alert (args.length)' nó cho tôi biết 0, trong khi 'arguments.length' cho tôi biết. ** Chỉnh sửa: ** Crap ... Tôi hiểu rồi. Các chức năng vô danh là vặn nó lên. – mpen

+1

Đây là một tối ưu hóa nhỏ mà sẽ bảo vệ bạn khỏi một sửa đổi 'Array':' [] .slice.call (arguments) '. – fny

+1

Tại sao không chỉ sử dụng '[] .shift.call (arguments)'? ;) – thewildpendulum

2

Bạn sẽ cần phải chuyển đổi nó thành một mảng và sau đó thay đổi. Hoặc, cách khác, thả mục đầu tiên khi chuyển đổi thành một mảng. Array.prototype.slice.call(arguments, 1) sẽ hoạt động cho việc này.

6

Bạn có thể chuyển đổi arguments thành một mảng bình thường như thế này:

var args = Array.prototype.slice.call(arguments); 
1

Bạn có thể chuyển đổi các đối số cho một mảng thực tế và sau đó sử dụng mảng trong phần còn lại của logic của bạn trong hàm.

function runScripts() 
{ 
    var i=0, l=arguments.length, arr=[]; 
    while(i<l) 
    { 
    arr.push(arguments[i++]); 
    } 
...rest of your function code 

Chỉnh sửa để thêm: Tôi đã có vấn đề với prototypecall trong các phiên bản cũ của trình duyệt IE, vì vậy nó thực sự phụ thuộc vào những gì hỗ trợ bạn sẽ cần.

+0

May là tôi không cần hỗ trợ IE :) Chỉ cần Chrome. – mpen

9

[].shift.call(arguments) cũng hợp lệ. Tôi đang sử dụng mã này trong mã sản xuất và nó hoạt động như mong đợi.

Với phương pháp này, chức năng của bạn trở nên gọn gàng hơn một chút:

function executeScripts() { 
    if (arguments.length === 0) return; 
    chrome.tabs.executeScript(null, { 
     file: [].shift.call(arguments) 
    }, function() { 
     executeScripts.apply(null, arguments); 
    }); 
} 

Nếu bạn nhìn vào MDN, họ tuyên bố rằng shift() được thực hiện với sự linh hoạt này trong tâm trí.

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/shift

+0

Quá xấu, chúng không chỉ thực hiện' đối số' như một mảng thực để bắt đầu. Nhưng bạn đúng. Trong thực tế, tất cả các phương thức từ 'Array' có thể được sử dụng trên đối tượng * any *, miễn là đối tượng đó có thuộc tính' length'. Đó là bắt buộc bởi đặc tả ngôn ngữ. Bí mật của phép thuật này là bên dưới bề mặt, các mảng chỉ là các đối tượng có các số như các khóa thuộc tính. –

3

Chỉ muốn chỉ ra một vấn đề tiềm năng với [] .shift.call (arguments).

Điều này dường như có ý định không rõ ràng về việc chuyển đối số của bạn - ngay cả đối với các tham số có tên là - ngay cả khi được sử dụng trước câu lệnh thay đổi.

Ví dụ,

function testShift (param1, param2) { 
    [].shift.call(arguments); 
    if (param1=="ONE") alert("ONE"); 
} 

Nếu bạn thực hiện cuộc gọi sau đây, những gì có thể bạn mong đợi xảy ra?

testShift("ONE", "TWO"); 

Nếu bạn mong đợi param1 ở lại "ONE", khắc phục của bạn là đặt var thành param1 trước khi thay đổi xảy ra. Có vẻ như javascript không ràng buộc param1 cho đến khi dòng được gọi - không phải khi hàm được gọi ...do đó, các sửa đổi đối số trước khi tham số được sử dụng có thể có các hiệu ứng không mong muốn.

Hy vọng rằng bây giờ, bạn sẽ có thể mong đợi điều đó.

+0

TIL. Đó là hành vi khá bất ngờ. Cảm ơn bạn đã chia sẻ. – mpen

0

tôi đã đi với điều này:

function executeScripts() { 
    if (arguments.length === 0) return; 
    var args = Array.prototype.slice.call(arguments); 
    chrome.tabs.executeScript(null, { 
     file: args.shift() 
    }, function() { 
     executeScripts.apply(null, args); 
    }); 
} 

Nó rất hữu ích khi viết Extensions của Google Chrome. Tôi muốn sử dụng jQuery trong kịch bản nội dung của tôi, nhưng sau đó bạn phải tải nó trước. Hóa ra bằng cách chuỗi các cuộc gọi đến chrome.tabs.executeScript bạn có thể làm điều này:

chrome.browserAction.onClicked.addListener(function(tab) { 
    executeScripts('jquery-1.4.4.min.js', 'content.js'); 
}); 
1

Đây là một bài viết giải thích điều này thực sự tốt. Tôi đã sao chép một số điểm chính bên dưới. http://www.javascriptkit.com/javatutors/arrayprototypeslice.shtml

Đối với mảng, hãy nhớ rằng bạn có thể gọi hàm slice để nhận mảng phụ.

var abc = [1,2,3,4,5]; 
abc.slice(0); //[1,2,3,4,5] 
abc.slice(1,3); //[2,3] 

Vì đối số đối số chỉ là mảng, không thực sự là mảng. Hàm call()/apply() về cơ bản chỉ "mượn" hàm slice từ Array và sử dụng nó trên đối tượng Argument và thậm chí bạn có thể chuyển các tham số vào hàm slice giống như tác động lên mảng.

var myobject ={ // array-like collection 
    length: 4, 
    '0': 'zero', 
    '1': 'one', 
    '2': 'two', 
    '3': 'three' 
} 

var myarray = Array.prototype.slice.call(myobject) 
// returns myobject as a true array: ["zero", "one", "two", "three"] 

var myarray = Array.prototype.slice.call(myobject, 1) 
// returns ["one", "two", "three"] 

Câu hỏi còn lại là lý do tại sao chúng tôi gọi slice() trên đối tượng mẫu của mảng thay vì một mảng. Lý do là vì đây là tuyến đường trực tiếp nhất để truy cập vào phương thức slice() của Array khi đó là tất cả những gì chúng ta quan tâm; chúng ta có thể lần đầu tiên tạo ra một thể hiện mảng, nhưng đó là kém hiệu quả và có lẽ sâu sắc hơn:

var myarray = new Array().prototype.slice.call(myobject) // less efficient 
2

Trong ES6 bây giờ bạn có thể sử dụng Array.from() MDN ref

ví dụ

const args = Array.from(arguments); 
const str = args.shift(); 
+1

Trong ES6 bạn cũng có thể sử dụng variadic args: 'function f (... args) {args.shift(); } ' – mpen

Các vấn đề liên quan