Làm thế nào để bạn đánh giá một mã javascript?

Đây là chương thứ ba của loạt bài Viết một khung JavaScript. Trong chương này, tôi sẽ giải thích các cách đánh giá mã khác nhau trong trình duyệt và các vấn đề mà chúng gây ra. Tôi cũng sẽ giới thiệu một phương pháp dựa trên một số tính năng JavaScript mới hoặc ít được biết đến hơn.

Loạt bài này nói về một framework phía máy khách mã nguồn mở, được gọi là NX. Trong loạt bài này, tôi giải thích những khó khăn chính mà tôi phải vượt qua khi viết framework. Nếu bạn quan tâm đến NX, vui lòng truy cập trang chủ

Bộ truyện bao gồm các chương sau

  1. Cấu trúc dự án
  2. thời gian thực hiện
  3. Đánh giá mã hộp cát (chương hiện tại)
  4. Giới thiệu ràng buộc dữ liệu
  5. Liên kết dữ liệu với Proxy ES6
  6. yếu tố tùy chỉnh
  7. Định tuyến phía máy khách

Ác ma

Hàm 

function compileCode (src) {
  return new Function(src)
}
8 đánh giá mã JavaScript được biểu thị dưới dạng chuỗi

Một giải pháp phổ biến để đánh giá mã là hàm 

function compileCode (src) {
  return new Function(src)
}
8 . Mã được đánh giá bởi
function compileCode (src) {
  return new Function(src)
}
8 có quyền truy cập vào các lần đóng và phạm vi toàn cầu, điều này dẫn đến một vấn đề bảo mật được gọi là chèn mã và khiến 
function compileCode (src) {
  return new Function(src)
}
8 trở thành một trong những tính năng khét tiếng nhất của JavaScript

Mặc dù bị cau mày, nhưng

function compileCode (src) {
  return new Function(src)
}
8 rất hữu ích trong một số tình huống. Hầu hết các khung giao diện người dùng hiện đại đều yêu cầu chức năng của nó nhưng không dám sử dụng vì vấn đề được đề cập ở trên. Do đó, nhiều giải pháp thay thế đã xuất hiện để đánh giá các chuỗi trong hộp cát thay vì phạm vi toàn cầu. Hộp cát ngăn mã truy cập dữ liệu an toàn. Thông thường, nó là một đối tượng JavaScript đơn giản, thay thế đối tượng toàn cầu cho mã được đánh giá

cách chung

Phương án thay thế 

function compileCode (src) {
  return new Function(src)
}
8 phổ biến nhất là triển khai lại hoàn toàn – quy trình gồm hai bước, bao gồm phân tích cú pháp và diễn giải chuỗi đã truyền. Đầu tiên, trình phân tích cú pháp tạo cây cú pháp trừu tượng, sau đó trình thông dịch sẽ duyệt cây và diễn giải nó dưới dạng mã bên trong hộp cát

Đây là một giải pháp được sử dụng rộng rãi, nhưng nó được cho là quá nặng đối với một thứ đơn giản như vậy. Viết lại mọi thứ từ đầu thay vì vá lỗi 

function compileCode (src) {
  return new Function(src)
}
8 tạo ra rất nhiều lỗi và cũng cần phải sửa đổi thường xuyên để theo kịp các bản cập nhật ngôn ngữ mới nhất

một cách khác

NX cố gắng tránh triển khai lại mã gốc. Đánh giá được xử lý bởi một thư viện nhỏ sử dụng một số tính năng JavaScript mới hoặc ít được biết đến hơn

Phần này sẽ dần dần giới thiệu các tính năng này và sử dụng chúng để giải thích thư viện đánh giá mã nx-compile. Thư viện có một hàm tên là 

function compileCode (src) {
  return new Function(src)
}
3, hoạt động như bên dưới

const code = compileCode('return num1 + num2')

// this logs 17 to the console
console.log(code({num1: 10, num2: 7}))

const globalNum = 12
const otherCode = compileCode('return globalNum')

// global scope access is prevented
// this logs undefined to the console
console.log(otherCode({num1: 2, num2: 3}))

Đến cuối bài viết này, chúng ta sẽ triển khai hàm 

function compileCode (src) {
  return new Function(src)
}
3 trong chưa đầy 20 dòng

Chức năng mới()

Hàm tạo Hàm tạo một đối tượng Hàm mới. Trong JavaScript, mọi chức năng thực sự là một đối tượng Chức năng

Hàm tạo 

