1. 程式人生 > >珠峰JS筆記2.1(函式,原型,DOM,this)

珠峰JS筆記2.1(函式,原型,DOM,this)

> 原型深入

var oBox = document.getElementById('box');
var op = oBox.getElementById('txt'); //報錯,沒有這個方法

報錯:Uncaught TypeError: oBox.getElementById is not a function

函式的三種角色

函式本身也會有一些自己的屬性: ( 控制檯 dir(Fn) )
-》length: 0 形參的個數
-》name: Fn 函式名
-》prototype 類的原型,在原型上定義的方法都是當前 Fn 這個類例項的公有方法
-》__proto__

把函式當做一個普通的物件,指向 Function 這個類

函式在整個js中是最複雜也是最重要的知識
一個函式存在多面性
1)普通函式它本身就是一個普通的函式,執行的時候形成私有的作用域(閉包),形參賦值,預解釋,程式碼執行,執行完成後記憶體銷燬、不銷燬

2) 它有自己的例項,也有一個叫做 prototype 屬性是自己的原型,它的例項都可以指向自己的原型

3)普通物件和 var obj = { } 中的obj一樣,就是一個普通的物件,它作為物件可以有一些自己的私有屬性,也可以通過__proto__找到 Function.prototype
這三者之間是沒什麼必然的關係的

function
Fn(){ var num = 500; this.x = 100; } Fn.prototype.getX = function(){ console.log(this.x); } Fn.aaa = 1; var f = new Fn; console.log(f.num); // undefined console.log(f.aaa); // undefined

call 方法
fn.call( obj ); // call 方法的作用:
首先讓原型上的 call 方法執行,在執行 call 方法的時候,讓 fn 方法中的 this 變為第一個引數值 obj;然後再把 fn 這個函式執行

arrayObject.slice(start,end)

( 下標,0開始) 從已有的陣列中返回選定的元素,
該方法並不會修改陣列,而是返回一個子陣列
如果想刪除陣列中的一段元素,應該使用方法 Array.splice()
返回一個新的陣列,包含從 start 到 end (不包括該元素)的 arrayObject 中的元素

start 必需。規定從何處開始選取。
如果是負數,那麼它規定從陣列尾部開始算起的位置。-1 指最後一個元素,
end 可選。規定從何處結束選取。該引數是陣列片斷結束處的陣列下標。
如果沒有指定該引數,從 start 到陣列結束的所有元素。
如果這個引數是負數,那麼它規定的是從陣列尾部開始算起的元素

模擬 call 方法的實現

案例:

function fn1(){ console.log(1)}
function fn2(){ console.log(2)}	
fn1.call(fn2);	// 結果 1
fn1.call.call(fn2);	// 結果 2

fn1.call  首先 fn1 通過原型鏈找到 Function.prototype 上的 call 方法,
然後讓 call 方法通過原型再次找到 call 方法,這時讓方法執行,
方法中的 this 是 fn1.call, 首先讓這個方法中的 this 變為 fn2,
然後再讓 fn1.call 執行


最後讓 zhufeng 執行

Function.prototype.call(fn1);  // 什麼也沒有
Function.prototype.call.call(fn2);  // 2 

call apply bind

function fn(){
	console.log(this);
}
fn.call();   // window
fn.call(null);   // window
fn.call(undefined);   // window

在非嚴格模式下,得到的結果都是 window
在嚴格模式下 'use strict'; 
結果:  undefined   /  null  / undefined

apply 和 call 方法的作用是一樣的,都是用來改變方法中的 this 關鍵字,並把方法執行,在嚴格和非嚴格模式下對於第一個引數是 null / undefined 的規律也是一樣的

fn.call( obj, 100, 200);
fn.apply( obj, [ 100, 200]);

bind 這個方法在 ie6-8 下不相容,也是用來改變 this 關鍵字的,但不執行函式,執行 bind 有一個返回值,是改變 this 關鍵字後的函式 (看似預處理)

