文章加密

;

2024年12月2日 星期一

Object.is() 与 == 运算符并不等价。 也不等价于 === 运算符

 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%85%BC%E5%AE%B9%E6%80%A7


Object.is() 与 == 运算符并不等价。== 运算符在测试相等性之前,会对两个操作数进行类型转换(如果它们不是相同的类型),这可能会导致一些非预期的行为,例如 "" == false 的结果是 true,但是 Object.is() 不会对其操作数进行类型转换。

Object.is() 也等价于 === 运算符。Object.is() 和 === 之间的唯一区别在于它们处理带符号的 0 和 NaN 值的时候。=== 运算符(和 == 运算符)将数值 -0 和 +0 视为相等,但是会将 NaN 视为彼此不相等。

constructor, __proto__, prototype

https://www.lagagain.com/post/7%E5%A4%A9%E6%90%9E%E6%87%82js%E9%80%B2%E9%9A%8E%E8%AD%B0%E9%A1%8Cday02-new-factory/


題目

function FooConstructor(name){

  this.name = name;

  this.hello = function(){

     console.log(`Hello, ${this.name}`);

  }

}


var obj2 = new FooConstructor("Foo");


var obj3 = {};

obj3.constructor = FooConstructor;

obj3.constructor("Kitty");

obj3.hello(); // => Hello, Kitty


var obj4 = {};

obj4.__proto__ = FooConstructor.prototype;

obj4.constructor("K-on"); 

obj4.hello(); // => Hello, K-on


Object.is(obj3.constructor, obj4.constructor);  // true


考題

FooConstructor.prototype.bye = function(){console.log(`Bye Bye, ${this.name}`)};

obj2.bye(); // => Bye Bye, Foo

// obj3.bye(); // error! 沒繼承到`FooConstructor`,不受影響

obj4.bye(); // => Bye Bye, K-on



var name; // just declare
console.log(name); // => underfined

FooConstructor("JavaScript");  // 沒寫到new

// Oops! 怎麼被改到了??   
console.log(name);  // Javascript


可以改寫func如下避免:

The new.target meta-property lets you detect whether a function or constructor was called using the new operator. 


function FooConstructor(name){

  if (!new.target) throw "FooConstructor() must be called with new";

  this.name = name;

  this.hello = function(){

     console.log(`Hello, ${this.name}`);

  }

}


2024年12月1日 星期日

glob

 1. [] 塞選出任何符合這裏面放的字的項目

例如:

  • Cat.png
  • Bat.png
  • Rat.png
  • car.png
  • list.png
  • mom.jpg
  • cat.jpg

If you wanted to match only the title cased files in this list, you could use the pattern [CBR]at.

This would return the result:

  • Cat.png
  • Bat.png
  • Rat.png

2.
[!CBR]at 這個則是和上面的相反:
  • car.png
  • list.png
  • mom.jpg
  • cat.jpg

3.
Backslashes are used to remove the special meaning of single characters '?''*', and '[', so that they can be used in patterns.

4.

Asterisks (*)

The most common wildcard that you'll see is the asterisk. This character is used in many ways but is mainly used to match any number of characters (like parts of a string).

The three main use cases of asterisks that I've seen used are:

  • * - On Linux, will match everything except slashes. On Windows, it will avoid matching backslashes as well as slashes.
  • ** - Recursively matches zero or more directories that fall under the current directory.
  • *(pattern_list) - Only matches if zero or one occurrence of any pattern is included in the pattern-list above

These use cases can also be used in conjunction with each other! For example, to find all Markdown files recursively that end with .md, the pattern would be **/*.md

Note*.md would only return the file paths in the current directory, which is why we append **/ at the beginning.


5.

Something important that I want to note is that while wildcard patterns are similar to regex patterns, they are not explicitly the same for two main reasons:
  1. Globs are meant to match filenames rather than text
  2. Not all conventions are the same between them (example: * means zero or more copies of the same thing in regex)