function compileCode (src) {
  return new Function(src)
}
5 là một thay thế cho 
function compileCode (src) {
  return new Function(src)
}
8.
function compileCode (src) {
  return new Function(src)
}
7 đánh giá chuỗi 
function compileCode (src) {
  return new Function(src)
}
8 đã truyền dưới dạng mã và trả về một hàm mới thực thi mã đó. Nó khác với 
function compileCode (src) {
  return new Function(src)
}
8 ở hai điểm chính

  • Nó đánh giá mã đã truyền chỉ một lần. Gọi hàm được trả về sẽ chạy mã mà không cần đánh giá lại mã
  • Nó không có quyền truy cập vào các biến đóng cục bộ, tuy nhiên, nó vẫn có thể truy cập phạm vi toàn cầu
function compileCode (src) {
  return new Function(src)
}

function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}
0 là lựa chọn thay thế tốt hơn cho 
function compileCode (src) {
  return new Function(src)
}
8 đối với trường hợp sử dụng của chúng ta. Nó có hiệu suất và bảo mật vượt trội, nhưng quyền truy cập phạm vi toàn cầu vẫn phải được ngăn chặn để làm cho nó khả thi

Từ khóa 'với'

Câu lệnh with mở rộng chuỗi phạm vi cho một câu lệnh

function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}
2 là một từ khóa ít được biết đến hơn trong JavaScript. Nó cho phép thực thi bán hộp cát. Mã bên trong một khối 
function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}
2 cố gắng truy xuất các biến từ đối tượng hộp cát đã truyền trước, nhưng nếu không tìm thấy ở đó, mã sẽ tìm biến trong phạm vi đóng và phạm vi toàn cầu. Quyền truy cập vào phạm vi đóng bị ngăn chặn bởi 
function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}
0 vì vậy chúng tôi chỉ phải lo lắng về phạm vi toàn cầu

function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}

function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}
2 sử dụng _______76 toán tử trong nội bộ. Đối với mọi quyền truy cập biến bên trong khối, nó sẽ đánh giá điều kiện 
function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}
7. Nếu điều kiện là đúng, nó sẽ lấy biến từ sandbox. Nếu không, nó sẽ tìm biến trong phạm vi toàn cầu. Bằng cách đánh lừa 
function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}
2 để luôn đánh giá 
function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}
7 là trung thực, chúng tôi có thể ngăn nó truy cập vào phạm vi toàn cầu

proxy ES6

Đối tượng Proxy được sử dụng để xác định hành vi tùy chỉnh cho các hoạt động cơ bản như tra cứu hoặc gán thuộc tính

ES6 

const code = compileCode('return num1 + num2')

// this logs 17 to the console
console.log(code({num1: 10, num2: 7}))

const globalNum = 12
const otherCode = compileCode('return globalNum')

// global scope access is prevented
// this logs undefined to the console
console.log(otherCode({num1: 2, num2: 3}))
70 bao bọc một đối tượng và xác định các hàm bẫy, có thể chặn các hoạt động cơ bản trên đối tượng đó. Các chức năng bẫy được gọi khi một hoạt động xảy ra. Bằng cách bọc đối tượng sandbox trong một 
const code = compileCode('return num1 + num2')

// this logs 17 to the console
console.log(code({num1: 10, num2: 7}))

const globalNum = 12
const otherCode = compileCode('return globalNum')

// global scope access is prevented
// this logs undefined to the console
console.log(otherCode({num1: 2, num2: 3}))
70 và xác định một 
const code = compileCode('return num1 + num2')

// this logs 17 to the console
console.log(code({num1: 10, num2: 7}))

const globalNum = 12
const otherCode = compileCode('return globalNum')

// global scope access is prevented
// this logs undefined to the console
console.log(otherCode({num1: 2, num2: 3}))
72 bẫy, chúng ta có thể ghi đè hành vi mặc định của toán tử 
function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}

const code = compileCode('return num1 + num2')

// this logs 17 to the console
console.log(code({num1: 10, num2: 7}))

const globalNum = 12
const otherCode = compileCode('return globalNum')

// global scope access is prevented
// this logs undefined to the console
console.log(otherCode({num1: 2, num2: 3}))
7

Đoạn mã trên đánh lừa khối 

function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}
2 .
function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}
7 sẽ luôn trả về giá trị true vì 
const code = compileCode('return num1 + num2')

// this logs 17 to the console
console.log(code({num1: 10, num2: 7}))

