ECMAScript 2022 正式發布,有哪些新特性?

大家好,我是若川。持續組織了近一年的源碼共讀活動,感興趣的可以?點此加我微信ruochuan12?參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》?包含20余篇源碼文章。歷史面試系列。另外:目前建有江西|湖南|湖北籍前端群,可加我微信進群。


2022 年 6 月 22 日,第 123 屆 ECMA 大會批準了 ECMAScript 2022 語言規范,這意味著它現在正式成為標準。下面就來看看 ECMAScript 2022 有哪些新特性!

887ab3eaa9f5f276507e8fa8403a6e05.png

總覽:

  1. Top-level Await

  2. Object.hasOwn()

  3. at()

  4. error.cause

  5. 正則表達式匹配索引

1. Top-level Await

在ES2017中,引入了 async 函數和 await 關鍵字,以簡化 Promise 的使用,但是 await 關鍵字只能在 async 函數內部使用。嘗試在異步函數之外使用 await 就會報錯:SyntaxError - SyntaxError: await is only valid in async function

頂層 await 允許我們在 async 函數外面使用 await 關鍵字。它允許模塊充當大型異步函數,通過頂層 await,這些 ECMAScript 模塊可以等待資源加載。這樣其他導入這些模塊的模塊在執行代碼之前要等待資源加載完再去執行。

由于 await 僅在 async 函數中可用,因此模塊可以通過將代碼包裝在 async 函數中來在代碼中包含 await

//?a.jsimport?fetch??from?"node-fetch";let?users;export?const?fetchUsers?=?async?()?=>?{const?resp?=?await?fetch('https://jsonplaceholder.typicode.com/users');users?=??resp.json();}fetchUsers();export?{?users?};//?usingAwait.jsimport?{users}?from?'./a.js';console.log('users:?',?users);console.log('usingAwait?module');

我們還可以立即調用頂層async函數(IIAFE):

import?fetch??from?"node-fetch";(async?()?=>?{const?resp?=?await?fetch('https://jsonplaceholder.typicode.com/users');users?=?resp.json();})();export?{?users?};

這樣會有一個缺點,直接導入的 usersundefined,需要在異步執行完成之后才能訪問它:

//?usingAwait.js
import?{users}?from?'./a.js';console.log('users:',?users);?//?undefinedsetTimeout(()?=>?{console.log('users:',?users);
},?100);console.log('usingAwait?module');

當然,這種方法并不安全,因為如果異步函數執行花費的時間超過100毫秒, 它就不會起作用了,users 仍然是 undefined

另一個方法是導出一個 promise,讓導入模塊知道數據已經準備好了:

//a.js
import?fetch??from?"node-fetch";
export?default?(async?()?=>?{const?resp?=?await?fetch('https://jsonplaceholder.typicode.com/users');users?=?resp.json();
})();
export?{?users?};//usingAwait.js
import?promise,?{users}?from?'./a.js';
promise.then(()?=>?{?console.log('usingAwait?module');setTimeout(()?=>?console.log('users:',?users),?100);?
});

雖然這種方法似乎是給出了預期的結果,但是有一定的局限性:導入模塊必須了解這種模式才能正確使用它

而頂層await就可以解決這些問題:

//?a.jsconst?resp?=?await?fetch('https://jsonplaceholder.typicode.com/users');const?users?=?resp.json();export?{?users};//?usingAwait.jsimport?{users}?from?'./a.mjs';console.log(users);console.log('usingAwait?module');

頂級 await 在以下場景中將非常有用:

  • 動態加載模塊:

const?strings?=?await?import(`/i18n/${navigator.language}`);
  • 資源初始化:

const?connection?=?await?dbConnector();
  • 依賴回退:

let?translations;
try?{translations?=?await?import('https://app.fr.json');
}?catch?{translations?=?await?import('https://fallback.en.json');
}

該特性的瀏覽器支持如下:

bbe68421c2b82ed78a26f2e2503c4c89.png

2. Object.hasOwn()

在ES2022之前,可以使用 Object.prototype.hasOwnProperty() 來檢查一個屬性是否屬于對象。