monorepo: lerna + 創建私服 verdaccio + jest +eslint

https://www.bilibili.com/video/BV1s44y1C7an/?spm_id_from=333.337.search-card.all.click&vd_source=bfad58a748511d951ef1e6cc2082b1c8

  1. lerna 
  2. 創建私服 verdaccio 
  3. jest
  4. eslint
  5. prettier
  6. editconfig


 https://lerna.js.org/docs/getting-started


https://verdaccio.org/



2024年11月28日 星期四

(未讀)多閱讀鐵人賽的文章 / es歷年新增 和 generator 和 TypeScript 和 Testing

 https://ithelp.ithome.com.tw/users/20128534/ironman/4377


https://ithelp.ithome.com.tw/articles/10264271

https://ithelp.ithome.com.tw/m/users/20103315/ironman/4764

待看
https://jkchao.github.io/typescript-book-chinese/typings/literals.html#%E6%8E%A8%E6%96%AD
https://ithelp.ithome.com.tw/m/articles/10267302
https://www.explainthis.io/zh-hant/swe/why-use-monorepo

1. es歷年新增
https://medium.com/%E6%8B%89%E6%8B%89%E7%9A%84%E7%A8%8B%E5%BC%8F%E7%AD%86%E8%A8%98/%E8%A2%AB%E8%80%83%E5%80%92%E7%B3%BB%E5%88%97qq-es5-es6-es7-es8-es9-es10-c3dab7653373


2. generator
function *g1(){
    let products = ["Apple", "Banana", "Orange"]
    for(var i in products){
        yield products[i]
    }
}

for(product of g1()){
    console.log(product);
}
/* Output:
Apple
Banana
Orange
*/

注意到函式名稱前加了個星號(*)了嗎?然後yield配合上當下產生的產品。That's right! 就是這麼簡單。

var obj = g1();
console.log(obj); // => Object [Generator] {}
console.dir(obj.next()); // => { value: 'Apple', done: false }
console.dir(obj.next()); // => { value: 'Banana', done: false }
console.dir(obj.next()); // => { value: 'Orange', done: false }
console.dir(obj.next()); // => { value: undefined, done: true }
console.dir(obj.next()); // => { value: undefined, done: true }

生成器物件有next()方法可以取的下一件物品。得到的值會是一個有valuedone欄位。value就是預取得的物件,done在檢查是不是到盡頭沒材料了。




3.
為什麼是 TypeScript 和 React Testing 呢?主要是因為我認為有了這兩把刷子,將能夠大幅提升程式的可維護性,前者可以讓接手程式碼的人、甚至是三個月後的自己更容易知道當初寫的程式碼要怎麼用,後者可以避免未來重構或需求修改的時候發生自己意想不到的問題。

減少 Bug 並使得維護更加容易,比如:

  1. 打錯字(Typo)或寫錯變數、物件性質或方法的名稱。例如:String.prototype.toUppercase 以及 String.prototype.toUpperCase ,筆者到現在還是記不了到底字串要用哪種方法才能全部轉大寫,每次都要重新上網查挺麻煩的。但有了 TypeScript,它會自動幫我們提示要用後者 —— 也就是 toUpperCase 這個方法來避免錯誤。(如圖二)
  2. 可以避免類別或物件(Class & Object)的性質與方法(Property & Method)格式錯誤。例如:我們想要定義某類別必須包含 A 以及 B 方法,但是方法的型別也有很多種類,有時候需不需要回傳新的值也是一個問題。我們可以藉由 TypeScript 自動確認我們寫的方法有沒有回傳相對應的型別與格式
  3. 藉由 TypeScript 在 IDE 上的 Autocomplete Feature 來協助開發並且防止錯誤。(如圖一)

ts, 特別注重css

css:

1.
父元素底下的子元素間距離左邊的元素間格8px並且不加在最後一個元素上,省了寫not last child的部分
.alert {
  padding:10px;
  >*+* {
     margin-left: 8px;
   }
}




