lưu ý gần đây: Trong khi tôi hãnh diện rằng câu trả lời này đã nhận được nhiều sự ủng hộ, tôi cũng hơi kinh hoàng. Nếu một người cần chuyển đổi các chuỗi ký hiệu dấu chấm như "xabc" thành các tham chiếu, thì đó có thể (có thể) là một dấu hiệu cho thấy có điều gì đó rất sai đang xảy ra (trừ khi bạn có thể thực hiện một số khử tuần tự lạ).
Điều đó có nghĩa là, những người mới tìm được 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 các vấn đề về hiệu suất, VÀ bạn sẽ không cần phải xây dựng sự trừu tượng của mình để làm cho nó phức tạp hơn sau này. Trong thực tế, nếu điều này sẽ làm giảm độ phức tạp của mã và giữ 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 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ụ như hình thức chuyển đối tượng mặc định của ứng dụng xung quanh và hủy bỏ chúng). Giống như hỏi "làm thế nào tôi có thể tra cứu một hàm 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à siêu lập trình không cần thiết, và loại vi phạm kiểu mã hóa không có hiệu ứng phụ, và sẽ có hiệu năng đạt được). Thay vào đó, 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ụ ['x', 'a', 'b', 'c'] hoặc thậm chí một cái gì đó trực tiếp / đơn giản / đơn giản hơn nếu có thể: như không bị mất theo dõi các tham chiếu ở vị trí đầu tiên (lý tưởng nhất nếu chỉ ở phía máy khách hoặc chỉ phía máy chủ), v.v. (Một id duy nhất tồn tại trước đó sẽ không phù hợp để thêm, nhưng có thể được sử dụng nếu thông số khác yêu cầu tồn tại bất kể.)
trường hợp 2 : Làm việc với dữ liệu tuần tự 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 như một chuỗi "1999-12-30" chứ không phải là một đối tượng Ngày (có thể gây ra lỗi múi giờ hoặc thêm độ phức tạp nối tiếp nếu không cẩn thận). Hoặc bạn biết những gì bạn đang làm.
- Điều này có thể tốt. Hãy cẩn thận rằng không có chuỗi dấu chấm "." trong các mảnh đầu vào khử trùng 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 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)
[sửa] Hoặc trong ECMAScript 6:
'a.b.etc'.split('.').reduce((o,i)=>o[i], obj)
(Không phải tôi nghĩ eval luôn xấu như những người khác đề xuất (mặc dù nó thường như vậy), tuy nhiên những người đó sẽ hài lòng vì phương pháp này không sử dụng eval. Ở trên sẽ tìm thấy obj.a.b.etcđược đưa ra objvà chuỗi "a.b.etc".)
Để đáp ứng với những người vẫn còn sợ sử dụng reducemặc dù nó nằm trong tiêu chuẩn ECMA-262 (phiên bản thứ 5), đây là một 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 các 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 mọi hàm lồng nhau không được định nghĩa 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 trong một không gian tên đóng, đối tượng hoặc toàn cục).
chỉnh sửa :
Để trả lời một câu hỏi thú vị trong các ý kiế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 thiết lập 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
(sidenote: thật đáng buồn khi không thể trả lại một đối tượng bằng Setter, vì điều đó sẽ vi phạm quy ước gọi điện, bình luận viên dường như thay vào đó đề cập đến một chức năng kiểu setter chung với các hiệu ứng phụ như index(obj,"a.b.etc", value)làm obj.a.b.etc = value.)
Các reducephong cách là không thực sự phù hợp với điều đó, nhưng chúng ta có thể thay đổ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);
}
Bản giới thiệu:
> 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 setIndex(...). Tôi muốn kết thúc một lưu ý phụ rằng người đặt câu hỏi ban đầu có thể (nên?) Đang làm việc với các mảng chỉ số (mà họ có thể nhận được từ .split), 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 bình luận hỏi:
Còn mảng thì sao? một cái gì đó như "ab [4] .cd [1] [2] [3]"? CấmAlexS
Javascript là một ngôn ngữ rất kỳ lạ; trong các đối tượng chung chỉ có thể có các chuỗi làm khóa thuộc tính của chúng, vì vậy, ví dụ nếu xlà một đối tượng chung chung x={}, thì x[1]sẽ trở thành x["1"]... bạn đọc đúng ... yup ...
Mảng Javascript (vốn là phiên bản của Object) đặ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ư x=[]; x["puppy"]=5;.
Nhưng nói chung (và có những trường hợp ngoại lệ), x["somestring"]===x.somestring(khi được phép; bạn không thể làm được x.123).
(Hãy nhớ rằng bất kỳ trình biên dịch JS nào bạn đang sử dụng đều có thể chọn, có thể, để biên dịch chúng xuống các biểu diễn saner nếu nó có thể chứng minh rằng nó sẽ không vi phạm thông số kỹ thuật.)
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 giả sử những đối tượng đó chỉ chấp nhận số nguyên (do hạn chế trong miền vấn đề của bạn) hay không. Giả sử không. Sau đó, một biểu thức hợp lệ là một nối của một định danh cơ sở cộng với một số .identifiers cộng với một số ["stringindex"]s
Điều này sau đó sẽ tương đương với a["b"][4]["c"]["d"][1][2][3], mặc dù chúng ta có lẽ cũng nên hỗ trợ a.b["c\"validjsstringliteral"][3]. Bạn sẽ phải kiểm tra phần ngữ pháp ecmascript trên chuỗi ký tự chuỗi để xem cách phân tích một chuỗi ký tự hợp lệ. 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) đó alà định danh javascript hợp lệ .
Một câu trả lời đơn giản cho câu hỏi của bạn mặc dù, nếu dây 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 chiều dài 1+ chuỗi ký tự không có trong bộ ,hoặc [hoặc ]:
> "abc[4].c.def[1][2][\"gh\"]".match(/[^\]\[.]+/g)
// ^^^ ^ ^ ^^^ ^ ^ ^^^^^
["abc", "4", "c", "def", "1", "2", ""gh""]
Nếu chuỗi của bạn không chứa ký tự thoát hoặc "ký tự và vì Mã định danh là ngôn ngữ con của StringLiterals (tôi nghĩ ???) trước tiên bạn có thể chuyển đổi 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 vào 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 đối với 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à thực hiện giải pháp được lập trình quá mức, kém hiệu quả nhất mà chúng ta có thể đưa ra ... vì lợi ích của hamfist thuần túy cú pháp . Với các đối tượng Proxy ES6! ... Chúng ta cũng hãy xác định một số thuộc tính (imho là tốt và tuyệt vời nhưng) có thể phá vỡ các thư viện được viết không đúng. Có lẽ bạn nên cảnh giác khi sử dụng nó nếu bạn quan tâm đến hiệu suất, sự tỉnh táo (của bạn hoặc của 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);
}
Bản giới thiệu:
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']);
Đầu ra:
obj là: {"a": {"b": {"c": 1, "d": 2}}}
(ghi đè proxy) objHyper ['abc'] là: 1
(bộ ghi đè proxy) objHyper ['abc'] = 3, bây giờ obj là: {"a": {"b": {"c": 3, "d": 2}}}
(đằng sau hậu trường) objHyper là: Proxy {a: {'}}
(phím tắt) obj.H ['abc'] = 4
(phím tắt) obj.H ['abc'] là obj ['a'] ['b'] ['c'] là: 4
ý tưởng không hiệu quả: Bạn có thể sửa đổi những điều trên để gửi dựa trên đối số đầu vào; hoặc sử dụng .match(/[^\]\[.]+/g)phương thức để hỗ trợ obj['keys'].like[3]['this']hoặc nếu instanceof Array, sau đó chỉ chấp nhận một mảng làm đầu vào như thế nào keys = ['a','b','c']; obj.H[keys].
Mỗi đề xuất rằng có thể bạn muốn xử lý các chỉ mục không xác định theo cách kiểu NaN 'mềm hơn' (ví dụ: index({a:{b:{c:...}}}, 'a.x.c')trả về không xác định thay vì chưa gõ TypeError) ...:
1) Điều này có ý nghĩa từ quan điểm "chúng ta nên trả lại không xác định thay vì ném lỗi" trong tình huống chỉ mục 1 chiều ({}) ['eg'] == không xác định, vì vậy "chúng ta nên trả lại 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 mà chúng tôi đang làm x['a']['x']['c'], điều này sẽ thất bại với TypeError trong ví dụ trên.
Điều đó nói rằng, bạn sẽ làm cho công việc này bằng cách thay thế chức năng khử của bạn bằng:
(o,i)=>o===undefined?undefined:o[i]hoặc
(o,i)=>(o||{})[i].
(Bạn có thể thực hiện việc này hiệu quả hơn bằng cách sử dụng vòng lặp for và ngắt / trả lại bất cứ khi nào phần con mà bạn chỉ mục tiếp theo không xác định được hoặc sử dụng tính năng bắt thử nếu bạn cho rằng những thất bại đó là đủ hiếm.)
evallà ác; đừng sử dụng nó