var tempFn = fn.bind( obj, 1, 2 ); 
tempFn( );

"use strict;" 告訴瀏覽器接下來編寫的的JS 程式碼採用嚴格模式
在嚴格模式下,自執行函式中的 this 永遠是 undefined

> 獲取陣列中最大值最小值

\1 先將陣列排序,再取第一個和最後一個
\2 假設法,假設第一個值是最值,再一個個往後比
判斷賦值時,多用三元運算子
\3 Math.min(12,23,11,26); // 引數不能直接傳一個數組,直接傳陣列,結果 NaN
怎麼直接使用這個方法:

var arr = [12,32,56,72,14,37];
console.log(eval('Math.max('+arr.toString()+')')); // 第一種
console.log(Math.min.apply(null,arr));  // 第二種

相關知識點
arrayObject.join(separator)
\引數可選。指定要使用的分隔符。如果省略該引數,則使用,逗號作為分隔符

var arr = [23,'apple',35];
console.log(arr.join());  //>
console.log(arr.join(' ')); //>  有空格
console.log(arr.join('')); //>  無空格
console.log(arr.join('&')); //>
結果:"23,apple,35" / "23 apple 35" / "23apple35" / "23&apple&35"

String().toString()都是可以將資料轉換為字串型別

stringObject.split(separator,howmany) 把一個字串分割成字串陣列
\separator 必需。字串或正則表示式,從該引數指定的地方分割 stringObject
\howmany 可選。該引數可指定返回的陣列的最大長度

var num = 15;
var a = num.toString();   // >引數為進位制,不寫預設為10
var b = num.toString(2);  // >
var c = num.toString(8);  // >
var d = num.toString(16);  // >
結果:> 15 / 1111 / 17 / f
不同:
toString 不能將 null 和 undefined 轉換為字串,報錯
String(null);  String(undefined); 是可以的

var str = arr.join('&');
console.log('splita:'+str.split()); // "splita:23&apple&35"
console.log('splitb:'+str.split('')); //"splitb:2,3,&,a,p,p,l,e,&,3,5"
console.log('splitb:'+ str.split('',3)); //"splitb:2,3,&"
console.log('splitc:'+str.split(' ')); // "splitc:23&apple&35"

eval 把一個字串變為 JS 表示式執行

console.log(eval('12+23+32')); // 67
console.log(eval('12,34,53,23,54')); // 54 只取到最後一個值

// 括號表示式
function fn1(){ console.log(this);};
function fn2(){console.log(this);};
var obj = {x:1,fn: fn2};
(fn2,obj.fn)(); //>
(obj.fn)(); //>
結果:"[object Window]" /  "[object Object]"
如果在嚴格模式下,第一個值為 undefined 第二個不變

> 求平均值

xxx(n,n,n,n,n,); // 求陣列平均值 去最高,去最低
引數 arguments 是類陣列,不是陣列,不能直接使用陣列的方法
\1 使用 call 方法
\2 將類陣列轉換為陣列

1 使用 call方法
function avgFn(){
	Array.prototype.sort.call(arguments,function(a,b){ return a-b;}); // 排序
	[].pop.call(arguments); // 刪除最後一個
	[].shift.call(arguments); // 刪除第一個
	return eval([].join.call(arguments,'+'))/arguments.length;
}
console.log(avgFn(1,2,3,4,5,6));
在 avgFn 中,console.log( arguments );   callee
2 將類陣列轉換為陣列
console.log(avgScore(3.5, 2.6,4.2,4.1,2.4)); //>"3.6333333333333333"
function avgScore(){
	var temp = [];
	// 將類陣列轉換為陣列
	for(var i=0; i<arguments.length;i++){
		temp[temp.length] = arguments[i];
	}
	temp.pop();
	temp.shift();
	return eval(temp.join('+'))/temp.length;
}
// 優化類陣列轉換為陣列的操作===================================== 
function avgScore(){
	var temp = Array.prototype.slice.call(arguments);
	// var temp = [].slice.call(arguments); 這樣寫也可以
	temp.pop();
	temp.shift();
	return eval(temp.join('+'))/temp.length;
}

