目錄
一.函式介紹
二.函式的作用
三.如何創建函式
1.創建函式的兩種方式
2.兩種方式的區別
四.函式內部屬性
1.arguments ☆☆☆
2.this
3.IIFE函式
4.作用域
五.函式呼叫
六.函式本質
七.函式的應用
1.函式當作引數應用
2.函式作為回傳值的情況
八.閉包
1.閉包創建方式
2.閉包的特點
3.閉包的優缺點
一.函式介紹
函式允許我們封裝一系列代碼來完成特定任務,當想要完成某一任務時,只需要呼叫相應的代 碼即可,方法(method)一般為定義在物件中的函式,瀏覽器為我們提供了很多內置方法,我們不需要撰寫代碼,只需要呼叫方法即可完成特定功能,
二.函式的作用
功能的封裝,直接呼叫,代碼復用率提高(代碼復用率是指相同代碼被復用的概率,在某種程度上,代碼復用率越高,代碼越容易閱讀,維護成本越低)
構建物件的模板(建構式)
三.如何創建函式
1.創建函式的兩種方式
//自定義函式
//方式一 函式宣告
function test(){//function關鍵詞 test函式名 ()參賽部分
//功能體
console.log(1)
}
//方式二 函式運算式 使用變數宣告
var t1 = function () {
console.log(2)
}
test();//1
t1();//2
2.兩種方式的區別
變數提升
//兩種方式的區別:1變數提升
var c = 2;
function cook(a='番茄',b='雞蛋'){//ab為形參 引數可以有默認值
//寫引數時盡量把沒有默認值的放在前面,這樣在有值呼叫時會優先把值傳給沒有默認值的引數
//if(typeof a==undefined){}可以用判斷陳述句來防止出現undefined的bug
var c = 1;
console.log(c);//1
//函式中會優先從區域尋找變數值,當區域沒有時才會在全域尋找,都沒有時就會報錯
console.log(a+'炒'+b);
}
cook();//番茄炒雞蛋
cook('蘿卜');//蘿卜炒雞蛋
var c = 2;
function cook(a='番茄',b='雞蛋'){
console.log(c);//undefined
//在函式中宣告變數,變數名會被提升到區域頂端
//所以這時區域有變數c,就不會在全域尋找,所以值為undefined
var c = 1;
console.log(a+'炒'+b);
}
function cook(a='番茄',b='雞蛋'){
var c = 1;
var res = a+'炒'+b;
//return設定函式回傳值
//若函式在執行完畢后,需要一個結果,這個結果可以通過某個變數接收,那么就可以設定return,
//若我們只是想要執行一段邏輯,這段邏輯執行完畢即可,那就不需要設定return,
return res;//回傳只有一個,并且回傳后所有代碼都無效了
console.log()//無效
}
var res = cook('apple');//res接收的就是函式的回傳值
console.log(res);//apple炒雞蛋
四.函式內部屬性
1.arguments ☆☆☆
arguments是一個類陣列物件,包含傳入函式的所有引數
//函式內部屬性
//arguments 是一個類陣列物件,包含傳入函式的所有引數
//類陣列物件不是陣列,可以當做陣列使用,但不能使用陣列的方法
//內部的下標跟陣列相似,還需要有length,如果一個物件包含這2個特征,才被稱為類陣列物件
function add(a,b){
console.log(arguments);
}
add(1,2);//[Arguments] { '0': 1, '1': 2 }
function add(a,b){
console.log(arguments.length);
}
add(1,2);//2
function add(a,b){
console.log(arguments[0]);
}
add(1,2);//1
//在arguments中有一個屬性 callee- 執行擁有該arguments的函式
//遞回函式 在函式體內部呼叫自身 傳入一個數10實作 10*9*8...*2*1
//使用遞回函式時需要在某個時刻跳出回圈
function digui(num) {
if (num<=1) {
return 1;
}else
return num * arguments.callee(num-1)
}
var res = digui(10);
console.log(res);
2.this
函式內部有this屬性,指向函式所在環境,
//函式的內部有this屬性,指向函式所在環境,
var a = 2;//不寫var 就會存在global中
function add() {
console.log(this.a)//從Window中找a
}//this指向函式所在環境,這個函式在外部,即為全域
//當在瀏覽器中時指向window,當在node中時指向global
add();
var a = 2;
function add() {
console.log(this.a);//2
//函式定義在全域 從window中找a,不會從函式內部找,所以與下面這個新var的a沒有關系
//當去掉this后console.log(a),這時會提升undefined,
//因為此時沒有指向,所以會優先從內部尋找,就會發生變數提升,與上面的a=2就沒有關系了
var a = 1;
console.log(a);//1
}
add();
var obj = {
a:3,
add(){//函式在物件內部定義,所以this指向該物件而非window或另一個函式
console.log(this.a);//3
}
}
總結:
函式或方法定義在全域,則可以在函式內部通過this呼叫這個函式或者在外部的方法
函式定義在物件內部,則可以在函式內通過this呼叫這個物件的內部屬性或方法
3.IIFE函式
//IIFE函式 在js es5中的塊級作用域(模擬的塊級作用域)
//塊級作用域是為了隔離作用域,防止污染全域
//es6塊級作用域寫法
var a = 1;
console.log(a);
{
var a = 2;
console.log(a);//相互獨立,各列印各的
}
//es5寫法
function test() {
var a = 3;
console.log(a);
}
test();//每次塊級作用域宣告完需要手動執行,而es6中是直接執行
(function () {//這個函式在宣告后會立即呼叫
var a = 4;
console.log(a);
})()
4.作用域
函式作用域:函式內部宣告的變數,在函式外部不能訪問
全域作用域:函式外部宣告的變數,在函式內部可以訪問,
當函式嵌套,在這個時候,內部函式與外部函式的這個變數就組成了閉包,
在js中函式內部不存在塊級作用域
五.函式呼叫
改變函式執行環境的三種方法:call()、apply()、bind()()
//如何改變函式的執行環境
name = 'xiaoming'
function sayName(a,b) {
console.log(this.name);
console.log(a+b);
}
var obj1 = {
name:'zhangsan',
}
var obj2 = {
name:'lis',
}
sayName();//xioaming NaN(非數值型別) nan是因為沒有給ab傳值
sayName.call(obj1);//將sayname方法的this指向變為obj1
sayName.call(obj2);//lis
sayName.apply(obj1);//zhangsan
sayName.bind(obj1)();//zhangsan
sayName(1,2);
sayName.call(obj1,1,2);
sayName.apply(obj1,[1,2]);
sayName.bind(obj1)(1,2);//回傳的是一個方法
六.函式本質
函式本質上也是一種物件,擁有屬性和方法
函式在記憶體中的表現形式:
函式當做特殊的物件,堆疊中保存函式名,堆中有兩塊區域:函式本身、函式原型,兩塊區域互相指向,你中有我我中有你,類似于Java的方法區,原型主要用在建構式中
函式本質是一個物件,每個函式都有一個原型物件,通過"函式名.prototype"來訪問該原型物件;原型物件中有個屬性"constructor"指向函式
prototype:用來進行指向,創建物件時,若想讓所創物件有方法,可以讓其定義在prototype上
//函式的剖析(構造器函式)
function Animal(name,age) {
this.name=name;
this.age=age;
}
Animal.prototype.sayHello=function () {
//原型屬性的功能:可以在原型屬性中定義某些方法和屬性,
//從而讓創建的物件擁有這個方法或屬性,如下面的物件xb
console.log(hello);
}
var d1 = new Animal('xb',2);
console.dir(Animal);
d1.sayHello();//hello


