ThreeJS 學習筆記——JavaScript 中的函式與物件
一、前言
ThreeJs 的程式碼不管是原始碼還是 demo 的程式碼,都是相對規範的。而對於 JavaScript/">JavaScript 基礎不紮實的同學,比如我這種入門的,還是需要好好補一下的。特別是 JavaScript 中的函式與物件,與 Java 中的差異還是相當大的,而且也是這門語言的核心基礎之一。
鑑於此,我還是默默地打開了ofollow,noindex">廖老師的 JavaScript ,然後埋頭苦讀。
二、函式
1.定義方法
至少2種定義: 一種是
Function namexxx(引數) {函式體}
另一種是
var namexxx
2.作用域
(1)使用 var 關鍵字定義的變數,如果在外部則是全域性的,如果在函式內部則是函式內的。
(2)使用 let 關鍵字定義的變數,是塊級別的,只在當前模組內有效。
(3)不使用關鍵字定義的變數則是全域性的。
注意:全域性變數都會被繫結到 window 物件中去。所以對於全域性物件來說,window.xxx 與 xxx 是一致的。所以可以大膽的猜測,js 中的全域性物件是唯一的,就是 window。
3.關於 this
(1) 在函式內部使用 this ,則表示為指向這個函式的物件。
(2) 在呼叫函式時,如果函式屬於某個物件,則需要以 obj.xxx() 的方式,而不是 xxx() 的方式。如果單獨呼叫 xxx() 方式,其實呼叫的是 window.xxx()。這是一個設計上的巨大缺陷,在 use strict 模式下還會報出錯誤。
(3) 巢狀函式的情況下,內函式的 this 指向也是 window。解決的辦法是在定義內函式前提前將 this 儲存在另一個變數中去,如 let that = this / var that = this。或者使用 apply(obj,argments[]) 方法指定 this。
4.方法
被繫結到物件上的函式稱為方法,與 java 不一樣,可以先定義函式,然後再將函式繫結到物件上去。如構造方法大部分就是這麼幹的。
var OBJLoader2Example = function ( elementToBindTo ) { ...... }; OBJLoader2Example.prototype = { constructor: OBJLoader2Example, ......
5.高階函式
JavaScript 中的函式其實都指向一個變數,因此在函式的引數中也可以傳遞一個函式,這種函式的引數又接收了另一個函式作為引數的函式便稱之為高階函式。這個有點類似於數學中方程中的 x 與 x平方的關係。
(1) map() / reduce(): map() / reduce() 都是 Array 的方法,map() 方法可以理解成是一個 map…to,其接收的引數是一個函式,而這個函引數接收一個引數。reduce() 可以理解成把 … 分解,其接收的引數也是一個函式,而這個函式接收兩個引數。
(2) filter(),起到過濾的作用,例如去除 Array 中的素數,其接收的函式的引數是一個。
(3) sort(),排序方法,對 Array 的資料進行排序,其接收的函式引數等同於 java 中的 compator(),而函式的引數是 2 個。如果不傳遞函式引數則預設作為 string 來處理。
6.閉包
(1)定義
當把一個函式當作返回值返回時,這個函式便稱之為一個閉包。
function count() { var arr = []; for (var i=1; i<=3; i++) { arr.push((function (n) { return function () { return n * n; } })(i)); } return arr; }
(2)特點
(1) 當把閉包賦值給一個變數xxx時並不會立即呼叫這個閉包,而是需要再進一步呼叫 xxx()。
(2) 閉包除了會儲存閉包本身(閉包內的變數以及執行程式碼),還會儲存定義閉包的函式的的引數以及變數。所以在閉包裡面還可以繼續訪問函式裡的變數。
(3) 閉包並不是真正的把外部變數克隆儲存起來,而只是持有其引用。如果在呼叫閉包之前改變了某個外部變數,那麼在呼叫閉包,閉包裡獲取到的變數的值是最近被修改的那一次的值。
(4) 閉包除了可以通過賦值給某個變數xxx,然後通過 xxx.closure() 來呼叫之外 ,還可以直接通過func.xxx() 來呼叫。
(5) 閉包的功能是非常強大的,其不僅僅只侷限於延遲呼叫,比如還有實現定義私有變數。
7.箭頭函式
箭頭函式類似於匿名函式,但其與匿名函式又有著明顯的區別就是其糾真了 this 的正確指向。寫法上有
x => x * x; (x,y) => x + y;
8.generator
generator(生成器)是ES6標準引入的新的資料型別。一個generator看上去像一個函式,但可以返回多次。
function* foo(x) { yield x + 1; yield x + 2; return x + 3; }
generator和函式不同的是,generator由function定義(注意多出的 號),並且,除了return語句,還可以用yield返回多次。
9.IIFE 立即呼叫函式
“立即呼叫的函式表示式”(Immediately-Invoked Function Expression),簡稱IIFE。寫法上有
(function(){ /* code */ }());
或者
(function(){ /* code */ })();
三、面向物件
1.概述
不同於 Java,JavaScript 中的沒有 class 與 例項的概念,其是直接通過原型(prototype)來實現面向物件程式設計。而所謂的繼承不過是把一個物件的原型(proto )指向另一個物件罷了。例如:
Var student = { Name:’sutdent’, Age:10 } Var XiaoMing = { Name:’xiaoming' } XiaoMing.__proto__ = student
通過上面的定義,XiaoMing 不僅有 name 屬性,其還有 age 屬性。當然,一般在實際專案中不這樣寫,而是推薦使用 Object.create() 來基於已有物件繼承實現新的物件。
2.建立物件
(1) 函式原型鏈
函式的原型即 prototype 屬性,JavaScript 中指定了其物件的 prototype 為要繼承的物件,各級的 prototype 就構成了原型鏈。當我們用 obj.xxx 去訪問一個屬性時,其首先會在 obj 裡面找,如果找不到就會到 prototype 上去找,如果還找不到就會到 prototype 的 prototype 裡去找,最後會到 object 的 prototype,如果還找不到就報 undefine 了。
(2) 建構函式
先定義一個普通的函式 function xxx(){} 。如果在使用時在其前面增加了關鍵字 new,那麼其便成為了建構函式。在建構函式中可以通過 this 來為物件定義屬性和方法。但要注意方法是屬於每個物件的,它們之間並不相等。如果要共享同一個方法,應該用 prototype 來定義,如下
Xxx.prototype.yyy = function(){}
這樣定義的 yyy方法就是共享的了。
3.原型鏈繼承
不能直接用 A.prototype = B.prototype,這樣的話 A 和 B 就共享了同一個 prototype。那繼承就沒有意義了。一個做法是通過定義一箇中間空物件,也即定義一個空的 function。如下是繼承的一個簡明的封裝與實現。
(1) 封裝一個 inherites 函式
Function inherits(Child , Parent) { //1.定義一個空的 function 物件 var Empty = function() {}; // 2.空 function 指定 Parent.prototype Empty.prototype = Pranet.prototype; // 3.指定 child.prototype 為空物件 Child.prototype = new Empty(); // 4.修復 child 的建構函式 child.constructor child.constructor = Child; }
(2) 還要在 Child 中通過 Parent.call()方法 繫結 this
function Child(props) { Parent.call(this.props); this.xxx = props.xxx; ….. }
4.class 繼承
ES6 開始增加了 class 以及 extends 關鍵字來實現類的定義和繼承,這就和 Java 的繼承很相似了。
(1)class 定義
class Student { constructor(name) { this.name = name; } hello() { alert('Hello, ' + this.name + '!'); } }
(2)class繼承
class PrimaryStudent extends Student { constructor(name, grade) { super(name); // 記得用super呼叫父類的構造方法! this.grade = grade; } myGrade() { alert('I am at grade ' + this.grade); } }
這樣看起來就舒服多了,有 class 也有 extends,彷彿又有了看 Java 程式碼的感覺。但其本質並不是像 Java 一樣真的有類的概念,而只是原型鏈的一種實現 ,不用我們辛辛苦苦地去封裝 inherites 函數了,當然看起來也好理解多了。
三、後記
這真的就只是一個學習筆記,但對於我來說學習完成後,把學到的東西和理解到的東西整理出來還是有一定收穫的。
最後,感謝廖老師的課程,真的是淺顯易懂,基礎不好而又感興趣的同學應當去通篇學習。同時也感謝你能讀到此文章,希望這個學習筆記能與君共勉。