公眾號 亮亮同學TT
cocoscreator 版本:2.4.x
技術交流: 117871561
js/ts 遵循es6標準
第一篇
Javascript部分
Javascript有哪些數據類型? 舉例兩個最常見的內置對象數據類型。
【答案】Number, String, Boolean, Null, Undefined, Object
常見內置對象類型:數組,函數如下一段代碼:
var a = [];
a[100] = 1;
console.log(a.length);
console.log(a[0]);
a[200] = undefined;
console.log(a.length);
console.lolg(a['100']);
請問四條log語句分別輸出什么?
【答案】分別輸出 101瀑罗,undefined, 201,1
- 如下代碼 打印結果分別是什么?
function counter_1() {
let count = 0;
function inc(count) {
return count++;
}
return () => inc(count);
}
let inc = this.counter_1()
console.log(inc());
console.log(inc());
console.log(inc());
答案是 0,0,0
解析:
雖然是閉包 米母,但是 變量不同, 每次調用閉包 都會傳參 毡琉,此時 參數是number類型 number類型屬于基礎類型 铁瞒,也就是說是值的傳遞 , 當進行值傳遞時 在棧內存中 實際上是 新開辟了一份新的內存桅滋,所以每次傳進來的參數都是全新的 參數是 0 慧耍,也就是count一直是0,里面做了count++操作 丐谋,所以 得到的是 +1之前的 數值 仍是0 最后每次 調用閉包都會是 0
技巧:變量就近原則
- 如下代碼 打印結果分別是什么芍碧?
function counter_2() {
let count = 0;
function inc() {
return count++;
}
return inc;
}
let inc = this.counter_2()
console.log(inc());
console.log(inc());
console.log(inc());
答案:
0,1,2
解析:
利用了閉包的特性,在函數運行結束后count變量不會被釋放号俐,此時 變量的內存情況是 存放在堆里面泌豆,因為是count++所以 是 先用后加+ 1 結果是 0,1,2
- 一個數組里面有number類型吏饿,string類型和數組 要將里面的所有非數組數據存到一個一元數組中.
答案:
let arr:any[] = []
function flatDeep(data: any) {
if (!Array.isArray(data)) {
arr.push(data)
} else {
for (let one of data) {
flatDeep(one);
}
}
}
解析:
利用遞歸踪危,每次遞歸查找篩選 非數組的元素 放入一維數組arr中蔬浙。
parseInt('1.9'); parseInt('hello')分別返回什么值?
【答案】分別返回 1, NaNnull和undefined的區(qū)別陨倡。
【參考答案】null表示一個“空”的值敛滋,它和0以及空字符串''不同,0是一個數值兴革,''表示長度為0的字符串绎晃,而null表示“空”。在其他語言中杂曲,也有類似JavaScript的null的表示庶艾,例如Java也用null,Swift用nil擎勘,Python用None表示咱揍。但是,在JavaScript中棚饵,還有一個和null類似的undefined煤裙,它表示“未定義”。
JavaScript的設計者希望用null表示一個空的值噪漾,而undefined表示值未定義硼砰。大多數情況下,我們都應該用null欣硼。undefined僅僅在判斷函數參數是否傳遞的情況下有用题翰。==和===的區(qū)別
【參考答案】==在比較的時候可以自動轉換數據類型。===嚴格比較诈胜,不會進行自動轉換豹障,要求進行比較的操作數必須類型一致,不一致時返回flase焦匈。函數中this指向什么血公? call, apply, bind的用法和區(qū)別。
【參考答案】this指代函數的運行環(huán)境缓熟,執(zhí)行obj.func()時坞笙,this指向obj,直接執(zhí)行func()時荚虚,this指向全局環(huán)境。
call, apply, bind都可以改變函數執(zhí)行時的運行環(huán)境籍茧,即this的指向版述。
call和apply都是調用時立刻執(zhí)行的,而bind調用后返回了綁定this對象的原函數寞冯,bind比較適合將this綁定后的函數傳入到其他函數中去執(zhí)行渴析,特別是作為回掉函數異步執(zhí)行晚伙。
call和bind的參數第一個參數是要綁定的對象,后面是要傳入原函數的多個參數俭茧;而apply第二個參數必須是一個數組咆疗,數組中是要傳入的參數。說說對 prototype和 __proto __ 的理解
【參考答案】prototype是函數才有的屬性母债,prototype本身也是個函數對象午磁;__proto __是所有對象都有的屬性,__proto __指向構造它的對象的對象的prototype毡们。例如:
> var o = new Object()
o.__proto__ == Object.prototype
< true
o是Object構造出的對象迅皇,o的__proto __指向Object的prototype,這樣o可以使用Object.prototype里面的方法衙熔。原型鏈:當js查找對象的屬性時登颓,先查找對象自身是否具有該屬性,如果沒有红氯,就會去__proto __指向的prototype對象上查找框咙,直到找到或者__proto __為null
- 使用構造函數實現一個類Foo,需要有屬性 count, 方法bar(), 并且寫出創(chuàng)建該類對象的方法
【參考答案】
function Foo(){
this.count = 0;
}
Foo.prototype.bar = function(){
}
var foo = new Foo();
foo.bar();
- 以下代碼片段輸出是什么痢甘,為什么喇嘱?如果想輸出0,1,2請問如何修改?
var s = [];
function foo() {
for(var i=0; i<3; i++){
s[i] = function(){
console.log(i);
}
}
}
foo();
s[0]();
s[1]();
s[2]();
【參考答案】輸出3,3,3产阱。因為foo()函數執(zhí)行時生成了三個閉包婉称,這三個閉包綁定了同一個變量i,第三個閉包生成時构蹬,i的值為3王暗。因此執(zhí)行這三個閉包時都會輸出3。
修改為輸出0,1,2的原則是讓三個閉包綁定不同的變量庄敛,所以在生成閉包時就要區(qū)分出來俗壹,一種修改方法如下:
let s = [];
function foo() {
for(let i=0; i<3; i++){
s[i] = function(){
console.log(i);
}
}
}
foo();
s[0]();
s[1]();
s[2]();
- 說說ES6為什么要引入let關鍵字
【參考答案】因為要解決var聲明對象產生的問題。
var是函數級作用域藻烤,而let是塊作用域绷雏。
var存在變量提升,即變量可以在聲明之前使用怖亭,值為undefined涎显,而let聲明的變量如果在聲明之前使用會拋出一個錯誤。
另外let不允許重復聲明變量兴猩。
ES6 規(guī)定暫時性死區(qū)和let期吓、const語句不出現變量提升,主要是為了減少運行時錯誤倾芝,防止在變量聲明前就使用這個變量讨勤,從而導致意料之外的行為箭跳。這樣的錯誤在 ES5 是很常見的,現在有了這種規(guī)定潭千,避免此類錯誤就很容易了谱姓。
Cocos creator部分
cocos creator和cocos2dx的區(qū)別
【參考答案】cocos creator是一套包含編輯器在內的開發(fā)框架,其內部引擎使用了cocos2d-x js的精簡修改版本刨晴。creator使用js/ts語言開發(fā)屉来,以內容創(chuàng)作為核心,腳本作為自定義組件添加到場景的節(jié)點上割捅。creator中需要動態(tài)載入的資源奶躯,放在工程的哪個子目錄中
【參考答案】asserts/resources寫出代碼片段:獲取節(jié)點node上Label組件,并設置其內容為'hello'
【參考答案】
let label = node.getComponent(cc.Label);
label.string = 'hello';
- 列舉出組件的生命周期回掉函數亿驾,并說明其調用時機
【參考答案】
onLoad 組件首次激活時觸發(fā)
onEnable 組件的enabled屬性從false變?yōu)閠rue時
start 組件第一次執(zhí)行update之前觸發(fā)
update 每一幀渲染前調用
lateUpdate 所有組件update調用后調用
onDisable 組件的enabled屬性從true變?yōu)閒alse時
onDestroy 組件或所在節(jié)點調用了destroy()時調用嘹黔,并在當前幀結束時統(tǒng)一回收組件
creator對齊UI控件使用什么組件?如果想制作一個和屏幕大小一樣的節(jié)點如何設置該組件
【參考答案】widget組件莫瞬。設置top,bottom,left,right為0px儡蔓,且該節(jié)點從直接的父節(jié)點到場景根節(jié)點都必須有widget組件且設置為同屏幕大小√垩或者1.10之后可設置target為最上層的節(jié)點喂江。寫一小段代碼,將節(jié)點node在1秒鐘之內從當前位置移動到(100,100)
【參考答案】
cc.tween(node)
.to(1,{position:cc.v2(100,100)})
.start()
游戲開發(fā)部分
- 什么是draw call? 為什么減少draw call可以優(yōu)化游戲速度旁振。如何減少draw call? 在creator中如何做
【參考答案】
1)Draw Call 簡稱 ”繪制調用“
就是CPU調用圖形繪制接口(api)(例如OpenGL中的glDrawElement命令获询。)來命令GPU進行圖形繪制(渲染)的操作。
2)在每次調用Draw Call之前拐袜,CPU需要向GPU發(fā)送很多內容吉嚣,包括數據,狀態(tài)蹬铺,命令等尝哆。在這一階段,CPU需要完成很多工作甜攀,例如檢查渲染狀態(tài)等秋泄。而一旦CPU完成了這些準備工作,GPU就可以開始本次的渲染规阀。GPU的渲染能力是很強的恒序,渲染300個和3000個三角網格通常沒有什么區(qū)別,因此渲染速度往往快于CPU提交命令的速度谁撼。如果Draw Call的數量太多奸焙,CPU就會把大量時間花費在提交Draw Call命令上,造成CPU的過載。
因此造成Draw Call性能問題的是CPU与帆。
3)解決辦法是 盡可能合批處理 即將小的dc 合并成大的dc 。
1墨榄,將碎圖 合并成大圖
1)玄糟,靜態(tài)合圖
2),動態(tài)合圖
3)袄秩,textuepaker打包圖集
注意:
1阵翎, 盡量將處于同一界面(UI)下的相鄰且渲染狀態(tài)相同的碎圖 打包成圖集
2,改變渲染狀態(tài)會打斷渲染合批之剧,例如改變紋理狀態(tài)(預乘郭卫、循環(huán)模式和過濾模式)或改變 Material(材質)、Blend(混合模式)等等背稼,所以使用自定義 Shader 也會打斷合批贰军。
3,合理控制圖集最大尺寸,避免單個圖像加載時間過長蟹肘。
4,尺寸太大的圖像沒有必要打進圖集(如背景圖)而且圖像尺寸越大词疼,加載的時間也越長,而且是非線性的那種增長帘腹,例如加載一張圖像比加載兩張圖像所消耗的時間還長贰盗。,得不償失阳欲。
5, 間距保持默認的 2 并保持勾選擴邊選項舵盈,避免圖像裁剪錯誤和出現黑邊的情況。
2球化,label用bmfont位圖
- 一張1024x1024,32位的貼圖秽晚,在內存里面占多少字節(jié)?
【參考答案】
圖像占用內存的公式是:numBytes = width * height * bitsPerPixel / 8
套用公式赊窥,RGBA8888 是32位
1024102432/8 = 4194304(字節(jié))
4194304/1024/1024 = 4M ,
1024*1024 RGBA8888 圖片加載到內存的大小 = 4M
cocos中sprite的Blend屬性爆惧,Src Blend Factor設置為 SRC_ALPHA, Dst Blend Factor設置為 ONE_MINUS_SRC_ALPHA是什么意思,有什么作用锨能?
【參考答案】這表示繪制這個Sprite時扯再,和Frame buffer上面已經有的像素進行混合的公式參數。以上參數設置的公式為: FinalColor = SpriteColor(RGB) * SpriteAlpha + BufferColor(RGB) * (1-SpriteAlpha)址遇。效果是標準的透明圖元渲染熄阻。如果做一個射擊游戲,需要發(fā)射大量的子彈倔约,為了避免頻繁的申請內存秃殉,一般會采取什么方法?
【參考答案】會采用對象池/內存對象緩存的機制。cocoscreator提供了相關的api nodepool對象池钾军。
常用算法:
對數
如果a^x=N(a>0,且a≠1)鳄袍,則x叫做以a為底N的對數,記做x=log(a)(N),其中a要寫于log右下吏恭。其中a叫做對數的底拗小,N叫做真數 。通常我們將以10為底的對數叫做常用對數樱哼,以e為底的對數稱為自然對數哀九。
「基本知識」:
時間復雜度
通常我們會用大O來表示一個算法的時間復雜度,即 某個算法的時間增量搅幅。
例如: 要在 一個 5個元素的數組中 查找 1個數 阅束, 不管從第幾個元素開始查找 要想 找到這個數 ,最多需要 5次查找操作 茄唐,最少需要1次 如果是 6個元素的數組息裸,要找到這個數 則 最多 6次 ,最少1次琢融。 通過 大O表示 時間復雜度就是 O(n), 即 每增加 n個元素 就需要 多查找n次 界牡,算法的時間增量就是o(n).通常大O表示算法復雜度增量 都是以 最壞的 情況為 準。
簡單查找
在一個數組中挨個查找某個數據漾抬,這種查找就是簡單查找
比如 5個元素的數組中 查找 1個數 宿亡,從頭到尾查找,最多需要查找五次纳令。
「時間復雜度」:O(n) , 算法時間是線性增長的挽荠。 n個數 最多查找n次 最少查找1次。
「例子」:
* 簡單查找
* @param arr 目標數組
* @param num 要查找的數字
*/
function simpleSearch(arr: number[], num: number):number {
for (let one of arr) {
if (one === num) {
return one;
}
}
return -1;
}
二分查找
「前提」:已經排序的數據
「邏輯」:在排好序(降序如 1,2,3)的 容器中 每次選擇 容器的 中間或者+1或者-1的數字平绩,進行比較圈匆,如果 該數字 大于要查找的數字 ,那么以該數字索引-1對應的值為最大元素捏雌,以原來最小的值為最小值 跃赚,繼續(xù) 折中 查找,如果 該數字 小于要查找的數字 則以 該數字索引+1所對應的值為最小值 性湿,原最大值為最大值 再次折中查找 纬傲,一直到最小數索引等于最大數索引 即 找到對應的值或者 查找完 沒有該值為止。
「時間復雜度」:O(log2n) 以2為底 真數為n的對數肤频。
比如 在 n個已經排好序的數中查找某個數據叹括,每次折中查找 ,那么找到這個數的 最多次數是 log2n 次 宵荒。如果 n是 10 那么 找到這個數 最多需要4次 即 2 * 2 * 2 * 2汁雷。因為222是 8不到10 不能完全查完容器的數據净嘀,所以還需要折中查找一次,所以是 4次
「例子」:
* arr 目標數組
* num 要查找的數字
*/
function binarySearch(arr: number[], num: number):number {
let low = 0;
let hight = arr.length - 1;
let count = 0;
while (low <= hight) {
count++;
let mid = Math.floor((low + hight) / 2);
let guess = arr[mid];
if (guess === num) return guess;
if (guess > num) {
hight = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
選擇排序
「邏輯」:需要遍歷n-1趟侠讯,n表示容器中n個元素挖藏,每趟從待排序的記錄序列中選擇最小或最大的數字放到已排序表的最前位置,直到全部排完厢漩。 每趟交換一次數據
「時間復雜度」:log(n*n) 最多需要 n * n 次排完
「穩(wěn)定性」:不穩(wěn)定
「例子」:
* 選擇排序 從小到大
* @param arr 容器
*/
function selectionSort(arr: number[]) {
let len = arr.length;
for (let i = 0; i < len - 1 ; i++) {
//設定第一個元素為最小元素
let minindex = i;
for (let j = i + 1; j < len ; j++) {
if (arr[minindex] > arr[j]) {
minindex = j;
}
}
//每次遍歷找出最小值與上一次的最小值交換
if (i != minindex) {
let temp = arr[minindex];
arr[minindex] = arr[i];
arr[i] = temp;
}
}
}
冒泡排序
「邏輯」:在要排序的一組數中熬苍,對當前還未排好序的范圍內的全部數,對相鄰的兩個數依次進行比較和調整袁翁,讓較大的數往下沉,較小的往上冒婿脸。即:每當兩相鄰的數比較后發(fā)現它們的排序與排序要求相反時粱胜,就將它們互換。 需要遍歷 n-1趟狐树,每趟 需要對比 n- 當前排序索引-1 次焙压。每趟 交換 n-當前排序索引 -1次 數據
「時間復雜度」:log(n*n) 最多需要 n * n 次排完
「穩(wěn)定性」:穩(wěn)定
「例子」:
* 冒泡排序 從小到大
* @param arr
*/
function bubbleSort(arr: number[]) {
let len = arr.length;
for (let i = 0; i < len - 1; i++) {
for (let j = 0; j < len - i - 1; j++) { // -i 是 排除已經沉到最下面的數,沒必要再次比較抑钟。
if (arr[j] > arr[j + 1]) {
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
快速排序
「邏輯」:
(1)首先設定一個分界值涯曲,通過該分界值將數組分成左右兩部分。
(2)將大于或等于分界值的數據集中到數組右邊在塔,小于分界值的數據集中到數組的左邊幻件。此時,左邊部分中各元素都小于或等于分界值蛔溃,而右邊部分中各元素都大于或等于分界值绰沥。
(3)然后,左邊和右邊的數據可以獨立排序贺待。對于左側的數組數據徽曲,又可以取一個分界值,將該部分數據分成左右兩部分麸塞,同樣在左邊放置較小值秃臣,右邊放置較大值。右側的數組數據也可以做類似處理哪工。
(4)重復上述過程奥此,可以看出,這是一個遞歸定義正勒。通過遞歸將左側部分排好序后得院,再遞歸排好右側部分的順序。當左章贞、右兩個部分各數據排序完成后祥绞,整個數組的排序也就完成了非洲。
「時間復雜度」:最好情況 nlog(2n),最差情況log(n*n)
「理想的情況是」:每次劃分所選擇的中間數恰好將當前序列幾乎等分,經過log2n趟劃分蜕径,便可得到長度為1的子表两踏。這樣,整個算法的時間復雜度為O(nlog2n)兜喻。
「最壞的情況是」:每次所選的中間數是當前序列中的最大或最小元素梦染,這使得每次劃分所得的子表中一個為空表,另一子表的長度為原表的長度-1朴皆。這樣帕识,長度為n的數據表的快速排序需要經過n趟劃分,使得整個排序算法的時間復雜度為O(n*n)
「穩(wěn)定性」:不穩(wěn)定
「例子」:
* 快速排序
* @param array 輸入待排序數組
* @returns 排序后的數組
*/
function quickSort(array:number[]) {
const sort = (arr, left = 0, right = arr.length - 1) => {
if (left >= right) {//如果左邊的索引大于等于右邊的索引說明整理完畢
return
}
let low = left
let index = right
const baseVal = arr[right] // 取無序數組最后一個數為基準值
while (low < index) {//把所有比基準值小的數放在左邊大的數放在右邊
//找到一個比基準值大的數交換
while (low < index && arr[low] <= baseVal) {
low++
}
arr[index] = arr[low] // 將較大的值放在右邊如果沒有比基準值大的數就是將自己賦值給自己(i 等于 j)
while (low < index && arr[index] >= baseVal) { //找到一個比基準值小的數交換
index--
break
}
arr[low] = arr[index] // 將較小的值放在左邊如果沒有找到比基準值小的數就是將自己賦值給自己(i 等于 j)
}
arr[index] = baseVal // 將基準值放至中央位置完成一次循環(huán)(這時候 j 等于 i )
sort(arr, left, index-1) // 將左邊的無序數組重復上面的操作
sort(arr, index+1, right) // 將右邊的無序數組重復上面的操作
}
sort(array)
return array
}