javascript event loop機制(未完)

前言

javascript是單一執行序,所以一次只能執行一件事。我們網頁上的操作也都會影響這機制,造成效能不好(時間延誤)。
而這其實很難用文字去解釋,我們可以看這支影片。
https://www.youtube.com/watch?v=8aGhZQkoFbQ

小結

1.盡量不要在 callback 當中執行負擔過重的函數,避免佔據 call stack
2.理解 microtask 與一般 task queue 執行順序不同。
--引用自參考資料

雖然理解底層跟實際面操作其實並不會有什麼大改變,因為還是得寫。
但至少可以透過這些底層觀念,讓我們思考有些程式碼其實有更好的寫法,就能減少這種效能問題。
(雖然使用者本身就只會覺得是網路lag而已)

其實還有microtask未補,待下次補完。

參考資料
https://ithelp.ithome.com.tw/articles/10214017
https://www.youtube.com/watch?v=8aGhZQkoFbQ

javascript控制時間setTimeout、setInterval

javascript在處理網頁上”連續執行”、”只執行一次”必見到的時間控制函式setTimeout、setInterval。

setTimeout

延遲某一段時間後才執行一次,每個setTimeout都可以得到一個timer ID。
單位:毫秒

1
2
3
var timeoutID = setTimeout(function[, delay, arg1, arg2, ...]);
var timeoutID = setTimeout(function[, delay]);
var timeoutID = setTimeout(code[, delay]);

用法:

1
const timeroutID = window.setTimeout((()=>{console.log('Hello world')}),1000);

Q:如果要清除setTimeout呢?
利用clearTimeout

用法:

1
2
const timeroutID = window.setTimeout((()=>{console.log('Hello world')}),1000);
window.clearTimeout(timeroutID);

setInterval

延遲時間後,不斷循環某段程式碼,每個setInterval都可以得到一個interval ID。
單位:毫秒

1
2
3
var intervalID = setInterval(func, [delay, arg1, arg2, ...]);
var intervalID = setInterval(function[, delay]);
var intervalID = setInterval(code, [delay]);

用法:

1
const intervalID = window.setInterval((()=>{console.log('Hello world')}),1000);

Q:如果要清除setInterval呢?
利用clearInterval

用法:

1
2
const intervalID = window.setInterval((()=>{console.log('Hello world')}),1000);
window.clearInterval(intervalID);

小結

「雖然 JavaScript 有著非同步事件的特色,但仍是依循單一執行緒的規則運作。 換句話說,當我們在主要執行緒內工作的時間太久,就勢必會延遲 Queue Callback 的執行。」--引用參考資料內文

所以,要計算非常精準的時間,setTimeout、setInterval,都一定會有誤差,可以用server端的正確時間去修正誤差,讓他至少在一個合理的範圍內。

此外,仍有IIFE(Immediately Invoked Function Expression)、scope問題,待補。

參考資料:
https://kuro.tw/posts/2019/02/23/%E8%AB%87%E8%AB%87-JavaScript-%E7%9A%84-setTimeout-%E8%88%87-setInterval/
https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval

C#陣列常見操作

因為每種程式語言都有它特殊的宣告方式,因此就花時間整理一下,往後需要使用時就能快速的熟悉回來。

陣列宣告

1.一維陣列

1
2
int [] array = new int[5];
Console.WriteLine(array[0]); // 輸出:0 陣列的元素會初始化為整數的元素類型預設值 0 。

2.二維陣列

1
2
int [,] array = new int[4,2];
Console.WriteLine(array[1,1]); // // 輸出:0 陣列的元素會初始化為整數的元素類型預設值 0 。

陣列初始化

1.一維陣列

1
2
int [] array = new int[]{1,2,3,4,5};
Console.WriteLine(array[0]); // 輸出:1

2.二維陣列

1
2
int [,] array = new int[4,2]{{1,2},{3,4},{7,5},{4,8}}; // 在宣告時如果有初始值後面就必須要填滿那些範圍的元素
Console.WriteLine(array[2,1]); // 輸出5

3.不規則陣列
感覺很少用到