2. v-bind在css 是vue3的var() 語法糖

限制與注意事項

  1. 數值需自行加單位
    如果綁定的值是數字,記得附加單位,例如:

    margin-top: v-bind(gap + 'px');
  2. 僅支持樣式作用域內
    v-bind() 只能用在 <style scoped> 中,不能直接用於全局樣式。

  3. 瀏覽器兼容性
    這依賴於 CSS 變數,因此需要現代瀏覽器的支持。


<template>
  <div class="box"></div>
</template>

<script>
export default {
  props: {
    gap: {
      type: String,
      default: '10',
    },
  },
};
</script>

<style scoped lang="scss">
.box {
  padding-top: v-bind(gap +'px'); // 將 props.gap 動態綁定到 CSS
}
</style>



// 以下 var()
<template>
  <div class="dynamic-box" :style="`--dynamic-color: ${color};`">
    Styled by props!
  </div>
</template>

<script>
export default {
  props: {
    color: {
      type: String,
      default: 'blue',
    },
  },
};
</script>

<style lang="scss">
.dynamic-box {
  background-color: var(--dynamic-color);
  padding: 10px;
  border: 1px solid var(--dynamic-color);
}
</style>



3.
  • :deep(.a + .a)
    這表示選擇作用域內類名為 .a 的元素,並選中其後直接緊接另一個 .a 元素的組合。這是正確且有效的寫法。

  • :deep(.a) { & + .a { } }
    這是一種嵌套的寫法,表示與上一個寫法等價,選擇 .a 元素,並設置它後面直接相鄰的 .a 元素樣式。

因此,兩種寫法的選擇器目的是相同的。


4. constant()env() 是 CSS 函數,用於訪問環境變數。這些變數通常由設備或瀏覽器設置,用於適配特定設備(例如具有瀏海的手機)。它們最常用於處理安全區域(safe area insets)的布局。


1. constant()

  • 概念constant() 是早期的 CSS 函數,曾被用來引用瀏覽器環境變數,例如 iOS Safari 中的安全區域(safe area)。
  • 狀態已被廢棄,現已由 env() 取代。
  • 支持情況:僅支持一些舊版的 iOS Safari(12 及更早版本)。

範例:

padding-top: constant(safe-area-inset-top);

在支持的瀏覽器中,這會將 padding-top 設置為設備頂部的安全區域高度。


2. env()

  • 概念env() 是現代 CSS 中推薦的方式,用於訪問環境變數,提供對設備特定屬性的支持,例如安全區域。
  • 支持情況:現代瀏覽器,包括 iOS Safari、Chrome 等。

範例:

padding-top: env(safe-area-inset-top);

這是目前標準方式,用來確保內容不會與瀏海等設備元素重疊。


使用場景

這兩個函數通常用於適配帶有安全區域的設備,尤其是 iPhone 帶有瀏海或圓角的情況。

常見環境變數

  • safe-area-inset-top:安全區域的上間距
  • safe-area-inset-bottom:安全區域的下間距
  • safe-area-inset-left:安全區域的左間距
  • safe-area-inset-right:安全區域的右間距

範例:

body { padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); }





結論

  • 如果需要兼容舊版 iOS Safari:可以同時使用兩者,例如:
    padding-top: constant(safe-area-inset-top); padding-top: env(safe-area-inset-top);
  • 在現代開發中,優先使用 env(),因為它是 CSS 的標準化功能且被廣泛支持。


5.字面量类型 ( Literal type )是 JavaScript 本身提供的一个准确变量

type Status = 'success' | 'error' | 'pending';  // 用|設定多的types,叫做union types 聯合型別

let requestStatus: Status = 'success'; // 僅允許這三種值


測驗練習:

function iTakeFoo(foo: 'foo') {}
const test = {
  someProp: 'foo'
};

iTakeFoo(test.someProp); // Error: Argument of type string is not assignable to parameter of type 'foo'

