Hướng dẫn convert dot notation to object javascript - chuyển đổi ký hiệu dấu chấm thành đối tượng javascript

Lưu ý gần đây: Mặc dù tôi rất tâng bốc rằng câu trả lời này đã nhận được nhiều upvote, tôi cũng hơi kinh hoàng. Nếu người ta cần chuyển đổi các chuỗi ghi chú dot như "X.A.B.C" thành các tài liệu tham khảo, thì nó có thể (có thể) là một dấu hiệu cho thấy có điều gì đó rất sai lầm đang diễn ra (trừ khi có thể bạn đang thực hiện một số vấn đề kỳ lạ). While I'm flattered that this answer has gotten many upvotes, I am also somewhat horrified. If one needs to convert dot-notation strings like "x.a.b.c" into references, it could (maybe) be a sign that there is something very wrong going on (unless maybe you're performing some strange deserialization).

Điều đó có nghĩa là, những người mới tìm đường đến câu trả lời này phải tự hỏi mình câu hỏi "Tại sao tôi lại làm điều này?"

Tất nhiên nói chung là tốt để làm điều này nếu trường hợp sử dụng của bạn nhỏ và bạn sẽ không gặp phải vấn đề về hiệu suất và bạn sẽ không cần phải xây dựng dựa trên sự trừu tượng của mình để làm cho nó phức tạp hơn sau này. Trên thực tế, nếu điều này sẽ làm giảm độ phức tạp của mã và giữ cho mọi thứ đơn giản, có lẽ bạn nên tiếp tục và làm những gì OP đang yêu cầu. Tuy nhiên, nếu đó không phải là trường hợp, hãy xem xét nếu có bất kỳ trong số này áp dụng:

Trường hợp 1: Là phương pháp chính để làm việc với dữ liệu của bạn (ví dụ: dưới dạng hình thức chuyển các đối tượng mặc định của ứng dụng của bạn xung quanh và phá hủy chúng). Giống như hỏi "Làm thế nào tôi có thể tra cứu một chức năng hoặc tên biến từ một chuỗi".

  • Đây là thực tiễn lập trình xấu (cụ thể là không cần thiết cụ thể và loại vi phạm phong cách mã hóa không có chức năng phụ, và sẽ có các lượt truy cập hiệu suất). Những người mới thấy mình trong trường hợp này, thay vào đó nên xem xét làm việc với các biểu diễn mảng, ví dụ: . bên hoặc phía máy chủ), v.v. (ID duy nhất có sẵn sẽ không phù hợp để thêm, nhưng có thể được sử dụng nếu thông số kỹ thuật khác yêu cầu sự tồn tại của nó bất kể.)

Trường hợp 2: Làm việc với dữ liệu tuần tự hóa hoặc dữ liệu sẽ được hiển thị cho người dùng. Giống như sử dụng một ngày làm chuỗi "1999-12-30" thay vì đối tượng ngày (có thể gây ra lỗi múi giờ hoặc thêm độ phức tạp tuần tự hóa nếu không cẩn thận). Hoặc bạn biết bạn đang làm gì.

  • Điều này có thể tốt. Hãy cẩn thận rằng không có chuỗi chấm "." trong các mảnh đầu vào vệ sinh của bạn.

Nếu bạn thấy mình sử dụng câu trả lời này mọi lúc và chuyển đổi qua lại giữa chuỗi và mảng, bạn có thể ở trong trường hợp xấu và nên xem xét một giải pháp thay thế.

Đây là một lớp lót một lớp thanh lịch ngắn hơn 10 lần so với các giải pháp khác:

function index(obj,i) {return obj[i]}
'a.b.etc'.split('.').reduce(index, obj)

[Chỉnh sửa] hoặc trong Ecmascript 6:

'a.b.etc'.split('.').reduce((o,i)=> o[i], obj)

.

Đáp lại những người vẫn còn sợ sử dụng

'a.b.etc'.split('.').reduce((o,i)=> o[i], obj)
3 mặc dù nó đang ở trong tiêu chuẩn ECMA-262 (Phiên bản thứ 5), đây là triển khai đệ quy hai dòng:

function multiIndex(obj,is) {  // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
    return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) {   // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
    return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')

Tùy thuộc vào tối ưu hóa mà trình biên dịch JS đang thực hiện, bạn có thể muốn đảm bảo bất kỳ chức năng lồng nào được xác định lại trên mỗi cuộc gọi thông qua các phương thức thông thường (đặt chúng vào một phần đóng, đối tượng hoặc không gian tên toàn cầu).

edit::

Để trả lời một câu hỏi thú vị trong các bình luận:

Làm thế nào bạn sẽ biến điều này thành một setter là tốt? Không chỉ trả về các giá trị theo đường dẫn, mà còn đặt chúng nếu một giá trị mới được gửi vào hàm? - Swader ngày 28 tháng 6 lúc 21:42

.

Phong cách

'a.b.etc'.split('.').reduce((o,i)=> o[i], obj)
3 không thực sự phù hợp với điều đó, nhưng chúng ta có thể sửa đổi việc thực hiện đệ quy:

function index(obj,is, value) {
    if (typeof is == 'string')
        return index(obj,is.split('.'), value);
    else if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}

Demo:

> obj = {a:{b:{etc:5}}}

> index(obj,'a.b.etc')
5
> index(obj,['a','b','etc'])   #works with both strings and lists
5

> index(obj,'a.b.etc', 123)    #setter-mode - third argument (possibly poor form)
123

> index(obj,'a.b.etc')
123

... Mặc dù cá nhân tôi khuyên bạn nên tạo một chức năng riêng biệt

'a.b.etc'.split('.').reduce((o,i)=> o[i], obj)
7. Tôi muốn kết thúc một nốt phụ mà người đăng ký ban đầu của câu hỏi có thể (nên?) Làm việc với các mảng chỉ số (mà họ có thể nhận được từ
'a.b.etc'.split('.').reduce((o,i)=> o[i], obj)
8), thay vì chuỗi; Mặc dù thường không có gì sai với chức năng tiện lợi.


Một người bình luận hỏi:

Còn mảng thì sao? Một cái gì đó như "A.B [4] .C.D [1] [2] [3]"? Cấmalexs

JavaScript là một ngôn ngữ rất kỳ lạ; Nói chung, các đối tượng chỉ có thể có các chuỗi làm khóa tài sản của chúng, vì vậy, ví dụ nếu

'a.b.etc'.split('.').reduce((o,i)=> o[i], obj)
9 là một đối tượng chung như
function multiIndex(obj,is) {  // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
    return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) {   // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
    return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')
0, thì
function multiIndex(obj,is) {  // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
    return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) {   // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
    return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')
1 sẽ trở thành ________ 22 ... bạn đọc đúng ... yup ...

Các mảng JavaScript (bản thân chúng là các trường hợp của đối tượng) đặc biệt khuyến khích các khóa số nguyên, mặc dù bạn có thể làm một cái gì đó như

function multiIndex(obj,is) {  // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
    return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) {   // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
    return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')
3.

Nhưng nói chung (và có những trường hợp ngoại lệ),

function multiIndex(obj,is) {  // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
    return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) {   // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
    return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')
4 (khi nó được phép; bạn không thể làm
function multiIndex(obj,is) {  // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
    return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) {   // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
    return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')
5).

.

Vì vậy, câu trả lời cho câu hỏi của bạn sẽ phụ thuộc vào việc bạn có giả sử các đối tượng đó chỉ chấp nhận số nguyên (do hạn chế trong miền vấn đề của bạn) hoặc không. Hãy giả sử không. Sau đó, một biểu thức hợp lệ là sự kết hợp của một định danh cơ sở cộng với một số

function multiIndex(obj,is) {  // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
    return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) {   // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
    return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')
6 cộng với một số
function multiIndex(obj,is) {  // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
    return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) {   // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
    return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')
7s.

Chúng ta hãy bỏ qua một lúc mà tất nhiên chúng ta có thể làm những việc khác một cách hợp pháp trong ngữ pháp như

function multiIndex(obj,is) {  // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
    return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) {   // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
    return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')
8; Số nguyên không phải là (đó) 'Đặc biệt'.

Tuyên bố của người bình luận sau đó sẽ tương đương với

function multiIndex(obj,is) {  // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
    return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) {   // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
    return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')
9, mặc dù có lẽ chúng ta cũng nên hỗ trợ
function index(obj,is, value) {
    if (typeof is == 'string')
        return index(obj,is.split('.'), value);
    else if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}
0. Bạn sẽ phải kiểm tra phần ngữ pháp Ecmascript trên các chuỗi chữ để xem làm thế nào để phân tích một chuỗi hợp lệ theo nghĩa đen. Về mặt kỹ thuật, bạn cũng muốn kiểm tra (không giống như trong câu trả lời đầu tiên của tôi) rằng
function index(obj,is, value) {
    if (typeof is == 'string')
        return index(obj,is.split('.'), value);
    else if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}
1 là định danh JavaScript hợp lệ.

Mặc dù vậy, một câu trả lời đơn giản cho câu hỏi của bạn, nếu chuỗi của bạn không chứa dấu phẩy hoặc dấu ngoặc, sẽ chỉ là phù hợp với độ dài 1+ chuỗi ký tự không có trong tập

function index(obj,is, value) {
    if (typeof is == 'string')
        return index(obj,is.split('.'), value);
    else if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}
2 hoặc
function index(obj,is, value) {
    if (typeof is == 'string')
        return index(obj,is.split('.'), value);
    else if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}
3 hoặc
function index(obj,is, value) {
    if (typeof is == 'string')
        return index(obj,is.split('.'), value);
    else if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}
4:

> "abc[4].c.def[1][2][\"gh\"]".match(/[^\]\[.]+/g)
// ^^^ ^  ^ ^^^ ^  ^   ^^^^^
["abc", "4", "c", "def", "1", "2", ""gh""]

Nếu các chuỗi của bạn không chứa ký tự thoát hoặc các ký tự

function index(obj,is, value) {
    if (typeof is == 'string')
        return index(obj,is.split('.'), value);
    else if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}
5 và vì định danh là một ngôn ngữ phụ của StringLiterals (tôi nghĩ ???) trước tiên bạn có thể chuyển đổi các dấu chấm của mình thành []:

> var R=[], demoString="abc[4].c.def[1][2][\"gh\"]";
> for(var match,matcher=/^([^\.\[]+)|\.([^\.\[]+)|\["([^"]+)"\]|\[(\d+)\]/g; 
      match=matcher.exec(demoString); ) {
  R.push(Array.from(match).slice(1).filter(x=> x!==undefined)[0]);
  // extremely bad code because js regexes are weird, don't use this
}
> R

["abc", "4", "c", "def", "1", "2", "gh"]

Tất nhiên, luôn luôn cẩn thận và không bao giờ tin tưởng dữ liệu của bạn. Một số cách xấu để làm điều này có thể hoạt động cho một số trường hợp sử dụng cũng bao gồm:

// hackish/wrongish; preprocess your string into "a.b.4.c.d.1.2.3", e.g.: 
> yourstring.replace(/]/g,"").replace(/\[/g,".").split(".")
"a.b.4.c.d.1.2.3"  //use code from before

Chỉnh sửa đặc biệt 2018:

Chúng ta hãy đi vòng tròn đầy đủ và làm giải pháp không hiệu quả nhất, không thể hiểu được khủng khiếp mà chúng ta có thể đưa ra ... vì lợi ích của Purityhamfistery cú pháp. Với các đối tượng proxy ES6! ... Chúng ta cũng định nghĩa một số thuộc tính mà (IMHO vẫn ổn và tuyệt vời nhưng) có thể phá vỡ các thư viện được viết không đúng cách. Có lẽ bạn nên cảnh giác khi sử dụng điều này nếu bạn quan tâm đến hiệu suất, sự tỉnh táo (của bạn hoặc người khác '), công việc của bạn, v.v.

// [1,2,3][-1]==3 (or just use .slice(-1)[0])
if (![1][-1])
    Object.defineProperty(Array.prototype, -1, {get() {return this[this.length-1]}}); //credit to caub

// WARNING: THIS XTREME™ RADICAL METHOD IS VERY INEFFICIENT,
// ESPECIALLY IF INDEXING INTO MULTIPLE OBJECTS,
// because you are constantly creating wrapper objects on-the-fly and,
// even worse, going through Proxy i.e. runtime ~reflection, which prevents
// compiler optimization

// Proxy handler to override obj[*]/obj.* and obj[*]=...
var hyperIndexProxyHandler = {
    get: function(obj,key, proxy) {
        return key.split('.').reduce((o,i)=> o[i], obj);
    },
    set: function(obj,key,value, proxy) {
        var keys = key.split('.');
        var beforeLast = keys.slice(0,-1).reduce((o,i)=> o[i], obj);
        beforeLast[keys[-1]] = value;
    },
    has: function(obj,key) {
        //etc
    }
};
function hyperIndexOf(target) {
    return new Proxy(target, hyperIndexProxyHandler);
}

Demo:

var obj = {a:{b:{c:1, d:2}}};
console.log("obj is:", JSON.stringify(obj));

var objHyper = hyperIndexOf(obj);
console.log("(proxy override get) objHyper['a.b.c'] is:", objHyper['a.b.c']);
objHyper['a.b.c'] = 3;
console.log("(proxy override set) objHyper['a.b.c']=3, now obj is:", JSON.stringify(obj));

console.log("(behind the scenes) objHyper is:", objHyper);

if (!({}).H)
    Object.defineProperties(Object.prototype, {
        H: {
            get: function() {
                return hyperIndexOf(this); // TODO:cache as a non-enumerable property for efficiency?
            }
        }
    });

console.log("(shortcut) obj.H['a.b.c']=4");
obj.H['a.b.c'] = 4;
console.log("(shortcut) obj.H['a.b.c'] is obj['a']['b']['c'] is", obj.H['a.b.c']);

Output:

obj là: {"a": {"b": {"c": 1, "d": 2}}}

.

.

.

(phím tắt) obj.h ['A.B.C'] = 4

.

Ý tưởng không hiệu quả: Bạn có thể sửa đổi điều trên để gửi dựa trên đối số đầu vào; Hoặc sử dụng phương thức

function index(obj,is, value) {
    if (typeof is == 'string')
        return index(obj,is.split('.'), value);
    else if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}
6 để hỗ trợ
function index(obj,is, value) {
    if (typeof is == 'string')
        return index(obj,is.split('.'), value);
    else if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}
7 hoặc nếu
function index(obj,is, value) {
    if (typeof is == 'string')
        return index(obj,is.split('.'), value);
    else if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}
8, thì chỉ cần chấp nhận một mảng làm đầu vào như
function index(obj,is, value) {
    if (typeof is == 'string')
        return index(obj,is.split('.'), value);
    else if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}
9.


Theo đề xuất rằng có thể bạn muốn xử lý các chỉ số không xác định theo cách thức 'mềm hơn' (ví dụ:

> obj = {a:{b:{etc:5}}}

> index(obj,'a.b.etc')
5
> index(obj,['a','b','etc'])   #works with both strings and lists
5

> index(obj,'a.b.etc', 123)    #setter-mode - third argument (possibly poor form)
123

> index(obj,'a.b.etc')
123
0 trả về không xác định thay vì kiểu mẫu không bị tổn thương) ...:

  1. Điều này có ý nghĩa từ quan điểm của "chúng ta nên trả về không xác định thay vì ném lỗi" trong tình huống chỉ số 1 chiều ({}) ['ví dụ.'] == Không xác định, vì vậy "chúng ta nên trả về không xác định thay vì ném lỗi" Trong tình huống N chiều.

  2. Điều này không có ý nghĩa từ quan điểm rằng chúng ta đang làm

    > obj = {a:{b:{etc:5}}}
    
    > index(obj,'a.b.etc')
    5
    > index(obj,['a','b','etc'])   #works with both strings and lists
    5
    
    > index(obj,'a.b.etc', 123)    #setter-mode - third argument (possibly poor form)
    123
    
    > index(obj,'a.b.etc')
    123
    
    1, điều này sẽ thất bại với một kiểu mẫu trong ví dụ trên.

Điều đó nói rằng, bạn sẽ thực hiện công việc này bằng cách thay thế chức năng giảm của mình bằng:

> obj = {a:{b:{etc:5}}}

> index(obj,'a.b.etc')
5
> index(obj,['a','b','etc'])   #works with both strings and lists
5

> index(obj,'a.b.etc', 123)    #setter-mode - third argument (possibly poor form)
123

> index(obj,'a.b.etc')
123
2, hoặc
> obj = {a:{b:{etc:5}}}

> index(obj,'a.b.etc')
5
> index(obj,['a','b','etc'])   #works with both strings and lists
5

> index(obj,'a.b.etc', 123)    #setter-mode - third argument (possibly poor form)
123

> index(obj,'a.b.etc')
123
3.

.