JavaScript從入門到精通之入門篇(二)函數和數組

入門篇大綱

在這裏插入圖片描述

第二部分 函數與數組

1.函數

函數的定義

普通函數

  • function 函數名 (表達式1…) { 代碼塊 }
  • js是解釋性語言,在當前script標籤代碼執行的開始階段,就會將普通函數放入堆中,也只是將引用放入堆中,函數中的內容並沒有解釋執行,只有當函數執行的時候纔會解析,執行一次函數,解析一次
  • 普通函數 function fn() {} 可以在定義之前調用執行
fn(); // 可以在定義之前調用
function fn() {
	console.log('普通函數');
}
fn(); // 也可以在定義之後調用
  • 函數重名的危險
    1. 普通函數和匿名函數重名,匿名函數會覆蓋普通函數,原因是普通函數在script標籤開始時就將函數放入堆中,而匿名函數是在代碼解析的時候才存入堆中,如果名稱相同,就會覆蓋原函數;
    2. 普通函數和變量重名,變量會覆蓋普通函數;
      例子:
//  1. 普通函數和匿名函數重名
function fn() {
	console.log('我是普通函數');
}
var fn = function() {
	console.log('我是匿名函數');
}
fn(); // 輸出:我是匿名函數
//  2. 普通函數和變量重名
function fn() {
	console.log('我是普通函數');
}
var fn = 10;
fn(); // 調用失敗,會報fn未定義的錯誤

匿名函數

  • 定義: var 變量 = function() { 代碼塊}
  • 將匿名函數賦值給變量,執行變量函數就是執行這個匿名函數,匿名函數不能再定義該函數之前進行調用
var fn = function() {
	console.log('匿名函數');
}
fn(); // 只能在定義之後調用匿名函數
  • 不建議使用匿名函數,通常使用addEventListener() 方法替代
// 兼容性極好 ie6+, 但是不建議使用
btn.onclick = function() {
	console.log(this); 
}
// 通常用下面方式代替,兼容性 ie8+
btn.addEventListener('click', clickHandler);
function clickHandler(e) {
	console.log(e);
}

構造函數

  • 定義:var 變量 = new Function(參數字符串, 執行語句代碼塊字符串)
  • 使用全字符串,參數在最前面,後面的字符串內容是執行代碼的語句
  • 缺點:代碼會做2次執行,第一次會將函數中的字符串解析爲普通代碼,第二次執行該代碼,因此,效率極低
  • 優點:可以用任何語言傳入該js代碼並且執行
var fn = new Function("a", "console.log(a)");
fn(10); // 輸出 10

自執行函數

  • 定義:(function () { 代碼塊}) ()
  • 自執行函數只能執行一次,執行完成後再也找不到,變成孤兒對象(但是有引用,不能被回收)
(function () {
	console.log('自執行函數');
})();

變量的作用域

全局變量

  • 所謂全局變量就是在script標籤中直接使用var定義的變量
  • 當在script中定義了變量,在定義前調用是undefined,這是因爲定義變量是在內存中開闢了該變量的存儲位置,並在代碼解釋執行時將值存在棧中,如果提前調用,計算機在內存中找不到對應的存儲位置,所以會報undefined
console.log(s); // 報錯:s is not defined 
var s = 100;
  • 如果在上一個script中調用下一個script中定義的變量,因爲沒有開闢存儲空間,所以永遠報錯
<script type="text/javascript">
  console.log(s); // 報錯:s is not defined 
</script>
<script type="text/javascript">
  var s = 100;
</script>
  • 一旦定義變量完成後,在後面的script中都是可以任意調用的,因爲這些變量都是全局變量
<script type="text/javascript">
  var s = 100;
  console.log(s); // 輸出: 100
</script>
<script type="text/javascript">
  console.log(s); // 輸出: 100
</script>

局部變量

  • 所謂局部變量就是在函數中使用var定義的變量
  • 函數中的定義的局部變量是有作用域的,他的範圍僅限於該函數內部,函數運行完成後,函數內定義的變量將會自動銷燬
