Cách truy cập đối tượng lồng nhau trong JavaScript một cách linh hoạt

JavaScript là một trong những ngôn ngữ linh hoạt nhất hiện có, nhưng đôi khi điều này đi kèm với chi phí hiệu suất đi kèm. Một ví dụ như vậy là việc sử dụng các thuộc tính được thêm động vào các đối tượng. Thật kỳ lạ, tác động hiệu suất này đến từ các công cụ JavaScript tối ưu hóa cho kiểu gõ tĩnh

Động cơ V8, cung cấp năng lượng cho Chrome và Node. js, sử dụng hình dạng và chuỗi chuyển tiếp để tối ưu hóa quyền truy cập thuộc tính đối tượng. Khi hai đối tượng có cùng thuộc tính, chúng được coi là có cùng hình dạng. Thêm thuộc tính mới vào đối tượng, tạo chuỗi chuyển tiếp để thêm thuộc tính mới. Không đi vào chi tiết hơn, rõ ràng là một hình dạng đơn giản sẽ truy cập nhanh hơn một chuỗi chuyển tiếp

Quay trở lại các thuộc tính được thêm động, công cụ không thể biết trước những thuộc tính nào sẽ được thêm vào đối tượng. Do đó, cuối cùng nó tạo ra một chuỗi chuyển tiếp cho từng thuộc tính mới được thêm vào. Đây không phải là vấn đề lớn đối với một vài thuộc tính, nhưng nó có thể trở thành vấn đề khi thêm nhiều thuộc tính vào một đối tượng

May mắn thay, điều này là dễ dàng để sửa chữa. Giải pháp đơn giản nhất là xác định trước tất cả các thuộc tính có thể có của một đối tượng và gán cho chúng một giá trị rỗng (i. e. null hoặc undefined). Bằng cách này, công cụ có thể tạo hình dạng cho đối tượng và tối ưu hóa quyền truy cập thuộc tính. Điều này không phải lúc nào cũng khả thi, nhưng đó là một cách thực hành tốt để làm theo

Tôi có thể chuyển cái gì cho hàm getter để cho phép tôi làm điều đó, (và tất nhiên cũng cho phép tôi nhận các thuộc tính không lồng nhau bằng cùng một hàm)

Tôi đã thử

function getPropByString(obj, propString) {
  if (!propString)
    return obj;

  var prop, props = propString.split('.');

  for (var i = 0, iLen = props.length - 1; i < iLen; i++) {
    prop = props[i];

    var candidate = obj[prop];
    if (candidate !== undefined) {
      obj = candidate;
    } else {
      break;
    }
  }
  return obj[props[i]];
}

var obj = {
  foo: {
    bar: {
      baz: 'x'
    }
  }
};

console.log(getPropByString(obj, 'foo.bar.baz')); // x
console.log(getPropByString(obj, 'foo.bar.baz.buk')); // undefined
8 và
function getPropByString(obj, propString) {
  if (!propString)
    return obj;

  var prop, props = propString.split('.');

  for (var i = 0, iLen = props.length - 1; i < iLen; i++) {
    prop = props[i];

    var candidate = obj[prop];
    if (candidate !== undefined) {
      obj = candidate;
    } else {
      break;
    }
  }
  return obj[props[i]];
}

var obj = {
  foo: {
    bar: {
      baz: 'x'
    }
  }
};

console.log(getPropByString(obj, 'foo.bar.baz')); // x
console.log(getPropByString(obj, 'foo.bar.baz.buk')); // undefined
9 nhưng chúng không hoạt động

Tôi cho rằng tôi có thể phân tích cú pháp cho các dấu chấm hoặc dấu ngoặc (như tại đây. Trong javascript, kiểm tra thuộc tính được lồng sâu trong biểu đồ đối tượng?). Có cách nào sạch hơn không? . )

Đặc biệt là vì tôi cần thiết lập sâu nhiều lần trong một vòng lặp cho một loạt các phần tử mảng

Giải pháp tốt nhất

Bạn có thể sử dụng chức năng truy cập sâu dựa trên chuỗi cho đường dẫn. Lưu ý rằng bạn không thể có bất kỳ dấu chấm nào trong tên thuộc tính