1
2
3
4
5
int[][] array = new int[3][]; // 宣告時必須int[][] 而不是 int[,]
array[0] = new int[]{1,3};
array[1] = new int[]{5,3,3};
array[2] = new int[]{1,31,5};
Console.Write(array[2][1]);//輸出 31

4.隱含型別陣列
用var開頭,裡面的元素符合同一個型態即可

1
2
var array = new [] {"hi!",null,"world"};
Console.Write(array[2]);//輸出 world

遍歷陣列方式

1.使用for,如果處理多維較為方便,且比較直覺

1
2
3
4
int [] array = new int[]{1,2,3,4,5};
for(int i=0;i<array.Length;i++){
Console.Write(array[i]);
} // 輸出:12345

2.foreach,如果是單維想簡潔些可以用

1
2
3
4
int [] array = new int[]{1,2,3,4,5};
foreach(int num in array){
Console.Write(num);
} // 輸出:12345

把陣列傳入函式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System;			
public class Program
{
public static void printArray(int [] arr){
for(int i=0;i<arr.Length;i++){
Console.Write(arr[i]);
}
}
public static void Main()
{
int [] array = new int[]{1,2,3,4,5};
printArray(array); // 輸出12345
}
}

結語

C#強型別的語言在宣告陣列確實比較麻煩。但相對的,這種寫法比較好”除錯”。
如果平時寫弱型態的如javascript,那要多記一下寫法。
而既然使用了”new 運算子”就是傳址了,要稍微小心寫法。

參考資料
https://docs.microsoft.com/zh-tw/dotnet/csharp/programming-guide/arrays/

C# 遞迴

遞迴定義

  1. 遞迴(Recursion),是指在函式中使用函式自身的方法。
  2. 遞迴函式必須有終止條件,才能被計算。

實際演練

累加,計算1+2+3…+n的遞迴。

C#程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;

public class Program
{
public static int accumulate(int n){
if(n==1){
return 1;
}else if(n >=2 ){
return n + accumulate(n-1);
}
return 0;
}

public static void Main()
{
Console.WriteLine("1+2+3..+10=" + accumulate(10)); // 1+2+3..+10=55
}
}

結語

觀察前後項,然後對改變的數字去推出一個數學的關係。以目前這題算直覺,但扯到二元樹遍歷時就要燒些惱了。
而這題為什麼最後要寫的return 0; 那是因為C#語法不允許函式沒有回傳值,所以隨意傳個0。

參考資料
https://www.csie.ntu.edu.tw/~b98902112/cpp_and_algo/cpp02/recursion.html

SQL基礎操作語法(select、insert、update、delete)

記得大學時常常寫SQL,所以語法很熟悉。
但先前工作是前端變成很少接觸這塊,幾乎快2年沒碰,現在就盡可能的把我學的記錄下來。
而更早之前則是使用ORM,之後就超級少遇到了,被ORM慣壞了。

這篇先從基礎開始。

查詢,對Table01裡的所有欄位進行搜尋
*表示所有欄位

1
select * from Table01

新增,對table插入一列值

1
insert into Table01 (name,email) values ('yuhsiang','test@test.com')

修改,對table的欄位去設定數值,並指定id為6的資料去修改

1
update Table01 set name='tom',email='tom@email.com' where id='6'

刪除,對指定的資料去刪除

1
delete Table01 where id='1'

可以發現,我們賦予值的資料都有’’,而欄位則沒有。
且要修改特定某一筆時,是使用where加上該列的主鍵(PK)去辨識出該列的資料。

參考資料:
https://dotblogs.com.tw/jackbgova/2014/12/03/147522

javascript ES6 整理(上)

陣列、變數、函數。

1. let const 變數宣告

1
2
3
4
5
6
let  x = 10
{
let x = 2
console.log(x) // 2
}
console.log(x) // 10

let,有自己的區塊作用域,不會蓋掉底下宣告的變數

1
2
3
4
5
6
var x = 10
for(var i=0;i<5;i++){
var x = 5
console.log(x) //5
}
console.log(x) // 5

var,沒有自己的區塊作用域,會蓋掉底下宣告的變數

1
const x = 10; 

const用來宣告常數不會被改變,不過如果是物件,仍然可以使用提供的方法去改變裡面的數值。