var s;
function fn() {
	var c = 10; // 這是局部變量
	c += 3; 
	console.log(c); // 輸出:13
	// 可以在函數中修改全局變量的值,修改後,全局調用有效
	s == 10;
	console.log(s) // 輸出:10
}
fn();

局部變量和全局變量重名

  • 如果在函數中定義了某個局部變量名,那麼在該函數中所有的這個變量都是局部變量,不能通過直接使用變量名的方法調用到外部同名的全局變量

例子:

 var s = 10;
 function fn() {
 	var s = 20;
 	s += 20;
 	window.s += 10; // 想要在函數內調用與局部變量同名的全局變量,需要加上window前綴
 	console.log(s); // 輸出:40
 	console.log(window.s); // 輸出:20
 }
 fn();

典型例子

 var s = 10;
 function fn() {
 	console.log(s); // 輸出:undefined
 	s += 20;
 	console.log(s); // 輸出:NaN 因爲是 undefined += 10
 	var s = 20;	// 函數內一旦定義,在其前後調用都爲佈局變量
 }
 fn();

參數

定義

由於js是一種弱類型語言,在使用參數時需要注意以下兩點

  • 參數類型:因爲參數類型不能固定,如果是對外開放的函數參數,一定需要在代碼中判斷輸入參數的類型
  • 初始化:在ES5中函數的參數不能設置初始化值,所以需要在執行代碼時給他自定義一個默認的初始值;而在ES6中是可有初始化參數設置,直接可以使用,比如function(a, b, type='+'),參數type默認是"+"

實參和形參

對象參數

如果參數爲對象,傳入的是引用對象的地址

function fn(o) {
  var obj = {a: 1};
  console.log(o === obj); // false
  console.log(o === obj3); // true
}
var obj3 = {a: 3};
fn(obj3);

函數參數–回調

參數如果傳入的是一個函數名,在當前函數中運行了這個參數,這就是回調

function fn1(o, fn){
	o.a += 1;
	fn(o); // 執行回調
	console.log(o.a); // 輸出: 12
}
function fn2(_o){
	_o.a += 10;
}
var obj = {a: 1};
fn1(obj, fn2);

函數執行自己–遞歸

var sum1 = 0;
var i = 0;
function fn() {
  if (i === 100){
  	return// 跳出函數外
  }
  sum += i;
  i++;
  fn(); // 執行回調
}

return

注意事項

  • return語句會終止函數的執行並返回函數的值,return是JavaScript裏函數返回值的關鍵字
  • 一個函數內處理的結果可以使用return返回,這樣在調用函數的地方就可以用變量接受返回結果
  • return關鍵字內任何類型的變量數據或表達式都可以進行返回,甚至什麼都不返回也可以
  • return也可以作爲阻止後續代碼執行的語句被使用
  • function abc() { if (bool) return}這樣可以有效的控制語句在一定的條件下執行,不過一定要與break區分,break是用在條件當中,跳出的也僅僅是條件和循環,但是return卻可以跳出函數,僅僅是針對函數使用,如果沒有函數是不能使用return的

應用場景

  1. 返回一個數據
function createObj(_a) {
	return {a: _a}; // 返回一個數據對象
}
console.log(createObj(3) === createObj(3)); // 輸出:false
  1. 工廠模式或者單例模式
var box;
function createBox(_a, _b) {
	if (!box) {
		box = {};
	}
	box.a = _a;
	box.b = _b;
	return box;
}
console.log(createBox(3, 5) === createBox(10, 20)); // 輸出: true
  1. 通過參數傳入的對象
function setObjProper(obj) {
	obj = obj || {}; // 寬模式
	obj.n = 3;
	return obj;
}
var obj = setObjProper({});
var obj1 = setObjProper();
var obj2 = {a: 5};
console.log(obj2 === setObjProper(obj2)); // 輸出:true
console.log(obj, obj1); // 輸出:{n: 3}  {n: 3}
  1. 如果參數一個函數,返回的是回調函數的結果
function fn1(fn) {
	var obj = {};
	return fn(obj); // 返回回調函數的結果
}
function fn2(obj) {
	obj.a = 10;
	return obj;
}
console.log(fn1(fn2)); // 輸出:{a: 10}
  1. 返回的是一個私密的對象 (閉包)