function getPropByString(obj, propString) {
  if (!propString)
    return obj;

  var prop, props = propString.split('.');

  for (var i = 0, iLen = props.length - 1; i < iLen; i++) {
    prop = props[i];

    var candidate = obj[prop];
    if (candidate !== undefined) {
      obj = candidate;
    } else {
      break;
    }
  }
  return obj[props[i]];
}

var obj = {
  foo: {
    bar: {
      baz: 'x'
    }
  }
};

console.log(getPropByString(obj, 'foo.bar.baz')); // x
console.log(getPropByString(obj, 'foo.bar.baz.buk')); // undefined

Nếu chuỗi truy cập trống, trả về đối tượng. Mặt khác, tiếp tục đi dọc theo đường truy cập cho đến người truy cập cuối cùng thứ hai. Nếu đó là một đối tượng, trả về giá trị

if(o.myProperty === undefined) {
  alert("myProperty value is the special value `undefined`");
}
0 cuối cùng. Nếu không, trả về không xác định

Giải pháp liên quan

Javascript – Phát hiện thuộc tính đối tượng không xác định

Cách thông thường để kiểm tra xem giá trị của một tài sản có phải là giá trị đặc biệt

if(o.myProperty === undefined) {
  alert("myProperty value is the special value `undefined`");
}
1 hay không, là

if(o.myProperty === undefined) {
  alert("myProperty value is the special value `undefined`");
}

Để kiểm tra xem một đối tượng không thực sự có một thuộc tính như vậy hay không và do đó sẽ trả về

if(o.myProperty === undefined) {
  alert("myProperty value is the special value `undefined`");
}
1 theo mặc định khi bạn thử và truy cập nó

if(!o.hasOwnProperty('myProperty')) {
  alert("myProperty does not exist");
}

Để kiểm tra xem giá trị được liên kết với mã định danh có phải là giá trị đặc biệt

if(o.myProperty === undefined) {
  alert("myProperty value is the special value `undefined`");
}
1 hay mã định danh đó chưa được khai báo. Ghi chú. phương pháp này là cách duy nhất để đề cập đến một không được khai báo (lưu ý. khác với việc có giá trị của mã định danh
if(o.myProperty === undefined) {
  alert("myProperty value is the special value `undefined`");
}
1) mà không có lỗi sớm

if(typeof myVariable === 'undefined') {
  alert('myVariable is either the special value `undefined`, or it has not been declared');
}

Trong các phiên bản JavaScript trước ECMAScript 5, thuộc tính có tên "không xác định" trên đối tượng chung có thể ghi được và do đó, một kiểm tra đơn giản

if(o.myProperty === undefined) {
  alert("myProperty value is the special value `undefined`");
}
5 có thể hoạt động không mong muốn nếu nó vô tình được xác định lại. Trong JavaScript hiện đại, thuộc tính chỉ đọc

Tuy nhiên, trong JavaScript hiện đại, "không xác định" không phải là một từ khóa và do đó, các biến bên trong các hàm có thể được đặt tên là "không xác định" và che khuất thuộc tính toàn cầu

Nếu bạn lo lắng về trường hợp cạnh (không chắc) này, bạn có thể sử dụng toán tử void để lấy chính giá trị

if(o.myProperty === undefined) {
  alert("myProperty value is the special value `undefined`");
}
1 đặc biệt

if(myVariable === void 0) {
  alert("myVariable is the special value `undefined`");
}

Javascript – Cách đóng JavaScript hoạt động

Một đóng cửa là một cặp của

  1. Một chức năng, và
  2. Tham chiếu đến phạm vi bên ngoài của hàm đó (môi trường từ vựng)

Môi trường từ vựng là một phần của mọi ngữ cảnh thực thi (khung ngăn xếp) và là bản đồ giữa các mã định danh (nghĩa là. tên biến cục bộ) và giá trị

Mỗi chức năng trong JavaScript duy trì một tham chiếu đến môi trường từ vựng bên ngoài của nó. Tham chiếu này được sử dụng để định cấu hình ngữ cảnh thực thi được tạo khi một hàm được gọi. Tham chiếu này cho phép mã bên trong hàm "nhìn thấy" các biến được khai báo bên ngoài hàm, bất kể khi nào và ở đâu hàm được gọi

Nếu một chức năng được gọi bởi một chức năng, đến lượt nó được gọi bởi một chức năng khác, thì một chuỗi tham chiếu đến môi trường từ vựng bên ngoài được tạo. Chuỗi này được gọi là chuỗi phạm vi