Object.hasOwn 特性是一種更簡潔、更可靠的檢查屬性是否直接設置在對象上的方法:

const?example?=?{property:?'123'
};console.log(Object.prototype.hasOwnProperty.call(example,?'property'));
console.log(Object.hasOwn(example,?'property'));

該特性的瀏覽器支持如下:

f96d348536c107dd47610c06d0c89dcc.png

3. at()

at() 是一個數組方法,用于通過給定索引來獲取數組元素。當給定索引為正時,這種新方法與使用括號表示法訪問具有相同的行為。當給出負整數索引時,就會從數組的最后一項開始檢索:

const?array?=?[0,1,2,3,4,5];console.log(array[array.length-1]);??//?5
console.log(array.at(-1));??//?5console.log(array[array.lenght-2]);??//?4
console.log(array.at(-2));??//?4

除了數組,字符串也可以使用at()方法進行索引:

const?str?=?"hello?world";console.log(str[str.length?-?1]);??//?d
console.log(str.at(-1));??//?d

4. error.cause

在 ECMAScript 2022 規范中,new Error() 中可以指定導致它的原因:

function?readFiles(filePaths)?{return?filePaths.map((filePath)?=>?{try?{//?···}?catch?(error)?{throw?new?Error(`While?processing?${filePath}`,{cause:?error});}});
}

5. 正則表達式匹配索引

該特性允許我們利用 d 字符來表示我們想要匹配字符串的開始和結束索引。以前,只能在字符串匹配操作期間獲得一個包含提取的字符串和索引信息的數組。在某些情況下,這是不夠的。因此,在這個規范中,如果設置標志 /d,將額外獲得一個帶有開始和結束索引的數組。

const?matchObj?=?/(a+)(b+)/d.exec('aaaabb');console.log(matchObj[1])?//?'aaaa'
console.log(matchObj[2])?//?'bb'

由于 /d 標識的存在,matchObj還有一個屬性.indices,它用來記錄捕獲的每個編號組:

console.log(matchObj.indices[1])??//?[0,?4]
console.log(matchObj.indices[2])??//?[4,?6]

我們還可以使用命名組:

const?matchObj?=?/(?<as>a+)(?<bs>b+)/d.exec('aaaabb');console.log(matchObj.groups.as);??//?'aaaa'
console.log(matchObj.groups.bs);??//?'bb'

這里給兩個字符匹配分別命名為asbs,然后就可以通過groups來獲取到這兩個命名分別匹配到的字符串。

它們的索引存儲在 matchObj.indices.groups 中:

console.log(matchObj.indices.groups.as);??//?[0,?4]
console.log(matchObj.indices.groups.bs);??//?[4,?6]

匹配索引的一個重要用途就是指向語法錯誤所在位置的解析器。下面的代碼解決了一個相關問題:它指向引用內容的開始和結束位置。

const?reQuoted?=?/“([^”]+)”/dgu;
function?pointToQuotedText(str)?{const?startIndices?=?new?Set();const?endIndices?=?new?Set();for?(const?match?of?str.matchAll(reQuoted))?{const?[start,?end]?=?match.indices[1];startIndices.add(start);endIndices.add(end);}let?result?=?'';for?(let?index=0;?index?<?str.length;?index++)?{if?(startIndices.has(index))?{result?+=?'[';}?else?if?(endIndices.has(index+1))?{result?+=?']';}?else?{result?+=?'?';}}return?result;
}console.log(pointToQuotedText('They?said?“hello”?and?“goodbye”.'));
//?'???????????[???]???????[?????]??'

6. 類

(1)公共實例字段

公共類字段允許我們使用賦值運算符 (=) 將實例屬性添加到類定義中。下面是一個計數器的例子:

import?React,?{?Component?}?from?"react";export?class?Incrementor?extends?Component?{constructor()?{super();this.state?=?{count:?0,};this.increment?=?this.increment.bind(this);}increment()?{this.setState({?count:?this.state.count?+?1?});}render()?{return?(<button?onClick={this.increment}>Increment:?{this.state.count}</button>);}
}

在這個例子中,在構造函數中定義了實例字段和綁定方法,通過新的類語法,可以使代碼更加直觀。新的公共類字段語法允許我們直接將實例屬性作為屬性添加到類上,而無需使用構造函數方法。這樣就簡化了類的定義,使代碼更加簡潔、可讀:

import?React?from?"react";export?class?Incrementor?extends?React.Component?{state?=?{?count:?0?};increment?=?()?=>?this.setState({?count:?this.state.count?+?1?});render?=?()?=>?(<button?onClick={this.increment}>Increment:?{this.state.count}</button>);
}

有些小伙伴可能就疑問了,這個功能很早就可以使用了呀。但是它現在還不是標準的 ECMAScript,默認是不開啟的,如果使用 create-react-app 創建 React 項目,那么它默認是啟用的,否則我們必須使用正確的babel插件才能正常使用(@babel/preset-env)。

下面來看看關于公共實例字段的注意事項:

  • 公共實例字段存在于每個創建的類實例上。它們要么是在Object.defineProperty()中添加,要么是在基類中的構造時添加(構造函數主體執行之前執行),要么在子類的super()返回之后添加:

class?Incrementor?{count?=?0
}const?instance?=?new?Incrementor();
console.log(instance.count);?//?0
  • 未初始化的字段會自動設置為 undefined

class?Incrementor?{count
}const?instance?=?new?Incrementor();
console.assert(instance.hasOwnProperty('count'));
console.log(instance.count);??//?undefined
  • 可以進行字段的計算:

const?PREFIX?=?'main';class?Incrementor?{[`${PREFIX}Count`]?=?0
}const?instance?=?new?Incrementor();
console.log(instance.mainCount);???//?0

(2)私有實例字段、方法和訪問器

默認情況下,ES6 中所有屬性都是公共的,可以在類外檢查或修改。下面來看一個例子:

class?TimeTracker?{name?=?'zhangsan';project?=?'blog';hours?=?0;set?addHours(hour)?{this.hours?+=?hour;}get?timeSheet()?{return?`${this.name}?works?${this.hours?||?'nothing'}?hours?on?${this.project}`;}
}let?person?=?new?TimeTracker();
person.addHours?=?2;?//?標準?setter
person.hours?=?4;????//?繞過?setter?進行設置
person.timeSheet;

可以看到,在類中沒有任何措施可以防止在不調用 setter 的情況下更改屬性。

而私有類字段將使用哈希#前綴定義,從上面的示例中,可以修改它以包含私有類字段,以防止在類方法之外更改屬性:

class?TimeTracker?{name?=?'zhangsan';project?=?'blog';#hours?=?0;??//?私有類字段set?addHours(hour)?{this.#hours?+=?hour;}get?timeSheet()?{return?`${this.name}?works?${this.#hours?||?'nothing'}?hours?on?${this.project}`;}
}let?person?=?new?TimeTracker();
person.addHours?=?4;?//?標準?setter
person.timeSheet?????//?zhangsan?works?4?hours?on?blog

當嘗試在 setter 方法之外修改私有類字段時,就會報錯:

person.hours?=?4?//?Error?Private?field?'#hours'?must?be?declared?in?an?enclosing?class

還可以將方法或 getter/setter 設為私有,只需要給這些方法名稱前面加#即可:

class?TimeTracker?{name?=?'zhangsan';project?=?'blog';#hours?=?0;???//?私有類字段set?#addHours(hour)?{this.#hours?+=?hour;}get?#timeSheet()?{return?`${this.name}?works?${this.#hours?||?'nothing'}?hours?on?${this.project}`;}constructor(hours)?{this.#addHours?=?hours;console.log(this.#timeSheet);}
}let?person?=?new?TimeTracker(4);?//?zhangsan?works?4?hours?on?blog

由于嘗試訪問對象上不存在的私有字段會發生異常,因此需要能夠檢查對象是否具有給定的私有字段。可以使用 in 運算符來檢查對象上是否有私有字段:

class?Example?{#fieldstatic?isExampleInstance(object)?{return?#field?in?object;}
}

(3)靜態公共字段

在ES6中,不能在類的每個實例中訪問靜態字段或方法,只能在原型中訪問。ES 2022 提供了一種在 JavaScript 中使用 static 關鍵字聲明靜態類字段的方法。下面來看一個例子:

class?Shape?{static?color?=?'blue';static?getColor()?{return?this.color;}getMessage()?{return?`color:${this.color}`?;}
}

可以從類本身訪問靜態字段和方法:

console.log(Shape.color);?//?blueconsole.log(Shape.getColor());?//?blueconsole.log('color'?in?Shape);?//?trueconsole.log('getColor'?in?Shape);?//?trueconsole.log('getMessage'?in?Shape);?//?false

實例不能訪問靜態字段和方法:

const?shapeInstance?=?new?Shape();console.log(shapeInstance.color);?//?undefinedconsole.log(shapeInstance.getColor);?//?undefinedconsole.log(shapeInstance.getMessage());// color:undefined

靜態字段只能通過靜態方法訪問:

console.log(Shape.getColor());?//?blue
console.log(Shape.getMessage());?//TypeError:?Shape.getMessage?is?not?a?function

這里的 Shape.getMessage() 就報錯了,因為 getMessage 不是一個靜態函數,所以它不能通過類名 Shape 訪問。可以通過以下方式來解決這個問題:

getMessage()?{return?`color:${Shape.color}`?;
}

靜態字段和方法是從父類繼承的:

class?Rectangle?extends?Shape?{?}console.log(Rectangle.color);?//?blue
console.log(Rectangle.getColor());?//?blue
console.log('color'?in?Rectangle);?//?true
console.log('getColor'?in?Rectangle);?//?true
console.log('getMessage'?in?Rectangle);?//?false

(4)靜態私有字段和方法

與私有實例字段和方法一樣,靜態私有字段和方法也使用哈希 (#) 前綴來定義:

class?Shape?{static?#color?=?'blue';static?#getColor()?{return?this.#color;}getMessage()?{return?`color:${Shape.#getColor()}`?;}
}
const?shapeInstance?=?new?Shape();
shapeInstance.getMessage();?// color:blue

私有靜態字段有一個限制:只有定義私有靜態字段的類才能訪問該字段。這可能在使用 this 時導致出乎意料的情況:

class?Shape?{static?#color?=?'blue';
static?#getColor()?{return?this.#color;
}
static?getMessage()?{return?`color:${this.#color}`?;
}
getMessageNonStatic()?{return?`color:${this.#getColor()}`?;
}
}class?Rectangle?extends?Shape?{}console.log(Rectangle.getMessage());?//?Uncaught?TypeError:?Cannot?read?private?member?#color?from?an?object?whose?class?did?not?declare?it
const?rectangle?=?new?Rectangle();
console.log(rectangle.getMessageNonStatic());?//?TypeError:?Cannot?read?private?member?#getColor?from?an?object?whose?class?did?not?declare?it

在這個例子中,this 指向的是 Rectangle 類,它無權訪問私有字段 #color。當我們嘗試調用 Rectangle.getMessage() 時,它無法讀取 #color 并拋出了 TypeError。可以這樣來進行修改:

class?Shape?{static?#color?=?'blue';static?#getColor()?{return?this.#color;}static?getMessage()?{return?`${Shape.#color}`;}getMessageNonStatic()?{return?`color:${Shape.#getColor()}?color`;}
}class?Rectangle?extends?Shape?{}
console.log(Rectangle.getMessage());?// color:blue
const?rectangle?=?new?Rectangle();
console.log(rectangle.getMessageNonStatic());?// color:blue

(5)類靜態初始化塊

靜態私有和公共字段只能讓我們在類定義期間執行靜態成員的每個字段初始化。如果我們需要在初始化期間像 try…catch 一樣進行異常處理,就不得不在類之外編寫此邏輯。該規范就提供了一種在類聲明/定義期間評估靜態初始化代碼塊的優雅方法,可以訪問類的私有字段。

先來看一個例子:

class?Person?{static?GENDER?=?"Male"static?TOTAL_EMPLOYED;static?TOTAL_UNEMPLOYED;try?{//?...}?catch?{//?...}
}

上面的代碼就會引發錯誤,可以使用類靜態塊來重構它,只需將try...catch包裹在 static 中即可:

class?Person?{static?GENDER?=?"Male"static?TOTAL_EMPLOYED;static?TOTAL_UNEMPLOYED;static?{try?{//?...}?catch?{//?...}}
}

此外,類靜態塊提供對詞法范圍的私有字段和方法的特權訪問。這里需要在具有實例私有字段的類和同一范圍內的函數之間共享信息的情況下很有用。

let?getData;class?Person?{#xconstructor(x)?{this.#x?=?{?data:?x?};}static?{getData?=?(obj)?=>?obj.#x;}
}function?readPrivateData(obj)?{return?getData(obj).data;
}const?john?=?new?Person([2,4,6,8]);readPrivateData(john);?//?[2,4,6,8]

這里,Person 類與 readPrivateData 函數共享了私有實例屬性。

fa849748270b743719710dda95bf68d0.gif

·················?若川簡介?·················

你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》20余篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經堅持寫了8年,點擊查看年度總結。
同時,最近組織了源碼共讀活動,幫助4000+前端人學會看源碼。公眾號愿景:幫助5年內前端人走向前列。

71e5a669d0fd3c187edffe2aeea89bcd.png

掃碼加我微信 ruochuan12、拉你進源碼共讀

今日話題

目前建有江西|湖南|湖北?籍 前端群,想進群的可以加我微信 ruochuan12?進群。分享、收藏、點贊、在看我的文章就是對我最大的支持

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/274618.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/274618.shtml
英文地址,請注明出處:http://en.pswp.cn/news/274618.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

字母框如何影響UI內容的理解

What is your earliest memory of reading? Mine’s reading comics. I preferred films over books, I still do, but I seemed to have a fascination for comics. The experience of reading a comic, to me, was somewhere between watching a film and reading a novel, …

Vue2.7 本周發布?支持組合式 API、setup、css v-bind

大家好&#xff0c;我是若川。持續組織了近一年的源碼共讀活動&#xff0c;感興趣的可以 點此加我微信ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列。…

linux中用戶忘記root的密碼--ubuntu版本

基于ubuntu操作系統的情況&#xff0c;當用戶忘記root密碼后&#xff0c; 在普通用戶登陸后 輸入sudu su root 之后系統要求輸入當前用戶的密碼&#xff0c;用戶輸入密碼后&#xff0c;就可以進入root的模式了 就可以操作任何任務。轉載于:https://www.cnblogs.com/zhengyn/arc…

馬上7月,誠邀新老朋友參加近5000人的源碼共讀活動!

大家好&#xff0c;我是若川。最近有不少新朋友關注我。誠邀各位新老讀者朋友參加源碼共讀活動。活動介紹可以點擊文末的閱讀原文。https://juejin.cn/post/7079706017579139102很多人關注我的公眾號是因為我寫了一系列源碼文章&#xff0c;想參與源碼共讀活動。雖然現在有近50…

hashmap 從頭到尾_如何從頭到尾設計一個簡單的復古徽標

hashmap 從頭到尾在紙上素描粗糙的概念 (Sketch rough concepts on paper) Start by sketching out a few ideas for your logo on paper. These don’t have to be detailed drawings. Instead, it’s about getting your ideas out quickly. In this early stage, you can ex…

(轉)android技巧01:Preferencescreen中利用intent跳轉activity

原文連接&#xff1a;http://blog.csdn.net/xianming01/article/details/7543464 設置中的Preferencescreen不僅可以作為設置界面顯示&#xff0c;而且還能夠啟動activity&#xff0c;下面主要是對啟動activity的介紹1. Preferencescreen中啟動activity 例如wireless_setting.x…

Vue 2.7 正式發布,代號為 Naruto

大家好&#xff0c;我是若川。持續組織了近一年的源碼共讀活動&#xff0c;感興趣的可以 點此加我微信ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列。…

js設置css色相旋轉_色相旋轉顏色方案是否保留了對色盲友好的能力?

js設置css色相旋轉Hue rotation is often an easy way to change the appearance of a plot or figure without the need to create a new colour bar. However, when dealing with colourblindness, it is important to ensure that the spacing between colours is sufficien…

Tyvj 1921 Freda的煩惱

我就不說是CF hot days 原題了&#xff0c;我會告訴你使用Math庫是因為一開始偷懶不想寫Min函數么- 1 Uses math;2 Var n,m,i,t,tt,x,cost,ans,tmp:qword;3 Function min(x,y:qword):qword;4 Begin5 if x>y then exit(y);6 exit(x);7 end;8 Begin9 readln(n,m);10 …

ux設計中的各種地圖_在UX設計中使用阿拉伯語

ux設計中的各種地圖Last year I got to work on an app that was ultimately going to be deployed globally in every market and every language including RTL (Right-to-Left) languages — with a specific focus on Arabic.去年&#xff0c;我開始致力于開發一個應用程序…

如何為前端項目一鍵自動添加eslint和prettier的支持

本文來自讀者那個曾經的少年回來了 寫的源碼共讀35期筆記文章&#xff0c;授權投稿&#xff0c;寫的真好。本文參加了由公眾號若川視野 發起的每周源碼共讀活動&#xff0c;點此加我微信 ruochuan12 了解詳情一起參與。本文屬于源碼共讀第35期 | 為 vite 項目自動添加 eslint 和…

Server.Transfer方法在頁面間傳值

a.aspx頁面代碼&#xff1a; protected void Button5_Click(object sender, EventArgs e){Server.Transfer("b.aspx");}public string name{get{return this.TextBox1.Text;}} b.aspx頁面代碼&#xff1a; private void TheFour(){a newWebContext.Handler as a;//創…

極端原理_為極端而設計

極端原理*You can also read this article in German here.*您也可以 在此處 閱讀 德語文章 。 In this article I’m going to draw on the Design thinking concept of designing for extreme users and I will argue that designing for extreme users can be considered a…

當CV工程師碰到了拷貝粘貼的需求——useClipboard的使用及源碼解析

大家好&#xff0c;我是若川。持續組織了近一年的源碼共讀活動&#xff0c;感興趣的可以 點此加我微信ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列。…

centos利用tar包安裝phpmyadmin

我的網站根目錄地址為/var/www/html&#xff0c;phpmyadmin安裝包在/tmp下。 注意&#xff1a;php版本低于5.1的建議安裝phpmyadmin2.11&#xff0c;因為phpmyadmin3.3對php版本的最低要求是php5.2 1 [rootCentOS ~]# tar -zxvf /tmp/phpMyAdmin-2.11.9-all-languages.tar.gz …

ux和ui_從UI切換到UX設計

ux和uiI still remember those days, when I was a soon-to-be graphic design graduate who started to question what my future will be. At that time, I realized that I loved graphic design, but I wasn’t sure if I enjoyed doing it. Creating logos, posters, broc…

春季招聘后前端工程師的就業指南

盡管疫情反復&#xff0c;大廠裁員&#xff0c;招聘季仍是在困難中有條不紊地落下了尾聲。回顧今年的春季招聘&#xff0c;北京青年報記者發現&#xff0c;互聯網“大廠”依然對“研發崗”需求最為旺盛。但許多企業最近都在圍繞“降本提效”來進行業務調整&#xff0c;這對技術…

探索式測試的思維模型

上一章介紹了探索式測試的定義。在實際項目的測試執行過程中&#xff0c;讀者是否曾遇到如下的幾個現象&#xff1a; 測試人員按照一個測試用例來執行測試&#xff0c;得到的程序輸出與預期輸出不一致。 測試人員判斷程序的行為并不是缺陷&#xff0c;但根據新的輸出想到了新的…

圖解Git分支和命令

大家好&#xff0c;我是若川。持續組織了近一年的源碼共讀活動&#xff0c;感興趣的可以 點此加我微信ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列。…

vsco_VSCO重新設計:更直觀,更簡化的界面

vscoAmong the many photo-editing apps, VSCO has definitely become a popular favorite among both experienced photographers as well as “aesthetic” Instagram users. However, my interaction with the app starts and ends with using a few key filters and (maybe…