function fn() {
	return function () {
		console.log('aaa');
	}
}
fn()(); // 輸出: aaa
  1. 返回一個數組,返回多個元素
  2. 返回一個對象,返回多個元素
  3. 返回一個函數體
  4. 跳出

2.數組

定義

  • 數據將無序的數據做有序的排列,存儲在一個變量中
  • 原生JS中沒有數組類型,原生JS中的數據可以存儲多個不同類型的數據(弱類型)
  • Array.isArray(arr) 是判斷arr是否爲數組
  • 數組中,標識元素所在的位置,叫做下標,也叫做索引
  • 數組中,標識元素的內容,arr[0]這個就叫做下標變量

數組的創建

  1. 字面量創建
    var arr = [1, 2, 3, 4, 5]
  2. 對象構造函數創建
    var arr = new Object([1, 2, 3, 4, 5])
  3. 構造函數創建
    var arr = new Array()
    注意:
    • 創建數組時,有且僅有一個數值類型的參數,這個參數就是數組的長度
    • 創建數組時,有且僅有一個非數組類型的參數或者有兩個或兩個以上的參數,這些參數都是數組的元素

數組的長度

  • 數組的長度是可以設置的
  • 將數組的長度設置爲0,表示清空數組
  • 如果數組的長度小於原數組長度,意味着將數組從0位截取到指定位置
var arr = [1, 2, 3, 4, 5];
arr[arr.length-1] = 6; // 將數組中最後一個元素5替換爲6
arr[arr.length] = 6; // 給數組的尾部添加一個新元素6

數組的遍歷

  1. for循環遍歷
var arr = [1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i++) {
	console.log(i + '=====' + arr[i]); 
	// 輸出: 0=====1
    //		 1=====2
    //       2=====3 
    //       3=====4 
    //       4=====5
}
  1. for in 遍歷 (能遍歷出數組中所有的對象)
var arr = [1, 2, 3, 4, 5];
arr.key = 100;
for (let prop in arr) {
	console.log(prop + '=====' + arr[prop]); 
	// 輸出: 0=====1
    //		 1=====2
    //       2=====3 
    //       3=====4 
    //       4=====5
    //       key=====100
}
  1. forEach遍歷
    forEach遍歷當前數組,沒有返回值,不會返回新數組
var arr = [1, 2, 3, 4, 5];
arr.forEach(function(t, i, arr)) {// 參數爲:元素值,元素下標,數組對象
	console.log(t, i, arr)
})
  1. map遍歷
    map遍歷數組,將當前數組中的元素返回給新數組,不適用return時,新數組的長度與原數組相同,但是每個元素都是undefined
var arr = [1, 2, 3, 4, 5];
arr = arr.map(function (t) {
	if (t % 2 === 0) { // 數組元素中偶數元素 + 1
		return t + 1;
	}
	return t;
});
console.log(arr); // 輸出:[1, 3, 3, 5, 5]

冒泡排序

var arr = [6, 3, 4, 5, 7, 1, 2, 9, 0, 8];
// 要點1: 外層循環從後向前
for (var i = arr.length; i > 0; i--) {
	// 要點2:內層循環從前向後
	for (var j = 0; j < arr.length; j++) {
		// 要點3:只判斷內層循環的當前爲和下一位的大小,然後互換
		if (arr[j] > arr[j+1]) { // 升序: j>j+1 降序:j<j+1
			var temp = arr[j];
			arr[j] = arr[j+1];
			arr[j+1] = temp;
		}	
	}
}
console.log(arr); // 輸出:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

選擇排序

var arr = [6, 3, 4, 5, 7, 1, 2, 9, 0, 8];
// 要點1:外層循環從頭到尾
for (var i = 0; i < arr.length; i++) {
	// 要點2:每次初始最小值是當前循環所在的值
	var min = i; 
	// 要點3:從當前所在值的下一項到尾部循環
	for (var j = min + 1; j < arr.length; j++) {
		// 要點4:比較大小,找出後面元素中最小值所在的下標
		if (arr[min] > arr[j]) { // 升序:min>j  降序:min<j
			min = j;
		}
	}
	// 要點5:互換當前下標值和最小值的下標值
	var temp = arr[i];
	arr[i] = arr[min];
	arr[min] = temp;
}
console.log(arr) // 輸出:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

