Skip to content

zh

Java:變數總是以複製的方式傳遞

我正在學習Java。理解是否函數參數是通過複製還是引用傳遞的一個重要觀念。

複製傳遞意味著當一個變數被傳遞給函數時,會製作該變數的一個副本。引用傳遞則意味著函數中的程式碼運作在原始變數上,而非其副本上。

在Java中,變數始終會通過複製來傳遞。讓我們通過以下三種情況來探索這一點:

情況1:傳遞基本類型

void incrementValue(int inFunction) {
  inFunction++;
  System.out.println("In function: " + inFunction);
}

int original = 10;
System.out.println("Original before: " + original);
incrementValue(original);
System.out.println("Original after: " + original);

結果如下:

Original before: 10
In function: 11
Original after: 10

原始值並未變動。

情況2:將基本類型包裹在對象中傳遞

void incrementValue(int[] inFunction){
  inFunction[0]++;
  System.out.println("In function: " + inFunction[0]);
}

int[] arOriginal = {10, 20, 30};
System.out.println("Original before: " + arOriginal[0]);
incrementValue(arOriginal);
System.out.println("Original after: " + arOriginal[0]);

結果如下:

Original before: 10
In function: 11
Original after: 11

原始值有變動!這是因為複雜對象變數是引用。引用變數指向內存中的位置。當變數被傳遞到函數時,始終會創建新的引用。兩個引用都指向原始對象或值。

int[] original = {10, 20, 30};

original[0] --> | 10 | <-- inFunction[0]
                | 20 |
                | 30 |

兩個陣列元素都指向相同的內存位置。

情況3:傳遞字符串

void changeString(String inFunction){
  inFunction = "New!";
  System.out.println("In function: " + inFunction);
}

String original = "Original!";
System.out.println("Original before: " + original);
changeString(original);
System.out.println("Original after: " + original);

結果如下:

Original before: Original!
In function: New!
Original after: Original!

請記住,字符串是不變的。當傳遞給函數時,會創建新的字符串,使原始字符串保持不變。

愛上React的三大理由

2015年3月3日在香港JavaScript和Node.js聚會上的記錄:

今天,我想談談React,一個用於創建用戶界面的JavaScript庫。我在幾個項目中使用過它,並且越多地與它共事,我就越欣賞其功能。我將解釋為什麼我覺得React如此引人入勝,以及為什麼你應該考慮使用它。

作為一名軟件工程師,我明白開發人員每天面臨的挑戰。在使用React之前,寫程式碼經常會讓人感到不適,尤其是在構建用戶界面的時候。可能的狀態有很多,並且測試所有狀態都是不切實際的。你可能會遇到可變DOM問題或無法預測的用戶輸入。你的用戶界面可能行為不正常,或者在大型應用程式中無法良好地擴展。

程式設計是一門藝術,特別是組織復雜性的藝術。在建立UI時,ReactJS幫助您管理這種復雜性。

什麼是 React?

React 是 MVC(Model-View-Controller)中的 'view'。與像MeteorJS這樣的全面框架不同,React 主要專注於 UI。 它由 Facebook 和 Instagram 開發,並被用於他們的生產環境中。例如,您在Facebook.com 上看到的評論框就是一個 React 組件。

為什麼選擇 React?

我將重點關注三個關鍵點:React 組件,效能,以及處理動態數據。

1. React 組件

在我之前作為 MeteorJS 開發人員的角色中,我發現像 Handlebars 或 Spacebars 這樣的傳統模板語言很有限。相反,React 使用組件而不是模板。這種模組化的方法允許更大的靈活性,可重用性和可測試性。

2. 效能

由於發明了虛擬 DOM,React 提供了令人印象深刻的速度。傳統的方法通常需要對整個頁面進行重新渲染,即使是對微小變化也是如此。 虛擬 DOM 通過僅更新 DOM 的變更部分來最小化這種昂貴的操作。

3. 管理動態數據

狀態管理是 UI 開發中的一大挑戰。 React 通過採用單向資料流來解決這個問題,從而提高了可維護性並簡化了調試。