結論
如果沒有必要需求還是用let或const好,因為不會往下覆之後宣告的變數。

2. arrow function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<button>按鈕</button>
<script>
// 使用 addEventListener 監聽事件
var button = document.querySelector('button');
var fn_arrow = () => {
// 建立 function 時 this 指向 Window
console.log(this.constructor.name); // 執行 function 時 this 指向 Window
};
var fn = function(){
// 建立 function 時 this 指向 Window
console.log(this.constructor.name); // 執行 function 時 this 指向 HTMLButtonElement
};

button.addEventListener('click', fn_arrow);
button.addEventListener('click', fn);

結論
ES6可以簡化ES5的function寫法(匿名函式)等。
ES6 arrow function的this都指向Window,是固定的。
ES5 this 指向在運作時的作用域中,以上範例就是在button的作用域。
看起來如果只是單純callback那ES6箭頭函式會簡潔些,但如果要操作某作用域還是得用ES5寫法。
參考:https://ithelp.ithome.com.tw/articles/10195669
(*未完還有this問題待補)

3. 陣列操作

forEach

1
2
3
4
5
const array = [
1,2,3,4,5
];

array.forEach((item,index) => console.log(index,item));

遍歷元素使用,基本上可以取代掉for。但在裡面async/await不能生效,要用for。
此外,不會回傳任何值。
參考:
https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop

map

1
2
3
4
const array1 = [1, 2, 3 ,4];
const array2 = array1.map(item => item*2);
console.log(array1) // [ 1, 2, 3, 4 ]
console.log(array2) // [ 2, 4, 6, 8 ]

會透過函式內所回傳的值組合成一個新的陣列
並不會改變原陣列

reduce

1
2
3
4
5
6
7
8
9
10
const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// output: 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// output: 15

累加器,通常是要把所有陣列內容加起來才會用到,將陣列化為單一值。

參考:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

from

1
2
const myArr = Array.from("ABCDEFG");
console.log(myArr) // 拆數組[ "A", "B", "C", "D", "E", "F", "G" ]

幾乎很少用到,可將字串拆成字組。而在IE上無法運行此函式。

find

1
2
3
const numbers = [4, 9, 16, 25, 29];
const target = numbers.find(item=>item === 4);
console.log(target)

通常在找陣列中的某元素(物件)會用到,返回是單一的元素。

keys

1
2
3
4
5
6
7
8
9
const fruits = ["Banana", "Orange", "Apple", "Mango"];
const keys = fruits.keys();

let text = "";
for (let x of keys) {
console.log(x)
}

//0 1 2 3

取得陣列所有index

findIndex

1
2
3
const numbers = [4, 9, 16, 25, 29];
const targetIndex = numbers.findIndex((value, index)=>value > 9);
console.log(targetIndex) // 2 (即數字16陣列位置)

返回第一個符合條件的陣列index。

單向鏈結串列(Singly linked list)

整理

單向鏈結串列(Singly linked list)示意圖

每一個節點內含
next // 儲存下一個的節點
val // 數值

如果該節點next為null代表已經沒有下個節點串接。

如上圖範例:
數值為12節點的下個節點(next),接數值為99節點
數值為99節點的下個節點(next),接數值為37節點
而數值37節點的下個節點(next),表示沒有結點串接為null

Big-O
search 搜尋 O(N)

特點
只能做循序存取 (Sequential access)

C#程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

using System;

//節點
public class LinkNode
{
public int val;
public LinkNode next;
public LinkNode(int val,LinkNode next){
this.val = val;
this.next = next;
}
public LinkNode(int val){
this.val = val;
this.next = null;
}
};

