Làm thế nào và tại sao 'a' ['toUpperCase'] () trong JavaScript hoạt động?


209

JavaScript làm tôi ngạc nhiên và đây là một ví dụ khác. Tôi chỉ bắt gặp một số mã mà lúc đầu tôi không hiểu. Vì vậy, tôi đã gỡ lỗi nó và đi đến kết quả này:

alert('a'['toUpperCase']());  //alerts 'A'

Bây giờ điều này phải rõ ràng nếu toUpperCase()được định nghĩa là một thành viên của kiểu chuỗi, nhưng ban đầu nó không có ý nghĩa gì với tôi.

Dù sao,

  • cái này có hoạt động không vì toUpperCaselà thành viên của 'a'? Hoặc có một cái gì đó khác đang diễn ra đằng sau hậu trường?
  • các đang tôi đang đọc có chức năng như sau:

    function callMethod(method) {
        return function (obj) {
            return obj[method](); //**how can I be sure method will always be a member of obj**
        }
    }
    
    var caps2 = map(['a', 'b', 'c'], callMethod('toUpperCase')); // ['A','B','C'] 
    // ignoring details of map() function which essentially calls methods on every 
    // element of the array and forms another array of result and returns it

    Đây là một hàm chung để gọi bất kỳ phương thức nào trên đối tượng BẤT K .. Nhưng điều đó có nghĩa là phương thức đã chỉ định sẽ là một thành viên ngầm định của đối tượng được chỉ định?

Tôi chắc chắn rằng tôi đang thiếu một số hiểu biết nghiêm túc về khái niệm cơ bản về các hàm JavaScript. Xin hãy giúp tôi hiểu điều này.


24
Có hai cách để truy cập các thuộc tính của một đối tượng: ký hiệu dấu chấm và ký hiệu dấu ngoặc. Hơi liên quan: stackoverflow.com/a/11922384/218196 . Bạn đã biết về ký hiệu dấu ngoặc vì bạn luôn sử dụng nó khi truy cập các phần tử mảng : arr[5]. Nếu số có tên định danh hợp lệ, bạn có thể sử dụng ký hiệu dấu chấm : arr.5.
Felix Kling

2
Nó giống như 5['toString']().
0x499602D2


Đọc liên quan: 1) Kế thừa và chuỗi nguyên mẫu: developer.mozilla.org/en-US/docs/JavaScript/Guide/iêu 2) Cuộc sống bí mật của JavaScript Nguyên thủy: javascriptweblog.wordpress.com/2010/09/27/
Theraot

21
Lúc đầu đọc tôi nghĩ tiêu đề là "làm thế nào và tại sao JavaScript hoạt động?" À
Có vấn đề

Câu trả lời:


293

Để phá vỡ nó.

  • .toUpperCase() là một phương pháp của String.prototype
  • 'a'là một giá trị nguyên thủy, nhưng được chuyển đổi thành biểu diễn Object của nó
  • Chúng tôi có hai ký hiệu có thể để truy cập các thuộc tính / phương thức đối tượng, ký hiệu dấu chấm và dấu ngoặc

Vì thế

'a'['toUpperCase'];

là truy cập thông qua ký hiệu ngoặc trên tài sản toUpperCase, từ String.prototype. Vì thuộc tính này tham chiếu một phương thức , chúng ta có thể gọi nó bằng cách đính kèm()

'a'['toUpperCase']();

Đây sẽ là một câu hỏi phỏng vấn vui nhộn
FluffyBying

78

foo.barfoo['bar']bằng nhau nên mã bạn đã đăng giống như

alert('a'.toUpperCase())

Khi sử dụng foo[bar](lưu ý thiếu dấu ngoặc kép) bạn không sử dụng tên bằng chữ barnhưng bất kỳ giá trị nào mà biến barchứa. Vì vậy, sử dụng foo[]ký hiệu thay vì foo.cho phép bạn sử dụng tên thuộc tính động.


Hãy cùng xem callMethod:

Trước hết, nó trả về một hàm lấy objlàm đối số của nó. Khi chức năng đó được thực thi, nó sẽ gọi methodđối tượng đó. Vì vậy, phương thức đã cho chỉ cần tồn tại trên objchính nó hoặc ở đâu đó trên chuỗi nguyên mẫu của nó.

Trong trường hợp toUpperCasephương thức đó xuất phát String.prototype.toUpperCase- sẽ khá ngu ngốc khi có một bản sao riêng của phương thức cho mỗi chuỗi tồn tại.


25

Bạn có thể truy cập các thành viên của bất kỳ đối tượng nào bằng .propertyNameký hiệu hoặc ["propertyName"]ký hiệu. Đó là tính năng của ngôn ngữ JavaScript. Để chắc chắn rằng thành viên đó có trong đối tượng, chỉ cần kiểm tra, nếu nó được xác định:

function callMethod(method) {
    return function (obj) {
        if (typeof(obj[method]) == 'function') //in that case, check if it is a function
           return obj[method](); //and then invoke it
    }
}

17

Về cơ bản, javascript coi mọi thứ là một Đối tượng, hay đúng hơn là mọi đối tượng có thể được xem như một từ điển / mảng kết hợp. Và các hàm / phương thức được định nghĩa chính xác theo cách tương tự cho đối tượng - như là một mục trong mảng kết hợp này.

Vì vậy, về cơ bản, bạn đang tham khảo / gọi điện (chú ý ' () ') thuộc tính 'toUpperCase' của đối tượng 'a' (trong trường hợp này là loại chuỗi).

Đây là một số mã trên đỉnh đầu của tôi:

function myObject(){
    this.msg = "hey there! ;-)";
    this.woop = function(){
        alert(this.msg); //do whatever with member data
    }
}

var obj = new myObject();
alert( obj.msg );
alert( obj['msg'] );
obj['woop']();

10

anyObject['anyPropertyName']giống như anyObject.anyPropertyNamekhianyPropertyName nhân vật không có vấn đề.

Xem Làm việc với các đối tượng , từ MDN.

Các toUpperCasephương pháp được gắn vào kiểu String. Khi bạn gọi một hàm trên một giá trị nguyên thủy, ở đây 'a', nó sẽ tự động được thăng cấp lên một đối tượng, ở đây là Chuỗi :

Trong bối cảnh nơi một phương thức được gọi trên chuỗi nguyên thủy hoặc tìm kiếm thuộc tính xảy ra, JavaScript sẽ tự động bọc chuỗi nguyên thủy và gọi phương thức hoặc thực hiện tra cứu thuộc tính.

Bạn có thể thấy chức năng tồn tại bằng cách đăng nhập String.prototype.toUpperCase.


9

Vì vậy, trong Javascript, objectsobjects. Đó là bản chất của chúng {}. Các thuộc tính đối tượng có thể được đặt bằng một trong hai cách sau: a.greeting = 'hello';hoặc a['greeting'] = 'hello';. Cả hai cách làm việc.

Công tác hồi quy giống nhau. a.greeting(không có dấu ngoặc kép) là 'hello', a['greeting']'hello'. Ngoại lệ: nếu thuộc tính là một số, chỉ có phương thức ngoặc hoạt động. Phương pháp dấu chấm không.

Vì vậy, 'a'một đối tượng với 'toUpperCase'tài sản mà thực sự là một chức năng. Bạn có thể truy xuất hàm và gọi nó sau đó theo cách nào đó: 'a'.toUpperCase()hoặc'a'['toUpperCase']() .

Nhưng imo cách tốt hơn để viết chức năng bản đồ sẽ là

var caps = ['a','b','c'].map( function(char) { return char.toUpperCase(); } )

Ai cần callMethodchức năng đó?


Giải thích tuyệt vời :-)
Elisha Senoo

6

