现代 Javascript: ES6+ 中的 Imports, Exports, Let, Const 和 Promise - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
GrapeCityChina
V2EX    推广

现代 Javascript: ES6+ 中的 Imports, Exports, Let, Const 和 Promise

  •  8
     
  •   GrapeCityChina 2020年 12 月 24 日 6270 次点击
    这是一个创建于 1868 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在过去几年里,Javascript 有很多的更新。如果你想提升写代码的能力,这些更新将会对你有非常大的帮助。

    对于程序员来说,了解这门语言的最新发展是非常重要的。它能使你跟上最新趋势,提高代码质量,在工作中出类拔萃,从而进一步提升你的薪资待遇。

    特别地,如果你想学习像 React 、Angular 或 Vue 这样的框架,你必须掌握这些最新的特性。

    最近,Javascript 增加了许多有用的功能,比如Nullish coalescing operator, optional chaining, Promises, async/await, ES6 destructuring,等等。

    那么现在,我们将探讨每个 Javascript 开发者都应该知道的概念。

    Javascript 中的 Let 和 const

    在 ES6 之前,Javascript 使用 var 关键字来声明变量,var 只有全局作用域和函数作用域,所谓全局作用域就是在代码的任何位置都能访问 var 声明的变量,而函数作用域在变量声明的当前函数内部访问变量。此时是没有块级作用域的。

    随着 let 和 const 这两个关键字的添加,JS 增加了块级作用域的概念。

    如何在 Javascript 中使用 let

    当我们在用 let 声明变量时,用于声明一次之后就不能再以相同的名称重新声明它。

    // ES5 Code var value = 10; console.log(value); // 10 var value = "hello"; console.log(value); // hello var value = 30; console.log(value); // 30 

    如上所示,我们多次使用 var 关键字重新声明了变量值。

    在 ES6 之前,我们可以使用 var 重新声明之前已经声明过的变量,这就会导致了一个问题:如果我们在不知情的情况下,在其他地方重新声明了该变量,很有可能会覆盖原先变量的值,造成一些难以调试的问题。

    所以,Let 解决很好地解决此问题。当你使用 let 重新声明变量值时,将会报错。

    // ES6 Code let value = 10; console.log(value); // 10 let value = "hello"; // Uncaught SyntaxError: Identifier 'value' has already been declared 

    但是,以下代码是合法的:

    // ES6 Code let value = 10; console.log(value); // 10 value = "hello"; console.log(value); // hello 

    我们发现上面的代码看起来没什么问题,是因为我们重新给 value 变量赋了一个新值,但是并没有重新声明。

    我们来看下下面的代码:

    // ES5 Code var isValid = true; if(isValid) { var number = 10; console.log('inside:', number); // inside: 10 } console.log('outside:', number); // outside: 10 

    如上所示,在使用 var 声明变量时,可以在 if 块之外访问该变量。

    而使用 let 声明的 number 变量只能在 if 块内访问,如果在 if 块外访问将会报错。

    我们来看下接下来的代码

     // ES6 Code let isValid = true; if(isValid) { let number = 10; console.log('inside:', number); // inside: 10 } console.log('outside:', number); // Uncaught ReferenceError: number is not defined 

    如上述代码所示,使用 let 分别在 if 块内、if 块外声明了 number 变量。在 if 块外,number 无法被访问,因此会出现引用错误。

    但是,如果变量 number 在 if 块外已经声明,将会出现下面的结果。

     // ES6 Code let isValid = true; let number = 20; if(isValid) { let number = 10; console.log('inside:', number); // inside: 10 } console.log('outside:', number); // outside: 20 

    现在在单独的范围内有两个 number 变量。在 if 块外,number 的值为 20 。

     // ES5 Code for(var i = 0; i < 10; i++){ console.log(i); } console.log('outside:', i); // 10 

    当使用 var 关键字时,i 在 for 循环之外也可以访问到。

     // ES6 Code for(let i = 0; i < 10; i++){ console.log(i); } console.log('outside:', i); // Uncaught ReferenceError: i is not defined 

    而使用 let 关键字时,在 for 循环外部是不可访问的。

    因此,正如上述示例代码所示,let 声明的变量只能在块内部可用,而在块外部不可访问。

    我们可以使用一对大括号创建一个块,如下:

    let i = 10; { let i = 20; console.log('inside:', i); // inside: 20 i = 30; console.log('i again:', i); // i again: 30 } console.log('outside:', i); // outside: 10 

    前面有提到,let 在同一个块中不能重新声明变量,不过可以在另一个块中重新声明。如上代码所示,我们在块内重新声明了 i,并赋值 20,该变量仅可在该块中使用。

    在块外,当我们打印变量时,我们得到的是 10 而不是之前分配的值,这是因为块外,内部变变量 i 是不存在的。

    如果在块外未声明变量,那么将会报错:

     { let i = 20; console.log('inside:', i); // inside: 20 i = 30; console.log('i again:', i); // i again: 30 } console.log('outside:', i); // Uncaught ReferenceError: i is not defined 

    如何在 Javascript 使用 const

    const 关键字在块级作用域中的工作方式与 let 关键字完全相同。因此,我们来看下他们的区别。

    const 声明的变量为常量,其值是不能改变的。而 let 声明的变量,可以为其赋一个新值,如下所示:

    let number = 10; number = 20; console.log(number); // 20 

    但是以下情况,我们不能这样使用 const 。

     const number = 10; number = 20; // Uncaught TypeError: Assignment to constant variable. 

    我们甚至不能使用 const 像下面一样重新声明。

    const number = 20; console.log(number); // 20 const number = 10; // Uncaught SyntaxError: Identifier 'number' has already been declared 

    现在,看下面的代码:

    const arr = [1, 2, 3, 4]; arr.push(5); console.log(arr); // [1, 2, 3, 4, 5] 

    我们说过 const 声明的常量,它的值永远不会改变但是我们改变了上面的常量数组并没有报错。这是为什么呢?

    注意:数组是引用类型,而不是 Javascript 的基本类型

    实际存储在 arr 中的不是数组,而是数组存储的内存位置的引用(地址)。执行 arr.push(5),并没有改变 arr 指向的引用,而是改变了存储在那个引用上的值。

    对象也是如此:

    const obj = { name: 'David', age: 30 }; obj.age = 40; console.log(obj); // { name: 'David', age: 40 } 

    这里,我们也没有改变 obj 指向的引用,而是改变了存储在引用的值。

    因此,上述的代码将会起作用,但下面的代码是无效的。

    const obj = { name: 'David', age: 30 }; const obj1 = { name: 'Mike', age: 40 }; obj = obj1; // Uncaught TypeError: Assignment to constant variable. 

    这样写会抛出异常,因为我们试图更改 const 变量指向的引用。

    因此,在使用 const 时要记住一点:使用 const 声明常量时,不能重新声明,也不能重新赋值。如果声明的常量是引用类型,我们可以更改存储在引用的值。

    同理,下面的代码也是无效的。

    const arr = [1, 2, 3, 4]; arr = [10, 20, 30]; // Uncaught TypeError: Assignment to constant variable. 

    总结:

    • 关键字 let 和 const 在 Javascript 中添加块级作用域。

    • 当我们将一个变量声明为 let 时,我们不能在同一作用域(函数或块级作用域)中重新定义或重新声明另一个具有相同名称的 let 变量,但是我们可以重新赋值。

    • 当我们将一个变量声明为 const 时,我们不能在同一作用域(函数或块级作用域)中重新定义或重新声明具有相同名称的另一个 const 变量。但是,如果变量是引用类型(如数组或对象),我们可以更改存储在该变量中的值。

    好了,我们继续下一个话题: promises 。

    Javascript 中的 promises

    对于很多新开发者来说,promises 是 Javascript 中较难理解的部分。ES6 中原生提供了 Promise 对象,那么 Promise 究竟是什么呢?

    Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息。

    如何创造一个 promise

    使用 promise 构造函数创建一个 promise,如下所示:

    const promise = new Promise(function(resolve, reject) { }); 

    Promise 的构造函数接收一个函数作为参数,并且在内部接收两个参数:resolve,reject 。resolve 和 reject 参数实际上是我们可以调用的函数,具体取决于异步操作的结果。

    Promise有三种状态:

    • pending: 初始状态,不是成功或失败状态。

    • fulfilled:表示操作成功完成。

    • rejected: 表示操作失败。

    当我们创建 Promise 时,它处于等待的状态。当我们调用 resolve 函数时,它将进入已完成状态。如果调用 reject,他将进入被拒绝状态。

    在下面的代码中,我们执行了一个异步操作,也就是 setTimeout,2 秒后,调用 resolve 方法。

    const promise = new Promise(function(resolve, reject) { setTimeout(function() { const sum = 4 + 5; resolve(sum); }, 2000); }); 

    我们需要使用以下方法注册一个回调.then 获得 1promise 执行成功的结果,如下所示:

    const promise = new Promise(function(resolve, reject) { setTimeout(function() { const sum = 4 + 5; resolve(sum); }, 2000); }); promise.then(function(result) { console.log(result); // 9 }); 

    then 接收一个参数,是函数,并且会拿到我们在 promise 中调用 resolve 时传的的参数。

    如果操作不成功,则调用 reject 函数:

    const promise = new Promise(function(resolve, reject) { setTimeout(function() { const sum = 4 + 5 + 'a'; if(isNaN(sum)) { reject('Error while calculating sum.'); } else { resolve(sum); } }, 2000); }); promise.then(function(result) { console.log(result); }); 

    如果 sum 不是一个数字,那么我们调用带有错误信息的 reject 函数,否则我们调用 resolve 函数。

    执行上述代码,输出如下:

    image.png

    调用 reject 函数会抛出一个错误,但是我们没有添加用于捕获错误的代码。

    需要调用 catch 方法指定的回调函数来捕获并处理这个错误。

    promise.then(function(result) { console.log(result); }).catch(function(error) { console.log(error); }); 

    输出如下:

    image.png

    所以建议大家在使用 promise 时加上 catch 方法,以此来避免程序因错误而停止运行。

    链式操作

    我们可以向单个 promise 添加多个 then 方法,如下所示:

    promise.then(function(result) { console.log('first .then handler'); return result; }).then(function(result) { console.log('second .then handler'); console.log(result); }).catch(function(error) { console.log(error); }; 

    当添加多个 then 方法时,前一个 then 方法的返回值将自动传递给下一个 then 方法。

    image.png

    如上图所示,我们在第一个 then 方法中输出字符串,并将接收的参数 result ( sum )返回给下一个 result 。

    在下一个 then 方法中,输出字符串,并输出上一个 then 方法传递给它的 result 。

    如何在 Javascript 中延迟 promise 的执行

    很多时候,我们不希望立即创建 promise,而是希望在某个操作完成后再创建。

    我们可以将 promise 封装在一个函数中,然后从函数中返回 promise,如下所示:

    function createPromise() { return new Promise(function(resolve, reject) { setTimeout(function() { const sum = 4 + 5; if(isNaN(sum)) { reject('Error while calculating sum.'); } else { resolve(sum); } }, 2000); }); } 

    这样,我们就可以通过函数将参数传递给 promise,达到动态的目的。

    function createPromise(a, b) { return new Promise(function(resolve, reject) { setTimeout(function() { const sum = a + b; if(isNaN(sum)) { reject('Error while calculating sum.'); } else { resolve(sum); } }, 2000); }); } createPromise(1,8) .then(function(output) { console.log(output); // 9 }); // OR createPromise(10,24) .then(function(output) { console.log(output); // 34 }); 

    image.png

    此外,我们只能向 resolve 或 reject 函数传递一个值。如果你想传递多个值到 resolve 函数,可以将它作为一个对象传递,如下:

    const promise = new Promise(function(resolve, reject) { setTimeout(function() { const sum = 4 + 5; resolve({ a: 4, b: 5, sum }); }, 2000); }); promise.then(function(result) { console.log(result); }).catch(function(error) { console.log(error); }); 

    image.png

    如何在 Javascript 中使用箭头函数

    上述示例代码中,我们使用常规的 ES5 语法创建了 promise 。但是,通常使用箭头函数代替 ES5 语法,如下:

    const promise = new Promise((resolve, reject) => { setTimeout(() => { const sum = 4 + 5 + 'a'; if(isNaN(sum)) { reject('Error while calculating sum.'); } else { resolve(sum); } }, 2000); }); promise.then((result) => { console.log(result); }); 

    你可以根据自己需要使用 ES5 或 ES6 语法。

    ES6 Import 和 Export 语法

    在 ES6 之前,我们在一个 HTML 文件中可以使用多个 script 标签来引用不同的 Javascript 文件,如下所示:

    <script type="text/Javascript" src="home.js"></script> <script type="text/Javascript" src="profile.js"></script> <script type="text/Javascript" src="user.js"></script> 

    但是如果我们在不同的 Javascript 文件中有一个同名的变量,将会出现命名冲突,你实际得到的可能并不是你期望的值。

    ES6 增加了模块的概念来解决这个问题。

    在 ES6 中,我们编写的每一个 Javascript 文件都被称为模块。我们在每个文件中声明的变量和函数不能用于其他文件,除非我们将它们从该文件中导出并、在另一个文件中得到引用。

    因此,在文件中定义的函数和变量是每个文件私有的,在导出它们之前,不能在文件外部访问它们。

    export 有两种类型:

    • 命名导出:在一个文件中可以有多个命名导出

    • 默认导出:单个文件中只能有一个默认导出

    Javascript 中的命名导出

    如下所示,将单个变量命名导出:

    export const temp = "This is some dummy text"; 

    如果想导出多个变量,可以使用大括号指定要输出的一组变量。

    const temp1 = "This is some dummy text1"; const temp2 = "This is some dummy text2"; export { temp1, temp2 }; 

    需要注意的是,导出语法不是对象语法。因此,在 ES6 中,不能使用键值对的形式导出。

     // This is invalid syntax of export in ES6 export { key1: value1, key2: value2 } 

    import 命令接受一对大括号,里面指定要从其他模块导入的变量名。

     import { temp1, temp2 } from './filename'; 

    注意,不需要在文件名中添加.js 扩展名,因为默认情况下会考虑该拓展名。

     // import from functions.js file from current directory import { temp1, temp2 } from './functions'; // import from functions.js file from parent of current directory import { temp1 } from '../functions'; 

    提示一点,导入的变量名必须与被导入模块对外接口的名称相同。

    因此,导出应使用:

    // constants.js export const PI = 3.14159; 

    那么在导入的时候,必须使用与导出时相同的名称:

    import { PI } from './constants'; // This will throw an error import { PiValue } from './constants'; 

    如果想为输入的变量重新命名,可以使用 as 关键字,语法如下:

    import { PI as PIValue } from './constants'; 

    我们以为 PI 重命名为 PIValue,因此不能再使用 PI 变量名。

    导出时也可使用下面的重命名语法:

    // constants.js const PI = 3.14159; export { PI as PIValue }; 

    然后在导入是,必须使用 PIValue 。

    import { PIValue } from './constants'; 

    命名导出某些内容之前必须先声明它。

    export 'hello'; // this will result in error export const greeting = 'hello'; // this will work export { name: 'David' }; // This will result in error export const object = { name: 'David' }; // This will work 

    我们来看下面的 validations.js 文件:

    // utils/validations.js const isValidEmail = function(email) { if (/^[^@ ]+@[^@ ]+\.[^@ \.]{2,}$/.test(email)) { return "email is valid"; } else { return "email is invalid"; } }; const isValidPhOne= function(phone) { if (/^[\\(]\d{3}[\\)]\s\d{3}-\d{4}$/.test(phone)) { return "phone number is valid"; } else { return "phone number is invalid"; } }; function isEmpty(value) { if (/^\s*$/.test(value)) { return "string is empty or contains only spaces"; } else { return "string is not empty and does not contain spaces"; } } export { isValidEmail, isValidPhone, isEmpty }; 

    在 index.js 中,我们可以使用如下函数:

    // index.js import { isEmpty, isValidEmail } from "./utils/validations"; console.log("isEmpty:", isEmpty("abcd")); // isEmpty: string is not empty and does not contain spaces console.log("isValidEmail:", isValidEmail("[email protected]")); // isValidEmail: email is valid console.log("isValidEmail:", isValidEmail("ab@[email protected]")); // isValidEmail: email is invalid 

    Javascript 的默认导出

    如上所述,单个文件中最多只能有一个默认导出。但是,你可以在一个文件中使用多个命名导出和一个默认导出。

    要声明一个默认导出,我们需要使用以下语法:

    //constants.js const name = 'David'; export default name; 

    在导入时就不需要再使用花括号了。

    import name from './constants'; 

    如下,我们有多个命名导出和一个默认导出:

    // constants.js export const PI = 3.14159; export const AGE = 30; const NAME = "David"; export default NAME; 

    此时我们使用 import 导入时,只需要在大括号之前指定默认导出的变量名。

    // NAME is default export and PI and AGE are named exports here import NAME, { PI, AGE } from './constants'; 

    使用 export default 导出的内容,在导入的时候,import 后面的名称可以是任意的。

    // constants.js const AGE = 30; export default AGE; import myAge from ‘./constants’; console.log(myAge); // 30 

    另外,export default 的变量名称从 Age 到 myAge 之所以可行,是因为只能存在一个 export default 。因此你可以随意命名。还需注意的是,关键字不能在声明变量之前。

    // constants.js export default const AGE = 30; // This is an error and will not work 

    因此,我们需要在单独的一行使用关键字。

    // constants.js const AGE = 30; export default AGE; 

    不过以下形式是允许的:

    //constants.js export default { name: "Billy", age: 40 }; 

    并且需要在另一个文件中使用它

    import user from './constants'; console.log(user.name); // Billy console.log(user.age); // 40 

    还有,可以使用以下语法来导入 constants.js 文件中导出的所有变量:

    // test.js import * as constants from './constants'; 

    下面,我们将导入所有我们 constants.js 存储在 constants 变量中的命名和 export default 。因此,constants 现在将成为对象。

    // constants.js export const USERNAME = "David"; export default { name: "Billy", age: 40 }; 

    在另一个文件中,我们按一下方式使用。

    // test.js import * as constants from './constants'; console.log(constants.USERNAME); // David console.log(constants.default); // { name: "Billy", age: 40 } console.log(constants.default.age); // 40

    也可以使用以下方式组合使用命名导出和默认导出:

    // constants.js const PI = 3.14159; const AGE = 30; const USERNAME = "David"; const USER = { name: "Billy", age: 40 }; export { PI, AGE, USERNAME, USER as default }; import USER, { PI, AGE, USERNAME } from "./constants"; 

    总而言之:

    ES6 中,一个模块就是一个独立的文件,该文件内部的所有变量,外部都无法获取。如果想从外部读取模块内的某个变量,必须使用 export 关键字导出该变量,使用 import 关键字导入该变量。

    Javascript 中的默认参数

    ES6 增加了一个非常有用的特性,即在定义函数时提供默认参数。

    假设我们有一个应用程序,一旦用户登录系统,我们将向他们显示一条欢迎消息,如下所示:

    function showMessage(firstName) { return "Welcome back, " + firstName; } console.log(showMessage('John')); // Welcome back, John 

    但是,如果数据库中没有用户名,那该怎么办呢?所以,我们首先需要检查是否提供了 firstName,然后再显示相应的信息。

    在 ES6 之前,我们必须写这样的代码:

    function showMessage(firstName) { if(firstName) { return "Welcome back, " + firstName; } else { return "Welcome back, Guest"; } } console.log(showMessage('John')); // Welcome back, John console.log(showMessage()); // Welcome back, Guest 

    但现在使用 ES6 提供的默认参数,我们可以这样写:

    function showMessage(firstName = 'Guest') { return "Welcome back, " + firstName; } console.log(showMessage('John')); // Welcome back, John console.log(showMessage()); // Welcome back, Guest 

    函数的默认参数可以为任意值。

    function display(a = 10, b = 20, c = b) { console.log(a, b, c); } display(); // 10 20 20 display(40); // 40 20 20 display(1, 70); // 1 70 70 display(1, 30, 70); // 1 30 70 

    在上面的代码中,我们没有提供函数的所有参数,实际代码等同于:

    display(); // 等同于 display(undefined, undefined, undefined) display(40); 等同于 display(40, undefined, undefined) display(1, 70); 等同于 display(1, 70, undefined) 

    因此,如果传递的参数是 undefined,则对应的参数将使用默认值。

    我们还可以将对象或计算值指定为默认值,如下:

    const defaultUser = { name: 'Jane', location: 'NY', job: 'Software Developer' }; const display = (user = defaultUser, age = 60 / 2 ) => { console.log(user, age); }; display(); /* output { name: 'Jane', location: 'NY', job: 'Software Developer' } 30 */ 

    ES5 代码如下:

    // ES5 Code function getUsers(page, results, gender, nationality) { var params = ""; if(page === 0 || page) { params += `page=${page}&`; } if(results) { params += `results=${results}&`; } if(gender) { params += `gender=${gender}&`; } if(nationality) { params += `natiOnality=${nationality}`; } fetch('https://randomuser.me/api/?' + params) .then(function(response) { return response.json(); }) .then(function(result) { console.log(result); }) .catch(function(error) { console.log('error', error); }); } getUsers(0, 10, 'male', 'us'); 

    在这段代码中,我们通过在 getUsers 函数中传递各种可选参数来进行 API 调用。在进行 API 调用之前,我们添加了各种 if 条件来检查是否添加了参数,并基于此构造查询字符串,如下所示:

    https://randomuser.me/api/? page=0&results=10&gender=male&natiOnality=us 

    使用 ES6 的默认参数则不必添这么多 if 条件,如下所示:

    function getUsers(page = 0, results = 10, gender = 'male',natiOnality= 'us') { fetch(`https://randomuser.me/api/?page=${page}&results=${results}&gender=${gender}&natiOnality=${nationality}`) .then(function(response) { return response.json(); }) .then(function(result) { console.log(result); }) .catch(function(error) { console.log('error', error); }); } getUsers(); 

    这样一来,代码得到了大量的简化,即便我们不为 getUsers 函数提供任何参数时,它也能采用默认值。当然,我们也可以传递自己的参数:

    getUsers(1, 20, 'female', 'gb'); 

    它将覆盖函数的默认参数。

    null 不等于未定义

    注意: 定义默认参数时,null 和 undefined 是不同的。

    我们来看下面的代码:

    function display(name = 'David', age = 35, location = 'NY'){ console.log(name, age, location); } display('David', 35); // David 35 NY display('David', 35, undefined); // David 35 NY // OR display('David', 35, undefined); // David 35 NY display('David', 35, null); // David 35 null 

    当我们传递 null 作为参数时,它实际是给 location 参数赋一个空值,与 undefined 不一样。所以它不会取默认值“NY”。

    Array.prototype.includes

    ES7 增加了数组的 includes 方法,用来判断一个数组是否包含一个指定的值,如果是返回 true,否则 false 。

    // ES5 Code const numbers = ["one", "two", "three", "four"]; console.log(numbers.indexOf("one") > -1); // true console.log(numbers.indexOf("five") > -1); // false 

    数组可以使用 includes 方法:

    // ES7 Code const numbers = ["one", "two", "three", "four"]; console.log(numbers.includes("one")); // true console.log(numbers.includes("five")); // false 

    includes 方法可以使代码简短且易于理解,它也可用于比较不同的值。

    <pre style="line-height:18.0pt; vertical-align:baseline">const day = "monday";</pre> if(day === "monday" || day === "tuesday" || day === "wednesday") { // do something } // 以上代码使用 include 方法可以简化如下: const day = "monday"; if(["monday", "tuesday", "wednesday"].includes(day)) { // do something } 

    因此,在检查数组中的值时,使用 includes 方法将会非常的方便。

    结语

    从 ES6 开始,Javascript 中发生许多变更。对于 Javascript,Angular,React 或 Vue 开发人员都应该知道它们。

    了解这些变更可以使你成为更棒的开发者,甚至可以帮助您获得更高的薪水。而且,如果你只是在学习 React 之类的库以及 Angular 和 Vue 之类的框架,那么您一定要掌握这些新特性。

    29 条回复    2020-12-29 13:24:56 +08:00
    GrapeCityChina
        1
    GrapeCityChina  
    OP
       2020 年 12 月 24 日
    自顶,顺便打个广告:

    关于 ES 6,除了语法更新需要了解,开发者还需掌握一些第三方组件,以便更好的完成项目交付。
    例如,使用 SpreadJS 表格组件,可以向 Javascript 应用程序添加高级电子表格功能,包括支持 450 多种计算公式、在线导入导出 Excel 文档、数据透视表和可视化分析,以创建财务、分析、预算 /预测、数据收集、科学和许多其他类似的应用程序。

    SpreadJS 表格组件地址: https://www.grapecity.com.cn/developer/spreadjs
    HFX3389
        2
    HFX3389  
       2020 年 12 月 25 日
    好长
    gqbre
        3
    gqbre  
       2020 年 12 月 25 日
    导入导出那块不错
    mascteen
        4
    mascteen  
       2020 年 12 月 25 日
    再加一个 Proxy
    hantsy
        5
    hantsy  
       2020 年 12 月 25 日
    generaters, class 也是最新 ES 特性,我实在看到 Generaters 晕。
    guansixu
        6
    guansixu  
       2020 年 12 月 25 日
    赞,写的不错
    agee
        7
    agee  
       2020 年 12 月 26 日
    简单明了,赞
    zorui
        8
    zorui  
       2020 年 12 月 28 日
    赞,现在都 E11 了,es6 都是 5 年前的了。 拥抱 TS
    Roooookie
        9
    Roooookie  
       2020 年 12 月 28 日   5
    好家伙,这是当博客发呢
    corgiyun
        10
    corgiyun  
       2020 年 12 月 28 日
    大哥这是做笔记吗???
    baleeny
        11
    baleeny  
       2020 年 12 月 28 日
    @zorui 哈哈哈哈
    liamzz
        12
    liamzz  
       2020 年 12 月 28 日
    满满的 cv 感
    yamedie
        13
    yamedie  
       2020 年 12 月 28 日
    重点在 1 楼打个广告部分, 正文太长可以忽略
    kinghly
        14
    kinghly  
       2020 年 12 月 28 日 via Android   2
    还以为这个帖子是几年前的
    XIVN1987
        15
    XIVN1987  
       2020 年 12 月 28 日   1
    js 这个 import .. from ... 很魔性啊,自动补全怎么工作?

    是不是先打 form ...,然后在回到行首打 import 啊?
    Lemeng
        16
    Lemeng  
       2020 年 12 月 28 日
    好长好长
    AilF
        17
    AilF  
       2020 年 12 月 28 日
    JerryCha
        18
    JerryCha  
       2020 年 12 月 28 日
    借楼问一个问题,var 和 function 在提升的时候有优先级次序吗
    KamiBako
        19
    KamiBako  
       2020 年 12 月 28 日
    这个说得都还挺不错,刚刚看了一圈像是复习了一圈一样。
    ddzy
        20
    ddzy  
       2020 年 12 月 28 日
    @JerryCha 先到先得吧
    wednesdayco
        21
    wednesdayco  
       2020 年 12 月 28 日
    @XIVN1987 敲出需要 Import 的方法 /类 /变了等,自动提示 import,选择正确的 import,vscode 自动把 import from 加到文件首
    ruanimal
        22
    ruanimal  
       2020 年 12 月 28 日
    文末没有二维码,差评
    EPr2hh6LADQWqRVH
        23
    EPr2hh6LADQWqRVH  
       2020 年 12 月 28 日 via Android
    这有什么好置顶的。。。
    dingdangnao
        24
    dingdangnao  
       2020 年 12 月 28 日
    大小写看着难受。
    soulmt
        25
    soulmt  
       2020 年 12 月 28 日
    @JerryCha function 的优先级要高于 var
    rioshikelong121
        26
    rioshikelong121  
       2020 年 12 月 28 日
    @avastms 花钱置顶打广告啊。
    hubqin
        27
    hubqin  
       2020 年 12 月 28 日 via Android
    @XIVN1987 webstorm 下,使用到时自动 import
    Akat3uki
        28
    Akat3uki  
       2020 年 12 月 28 日
    挺好挺细的
    i4oolish
        29
    i4oolish  
       2020 年 12 月 29 日
    笔记不错,产品也不错
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2659 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 40ms UTC 05:00 PVG 13:00 LAX 21:00 JFK 00:00
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86