public class Program
{
// SingleLinkList 串接節點
public static void Main()
{

LinkNode node1 = new LinkNode(12);
LinkNode node2 = new LinkNode(99);
LinkNode node3 = new LinkNode(37);

node1.next = node2; // 綁定node1的尾端接node2
node2.next = node3; // 綁定node2的尾端接node3


Console.WriteLine("手動印出節點:");
Console.WriteLine(node1.val); // 節點1
Console.WriteLine(node1.next.val);// 節點2 (節點1的下1個元素)
Console.WriteLine(node1.next.next.val);// 節點3 (節點1的下2個元素)
if(node1.next.next.next == null){ //節點4,尾端沒串接任何資料為null
Console.WriteLine("null");
}

Console.WriteLine("遍歷節點:");
LinkNode currentNode = node1;
while(currentNode != null){ // null表示為末端
Console.WriteLine(currentNode.val);
// 將當前節點指到下個節點
currentNode = currentNode.next;
}

Console.WriteLine("在節點1末端添加一個數值38的節點:");

LinkNode cursor = node1;
LinkNode newNode = new LinkNode(38);

// 添加節點在最後面
while(true){
if(cursor.next == null){
cursor.next = newNode; // 找到最尾端把null指派成新節點38
break;
}else{
cursor = cursor.next; //不斷往下找節點
}
}

Console.WriteLine(node1.next.next.next.val); //Q1:可取得38,這邊其實我有些不懂,是new參考的關係嗎,為什麼能夠改到node1?
}
}

結果:

1
2
3
4
5
6
7
8
9
10
11
手動印出節點:
12
99
37
null
遍歷節點:
12
99
37
在節點1末端添加一個數值38的節點:
38

目前有標記的Q1是現在不懂的地方,是new參考的關係嗎,為什麼能夠改到node1的值?
按照這篇來說,他指出C#只有call by value、call by address。
https://dotblogs.com.tw/daniel/2018/02/26/150443

1
2
3
在C#廣義來說
基本型別 Struct (int,double,float,byte ...) 可看作 傳值
一般型別 Class (自訂Class ,SqlConnectio....) 可看作 傳址 更精確來說是傳Stack的值(指向Heap的記憶體位置)

沒傳參考,只有傳值和傳址,所以Q1可能是因為把node1當時的記憶體位置指派給cursor,所以就代表著node1的位置。
而直到最後終端null時,在把new出來的節點38的位址接上去。
把記憶體位置傳到另一個記憶體位置的值上

參考資料
https://kopu.chat/2017/06/02/c-%e8%aa%9e%e8%a8%80%ef%bc%9a%e9%8f%88%e7%b5%90%e4%b8%b2%e5%88%97linked-list%e7%9a%84%e5%bb%ba%e7%ab%8b%e8%88%87%e5%88%aa%e9%99%a4/
https://en.wikipedia.org/wiki/Linked_list
https://zh.wikipedia.org/wiki/%E9%93%BE%E8%A1%A8

LeetCode - 2. add-two-number

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list.

You may assume the two numbers do not contain any leading zero, except the number 0 itself.

Example 1:

Input: l1 = [2,4,3], l2 = [5,6,4]
Output: [7,0,8]
Explanation: 342 + 465 = 807.

Example 2:

Input: l1 = [0], l2 = [0]
Output: [0]

Example 3:

Input: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
Output: [8,9,9,9,0,0,0,1]

Constraints:

The number of nodes in each linked list is in the range [1, 100].
0 <= Node.val <= 9
It is guaranteed that the list represents a number that does not have leading zeros.

C#解答1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int val=0, ListNode next=null) {
* this.val = val;
* this.next = next;
* }
* }
*/

public class Solution {

public ListNode AddTwoNumbers(ListNode l1, ListNode l2) {
ListNode currentL1=l1,currentL2=l2;
ListNode resultListNode = new ListNode();
ListNode resultList = resultListNode; // 為什麼可以儲存?
Boolean isAddOne = false;
while(true){
int sum = 0;
if(currentL1!= null){
sum += currentL1.val;
currentL1 = currentL1.next;
}
if(currentL2!= null){
sum += currentL2.val;
currentL2 = currentL2.next;
}

if(isAddOne){
sum+=1;
}

if(sum >= 10){
isAddOne = true;
sum = sum%10;
}else{
isAddOne = false;
}

resultListNode.next = new ListNode(sum);
resultListNode = resultListNode.next; // ?? 因C#傳址可影響到原本的resultList

if(currentL1 == null && currentL2 == null && isAddOne == false){
break;
}
}
return resultList.next;
}
}

Runtime: 96 ms, faster than 97.02% of C# online submissions for Add Two Numbers.
Memory Usage: 28.2 MB, less than 82.24% of C# online submissions for Add Two Numbers.