結語

總結,React因其簡單和強大而脫穎而出。它使得可以開發可重複使用、可測試的組件。它提供出色的效能和有效管理動態數據。

就到這裏!有任何問題嗎?

這裡是幻燈片:幻燈片介紹

常見的 npm 權限問題

問題:

如果你正在使用Mac,並且通過從官方網站下載的pkg文件安裝了Node.js,那麼當你嘗試全局安裝一個npm模塊時,你可能會遇到以下的錯誤信息:

npm ERR! 請嘗試以root /管理員身份再次運行此命令。

我的解決方案:

不要用sudo命令來安裝包!

sudo npm install module -g

有些人在Stack Overflow上推薦上述解決方案,但我強烈建議不要用sudo來管理包。這種解決方案可能會暫時解決你的問題,但你將可能會在以後遇到更多問題。

下面是推薦的做法:

步驟1:使用以下命令確定你的用戶名:

whoami

例如,我的用戶名是victorleungtw。

步驟2:更改node模塊文件夾的擁有權:

sudo chown -R `whoami` /usr/local/lib/node_modules

執行這些步驟後,你將來在安裝npm包時就不必再使用sudo了。

Sublime Text 3:使用 OS X 命令列

問題

Sublime Text 3 包括一個命令行工具, subl。 不幸的是,當你在 OS X Yosemite 上安裝了編輯器後,這個工具無法直接工作。

我的解決方法

安裝Sublime Text 3後,使用以下命令創建一個符號鏈接:

ln -s /Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl /usr/local/bin/subl

在這裡,

  • /Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl 是應用程式在您的應用程式目錄中的存放位置。
  • /usr/local/bin 是您希望符號鏈接存在的選擇路徑。

要將Sublime Text設置為各種命令的默認編輯器,該命令提示輸入,請按照如下方式配置您的 EDITOR 環境變量:

export EDITOR='subl -w'

-w 標誌確保 subl 命令在文件關閉之前不會退出。

另外,您可以使用此命令將Sublime Text設置為您的默認Git編輯器:

git config --global core.editor "subl -n -w"

你需要知道的關於ECMAScript 6的12件事

ECMAScript 6 是 JavaScript 標準的下一個版本。

以下是您需要知道的有關 ES6 的12個酷事:

1. 箭頭函數

類似於 CoffeeScript,ES6 允許你使用肥箭頭語法定義一個函數。

var square = n => n * n

2. 箭頭範疇

‘this’關鍵字可能會令人困惑,因為它指的是調用它的東西。例如,當使用 setTimeout 時,‘this’ 指的是窗口物件。箭頭函數表達式解決了這個問題,將‘this’綁定到函數本身。

function yo() {
  this.name = "Victor"
  setTimeout(() => {
    console.log("yo " + this.name)
  }, 5000)
}
// Output: yo Victor

3. 字串模板

就像 CoffeeScript 一樣,ES6 包含了一個使用 ${} 語法用於變數的字串內插特性。注意下面的例子在第 2 行使用了反勾號而非單引號。

var person = { name: "Victor", age: 24 }
var hello = `My name is ${person.name} and I am ${person.age} years old`

4. Let 範疇

let 聲明了一個區塊範疇的本地變數。換句話說,它不會覆寫外部範疇中的變數值。

var x = 10
for (let i = 0; i < 10; i++) {
  let x = i * 2
}
console.log(x) // x is still 10, not 18

5. 陣列解構賦值

您不必逐一聲明多個變數,可以像這樣在一行中賦值給它們:

var [one, two, three] = [1, 2, 3]

6. 物件解構賦值

同樣地,您也可以為物件使用解構賦值:

var { firstName: name } = { firstName: "Victor" }

7. 物件字面量

您可以使用簡寫符號來構造一個物件,而不是寫 {firstName: firstName, lastName: lastName}

var firstName = "Victor",
  lastName = "Leung"
var person = { firstName, lastName }

8. 默認參數

您可以像這樣為參數分配默認值:

function sayHello(name, greeting = "yo man") {
  return `${greeting} ${name}`
}

9. 展開運算符

展開運算符(...)允許您將一個陣列的每個元素作為參數傳遞。

function threeNumbers(x, y, z) {
  console.log(x, y, z) // 0, 1, 2
}
var args = [0, 1, 2]
threeNumbers(...args)

10. 類別

就像其他物件導向的程式設計語言一樣,ES6 允許您使用新的類別語法來定義一個用於構建物件的藍圖。

class Person {
  sayHey(name) {
    return "hey " + name
  }
}

11. 類別繼承

您可以使用 extends 關鍵字來擴展一個類別。

class Victor extends Person {
  sayHey() {
    return super.sayHey("Victor")
  }
}

12. 產生器

產生器是可以被退出並稍後重新進入的函數。調用一個產生器函數並不會立即執行其內容。當迭代器的 next() 方法被調用時,產生器函數的內容會被執行,直到遇到第一個 yield 表達式。

function* idMaker() {
  var index = 0
  while (true) yield index++
}

var gen = idMaker()

console.log(gen.next().value) // 0
console.log(gen.next().value) // 1
console.log(gen.next().value) // 2

ECMAScript 6中有許多新特性。更多細節,請參考 MDN。官方發表過程於2015年3月在Mozilla開始,並預計於2015年6月完成。敬請期待!

我對Hack Reactor遠程Beta的評價

我參加了在香港的第二屆Hack Reactor Remote Beta,以提高我的JavaScript技術技能。

該課程結構嚴謹,提供了許多優秀的講座和先進技術的實戰經驗。

我從學習遞歸,閉包,和數據結構開始。一旦我對基礎有了堅實的掌握,我就轉向學習各種JavaScript框架,包括BackboneJS, AngularJS, 和 NodeJS,所有這些都是由各自領域的專家教導。為了完成課程,我進行了多個項目,這些項目讓我全面接觸到像ThreeJS和ReactJS這樣的新工具。對於我的論文項目,我建立了一個機器學習服務器來推薦啤酒,這既有挑戰性又很有趣。

與其他遠程學生的協作是一種非常有價值的經驗。我們密切合作,使用Floobits進行配對編程,並使用Google Hangouts進行每日站立會議。員工在HipChat上非常活躍,並且總是熱衷於提供幫助。講師們非常擅長教學,即使你不是電腦科學專業的人,他們也能使複雜的主題變得容易理解。我感謝他們為提供這種高質量的遠程課程而付出的努力。

我強烈推薦這個課程給任何對編程感興趣的人。它提供了五星級的學習體驗。

使用PredictionIO的啤酒推薦引擎

問題:

我們都愛啤酒。然而,今天我們面臨的是前所未有的選擇多樣性,讓我們感到不知所措和猶豫不決:我們下一個該喝什麼?

我的解決方案:

在我們的Hack Reactor論文計畫中,我構建了一個使用PredictionIO作為啤酒推薦引擎的機器學習服務器。廣義地說,該應用基於兩個主要策略:

  1. 內容過濾方法:這使用每種啤酒的各種特性來識別其性質。例如,我們利用breweryDB來確定特定啤酒的風格,其酒精度(ABV)和其國際苦味單位(IBU)。然後我們查詢數據庫以找到風格相似的啤酒。

  2. 協同過濾方法:這完全依賴於過去的用戶行為,特別是你的啤酒評級。它使用交替最小二乘法(ALS)算法的矩陣因子分解技術。我們通過從啤酒評級模式中推斷出來的因子向量來描述啤酒和用戶。啤酒和用戶因子之間的高度相關性將導致推薦。

使用矩陣因子分解的一個優點是其能夠加入附加資訊。當沒有顯性反饋(即,你的評級)時,我們可以使用隱性反饋,如你的瀏覽歷史和搜尋模式,來推斷出用戶偏好。

結果,OnTapp將你和適合你口味的啤酒配對起來。要獲得啤酒推薦並嘗試我們的示例,請訪問:http://ontappapp.com/.