Mỗi đối tượng JavaScript là một bảng băm do đó bạn có thể truy cập các thành viên của nó bằng cách chỉ định một khóa. ví dụ, nếu một biến là một chuỗi, thì nó sẽ có hàm toUpperCase. Vì vậy, bạn có thể gọi nó bằng cách

var str = "a"
str['toUpperCase'](). // you get the method by its name as a key and invoke it.

vì vậy, bằng str nội tuyến, bạn có thể có bên dưới

"a"["toUpperCase"]()

6

toUpperCase là một phương thức javascript tiêu chuẩn: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toUpperCase

Lý do nó hoạt động như 'a'['toUpperCase']()là hàm toUpperCase là một thuộc tính của đối tượng chuỗi 'a'. Bạn có thể tham chiếu các thuộc tính trên một đối tượng bằng cách sử dụng object[property]hoặc object.property. Cú pháp 'a''toUpperCase' cho biết bạn đang tham chiếu đúng 'toUppercase' của đối tượng chuỗi 'a', sau đó gọi nó ().


4

Nhưng điều đó có nghĩa là phương thức đã chỉ định sẽ là một thành viên ngầm định của đối tượng được chỉ định?

Không. Ai đó có thể vượt qua trong một đối tượng

  1. không có tài sản có tên toUpperCase; hoặc là
  2. có một tài sản được đặt tên toUpperCasekhông phải là một chức năng

Trong trường hợp đầu tiên, một lỗi sẽ được đưa ra vì truy cập vào một thuộc tính không tồn tại trả về undefinedvà chúng ta không thể gọi undefinedlà một hàm.

Trong trường hợp thứ hai, một lỗi sẽ được đưa ra bởi vì một lần nữa, chúng ta không thể gọi một hàm không phải là hàm.

Hãy nhớ rằng JavaScript là một ngôn ngữ được đánh máy rất lỏng lẻo. Ít hoặc không có loại kiểm tra xảy ra trừ khi và cho đến khi nó phải. Mã bạn đã hiển thị hoạt động trong một số trường hợp nhất định bởi vì, trong những trường hợp đó, đối tượng được truyền có một thuộc tính có tên toUpperCase, đó là một hàm.

Thực tế là objđối số không được đảm bảo có các loại thuộc tính phù hợp hoàn toàn không làm phiền JavaScript. Nó chấp nhận thái độ "chờ xem" và không đưa ra lỗi cho đến khi xảy ra sự cố thực sự trong thời gian chạy.


4

Hầu hết mọi thứ trong javascript đều có thể được coi là một đối tượng. Trong trường hợp của bạn, bảng chữ cái tự nó hoạt động như một đối tượng chuỗi và toUpperCasecó thể được gọi như phương thức của nó. Dấu ngoặc vuông chỉ là cách khác để truy cập các thuộc tính của đối tượng và vì đây toUpperCaselà một phương thức do đó ()cần đơn giản bên cạnh ['toUpperCase']hình thành['toUpperCase']() .

'a'['toUpperCase']() tương đương với 'a'.toUpperCase()

'a'['toUpperCase']() // returns A
'a'.toUpperCase() // returns A

3

Điều quan trọng cần lưu ý ở đây là, vì Javascript là một ngôn ngữ động , về cơ bản, mọi đối tượng chỉ là một bản đồ băm được tôn vinh ( với một vài ngoại lệ ). Và mọi thứ trong một đối tượng Javascript có thể được truy cập theo hai cách - ký hiệu dấu ngoặc và ký hiệu dấu chấm.

Tôi sẽ nhanh chóng đi qua hai ký hiệu trả lời phần đầu tiên của câu hỏi của bạn, và sau đó tôi sẽ đến phần thứ hai.

Ký hiệu khung

Chế độ này tương tự như truy cập hashmap và mảng trong các ngôn ngữ lập trình khác. Bạn có thể truy cập bất kỳ thành phần (dữ liệu (bao gồm các đối tượng khác) hoặc hàm) bằng cú pháp này.