这是由于 test 被推断为 { someProp: string },我们可以采用一个简单的类型断言来告诉 TypeScript 你想推断的字面量:

function iTakeFoo(foo: 'foo') {}

const test = {
  someProp: 'foo' as 'foo'
};

iTakeFoo(test.someProp); // ok

或者使用类型注解的方式,来帮助 TypeScript 推断正确的类型:

function iTakeFoo(foo: 'foo') {}

type Test = {
  someProp: 'foo';
};

const test: Test = {
  // 推断 `someProp` 永远是 'foo'
  someProp: 'foo'
};

iTakeFoo(test.someProp); // ok

6.keyof 顧名思義就是把「物件型別(object type)」的 key 拿出來,但要留意的是,這裡提到的是「物件型別」,而不是真的「物件」,前者是 TypeScript 的型別,後者是 JavaScript 中的一個物件(object):

// Person 是一個 object type
type Person = {
  firsName: string;
  lastName: string;
};

// person 是 JavaScript 的 object (value)
const person = {
  firstName: 'Aaron',
  lastName: 'Chen',
};

2024年11月1日 星期五

vue3 component reactive 用:key

 https://michaelnthiessen.com/force-re-render/


vue3 component reactive 用:key

 component裡面不用加,加在父層就可以了

2024年10月24日 星期四

reduce 做資料分組

 告訴我下面這段程式在做什麼? const list =txList.value.reduce((acc, item)=>{ if(!acc[item.txTerm])){ acc[item.txTerm] = [] } acc[item.txTerm].push(item) return acc },{}) ||[]; return list })

ChatGPT 說:
ChatGPT

這段程式碼的主要功能是將 txList.value 中的項目根據 txTerm 屬性進行分組。以下是程式碼的逐步解析:

  1. txList.value.reduce(...): 使用 reduce 方法來遍歷 txList.value 陣列,並將每個項目依據其 txTerm 屬性進行分組。

  2. (acc, item) => {...}: 這是一個回調函數,acc 是累積器,用來保存分組結果,item 是當前正在處理的項目。

  3. if(!acc[item.txTerm]): 檢查累積器 acc 中是否已經存在以 item.txTerm 為鍵的屬性。如果不存在,就創建一個空陣列。

  4. acc[item.txTerm].push(item): 將當前項目 item 添加到相應的 txTerm 鍵的陣列中。

  5. return acc: 返回累積器,繼續下一個項目的處理。

  6. {},: reduce 的初始值設為一個空物件 {}

  7. || []: 如果 reduce 的結果為假值(例如 undefined),則返回一個空陣列。

最後,這段程式碼的結果是將 txList.value 中的項目分組後返回的物件,其中每個 txTerm 對應一個項目陣列。

你說:
可以給個例子嗎
ChatGPT 說:
ChatGPT

當然可以!假設 txList.value 的內容如下:

javascript
const txList = { value: [ { id: 1, txTerm: 'A', amount: 100 }, { id: 2, txTerm: 'B', amount: 200 }, { id: 3, txTerm: 'A', amount: 150 }, { id: 4, txTerm: 'C', amount: 300 }, { id: 5, txTerm: 'B', amount: 250 }, ] };

運行你提供的程式碼後,list 的結果將會是:

javascript
{ A: [ { id: 1, txTerm: 'A', amount: 100 }, { id: 3, txTerm: 'A', amount: 150 } ], B: [ { id: 2, txTerm: 'B', amount: 200 }, { id: 5, txTerm: 'B', amount: 250 } ], C: [ { id: 4, txTerm: 'C', amount: 300 } ] }

在這個結果中,每個 txTerm(A、B、C)對應一個包含相關項目的陣列。這樣就成功地將項目按 txTerm 分組了!

2024年10月14日 星期一

New Words

 InAppBroswer, 前端BFF(Backends For Frontends, 服務於前端的後端), gradle vs Maven

2024年10月3日 星期四