思路
  這題是中級題目,但對我來說其實已經蠻吃力了,在進位出807這段其實很容易,但後面那段linknode塞進去時卡很久(註解那部分),最後還是上網查了一下,資料結構真的要熟悉,以及”抽象”思考的方式,目前就是要拿筆整理,腦中還是很難構造出。
後記補充:
第18、43行是參考,當作一個cursor來遍歷。

參考
LeetCode 2. Add Two Numbers
https://skyyen999.gitbooks.io/-leetcode-with-javascript/content/questions/2md.html
https://dotblogs.com.tw/daniel/2018/02/26/150443

LeetCode - 1. Two Sum

Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

You can return the answer in any order.

Example 1:

Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Output: Because nums[0] + nums[1] == 9, we return [0, 1].

Example 2:

Input: nums = [3,2,4], target = 6
Output: [1,2]

Example 3:

Input: nums = [3,3], target = 6
Output: [0,1]

Constraints:

2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
Only one valid answer exists.

Follow-up: Can you come up with an algorithm that is less than O(n^2) time complexity?

C#解答1

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Solution {
public int[] TwoSum(int[] nums, int target) {
for (int i = 0; i < nums.Length - 1 ; i++)
{
for(int j=i+1;j < nums.Length;j++){
if(nums[i]+nums[j] == target){
return new int[] { i,j };
}
}
}
return new int[]{};
}
}

Runtime: 316 ms, faster than 51.22% of C# online submissions for Two Sum.
Memory Usage: 32.1 MB, less than 96.22% of C# online submissions for Two Sum.

javascript解答1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
for (let i = 0; i < nums.length - 1 ; i++)
{
for(let j=i+1;j < nums.length;j++){
if(nums[i]+nums[j] === target){
return [i,j];
}
}
}
return [];
};

Runtime: 116 ms, faster than 35.30% of JavaScript online submissions for Two Sum.
Memory Usage: 39.2 MB, less than 93.93% of JavaScript online submissions for Two Sum.

思路
目前解法1是用兩個for迴圈,時間複雜度O(n^2)。
如果有問題歡迎指證!@@

延伸閱讀
[演算法]如何衡量程式的效率?——論時間複雜度(Time Complexity)

決定未來走的方向

決定未來走的方向了。

前言

目前有正職2年軟體工程師經驗,之後SOHO接了大學同學案子以及維護到現在,但疫情原因加上案源不穩下,覺得勢必要重回職場。工作到現在這段期間也學到不少事,覺得該給自己訂下一條路了--Web Developer 這條路,並以後端為主去延伸。

這是個好決定嗎?

以目前來講,前端、後端部分已有產出的經驗,所以目標開始走向後端、架構為重的開發。我不能說是最好的決定,但在摸熟了前端的大致內容,覺得在快速的變遷下還是有必要走到後端。以往後端都是偏向用laravel(因為當時微軟還沒開始開源),而現在走微軟.net core,將會是以C#為主要開發語言,以現在來說有相對完整的學習地圖,而C#也是我2015年接觸過的語言,前公司亦是用C#,所以要精進也比較容易些。

然後,下面是在工作期間曾經處理過、實際參與過的,API串接、切版、業務邏輯、設計規劃討論,前端的畫面、業務邏輯,期間使用RESTful API,後端經驗就是API、BUG修正等等。

目前完成且有實際上線的作品:

1.ERP形象頁
https://www.linkchain.tw/erp/
2.豬場e把抓
https://pigepm.coa.gov.tw/
3.農業易遊網
https://ezgo.coa.gov.tw/
4.田邊好幫手
https://m.coa.gov.tw/
5.農產品生產追溯
https://qrc.afa.gov.tw/

結論

以目前來說,就是製作一個涵蓋前、後端加上自己經驗的作品,然後一個月後再去面試。不知道結果會如何,不過是目前比較踏實的路了。現在25歲,還來得及,怕的是遲遲做不下結論。

最後,引用鋼之鍊金術師的一段話:「人沒有犧牲就什麼都得不到,為了得到什麼東西,就需要付出同等的代價。」
我已經決定了,就走下去。