Trong đoạn mã sau,

if(o.myProperty === undefined) {
  alert("myProperty value is the special value `undefined`");
}
7 tạo thành một bao đóng với môi trường từ vựng của ngữ cảnh thực thi được tạo khi gọi ___3_______8, đóng trên biến _______3_______9

function foo() {
  const secret = Math.trunc(Math.random()*100)
  return function inner() {
    console.log(`The secret number is ${secret}.`)
  }
}
const f = foo() // `secret` is not directly accessible from outside `foo`
f() // The only way to retrieve `secret`, is to invoke `f`

Nói cách khác. trong JavaScript, các hàm mang tham chiếu đến một "hộp trạng thái" riêng tư mà chỉ chúng (và bất kỳ hàm nào khác được khai báo trong cùng một môi trường từ vựng) mới có quyền truy cập. Hộp trạng thái này là vô hình đối với người gọi hàm, cung cấp một cơ chế tuyệt vời để ẩn và đóng gói dữ liệu

Và hãy nhớ. các hàm trong JavaScript có thể được truyền xung quanh giống như các biến (hàm hạng nhất), nghĩa là các cặp chức năng và trạng thái này có thể được truyền xung quanh chương trình của bạn. tương tự như cách bạn có thể truyền một thể hiện của một lớp xung quanh trong C++

Nếu JavaScript không có bao đóng, thì sẽ phải chuyển nhiều trạng thái hơn giữa các hàm một cách rõ ràng, làm cho danh sách tham số dài hơn và mã ồn hơn

Vì vậy, nếu bạn muốn một chức năng luôn có quyền truy cập vào một phần trạng thái riêng tư, bạn có thể sử dụng bao đóng

và chúng ta thường muốn liên kết trạng thái với một chức năng. Ví dụ, trong Java hoặc C++, khi bạn thêm một biến thể hiện riêng và một phương thức vào một lớp, bạn đang liên kết trạng thái với chức năng

Trong C và hầu hết các ngôn ngữ phổ biến khác, sau khi một hàm trả về, tất cả các biến cục bộ không còn truy cập được nữa do khung ngăn xếp bị hủy. Trong JavaScript, nếu bạn khai báo một hàm bên trong một hàm khác, thì các biến cục bộ của hàm bên ngoài vẫn có thể truy cập được sau khi trở về từ hàm đó. Theo cách này, trong đoạn mã trên,

if(o.myProperty === undefined) {
  alert("myProperty value is the special value `undefined`");
}
9 vẫn có sẵn cho đối tượng hàm
if(o.myProperty === undefined) {
  alert("myProperty value is the special value `undefined`");
}
7, sau khi nó được trả về từ
if(o.myProperty === undefined) {
  alert("myProperty value is the special value `undefined`");
}
8

Công dụng của Đóng cửa

Các bao đóng hữu ích bất cứ khi nào bạn cần một trạng thái riêng tư được liên kết với một chức năng. Đây là một kịch bản rất phổ biến - và hãy nhớ. JavaScript không có cú pháp lớp cho đến năm 2015 và nó vẫn không có cú pháp trường riêng. Đóng cửa đáp ứng nhu cầu này

Biến thể hiện cá nhân

Trong đoạn mã sau, hàm

if(!o.hasOwnProperty('myProperty')) {
  alert("myProperty does not exist");
}
3 đóng trên các chi tiết của ô tô

function Car(manufacturer, model, year, color) {
  return {
    toString() {
      return `${manufacturer} ${model} (${year}, ${color})`
    }
  }
}
const car = new Car('Aston Martin','V8 Vantage','2012','Quantum Silver')
console.log(car.toString())

Lập trình chức năng

Trong đoạn mã sau, hàm

if(o.myProperty === undefined) {
  alert("myProperty value is the special value `undefined`");
}
7 đóng trên cả
if(!o.hasOwnProperty('myProperty')) {
  alert("myProperty does not exist");
}
5 và
if(!o.hasOwnProperty('myProperty')) {
  alert("myProperty does not exist");
}
6

function curry(fn) {
  const args = []
  return function inner(arg) {
    if(args.length === fn.length) return fn(...args)
    args.push(arg)
    return inner
  }
}

function add(a, b) {
  return a + b
}

const curriedAdd = curry(add)
console.log(curriedAdd(2)(3)()) // 5

Lập trình hướng sự kiện