Đây chính xác là những gì bạn đang làm trong ví dụ của bạn. Bạn có 'a', đó là một chuỗi (và không là một ký tự theo nghĩa đen, giống như nó sẽ có trong một ngôn ngữ như C ++).

Sử dụng ký hiệu ngoặc, bạn truy cập toUpperCasephương thức của nó . Nhưng truy cập nó vẫn chưa đủ; alertchẳng hạn như chỉ cần gõ , trong Javascript, không gọi phương thức. Đó chỉ là một tuyên bố đơn giản. Để gọi hàm, bạn cần thêm dấu ngoặc đơn: alert()hiển thị hộp thoại đơn giản chứa undefined, vì nó không nhận được tham số. Bây giờ chúng ta có thể sử dụng kiến ​​thức này để giải mã mã của bạn, trở thành:

alert('a'.toUpperCase());

Mà dễ đọc hơn nhiều.

Trên thực tế, một cách tốt để hiểu điều này tốt hơn một chút là thực thi Javascript sau:

alert(alert)

Điều này gọi alertbằng cách chuyển cho nó một đối tượng chức năng, đồng thời alert, mà không thực hiện cảnh báo thứ hai. Những gì được hiển thị (trong Chrome 26, ít nhất) là như sau:

function alert() { [native code] } 

Gọi điện thoại:

alert(alert())

hiển thị hai hộp thông báo liên tiếp chứa undefined. Điều này rất dễ giải thích: phần bên trong alert()được thực thi trước, hiển thị undefined(vì nó không có bất kỳ tham số nào) và không trả về gì cả. Cảnh báo bên ngoài nhận giá trị trả về của cảnh báo bên trong - không có gì và cũng hiển thị undefinedtrong hộp thông báo.

Hãy thử tất cả các trường hợp trên jsFiddle!

Ký hiệu chấm

Đây là cách tiếp cận chuẩn hơn, cho phép các thành viên của một đối tượng được truy cập bằng .toán tử dot ( ). Đây là những gì mã của bạn sẽ trông giống như trong ký hiệu chấm:

alert('a'.toUpperCase())

Dễ đọc hơn nhiều. Vậy khi nào chúng ta nên sử dụng ký hiệu chấm và khi nào nên sử dụng ký hiệu dấu ngoặc?

So sánh

Sự khác biệt chính giữa hai phương pháp là ngữ nghĩa. Ngoài ra còn có một số chi tiết khác, nhưng tôi sẽ đến với những người trong một giây. Điều quan trọng nhất là những gì bạn thực sự muốn làm - một nguyên tắc nhỏ là bạn sử dụng ký hiệu chấm cho các trường và phương thức được thiết lập tốt, và ký hiệu dấu ngoặc khi bạn thực sự sử dụng đối tượng của mình làm bản đồ băm .

Một ví dụ tuyệt vời về lý do tại sao quy tắc này rất quan trọng có thể được hiển thị trong ví dụ của bạn - vì mã đang sử dụng ký hiệu dấu ngoặc ở nơi ký hiệu dấu chấm sẽ hợp lý hơn nhiều, nó làm cho mã khó đọc hơn. Và đó là một điều xấu, bởi vì mã được đọc nhiều lần hơn so với nó được viết .

Trong một số trường hợp, bạn phải sử dụng ký hiệu dấu ngoặc ngay cả khi sử dụng ký hiệu dấu chấm hợp lý hơn:

  • nếu một thành viên của một đối tượng có một tên chứa một hoặc nhiều khoảng trắng hoặc bất kỳ ký tự đặc biệt nào khác, bạn không thể sử dụng ký hiệu dấu chấm: foo.some method()không hoạt động, nhưng foo["some method"]()không;

  • nếu bạn cần truy cập động các thành viên của đối tượng, bạn cũng bị kẹt khi sử dụng ký hiệu dấu ngoặc;

Thí dụ:

for(var i = 0; i < 10; ++i) {
   foo["method" + i](); 
 }