AngularUI:處理Google地圖標記的滑鼠懸停事件

問題:

我花了一些時間才弄清楚這個問題,所以我覺得這個問題值得一篇自己的博客文章。當我試著用AngularUI連接Google Map標記的滑鼠懸停事件時,我發現自己很困惑。文件在這件事情上很含糊,說它只支援單一標記,而不支援多個標記。

對於單一標記,只需要在HTML中指定一個帶有 events 屬性的指令就很直接:

<ui-gmap-marker events="marker.events"></ui-gmap-marker>

然後在控制器中:

$scope.marker = {
  events: {
    mouseover: function (marker, eventName, args) {
      // 回調代碼在此
    },
  },
}

然而,相同的做法不適用於多個標記。我最初以為我可以簡單地為每個標記添加滑鼠懸停事件,就像這樣:

markers.push({
  events: {
    mouseover: function (mapModel, eventName, originalEventArgs) {
      console.log("I'd really like to show the info window on mouseover.")
    },
  },
})

我的解決方案:

為了解決這個問題,將以下內容添加到HTML markers 指令:

events="clickEventsObject"

並在控制器中:

$scope.clickEventsObject = {
  mouseover: markerMouseOver,
  mouseout: markerMouseOut,
}

function markerMouseOver(marker, e) {
  // 回調代碼在此
}

function markerMouseOut(marker, e) {
  // 回調代碼在此
}

它成功了!這個處理程序返回一個直接映射的模型陣列,該陣列屬於與事件一起發送的gMarker集群。如果你有任何問題,請隨時向我提問。😊

找出2015年新年決心的竅門

當一年即將結束,新的一年在1月1日開始時,許多人會設定新年決心。然而,92%的新年決心最終都會失敗。為什麼會這樣?

這個問題引起了我的興趣。以我的黑客思維,我喜歡將問題解決技巧應用到各種情況中,如果能突破這8%的成功率,我會非常高興。

研究顯示,'意志力'可能是一種有限的資源。我們都渴望改進自己,但僅靠意志力往往不足以達成目標。未能達成我們的目標可能會讓人沮喪,導致一種自我批評的循環,這並不幫助我們前進。

要產生有意義的變化,我們需要保持我們的目標既簡單又可實現。解決手邊易於應對的問題,可以提供繼續前進的動力。將模糊的抱負轉化為具體的目標並每天為之努力是很重要的。畢竟,我們是我們重複行動的總和。因此,卓越更多的是一種習慣,而非孤立的行為。

我的年度目標

為保持自己在正軌上,我制定了每日待辦事項,包括:

  1. 始終保持編程- 每天至少花一個小時解決玩具問題或接受編程挑戰。

  2. 優先考慮身體健康 - 每天花30分鐘慢跑或做其他運動。 健康的身體養育健康的心靈。

  3. 致力於學習- 每天花一個小時閱讀書籍或觀看教育影片,擴展你的知識。

  4. 加強社交網絡- 每天花一個小時和家人或朋友聯繫,無論是在午餐,晚餐,或一次簡單的聊天。

Angular.js 工廠:異步處理 $http

問題:

當我使用 $http 服務從遠程API獲取數據時,下面的代碼片段無法將數據返回到控制器。

myApp.factory("myFactory", function ($http) {
  var data = { anArray: [] }

  $http.get("/api").success(function (response) {
    data.anArray.push(response)
  })

  return data
})

這個問題的出現是因為在 $http GET請求將數據推送到數組之前,就執行了 return 語句。

我的解決方案:

為了異步處理數據,當數據被接收到時,控制器需要指導服務採取什麼行動:

myApp.factory("myFactory", function ($http) {
  return {
    getData: function () {
      return $http.get("/api")
    },
  }
})

myApp.controller("MyController", [
  "$scope",
  "myFactory",
  function ($scope, myFactory) {
    $scope.data = []

    var handleSuccess = function (response, status) {
      $scope.data = response
    }

    myFactory.getData().success(handleSuccess)
  },
])

如果你有任何問題,請讓我知道。😊