Trong đoạn mã sau, hàm

if(!o.hasOwnProperty('myProperty')) {
  alert("myProperty does not exist");
}
7 đóng trên biến
if(!o.hasOwnProperty('myProperty')) {
  alert("myProperty does not exist");
}
8

const $ = document.querySelector.bind(document)
const BACKGROUND_COLOR = 'rgba(200,200,242,1)'

function onClick() {
  $('body').style.background = BACKGROUND_COLOR
}

$('button').addEventListener('click', onClick)
function getPropByString(obj, propString) {
  if (!propString)
    return obj;

  var prop, props = propString.split('.');

  for (var i = 0, iLen = props.length - 1; i < iLen; i++) {
    prop = props[i];

    var candidate = obj[prop];
    if (candidate !== undefined) {
      obj = candidate;
    } else {
      break;
    }
  }
  return obj[props[i]];
}

var obj = {
  foo: {
    bar: {
      baz: 'x'
    }
  }
};

console.log(getPropByString(obj, 'foo.bar.baz')); // x
console.log(getPropByString(obj, 'foo.bar.baz.buk')); // undefined
0

mô đun hóa

Trong ví dụ sau, tất cả các chi tiết triển khai được ẩn bên trong một biểu thức hàm được thực thi ngay lập tức. Các chức năng

if(!o.hasOwnProperty('myProperty')) {
  alert("myProperty does not exist");
}
9 và
if(!o.hasOwnProperty('myProperty')) {
  alert("myProperty does not exist");
}
3 đóng trên trạng thái tư nhân và các chức năng họ cần để hoàn thành công việc của mình. Việc đóng cửa đã cho phép chúng tôi mô đun hóa và đóng gói mã của chúng tôi

function getPropByString(obj, propString) {
  if (!propString)
    return obj;

  var prop, props = propString.split('.');

  for (var i = 0, iLen = props.length - 1; i < iLen; i++) {
    prop = props[i];

    var candidate = obj[prop];
    if (candidate !== undefined) {
      obj = candidate;
    } else {
      break;
    }
  }
  return obj[props[i]];
}

var obj = {
  foo: {
    bar: {
      baz: 'x'
    }
  }
};

console.log(getPropByString(obj, 'foo.bar.baz')); // x
console.log(getPropByString(obj, 'foo.bar.baz.buk')); // undefined
1

ví dụ

ví dụ 1

Ví dụ này cho thấy các biến cục bộ không được sao chép trong bao đóng. việc đóng cửa duy trì một tham chiếu đến chính các biến ban đầu. Như thể khung ngăn xếp vẫn tồn tại trong bộ nhớ ngay cả sau khi chức năng bên ngoài thoát

function getPropByString(obj, propString) {
  if (!propString)
    return obj;

  var prop, props = propString.split('.');

  for (var i = 0, iLen = props.length - 1; i < iLen; i++) {
    prop = props[i];

    var candidate = obj[prop];
    if (candidate !== undefined) {
      obj = candidate;
    } else {
      break;
    }
  }
  return obj[props[i]];
}

var obj = {
  foo: {
    bar: {
      baz: 'x'
    }
  }
};

console.log(getPropByString(obj, 'foo.bar.baz')); // x
console.log(getPropByString(obj, 'foo.bar.baz.buk')); // undefined
2

ví dụ 2

Trong đoạn mã sau, ba phương thức

if(typeof myVariable === 'undefined') {
  alert('myVariable is either the special value `undefined`, or it has not been declared');
}
1,
if(typeof myVariable === 'undefined') {
  alert('myVariable is either the special value `undefined`, or it has not been declared');
}
2 và
if(typeof myVariable === 'undefined') {
  alert('myVariable is either the special value `undefined`, or it has not been declared');
}
3 đều đóng trên cùng một môi trường từ vựng

Và mỗi khi

if(typeof myVariable === 'undefined') {
  alert('myVariable is either the special value `undefined`, or it has not been declared');
}
4 được gọi, một bối cảnh thực thi mới (khung ngăn xếp) được tạo và một biến hoàn toàn mới
if(typeof myVariable === 'undefined') {
  alert('myVariable is either the special value `undefined`, or it has not been declared');
}
5 và một bộ hàm mới (
if(typeof myVariable === 'undefined') {
  alert('myVariable is either the special value `undefined`, or it has not been declared');
}
1, v.v. ) được tạo, đóng trên biến mới này