const globalNum = 12
const otherCode = compileCode('return globalNum')

// global scope access is prevented
// this logs undefined to the console
console.log(otherCode({num1: 2, num2: 3}))
72 bẫy luôn trả về giá trị true. Mã bên trong khối 
function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}
2 sẽ không bao giờ thử truy cập vào đối tượng chung

Biểu tượng. không thể dò được

Một biểu tượng là một kiểu dữ liệu duy nhất và bất biến và có thể được sử dụng làm mã định danh cho các thuộc tính đối tượng

const code = compileCode('return num1 + num2')

// this logs 17 to the console
console.log(code({num1: 10, num2: 7}))

const globalNum = 12
const otherCode = compileCode('return globalNum')

// global scope access is prevented
// this logs undefined to the console
console.log(otherCode({num1: 2, num2: 3}))
78 là một ký hiệu nổi tiếng. Biểu tượng nổi tiếng là JavaScript tích hợp sẵn 
const code = compileCode('return num1 + num2')

// this logs 17 to the console
console.log(code({num1: 10, num2: 7}))

const globalNum = 12
const otherCode = compileCode('return globalNum')

// global scope access is prevented
// this logs undefined to the console
console.log(otherCode({num1: 2, num2: 3}))
79, biểu thị hành vi ngôn ngữ bên trong. Ví dụ, các ký hiệu nổi tiếng có thể được sử dụng để thêm hoặc ghi đè lên hành vi lặp lại hoặc chuyển đổi nguyên thủy

Biểu tượng. biểu tượng nổi tiếng không thể dò được được sử dụng để chỉ định một giá trị đối tượng có tên thuộc tính riêng và được kế thừa được loại trừ khỏi các ràng buộc môi trường 'với'

const code = compileCode('return num1 + num2')

// this logs 17 to the console
console.log(code({num1: 10, num2: 7}))

const globalNum = 12
const otherCode = compileCode('return globalNum')

// global scope access is prevented
// this logs undefined to the console
console.log(otherCode({num1: 2, num2: 3}))
78 xác định các thuộc tính không thể đọc được của một đối tượng. Các thuộc tính không thể truy xuất không bao giờ được truy xuất từ ​​đối tượng hộp cát trong câu lệnh 
function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}
2, thay vào đó, chúng được truy xuất ngay từ phạm vi đóng hoặc phạm vi toàn cục.
const code = compileCode('return num1 + num2')

// this logs 17 to the console
console.log(code({num1: 10, num2: 7}))

const globalNum = 12
const otherCode = compileCode('return globalNum')

// global scope access is prevented
// this logs undefined to the console
console.log(otherCode({num1: 2, num2: 3}))
78 là một tính năng rất hiếm khi được sử dụng. Bạn có thể đọc về lý do nó được giới thiệu trên trang này

Chúng tôi có thể khắc phục vấn đề trên bằng cách xác định một 

function compileCode (src) {
  return new Function(src)
}
13 bẫy trên hộp cát 
const code = compileCode('return num1 + num2')

// this logs 17 to the console
console.log(code({num1: 10, num2: 7}))

const globalNum = 12
const otherCode = compileCode('return globalNum')

// global scope access is prevented
// this logs undefined to the console
console.log(otherCode({num1: 2, num2: 3}))
70, bẫy này chặn truy xuất 
const code = compileCode('return num1 + num2')

// this logs 17 to the console
console.log(code({num1: 10, num2: 7}))

const globalNum = 12
const otherCode = compileCode('return globalNum')

// global scope access is prevented
// this logs undefined to the console
console.log(otherCode({num1: 2, num2: 3}))
78 và luôn trả về không xác định. Điều này sẽ đánh lừa khối 
function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}
2 nghĩ rằng đối tượng hộp cát của chúng ta không có thuộc tính không thể khám phá

function compileCode (src) {
  return new Function(src)
}
1

WeakMap cho bộ nhớ đệm

Mã này hiện đã được bảo mật, nhưng hiệu suất của nó vẫn có thể được nâng cấp vì nó tạo ra một 

const code = compileCode('return num1 + num2')

// this logs 17 to the console
console.log(code({num1: 10, num2: 7}))

const globalNum = 12
const otherCode = compileCode('return globalNum')

// global scope access is prevented
// this logs undefined to the console
console.log(otherCode({num1: 2, num2: 3}))
70 mới trên mỗi lần gọi hàm được trả về. Điều này có thể được ngăn chặn bằng cách lưu vào bộ đệm và sử dụng cùng một 
const code = compileCode('return num1 + num2')

