一文讀懂ES6(附PY3對比)
程式碼示例: ofollow,noindex">https://github.com/lotapp/BaseCode/tree/master/javascript/1.ES6
ES6現在瀏覽器基本上都支援了,可以收一波韭菜了~(關鍵移動端都支援了)
var let const
可以重複定義的驗證
1.var可以重複定義
var a1 = 12; // 定義了一個a變數 // ...寫了若干程式碼 var a1 = 5; // 然後又重新定義了一個a,這時候可能就有潛在bug了 console.log(a1); // 5
2. let不可以重複定義,相當於C#的變數 (以後var全部換成let)
let a2 = 12; let a2 = 5; // 識別符號a已經被宣告 console.log(a2); // Identifier 'a2' has already been declared
3.const:不可以重複定義,相當於C#的常量
const a3 = 12; const a3 = 5; // 識別符號a已經被宣告 console.log(a3); // Identifier 'a3' has already been declared
1.var可以修改
var a = 2; a = 3; // var 可以修改 console.log(a); //3
2.let可以修改
let b = 2; b = 3; // let可以修改 console.log(b);
3.const不可以修改
const c = 12; c = 5; // 不能賦值給常量變數 console.log(c); // Assignment to constant variable.
1.var沒有塊級作用域
if(1<2){ var b1 = 1; } // var沒有塊級作用域 console.log(b1); // 1
2.let複合正常變數特性
// 和我們平時使用差不多了 if(1<2){ let b2 = 1; } // ReferenceError: b2 is not defined console.log(b2); // b2 沒有定義
3.更嚴格的const就更不說了
if(1<2){ const b3 = 1; } // ReferenceError: b3 is not defined console.log(b3); // b3 沒有定義
變數沒有修飾符的,直接定義,全域性變數 global
引用即可
1.var變數和Python類似
if 1 < 2: b = 1 print(b) # 1
2.全域性變數建議global引用一下
age = 20 def test(): global age print(age) # 20
結論:(以後 var
全部換成 let
)
var let const
這個特性不管是 C#
還是 ES6
都是從 Python
那借鑑過來的,特點:
- 左右兩邊結構一樣
- 定義和賦值同時完成
簡單版:
let [a,b,c] = [1,2,3]; console.log(a,b,c); // 1 2 3
變化版:
let {a,b,c} = {a:1,b:2,c:3}; // json格式對應也行 console.log(a,b,c); // 1 2 3
PS:把後面改成 {a1:1,b1:2,c1:3}
就變成 undefined undefined undefined
複雜版:
let [x, { a, b, c }, y] = [4, { a: 1, b: 2, c: 3 }, 5]; console.log(a, b, c, x, y); // 1 2 3 4 5
-
左右兩邊結構需要一樣
// 這種就不行,格式得對應(左邊3,右邊5個 ==> over) let [x, { a, b, c }, y] = { a: 1, b: 2, c: 3, x: 4, y: 5 }; console.log(a, b, c, x, y); // 未捕獲的TypeError:{...}不可迭代
-
定義和賦值同時完成
let [a, b, c]; [a, b, c] = [1, 2, 3]; // 未捕獲的SyntaxError:在解構宣告中缺少初始化程式 console.log(a, b, c);
3.1.箭頭函式(匿名函式)
以前寫法:
function (引數,引數){ 函式體 }
簡化寫法:
(引數,引數) => { 函式體 } 引數 => { 函式體 } 引數 => 表示式
舉個例子:
function add(x, y) { return x + y; } let add1 = function (x, y) { return x + y; } let add2 = (x, y) => { return x + y; } let add3 = (x,y) => x+y; console.log(add(1, 2)); // 3 console.log(add1(1, 2)); // 3 console.log(add2(1, 2)); // 3 console.log(add3(1, 2)); // 3
小驗證( 和Net用起來基本上一樣
)
- 如果只有一個引數:
()
可以省略let get_age = age => { return age - 2; } console.log(get_age(18)); // 16
- 如果函式體只有一句話:
{}
可以省略- 如果只有一句
return
,那麼return
也可以省略let get_age = age => age - 2; console.log(get_age(18)); // 16
- 沒有
return
也可以簡寫let print_age = age => console.log(age - 2); print_age(18); // 16
- 如果只有一句
PS:箭頭函式會改變this(後面會說)
// 原來: function show(a, b, c) { c = c || 7; // 預設引數 console.log(a, b, c); } // 現在: function show(a, b, c = 7) { console.log(a, b, c); } show(1, 2); // 1 2 7 show(1, 2, 3); // 1 2 3
舉個例子:
let show_args = (a, b, ...args) => console.log(a, b, args); // `...args`是個陣列(Python是元組) show_args(1, 2, 3, 4, 5, 6);// 1 2 [3, 4, 5, 6]
-
...args
必須是最後一個引數let show_args = (a, b, ...args, c) => console.log(a, b, args, c); // Uncaught SyntaxError: Rest parameter must be last formal parameter show_args(1, 2, 4, 5, 6, c = 3); // ...args必須是最後一個引數
PS:Python裡面可以:
def show(a, b, *args, c): print(a, b, args, c) # 1 2 (4, 5, 6) 3 show(1, 2, 4, 5, 6, c=3)
案例1:
let nums = [1, 2, 3, 4]; // 解包使用 let nums2 = [0, ...nums, 5, 6]; // [0,1,2,3,4,5,6] console.log(nums2);
PS:Python用法類似:
# 列表換成元組也一樣用 num_list = [1,2,3,4] # 不管是列表還是元組,這邊需要加*才能解包 num_list2 = [0,*num_list,5,6] # [0, 1, 2, 3, 4, 5, 6] print(num_list2)
案例2:
let nums = [1, 2, 3, 4]; let nums2 = [0, 5, 6]; nums.push(...nums2); // [1, 2, 3, 4, 0, 5, 6] console.log(nums);
PS:Python用法類似:
num_list = [1,2,3,4] num_list2 = [0,5,6] # [1, 2, 3, 4, 0, 5, 6] num_list.extend(num_list2) print(num_list) # 如果使用append就是巢狀版列表了 # num_list.append(num_list2) # [1, 2, 3, 4, [0, 5, 6]]
3.4.特殊的this(重要)
普通函式的this ==> 誰呼叫就是誰(經常變:誰呼叫是誰)
function show() { alert(this); // 1,2,3,4 console.log(this); // [1, 2, 3, 4, show: ƒ] } let arr = [1, 2, 3, 4]; arr.show = show; arr.show();
箭頭函式的this ==> 在誰的環境下 this
就是誰(不變:當前作用域)
let arr = [1, 2, 3, 4]; arr.show = () => { alert(this); // [object Window] console.log(this); // Window } arr.show();
再舉個例子: 在document內
document.onclick = function () { let arr = [1, 2, 3, 4]; arr.show = () => { console.log(this); // document } arr.show(); }
下面幾個方法都不會改變原陣列
對映,傳幾個引數進去,出來幾個引數。( 不改變陣列內容,生成新陣列 )
scor_arr = [100, 28, 38, 64] let results = scor_arr.map(item => item >= 60); // [true, false, false, true] console.log(results); // 不改變scor_arr內容,生成新陣列 // old: // let results = scor_arr.map(function (item) { //return item >= 60; // });
Python略有不同:( 把函式依次作用在list的每個元素上
)
scor_list = [100, 28, 38, 64] result_list = map(lambda item: item >= 60, scor_list) # [True, False, False, True] 不改變舊list的值 print(list(result_list))
PS:result_list:PY2直接返回list,PY3返回 Iterator
代言詞: 過濾 ( 不改變陣列內容,生成新陣列 )
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; nums2 = nums.filter(item => item % 2 == 0); // [2, 4, 6, 8, 10] console.log(nums2) // 不改變舊陣列的值
Python略有不同:( 把函式依次作用在list的每個元素上
)
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] nums2 = filter(lambda item: item % 2 == 0, nums) # [2, 4, 6, 8, 10] 不改變舊list的值 print(list(nums2))
PS:nums2:PY2直接返回list,PY3返回 Iterator
不做任何修改,純粹的遍歷,用法和 net
裡面的 ForEach
差不多
forEach
沒有返回值的驗證
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let sum = 0; let temp =nums.forEach(item => { sum += item; }); console.log(sum) // 55 console.log(temp) // 驗證了:forEach 沒有返回值
NetCore:
var sum = 0; var list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; list.ForEach(item => sum += item); Console.WriteLine(sum); // 55
不會改變原陣列的驗證
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; nums.forEach(item => { item += 1; }); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] console.log(nums); //不會改變nums,僅僅是遍歷
NetCore:
var list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; list.ForEach(item => item += 1); foreach (var item in list) { // 12345678910 Console.Write(item); //不會改變list裡面的值 }
代言詞: 彙總 ,和 map
有點相反, 進去一堆出來一個
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // x是第一個元素,y是第二個元素 nums.reduce((x, y, index) => { console.log(x, y, index); });
輸出:
1 2 1 undefined 3 2 undefined 4 3 undefined 5 4 undefined 6 5 undefined 7 6 undefined 8 7 undefined 9 8 undefined 10 9
逆推整個執行過程:
- x只被賦值一次
- y從第二個值開始往下遍歷
- 整個過程只遍歷9遍就結束了
- for迴圈需要10次遍歷結束
- y從第二個數開始的,少一次遍歷也正常
簡單說就是按順序依次執行函式,eg:
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; result = nums.reduce((x, y) => x + y); console.log(result / nums.length) // 5.5
reduce直接求平均值:( return 是關鍵)
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let result = nums.reduce((temp, item, index) => { temp += item; if (index < nums.length - 1) { return temp; } else { // 最後一次,返回平均值即可 return temp / nums.length; } }); console.log(result) // 5.5
reduce就兩個引數( 有且僅有
)
Python
的 Reduce
沒 js
的強大,一般都是對列表做一個累積的‘彙總’操作
import functools nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] result = functools.reduce(lambda x, y: x + y, nums) print(result / len(nums)) # 5.5
可以看看執行過程:
import functools nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] result = functools.reduce(lambda x, y: print(x, y), nums) print(result)
輸出:
1 2 None 3 None 4 None 5 None 6 None 7 None 8 None 9 None 10 None
通俗講:把類陣列集合轉化成陣列
HTML程式碼: /BaseCode/javascript/1.ES6/1.array.from.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Array.from的應用</title> </head> <body> <style type="text/css"> div { width: 200px; height: 200px; margin-right: 1px; float: left; } </style> <div></div> <div></div> <div></div> </body> </html>
JS部分:( collection是沒有forEach方法的,需要轉化成陣列 )
window.onload = () => { let collection = document.getElementsByTagName("div"); Array.from(collection).forEach(item => item.style.background = "blue"); }
鍵值相同可以只寫一個
let [a, b] = [1, 2]; json = { a, b, c: 3 }; console.log(json); // {a: 1, b: 2, c: 3} // 原來寫法 // json = { a: a, b: b, c: 3 };
函式function可以省略
json = { a: 1, b: 2, show() { console.log(this.a, this.b); } }; json.show(); // 1 2 // 原來寫法 // json = { //a: 1, //b: 2, //show: function () { //console.log(this.a, this.b); //} // };
Json字串的標準寫法:
- 只能用雙引號
- 所有名字必須用引號包裹
let str1 = '{ a: 12, b: 5 }'; // 錯誤 let str2 = '{ "a": 12, "b": 5 }';// 正確 let str3 = "{ 'a': 'abc', 'b': 5 }";// 錯誤(單引號) let str4 = '{ "a": "abc", "b": 5 }';// 正確(雙引號) // 測試是否為字串 [str1, str2, str3, str4].forEach(str => { try { let json = JSON.parse(str);// 轉換成字串 console.log(json); } catch (ex) { console.log(`字串:${str}轉換髮生異常:${ex}`); } });
輸出:
字串:{ a: 12, b: 5 }轉換髮生異常:SyntaxError: Unexpected token a in JSON at position 2 1.base.json.html:21 {a: 12, b: 5} 1.base.json.html:23 字串:{ 'a': 'abc', 'b': 5 }轉換髮生異常:SyntaxError: Unexpected token ' in JSON at position 2 1.base.json.html:21 {a: "abc", b: 5}
字串和Json相互轉換:
- **轉換成字串:
JSON.parse()
- 轉換成Json:
JSON.stringify()
let json1 = { name: "小明", age: 23, test: "我X!@#$%^&*(-_-)=+" }; let new_str = JSON.stringify(json1); console.log(new_str); // encodeURI對有些特殊符號並不會編碼替換 let urlstr = encodeURIComponent(`https://www.baidu.com/s?wd=${new_str}`); console.log(urlstr); console.log(decodeURIComponent(urlstr));
輸出:
{"name":"小明","age":23,"test":"我X!@#$%^&*(-_-)=+"} https://www.baidu.com/s?wd=%7B%22name%22:%22%E5%B0%8F%E6%98%8E%22,%22age%22:23,%22test%22:%22%E6%88%91X!@#$%25%5E&*(-_-)=+%22%7D https%3A%2F%2Fwww.baidu.com%2Fs%3Fwd%3D%7B%22name%22%3A%22%E5%B0%8F%E6%98%8E%22%2C%22age%22%3A23%2C%22test%22%3A%22%E6%88%91X!%40%23%24%25%5E%26*(-_-)%3D%2B%22%7D https://www.baidu.com/s?wd={"name":"小明","age":23,"test":"我X!@#$%^&*(-_-)=+"}
In [ ]:
let str1 = '{ a: 12, b: 5 }'; // 錯誤 let str2 = '{ "a": 12, "b": 5 }';// 正確 let str3 = "{ 'a': 'abc', 'b': 5 }";// 錯誤(單引號) let str4 = '{ "a": "abc", "b": 5 }';// 正確(雙引號) // 測試是否為字串 [str1, str2, str3, str4].forEach(str => { try { let json = JSON.parse(str);// 轉換成字串 console.log(json); } catch (ex) { console.log(`字串:${str}轉換髮生異常:${ex}`); } });
省的一個個去拼接了(PS: 反單引號
)
let [name,age]=["小明",23]; // 我叫小明,我今年23 console.log(`我叫${name},我今年${age}`);
Python3用起來差不多:
name, age = "小明", 23 # 我叫小明,今年23 print(f"我叫{name},今年{age}")
注意一點:換行內容如果不想要空格就頂行寫
console.log(`我叫: 小明 今年: 23`);
輸出:
我叫: 小明 今年: 23
Python就換成了 """
print("""我叫: 小明 今年: 23 """)
let url = "https://www.baidu.com"; if (url.startsWith("http://") || url.startsWith("https://")) { console.log("B/S"); } if (url.endsWith(".com")) { console.log(".com") }
url = "https://www.baidu.com" if url.startswith("https://") or url.startswith("http://"): print("B/S") if url.endswith(".com"): print(".com")
參考文件: JavaScript/Reference/Classes" rel="nofollow,noindex" target="_blank">https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes
以前JS的OOP嚴格意義上還是一個個函式,只是人為給他含義罷了,現在是真的支援了, 效能和語法都優化了
// People後面沒有括號 class People { // 建構函式 constructor(name, age) { this.name = name; this.age = age; } show() { console.log(`我叫:${this.name},今年${this.age}`); } // 靜態方法 static hello() { console.log("Hello World!"); } } // new別忘記了 let xiaoming = new People("小明", 23); xiaoming.show(); //我叫:小明,今年23 // xiaoming.hello(); 這個不能訪問(PY可以) People.hello(); //Hello World!
看看Python怎麼定義的:(self每個都要寫)
class People(object): def __init__(self, name, age): self.name = name self.age = age def show(self): print(f"我叫:{self.name},今年:{self.age}") @classmethod def hello(cls): print("Hello World!") xiaoming = People("小明", 23) xiaoming.show()# 我叫:小明,今年:23 # 這個雖然可以訪問到,但我一直都不建議這麼用(不然用其他語言會混亂崩潰的) xiaoming.hello()# Hello World! People.hello()# Hello World!
PS:JS後來居上,看起來語法比PY更簡潔點了
class Teacher extends People { constructor(name, age, work) { super(name, age); // 放上面 this.work = work; } show_job() { console.log(`我是做${this.work}工作的`); } } let xiaozhang = new Teacher("小張", 25, "思想教育"); xiaozhang.show(); // 我叫:小張,今年25 xiaozhang.show_job(); // 我是做思想教育工作的 Teacher.hello(); // Hello World!
PS:如果子類中存在建構函式,則需要在使用this之前首先呼叫super()
看看Python語法:
class Teacher(People): def __init__(self, name, age, work): self.work = work super().__init__(name, age) def show_job(self): print(f"我是做{self.work}工作的") xiaozhang = Teacher("小張", 25, "思想教育") xiaozhang.show()# 我叫:小張,今年:25 xiaozhang.show_job()# 我是做思想教育工作的 Teacher.hello()# Hello World!
多型這方面和Python一樣,有點後勁不足,只能偽實現,不能像Net、Java這些這麼強大
class Animal { constructor(name) { this.name = name; } run() { console.log(`${this.name}會跑`); } } class Dog extends Animal { run() { console.log(`${this.name}會飛快的跑著`); } } // 藉助一個方法來實現多型 let run = obj => { obj.run() } run(new Animal("動物")); // 動物會跑 run(new Dog("小狗")); // 小狗會飛快的跑著
Python也半斤八兩,十分相似
class Animal(object): def __init__(self, name): self.name = name def run(self): print(f"{self.name}會跑") class Dog(Animal): def run(self): print(f"{self.name}會飛快的跑著") # 藉助一個方法來實現多型 def run(obj): obj.run() run(Animal("動物"))# 動物會跑 run(Dog("小狗"))# 小狗會飛快的跑著
函式中的 bind
直接指定函式中 this
的值,防止改變
如果指定一個事件執行函式的時候,class裡面的this會變化,這時候不想外面套一層方法就可以使用bind
class People { constructor(name, age) { this.name = name; this.age = age; } show() { console.log(this); console.log(`我叫:${this.name},今年${this.age}`); } } let p = new People("小明", 23); // 按照道理應該可以,但是因為this變了,所以不行 //document.onclick = p.show; // 我叫:undefined,今年undefined // 原來寫法:外面包裹一層方法 //document.onclick = () => p.show(); // 我叫:小明,今年23 // 簡寫:bind document.onclick = p.show.bind(p); // 我叫:小明,今年23
小驗證:直接指定函式中 this
的值,防止改變
function show() { console.log(this); } document.onclick = show; // document document.onclick = show.bind("mmd"); // String {"mmd"}
小驗證: 箭頭函式的優先順序比bind高
document.onclick = function () { let arr = [1, 2, 3, 4]; arr.show = () => { console.log(this); // document } arr.show.bind("mmd"); // 箭頭函式的優先順序比bind高 arr.show(); }
- 只支援靜態方法,不支援靜態屬性
- class中不能定義私有變數和函式
- class中定義的所有方法都會被放倒原型當中,都會被子類繼承,而屬性都會作為例項屬性掛到this上
課後拓展: https://www.jianshu.com/p/5cb692658704
Python3 與 C# 面向物件之~封裝: https://www.cnblogs.com/dotnetcrazy/p/9202988.html
Python3 與 C# 面向物件之~繼承與多型: https://www.cnblogs.com/dotnetcrazy/p/9219226.html
微信小程式出來後,元件化的概念越來越火(可以理解為模組化),結合 OOP
真的很方便
4.1.React元件引入
先看個 React
的基礎案例(結合 Next.js
更爽)
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>React元件化</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="./js/react/16.6.3/react.production.min.js"></script> <script src="./js/react-dom/16.6.3/react-dom.production.min.js"></script> <script src="./js/babel-core/5.8.38/browser.min.js"></script> <!-- 注意下型別是"text/babel" --> <script type="text/babel"> window.onload=function () { let item = document.getElementById("test"); ReactDOM.render( <strong>公眾號:逸鵬說道</strong>, item ); }; </script> </head> <body> <div id="test"></div> </body> </html>
輸出:公眾號:逸鵬說道
業餘擴充套件: AMD
、 CMD
、 CJS
、 UMD
: https://blog.csdn.net/yikewang2016/article/details/79358563
1.React and babel
PS:React 16.x開始:
-
react.js
==>react.development.js
-
react.min.js
==>react.production.min.js
-
-
react-dom.js
==>react-dom.development.js
-
react-dom.min.js
==>react-dom.production.min.js
-
PS:如果自己找JS,搜尋三個包: react
、 react-dom
、 babel-core 5.x
(JSX)
npm國內映象: https://npm.taobao.org
配置一下即可: npm install -g cnpm --registry=https://registry.npm.taobao.org
然後就可以把 cnpm
當作 npm
來用了,比如上面三個js檔案:
-
cnpm install react
-
cnpm install react-dom
-
cnpm i babel-core@old
PS: i
是 install
的簡寫, -g
是安裝到全域性環境中,不加就只是安裝到當前目錄
用 OOP
來改造一下上面的案例:( ReactDOM
在執行 render
渲染 <MyTest>
標籤的時候會返回, MyTest類
裡面的 return值
)
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>OOP元件</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="./js/react/16.6.3/react.production.min.js"></script> <script src="./js/react-dom/16.6.3/react-dom.production.min.js"></script> <script src="./js/babel-core/5.8.38/browser.min.js"></script> <script type="text/babel"> class MyTest extends React.Component{ // 可以省略 constructor(...args){ super(...args); } // 必須定義的方法 render(){ return <strong>公眾號:逸鵬說道</strong>; } } window.onload=()=>{ let test = document.getElementById("test"); ReactDOM.render( <MyTest></MyTest>, test ); } </script> </head> <body> <div id="test"></div> </body> </html>
輸出:公眾號:逸鵬說道
好處通過這個案例可能還看不出來,但是你想一下:當那些常用功能模板成為一個個自己的元件時,你的每一次前端開發是多麼幸福的事情?
再語法衍生一下,來個稍微動態一點案例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>OOP元件</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="./js/react/16.6.3/react.production.min.js"></script> <script src="./js/react-dom/16.6.3/react-dom.production.min.js"></script> <script src="./js/babel-core/5.8.38/browser.min.js"></script> <script type="text/babel"> class Item extends React.Component{ render(){ console.log(this); return <strong>{this.props.str}</strong>; } } window.onload=()=>{ ReactDOM.render(<Item str="我叫小明"></Item>,document.getElementById("test")); } </script> </head> <body> <div id="test"></div> </body> </html>
輸出:( 兩種方式傳參:1.字串,2.{}表示式 )
有上面兩個鋪墊,現在做個自己的小元件:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>OOP元件</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="./js/react/16.6.3/react.production.min.js"></script> <script src="./js/react-dom/16.6.3/react-dom.production.min.js"></script> <script src="./js/babel-core/5.8.38/browser.min.js"></script> <script type="text/babel"> class Item extends React.Component{ render(){ console.log(this); return <li><strong>{this.props.str}</strong></li>; } } class MyList extends React.Component{ render(){ console.log(this); return <ul> {this.props.names.map(x=> <Item str={x}></Item>)} </ul> } } window.onload=()=>{ let test = document.getElementById("test"); ReactDOM.render( // 這邊的值可以從後臺獲取 <MyList names={["Golang","Python","NetCore","JavaScript"]}></MyList>, test ); } </script> </head> <body> <div id="test"></div> </body> </html>
輸出:( 兩種方式傳參:1.字串,2.{}表示式 )
課後拓展: https://www.babeljs.cn/learn-es2015/
最後方案和寫法類似於 Python
、 NetCore
,都是 async
和 await
,前面是一步步引入(本質)
基於 JQ
的非同步操作很常見,比如:
console.log("--------------------") $.ajax({ url: "./data/user.json", dataType: "json", success(data) { console.log(data); }, error(ex) { console.log("請求失敗"); } }); console.log("--------------------")
輸出:(的確是非同步操作,不等結果返回就先執行下面語句了)
-------------------- -------------------- {Name: "小明", Age: 23}
那麼它的本質是什麼呢?來看看返回什麼:
let p1 = $.ajax({ url: "./data/user.json", dataType: "json" }); console.log(p1);
輸出:(其實本質也是一個封裝版的 Promise
)
用等價寫法改裝一下:( then兩個方法,一個處理成功,一個處理失敗
)
let p1 = \$.ajax({ url: "./data/user.json", dataType: "json" }); // 不存在的檔案 let p2 = \$.ajax({ url: "./data/mmd.json", dataType: "json" }); p1.then(data => console.log(data), ex => console.log("請求失敗")); p2.then(data => console.log(data), ex => console.log("請求失敗"));
輸出:(忽略上面的 \
我的渲染有的問題,需要轉義一下)
{Name: "小明", Age: 23} 請求失敗
簡單的說就是: Promise:用同步的方式去寫非同步
語法: Promise(()=>{});
有點類似於 NetCore
的 Task
==> Task.Run(()=>{});
上面的那個例子本質上相當於:
let p1 = new Promise((resolve, reject) => { $.ajax({ url: "./data/user.json", dataType: "json", success(data) { resolve(data); }, error(ex) { reject(ex); } }); }); p1.then(data => console.log(data), ex => console.log("請求失敗"));
業務上難免都有相互依賴,以前寫法也只能在 sucess
回撥函式裡面一層層的巢狀
一個是比較麻煩,還有就是看起來特別不方便,一不小心就搞錯了,現在就簡單多了,then裡面直接處理
咋一看,沒啥好處啊?而且還複雜了,其實當任務多的時候好處就來了
比如結合JQ來模擬一個事務性的案例:
let p1 = $.ajax({ url: "./data/user.json", dataType: "json" }); let p2 = $.ajax({ url: "./data/list.txt", dataType: "json" }); let p3 = $.ajax({ url: "./data/package.json", dataType: "json" }); // 進行一系列處理(有一個失敗都會直接進入錯誤處理) Promise.all([p1, p2, p3]).then(arr => { // 這時候返回的是一個數組 console.log(arr); }, ex => { console.error(ex); });
輸出:
這個簡單過一下,和Python沒有太多不同
用 function*
來定義一個迭代器
function* show() { console.log("a"); yield "1"; console.log("b"); yield "2"; console.log("c"); return "d"; } let gen = show(); // 每一次next都執行到下一個yield停止 console.log(gen.next()); // 返回值.value 可以得到yield返回的值 console.log(gen.next().value); // done==true的時候代表迭代結束 console.log(gen.next());
輸出:
a {value: "1", done: false} b 2 c {value: "d", done: true}
通過上面輸出可以瞬間知道退出條件: done: true
,那麼遍歷方式就來了:
function* show() { yield "1"; yield "2"; return "d"; } let gen = show(); while (true) { let result = gen.next(); if (result.done) { console.log(`返回值:${result.value}`); break; } else { console.log(result.value); } }
輸出:
1 2 返回值:d
JS也提供了 for of
來遍歷:
for (let item of show()) { console.log(item); }
大體上差不多,結束條件是觸發 StopIteration
異常
def show(): yield "1" yield "2" return "d" gen = show() while True: try: print(next(gen)) except StopIteration as ex: print(f"返回值:{ex.value}") break for item in show(): print(item)# 1 2
輸出:
1 2 返回值:d 1 2
function* show2() { a = yield "111"; console.log(a); b = yield a; console.log(b); c = yield b; console.log(c); return "over"; } let gen = show2(); // 和Python一樣,第一個不傳參 console.log(gen.next()); console.log(gen.next("aaa")); console.log(gen.next("bbb")); console.log(gen.next("ccc"));
輸出:
{value: "111", done: false} aaa {value: "aaa", done: false} bbb {value: "bbb", done: false} ccc {value: "over", done: true}
傳參Python是使用的send方法,其實next本質也是send,這個之前講過了:
def show(): a = yield "111" print(a) b = yield a print(b) c = yield b print(c) return "over" gen = show() # 第一個不傳參 print(next(gen))# gen.send(None) print(gen.send("aaa")) print(gen.send("bbb")) try: print(gen.send("ccc")) except StopIteration as ex: print(ex.value)
輸出:
111 aaa bbb bbb ccc over
3. async/await
如果你現在還沒有用過 async
和 await
的話...需要好好提升下了,還是通俗說下吧:
- await你可以理解為:等待後面非同步方法的結果,這時候的結果就是你需要的
- async你可以理解為:使用了await的方法需要特殊標記一下
ES6出來前,我們用非同步一般是這麼用的:
- 和下文JS不關聯的外部JS,都打個非同步標籤來非同步載入
-
<script src="./js/test.js" async></script>
現在可以正經的使用了,繼續把上面那個例子衍生下:(之前用 then(()=>{},()=>{})
,現在可以使用 async
和 await
來簡化)
async function show(url) { let data = await $.ajax({ url: url, dataType: 'json' }); console.log(data) } show("./data/list.txt")
輸出:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
就算是批量任務也不用那麼麻煩了:
let show = async (urls) => { for (const url of urls) { let data = await $.ajax({ url: url, dataType: "json" }); console.log(data); } } show(["./data/user.json", "./data/list.txt", "./data/package.json"]);
輸出:
{Name: "小明", Age: 23} [1, 2, 3, 4, 5, 6, 7, 8, 9] {name: "data", version: "0.1.0", description: "測試", main: "index.js", scripts: {…}, …}
應用常見其實很多,比如執行某個事件、比如引入某個外部JS...,eg:
test.js
(async () => { let data = await $.ajax({ url: "./data/list.txt", dataType: 'json' }); console.log(data); })();
HTML中引用一下: <script src="./js/test.js" async></script>
輸出:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
和 NetCore
一樣,可以巢狀使用的, await
的物件一般都是 Promise
或者同是 async
修飾的方法
舉個例子:
test.js
:
let show = async (urls) => { for (const url of urls) { let data = await $.ajax({ url: url, dataType: "json" }); console.log(data); } return "ok"; } let test = async () => { let result = await show(["./data/user.json", "./data/list.txt", "./data/package.json"]); return result; }
頁面
<script src="./js/test.js"></script> <script> (async () => { let data = await test(); console.log(data); })(); </script>
輸出:
{Name: "小明", Age: 23} [1, 2, 3, 4, 5, 6, 7, 8, 9] {name: "data", version: "0.1.0", description: "測試", main: "index.js", scripts: {…}, …} ok
Python3.7開始,語法才開始簡潔:
import asyncio # 模擬一個非同步操作 async def show(): await asyncio.sleep(1) return "寫完文章早點睡覺哈~" # 定義一個非同步方法 async def test(msg): print(msg) return await show() # Python >= 3.7 result = asyncio.run(test("這是一個測試")) print(result) # Python >= 3.4 loop = asyncio.get_event_loop() result = loop.run_until_complete(test("這是一個測試")) print(result) loop.close()
輸出:
這是一個測試 寫完文章早點睡覺哈~
這個ES6裡面有,但是瀏覽器並沒有完全支援,所以現在大家還是用 Require.js
和 Sea.js
更多點
簡單看看就行:擴充套件連結 ~ https://www.cnblogs.com/diligenceday/p/5503777.html
模組定義:
export { a, test, user } let test = () => { console.log("test"); } let user = () => { console.log("user"); return "小明"; } let a = 1;
匯入模組:
import { a, test, user } from ".mod.js" console.log(a); test(); console.log(user());