function getPropByString(obj, propString) {
  if (!propString)
    return obj;

  var prop, props = propString.split('.');

  for (var i = 0, iLen = props.length - 1; i < iLen; i++) {
    prop = props[i];

    var candidate = obj[prop];
    if (candidate !== undefined) {
      obj = candidate;
    } else {
      break;
    }
  }
  return obj[props[i]];
}

var obj = {
  foo: {
    bar: {
      baz: 'x'
    }
  }
};

console.log(getPropByString(obj, 'foo.bar.baz')); // x
console.log(getPropByString(obj, 'foo.bar.baz.buk')); // undefined
3

ví dụ 3

Nếu bạn đang sử dụng các biến được khai báo bằng cách sử dụng

if(typeof myVariable === 'undefined') {
  alert('myVariable is either the special value `undefined`, or it has not been declared');
}
7, hãy cẩn thận để biết bạn đang đóng biến nào. Các biến được khai báo bằng cách sử dụng
if(typeof myVariable === 'undefined') {
  alert('myVariable is either the special value `undefined`, or it has not been declared');
}
7 được nâng lên. Đây là một vấn đề ít hơn nhiều trong JavaScript hiện đại do sự ra đời của
if(typeof myVariable === 'undefined') {
  alert('myVariable is either the special value `undefined`, or it has not been declared');
}
9 và
if(myVariable === void 0) {
  alert("myVariable is the special value `undefined`");
}
0

Trong đoạn mã sau, mỗi lần trong vòng lặp, một hàm mới

if(o.myProperty === undefined) {
  alert("myProperty value is the special value `undefined`");
}
7 được tạo, hàm này đóng trên
if(myVariable === void 0) {
  alert("myVariable is the special value `undefined`");
}
2. Nhưng vì
if(myVariable === void 0) {
  alert("myVariable is the special value `undefined`");
}
3 được nâng lên bên ngoài vòng lặp, tất cả các hàm bên trong này đóng trên cùng một biến, nghĩa là giá trị cuối cùng của
if(myVariable === void 0) {
  alert("myVariable is the special value `undefined`");
}
2 (3) được in ba lần

Làm cách nào để lấy giá trị đối tượng lồng nhau động trong JavaScript?

Bạn có thể truy cập động các thuộc tính đối tượng lồng nhau trong JavaScript bằng cách sử dụng dấu ngoặc vuông . Bạn cần sử dụng nhiều dấu ngoặc vuông cho một đối tượng lồng nhau. Tôi có một đối tượng lồng nhau với thuộc tính tên cũng là một đối tượng. Thuộc tính tên chứa hai thuộc tính lồng nhau.

Làm cách nào để truy cập động đối tượng bên trong đối tượng trong JavaScript?

Để truy cập thuộc tính đối tượng một cách linh hoạt trong JS, bạn cần phải chuyển tên thuộc tính dưới dạng một chuỗi trong dấu ngoặc vuông . Khi giá trị của khóa biến thay đổi, chúng tôi có quyền truy cập vào các thuộc tính khác nhau của đối tượng người dùng. Cách phổ biến nhất để truy cập các thuộc tính đối tượng trong JavaScript là dấu chấm.

Làm cách nào để truy cập đối tượng được lồng sâu trong JavaScript?

Cấu trúc dữ liệu lồng nhau là một mảng hoặc đối tượng tham chiếu đến các mảng hoặc đối tượng khác, i. e. giá trị của nó là mảng hoặc đối tượng. Có thể truy cập các cấu trúc như vậy bằng cách áp dụng liên tiếp ký hiệu dấu chấm hoặc dấu ngoặc vuông . Đây là một ví dụ. dữ liệu const = { mã. 42, mặt hàng. [{ Tôi. 1, tên. 'foo' }, { id. 2, tên. 'thanh' }] };

Làm cách nào để tạo đối tượng lồng nhau động trong JavaScript?

Chuỗi cung cấp tên của cài đặt sau đó được phân tách bằng dấu gạch dưới thành một mảng. Cài đặt mới sẽ được thêm vào đối tượng "Cài đặt" hiện có bằng cách tạo các đối tượng lồng nhau mới với tên được đặt bởi từng phần của mảng, ngoại trừ phần cuối cùng phải là một chuỗi cung cấp giá trị của cài đặt