七.函式的應用
1.函式當作引數應用
函式做引數應用:由于函式名本身就是變數,所以函式可以當做值來使用(引數,回傳值)
使用場景:定時器、Ajax的使用、陣列遍歷等
var arr = [1,2,3,4,5];
arr.forEach(function (v) {//v代表回圈列印的一項
//在foreach函式內部要放一個函式作為引數使用,即回呼函式
console.log(v);//1 2 3 4 5
})
//定時器
//bom方法 在node中無法測驗
setTimeout(function () {
console.log('可以搶票了');
},2000);//2000毫秒=2秒 兩秒后開始列印
2.函式作為回傳值的情況
function add(a,b) {
//在這里可以回傳函式、變數、物件
return function () {
return a+b;
}
}
var res=add(1,2);//回傳了function
console.log(res());//3
八.閉包
閉包簡單說就是創建一個函式,這個函式的函式體內部有一個變數,為了訪問這個變數又在函式體內部創建了一個函式,以此來訪問,即指有權訪問另一個函式作用域中的變數的函式,
1.閉包創建方式
閉包的創建方式,就是在一個函式內部創建另外一個函式,
//閉包
//經典例子
function getName() {
var num=0;
return function () {
return num;
}
}
//console.log(num);//報錯
//通常情況下我們無法直接訪問區域變數,而通過這個例子可以實作外部訪問函式內部變數的操作
var num = getName();
console.log(num());//0
2.閉包的特點
-函式的內部函式;-函式的內部參考外部的變數;-閉包不會被垃圾回識訓制回收
function getNum() {
var num = 0;
return function () {
var n = 0;
console.log(++num);
//num參考了外部的num所以不會被銷毀,所以逐次增加
console.log(++n);
//n在執行完被銷毀,因為沒有被參考,所以一直是1
}
}
var res = getNum();
//保持參考關系 res參考num,num又被內部參考,所以達到閉包效果不被銷毀
res();// 1 1
res();// 2 1
res();// 3 1
getNum()();//這樣寫getNum將會被銷毀,所以num一直為0
3.閉包的優缺點
優點:閉包可以調應函式內部變數,所以在封裝有優勢,若在實體中使用閉包,因為閉包可以持續擁有一個變數,這樣就可以實作變數的累加、快取等,
缺點:閉包只能取得包含函式中任何變數的最后一個值,
若閉包的變數不使用了,則因為其特殊的機制不被回收,從而造成記憶體損耗
解決方法:手動賦值為null
//閉包實體
for (var i = 1; i <=10; i++) {
setTimeout(function () {
console.log(i);
},1000)
}// 11 11 11 11...
//因為執行代碼時,同步不會等異步,所以外部for回圈先執行完畢,
//然后內部異步函式才開始執行,所以一直是11
//解決方法一: IIEF創建區域塊
for(var j = 1; j <=10; j++){
(function (j) {//接受區域塊變數
setTimeout(function () {
console.log(j);
})//每次執行for回圈都會執行一個區域塊
})(j)//傳入區域塊變數
}
//0 1 2 3 4...10
//解決方法二: 使用let
for (let i = 0; i <=10; i++) {
setTimeout(function () {
console.log(i);
},1000);
}
//0 1 2 3 4...10
//進階版實體
function getNum(num) {
var res = [];
for (var i = 0; i < num; i++) {
res[i] = (function(i){//把i的值賦給陣列res中下標為i的元素
return function(){
console.log(i);//從零開始輸出i的值
}
})(i)
}
return res;//回傳了包含多個函式的陣列
}
var arr = getNum(3)
arr[0]();//0
arr[1]();//1
arr[2]();//2
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/301771.html
標籤:其他