數組的方法

push

var arr = [1, 2, 3, 4, 5];
// 向數組尾部添加元素
var len = arr.push(6); // 返回數組的新長度
// 典型應用
var len = arr.push(6, 7, 8);
var len2 = arr.push([6, 7, 8]);
console.log(len, len2) // 輸出:8   9

pop

// 刪除數組尾部的元素
var t = arr.pop(); //返回被刪除的元素
// 典型應用
var arr = [-2, -1, 0, 1, 2, 3, 4];
var arr1 = [];
var t;
while (t = arr.pop()) { // 會獲取arr中0以後的所有元素,並倒序排列
	arr1.push(t);
}
console.log(arr1); // [4, 3, 2, 1]

shift

// 刪除數組的頭部元素
var t = arr.shift(); // 返回被刪除的元素

unshift

var arr = [1, 2, 3, 4, 5];
// 向數組頭部添加元素
var len = arr.unshift(); // 返回數組的新長度
var len = arr.unshift(0); // [0, 1, 2, 3, 4, 5]
// 儘量減少unshift的使用,從數組頭部添加元素費性能

join

var arr = [1, 2, 3, 4];
console.log(arr.join()); // 與arr.toString()相同
console.log(arr.join('#')); // 設置一個符號,用這個符號鏈接數組的每一個元素,形成一個新字符串
console.log(arr.join('')); // 元素緊密相連 

concat

var arr = [1, 2, 3, 4];
var arr1 = arr.concat(); // 沒有參數數,表示複製數組
var arr2 = arr.concat(5, 6); // 將arr數組鏈接 5,6兩個元素,形成新數組,原數組不變
var arr3 = arr.concat([5, 6, 7]); // 將arr數組和數組[5, 6, 7]合併形成新數組
var arr4 = [5, 6, 7];
var arr5 = arr.concat(arr4); // 將兩個數組鏈接形成新數組

splice

var arr = [1, 2, 3, 4, 5];
arr.splice(); // 數組插入刪除替換元素,並且返回被刪除元素組合成新數組
var arr1 = arr.splice(); // 創建一個新的空數組返回
var arr1 = arr.splice(3); // 從下標是3開始刪除到尾部
var arr1 = arr.splice(0); // 將arr的所有元素導入arr1中,清空arr
var arr1 = arr.splice(0, 2); // 從arr數組的下標0開始刪除2位元素
var arr1 = arr.splice(0, 0, -1); // 在第0位插入一個-1
var arr1 = arr.splice(-1, 0, -1); // 在第-1位(倒數第1位)插入一個-1
var arr1 = arr.splice(arr.length, 0, -1); // 在數組尾部插入一份-1
var arr1 = arr.splice(1, 2, -1, -2); // 從第1位開始替換兩個元素爲-1, -2

slice

var arr = [1, 2, 3, 4, 5];
arr.slice(); // 數組截取元素,並且返回被截取元素組合成新數組,原數組不變
var arr1 = arr.slice(); // 複製arr的所有元素給arr1,和原數組沒有引用關係
var arr1 = arr.slice(0); // 複製arr的所有元素給arr1,和原數組沒有引用關係
var arr1 = arr.slice(3); // 將數組從下標3開始到結尾截取形成新數組
var arr1 = arr.slice(-2); // 將數組從倒數第2位開始到結尾截取形成新數組
var arr1 = arr.slice(3, 4); // 將數組從下標3開始到下標4截取形成新數組
var arr1 = arr.slice(-2, 4); // 將數組從倒數第2位開始到下標4截取形成新數組
var arr1 = arr.slice(-2, -1); //  將數組從倒數第2位開始到倒數第1位截取形成新數組

indexOf

var arr = [1, 2, 3, 4, 5];
arr.indexOf(); // 在數組中查找元素,返回查找到元素的下標,沒有找到返回-1
console.log(arr.indexOf(2)); // 從0開始向後查找2
console.log(arr.indexOf(2, 3)); // 從下標3開始向後查找2

lastIndexOf