Điểm mấu chốt là bạn nên sử dụng cú pháp ngoặc khi sử dụng đối tượng làm bản đồ băm ( foods["burger"].eat()) và cú pháp dấu chấm khi làm việc với các trường và phương thức "thực tế" ( enemy.kill()). Với Javascript là một ngôn ngữ động, ranh giới giữa các trường "thực tế" và phương thức của một đối tượng và dữ liệu "khác" được lưu trữ bên trong có thể trở nên khá mờ. Nhưng miễn là bạn không trộn chúng theo những cách khó hiểu, bạn sẽ ổn thôi.


Bây giờ, vào phần còn lại của câu hỏi của bạn (cuối cùng !: P).

Làm thế nào tôi có thể chắc chắn phương pháp sẽ luôn là thành viên của obj

Bạn không thể. Thử nó. Cố gắng gọi derptrên một chuỗi. Bạn sẽ gặp lỗi trong các dòng:

Uncaught TypeError: Object a has no method 'derp' 

Đây là một hàm chung để gọi bất kỳ phương thức nào trên đối tượng BẤT K .. Nhưng điều đó có nghĩa là phương thức đã chỉ định sẽ là một thành viên ngầm định của đối tượng được chỉ định?

Vâng, trong trường hợp của bạn nó sẽ phải được. Nếu không, bạn kết thúc với lỗi tôi đã đề cập ở trên. Tuy nhiên, bạn không phải để sử dụng return obj[method]();trong các callMethod()chức năng. Bạn có thể thêm chức năng của riêng mình sau đó được sử dụng bởi chức năng bản đồ. Đây là một phương pháp được mã hóa cứng để biến tất cả các chữ cái thành một chữ cái viết hoa:

function makeCap()
{
    return function(obj) {
        return obj.toUpperCase();
    }
}

var caps2 = map(['a', 'b', 'c'], makeCap()); // ['A','B','C'] 
console.log(caps2)

Mã trong hướng dẫn bạn liên kết để sử dụng các chức năng một phần . Bản thân chúng là một khái niệm khó khăn. Đọc thêm về chủ đề đó sẽ giúp làm cho mọi thứ rõ ràng hơn tôi có thể làm cho chúng.


Lưu ý: đây là mã của chức năng bản đồ được sử dụng bởi mã trong câu hỏi, nguồn ở đây .

function map(arr, iterator) {
  var narr = [];
  for (var i = 0; i < arr.length; i++) narr.push(iterator(arr[i], i));
  return narr;
}

2

Nếu bạn hỏi làm thế nào nó thực sự hoạt động đó là cách tôi đọc nó. Ok đây là một hàm toán học đơn giản. Để hiểu nó bạn cần nhìn vào bảng ascii. Mà gán một giá trị số cho mỗi chữ cái. Để chuyển đổi nó, đối thủ cạnh tranh chỉ cần sử dụng một câu lệnh logic để chuyển đổi, ví dụ If (ChcrValue> 80 && charValue <106) // Đây là tập hợp các chữ cái viết thường sau đó charValue = charValue - 38; // khoảng cách giữa tập dưới và tập trên

Thật đơn giản, tôi thực sự không bận tâm đến việc tìm kiếm các giá trị chính xác tuy nhiên điều này về cơ bản là chuyển tất cả các chữ cái viết thường sang giá trị chữ hoa.


Làm thế nào điều này có liên quan đến câu hỏi?
Quasdunk

2
@glh, anh hỏi làm sao và tại sao 'a'['toUpperCase']()làm việc. Nhưng sự hiểu lầm là điều dễ hiểu, nếu ai đó không đọc câu hỏi.
LarsH

Câu trả lời của gr8, vì không có phong cách nào text-transform: uppercaseđược thêm vào, tôi cuối cùng cũng biết cách JS quản lý để thay đổi trường hợp, xóa nghi ngờ và học được một hoặc hai điều. cảm ơn rất nhiều.
dkjain
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.