相關知識點
arrayObject.slice(start,end)
\start 必需。規定從何處開始選取。如果是負數,那麼它規定從陣列尾部開始算起的位置。也就是說,-1 指最後一個元素,-2 指倒數第二個元素
\end 可選。規定從何處結束選取,若為空,切分的陣列包含從 start 到陣列結束,若為負數,從陣列尾部開始算起的元素

var arr = [12,43,2,5,2,6,46];
var newarr = arr.slice(); // 相當於複製了陣列
console.log(newarr[0]);  // 12 
console.log(arr.slice(1,-1)); //>[43,2,5,2,6]
console.log(arr.slice(2,1)); // 空 
模擬內建類的 slice 原理實現陣列克隆
Array.prototype.mySlice = function(){
	// this  指向當前要操作的這個陣列
	var arr = [];
	for (var i=0;i<this.length; i++) {
		arr[arr.length] = this[i];
	}
	return arr;
}

> 類陣列轉換為陣列

var oDiv = document.getElementsByTagName('div');
console.log(oDiv); // HTMLCollection 元素集合類,也是一個類陣列
var divo = document.getElementsByName('box');
console.log(divo); // NodeList 節點集合

var newdivs = Array.prototype.slice.call(oDiv);
console.log(newdivs);  // 已經是陣列了

在 ie6-8 瀏覽器中,不支援借用陣列的 slice 方法實現將元素集合的類陣列轉換為陣列,報錯:SCRIPT5014: Array.prototype.slice: ‘this’ 不是 JavaScript 物件
但是,物件於 arguments 借用陣列的方法是不存在任何相容性問題的

utils.js (在檔案中使用單例模式實現類陣列轉換為陣列)

var utils = {
	// listToArray 類陣列轉換為陣列
	listToArray: function(likeArray){
		var arr = [];
		try {
			arr = Array.prototype.slice.call(likeArray);
		} catch(e) {
			for (var i=0; i<likeArray.length; i++) {
				arr[arr.length] = likeArray[i];
			}
		}
		return arr;
	}
}

> 瀏覽器錯誤資訊捕獲

\使用 try / catch 捕獲異常資訊,不影響下面程式碼繼續執行
\如果 try 中的程式碼執行出錯了,會預設去執行 catch 中的程式碼
\不管 try 是否出錯,都要執行 finally 中的程式碼
\若是想要捕獲錯誤資訊,又不想讓下面的程式碼執行,在 catch 中手動丟擲一條錯誤資訊,終止程式碼執行 throw new Error('出錯啦')

try{
		// js 程式碼
} catch(e) { // 形參必須寫,
	console.log(e.message); // 收集當前程式碼出錯的原因
	throw new Error('出錯啦');
	// ReferenceError  引用錯誤
	// TypeError 型別錯誤
	// RangeError 範圍錯誤
} finally {
	    // 一般不寫 finally 
}

> sort() 方法

ary.sort( ); // 不傳參,只能處理 10 以內的數字排序
回撥函式:把一個方法 A 當做引數值傳遞給另外一個函式 B, 然後在執行的過程中,根據需要讓 A 方法執行

ary.sort( function( a, b ){
	// a 每次執行匿名函式的時候,找到的陣列中的當前項 
	// b 當前項的後一項
	return a-b;  // 升序,如果 a>b, a 和 b 交換位置 
	// reutrn 1; 這時結果為 呼叫 reverse 方法一樣
} );

案例:給二級陣列排序

var ary = [
	{ name: 'm鳴人', age: 12},
	{ name: 'z佐助', age: 13},
	{ name: 'k卡卡西', age: 29},
	{ name: 'w我愛羅', age: 13},
	{ name: 'l鹿丸', age: 14},
];
ary.sort(function(a,b){
	return (a.age - b.age)*-1;
});
ary.sort(function(a,b){
	return a.name.localeCompare(b.name);
});
console.log(ary);