var arr = [1, 2, 3, 4, 5];
arr.lastIndexOf(); // 在數組中從後向前查找元素,返回查找到元素的下標,沒有找到返回-1
console.log(arr.lastIndexOf(2)); // 從後向前查找2
console.log(arr.lastIndexOf(2, 3)); // 從下標3開始向前查找2

reverse

var arr = [1, 2, 6, 4, 3, 5];
// 將原數組倒序改變,返回的新數組和原數組是引用關係
var arr1 = arr.reverse();
console.log(arr, arr === arr1); // 輸出: [5, 3, 4, 6, 2, 1] true

sort

var arr = [1, 12, 6, 4, 3, 5];
// 將原數組中的元素按位排序,原數組發生改變,返回的新數組和原數組是引用關係
var arr1 = arr.sort();
console.log(arr, arr === arr1); // 輸出: [1, 12, 3, 4, 5, 6] true
// sort(function(pre, next){}); 可以實現升序降序排列
// 升序排序 輸出:[1, 3, 4, 5, 6, 12]
arr.sort(function(pre, next) {
	return pre - next; 
});
// 降序排序 輸出:[12, 6, 5, 4, 3, 1]
arr.sort(function(pre, next) {
	return next-pre;
});

some

判斷數組中的所有元素是否滿足某條件,只要有一個滿足條件返回true, 否則返回false

var arr = [1, 3, 5, 2, 7, 8, 0, 6]
var boolean = arr.some(function(t) {
	t ++;
	return t > 5;
})
console.log(boolean); // 輸出: true

every

判斷數組中的所有元素是否滿足某條件,只有當全部滿足返回true,否則返回false

var arr = [1, 3, 5, 2, 7, 8, 0, 6]
var boolean = arr.every(function(t) {
	t ++;
	return t > 5;
})
console.log(boolean); // 輸出: false

filter

判斷數組中的所有元素是否滿足某條件,並返回滿足條件的所有元素組成的新數組,原數組元素不變

var arr = [1, 3, 5, 2, 7, 8, 0, 6]
var arr1 = arr.filter(function(t) {
	t ++;
	return t > 5;
})
console.log(arr1); // 輸出: [5, 7, 8, 6]

reduce

// 如果沒有initValue值,sum就是第0項的值,item就是第一項值,所以index是從1開始
var s = arr.reduce(function(sum, item, index)) {
	console.log(sum, item, index);
	return sum + item;
}, initValue); // 這裏第二個參數是初始值,如果設置這個初始值,index就從0開始

from

var div = document.getElementsByTagName('div');
// es5:將對象轉換成數組
var arr = Array.prototype.slice.call(div); 
// es6: 將對象轉換成數組
var arr = Array.from(div);

參數數組

  • 在函數中,參數如果寫在所有參數的最前面,那就是必要參數
  • es5中所有的參數一般都需要按位填寫,如果沒有填寫,就會變成undefined
  • 多填進入的參數值不會被直接獲取到
  • 如果一個函數的參數不定,我們一般在函數定義時不寫參數,在函數體中使用參數數組arguments進行處理

arguments典型案例

// 1. 求若干數的最大值
function max() {
	var arr = Array.from(arguments);
	return arr.reduce(function(p1, p2){
		return p1 > p2 ? p1 : p2;
	});
}
console.log(max(1, 2, 3, 6, 3, 2, 6, 8, 9)); // 輸出:9

// 2.函數回調
function fn() {
	console.log(arguments.callee); // 獲取當前執行的函數
	console.log(arguments.callee.a); // 獲取屬性值
	console.log(arguments.callee.caller); // 回調當前函數的父級函數
}
fn.a = 10; // 給對象添加屬性
fn(2, 3, 4); // 執行函數
function fn2(fn) {
	fn();
}
fn2(fn); // 執行父級函數

二維數組

var arr = [
	[1, 2, 3, 4, 5],
	[1, 2, 3, 4, 5],
	[1, 2, 3, 4, 5],
	[1, 2, 3, 4, 5],
	[1, 2, 3, 4, 5],
]

對象數組

var arr = [
	{a: 1, b: 2, c: 3},
	{a: 1, b: 2, c: 3},
	{a: 1, b: 2, c: 3},
]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章