Cookie/ LocalStorage/ SessionStorage 差別

 https://ithelp.ithome.com.tw/articles/10268532

屬性CookielocalStoragesessionStorage
存儲容量較小,約 4KB較大,約 5MB較大,約 5MB
存放位置 瀏覽器 &  Server瀏覽器瀏覽器
持久性可設置過期時間永久性,除非清除瀏覽器資料關閉頁籤/瀏覽器後清空
使用場景身分驗證、追蹤與分析本地設置、離線應用臨時資料儲存、表單數據保留
Server請求時,會夾帶而外資訊,過多請求時,要注意可能產生效能問題資料存於 Client 端,與Server端無關,請求時不影響資料存於 Client 端,與Server端無關,請求時不影響

[TS] any v.s. unknown

unknown

我們不能夠對 unknown 型別的東西進行任何除了讀寫和比較以外的操作

功能一:保證變數的值不會被不小心地操作到

例子:

function compareArrayAtEven(arr1: unknown[], arr2: unknown[]): boolean {
    let diff = arr1.length - arr2.length;
    if(diff > 1 || diff < -1) return false;
    for(let i = 0; i < arr1.length && i < arr2.length; i++) {
        if(arr1[i] !== arr2[i] + 2) return false; // TypeScript 會在這邊報錯
    }
    return true;
}


功能二:負責把關來自外界的輸入

預防各種執行階段中的不可預期現象發生

例子:

export function splitString(arg: unknown): string[] {
    if(typeof arg == 'string') {
        // 檢查完了之後才做我們真正要做的事情,例如:
        return arg.split(',');
        // 此處 TypeScript 會正確地知道 arg 的型別為字串,
        // 因為剛才我們用 typeof 檢查過了。
        // 對於更複雜的型別檢查,可以用 instanceof 關鍵字、
        // 或是使用 TypeScript 的 TypeGuard 來達成,
        // 這是題外話,這邊先不深入討論。
    } else {
        // 否則看要怎麼進行錯誤處理;這邊的方法是傳回預設值
        return [];
        // 或者也可以丟出一個 Error 包含了我們自訂的錯誤訊息
    }
}


養成把對外 API 的參數宣告成 unknown 的好習慣,並且避免使用 TypeScript 中的 as 關鍵字來作型別斷言(取而代之地,應該使用 instanceof 或自訂的 TypeGuard 來確認型別)

any

any 隨便做都行,沒鑑別度

很大的程度上來說,是的;我個人現在是「TypeScript 程式碼中應該要幾乎沒有 any 才對」主張的擁護者。對於很多我接手的程式碼,我都會先搜尋出所有使用到 any 的地方,而它們大多都可以直接換成 unknown 而不用作額外的修改——而如果換成 unknown 之後某個地方因此就出現了編譯錯誤,那幾乎在所有的情況中,那都是突顯出了程式碼具有潛在的異味(bad smell),而釐清為什麼改成 unknown 之後有編譯錯誤、往往能讓程式碼變得更加健全

只有一種情況是我會勉強接受 any 的使用的,那就是當我們引入了一個第三方的型別,我們很清楚其規格、但是我們偏偏又沒有該型別的完整定義檔、而我們自己去寫定義檔又很浪費時間的時候。然而,這樣的使用有幾個前提是應該要遵守的:

  1. 使用了 any 型別的物件應該要充分地被封裝起來,使得使用它的程式碼都非常清楚該變數要怎麼操作。如果有很多程式碼依賴於該物件,那應該要提供一個有良好定義型別的介面來讓其它程式碼間接操作該物件,而不是讓所有程式碼直接取用它。
  2. 對於該物件傳回的值,應該馬上用型別斷言或型別檢查來確定其型別,而不是繼續讓傳回值維持 any 的狀態。

如果沒辦法做到這兩點,那最好還是花一點時間自己把該型別當中會用到的東西宣告一下,這不僅能讓程式碼更有條理,也可以避免很多潛在的手殘可能性。