// this logs 17 to the console
console.log(code({num1: 10, num2: 7}))

const globalNum = 12
const otherCode = compileCode('return globalNum')

// global scope access is prevented
// this logs undefined to the console
console.log(otherCode({num1: 2, num2: 3}))
70 cho mọi lệnh gọi hàm với cùng một đối tượng hộp cát

Proxy thuộc về một đối tượng hộp cát, vì vậy chúng ta chỉ cần thêm proxy vào đối tượng hộp cát làm thuộc tính. Tuy nhiên, điều này sẽ tiết lộ chi tiết triển khai của chúng tôi cho công chúng và nó sẽ không hoạt động trong trường hợp một đối tượng hộp cát không thể thay đổi bị đóng băng với 

function compileCode (src) {
  return new Function(src)
}
19. Sử dụng 
function compileCode (src) {
  return new Function(src)
}
90 là một giải pháp thay thế tốt hơn trong trường hợp này

Đối tượng WeakMap là tập hợp các cặp khóa/giá trị trong đó các khóa được tham chiếu yếu. Các khóa phải là các đối tượng và các giá trị có thể là các giá trị tùy ý

Có thể sử dụng 

function compileCode (src) {
  return new Function(src)
}
90 để đính kèm dữ liệu vào một đối tượng mà không cần mở rộng trực tiếp đối tượng đó bằng các thuộc tính. Chúng ta có thể sử dụng 
function compileCode (src) {
  return new Function(src)
}
92 để gián tiếp thêm 
function compileCode (src) {
  return new Function(src)
}
93 được lưu trong bộ nhớ đệm vào các đối tượng hộp cát

function compileCode (src) {
  return new Function(src)
}
9

Bằng cách này, chỉ một _______170 sẽ được tạo cho mỗi đối tượng hộp cát

ghi chú cuối cùng

Ví dụ 

function compileCode (src) {
  return new Function(src)
}
3 ở trên là một trình đánh giá mã hộp cát đang hoạt động chỉ trong 19 dòng mã. Nếu muốn xem mã nguồn đầy đủ của thư viện nx-compile, bạn có thể tìm thấy nó trong kho lưu trữ Github này

Ngoài việc giải thích việc đánh giá mã, mục tiêu của chương này là chỉ ra cách sử dụng các tính năng mới của ES6 để thay đổi các tính năng hiện có, thay vì phát minh lại chúng. Tôi đã cố gắng chứng minh toàn bộ sức mạnh của 

function compileCode (src) {
  return new Function(src)
}
93 và 
function compileCode (src) {
  return new Function(src)
}
97 thông qua các ví dụ

Phần kết luận

Nếu bạn quan tâm đến NX framework, vui lòng truy cập trang chủ. Những độc giả thích mạo hiểm có thể tìm thấy mã nguồn NX trong kho lưu trữ Github này

Đánh giá JavaScript là gì?

JavaScript eval() . Nếu đối số là một biểu thức, eval() đánh giá biểu thức. Nếu đối số là một hoặc nhiều câu lệnh JavaScript, eval() sẽ thực thi các câu lệnh. evaluates or executes an argument. If the argument is an expression, eval() evaluates the expression. If the argument is one or more JavaScript statements, eval() executes the statements.

Làm cách nào để sử dụng JavaScript eval?

eval() là một hàm toàn cục trong JavaScript để đánh giá một chuỗi đã chỉ định dưới dạng mã JavaScript và thực thi nó. .
Ví dụ. đánh giá. eval("alert('điều này được thực hiện bởi eval()')");.
Ví dụ. đánh giá. kết quả var; .
Ví dụ. eval với đối tượng JSON

JavaScript sử dụng mô hình đánh giá nào?

JavaScript và nhiều ngôn ngữ khác như Java, Python hoặc Ruby chỉ sử dụng chiến lược đánh giá pass-by-value . Đây là lý do tại sao JavaScript có thể được gọi là ngôn ngữ truyền giá trị.

Hàm nào được sử dụng để đánh giá một biểu thức trong JavaScript?

Hàm eval() dùng để đánh giá biểu thức. Nếu đối số đại diện cho một hoặc nhiều câu lệnh JavaScript, eval() sẽ đánh giá các câu lệnh. Chúng tôi không gọi eval() để đánh giá một biểu thức số học. JavaScript tự động đánh giá các biểu thức số học