> JSON

在 window 瀏覽器中,提供了一個叫做 JSON 的屬性,它裡面提供了 2 個方法
\1 JSON.parse 把 JSON 格式的字串–》JSON 格式的物件
\2 JSON.stringify 把 JSON 格式的物件轉換為 JSON 格式的字串

var obj = {'name':'鳴人',age: 12};
var str = JSON.stringify(obj);
console.log(str);  //>
console.log(JSON.parse(str)); //>

在 IE 6-8 瀏覽器中,window 下 沒有 JSON 物件,怎麼相容呢?
JSON 格式的字串 --》 JSON 物件

function strToJSON(str){    //  用eval 記住就好,要加小括號
	return 'JSON' in window ? JSON.parse(str) : eval("("+str+")");
}

> 資料繫結及 DOM 迴流

一行文字超出後自動擷取

text-overflow: ellipsis;  /*擷取後以...結尾; clip 沒有...i*/
white-space: nowrap;
overflow: hidden;

資料繫結
\1 利用動態建立元素節點和把它追加到頁面中的方式實現資料繫結
優勢:把需要動態繫結的內容一個個的追加到頁面中,對原來的元素沒有任何的影響
弊端:每建立一個元素新增到頁面,就會引發一次迴流,迴流次數過多,影響效能

\2 字串拼接的方式:首先迴圈要繫結的資料,然後把需要動態繫結的標籤以字串的方式拼接到一起,最後統一新增到頁面中
弊端:要新增到的物件中原有標籤如有繫結事件,都會消失 -->
優勢:事先把內容拼接好,最後一次新增到頁面,只引發一次迴流

字串拼接繫結資料是工作中最常用的方式, --> 模板引擎資料繫結(jade, kTemplate, angular.js, backbone.js 等等)

\3 文件碎片的方式
建立一個文件碎片,相當於臨時建立了一個容器
var frg = document.createDocumentFragment();
動態建立元素節點,繫結資料,新增到 文件碎片 frg 中,最後再新增到頁面
frg.appendChild( op );
odiv.appendChild( frg );
最後記得:frg = null;

1種:
for(var i=0; i<ary.length;i++){
	var cur = ary[i];
	var op = document.createElement('p');
	op.innerHTML = "<strong>"+(i+1)+"</strong>" + cur.name;
	odiv.appendChild(op);
}
2種:
var str = "";
for (var i=0; i<ary.length; i++) {
	var cur = ary[i];
	str +="<p>";
	str += "<strong>" + (i+1) +"</strong>";
	str += cur['name']; // 記得加 引號
	str +="</p>";
}
odiv.innerHTML += str;

JS 中 DOM 深入知識
迴流:reflow 當頁面中的 HTML 結構發生改變(增加,刪除元素,位置發生改變…)瀏覽器都需要重新的計算一遍最新的 DOM 結構,重新對頁面進行渲染
重繪:某一個元素的部分樣式發生改變(背景顏色),瀏覽器只需要重新的渲染當前的元素即可

> 表格排序及 DOM 對映

ul > li{9$}*5
可在控制檯 Element 下直接刪除 li 元素,控制檯檢視 li 元素集合的長度,-1

DOM 對映的機制: 頁面中的標籤和 JS 中獲取到的元素物件(元素集合)是緊緊的繫結在一起的,頁面中的 HTML 結構改變了,JS 不需要重新獲取,集合裡面的內容也會跟著自己改變

1 按 li 中的數字排序,先獲取 li 集合 lis ,是個類陣列,轉換為陣列 ary
2 將陣列進行排序
3 建立文件碎片 frg ,將 陣列中的 li 一個個新增到 frg
frg.appendChild( ary[i] );
4 將 frg 新增到 ul 中,由於 DOM 對映的機制,新增到 ul 中後,ul 中還是 5 個 li