我們的認(rèn)知中函數(shù)是一段可以反復(fù)調(diào)用的代碼塊够傍,函數(shù)還能接受輸入的參數(shù)形娇,不同的參數(shù)會(huì)返回不同的值,在js的世界了里有三種聲明函數(shù)的方法示姿。
- function命令
- 函數(shù)表達(dá)式
- new Function構(gòu)造函數(shù)(幾乎不使用,對(duì)構(gòu)造函數(shù)不理解的可以看js的設(shè)計(jì)模式,后續(xù)會(huì)有章節(jié)介紹)
- 題外話逊笆,=> 箭頭函數(shù) es6
那我們來(lái)看幾個(gè)列子
//函數(shù)聲明
function test() {
console.log(1)
}
test() //調(diào)用該函數(shù) 1
//函數(shù)表達(dá)式(匿名函數(shù)賦值給變量)
var test = function() {
console.log(2)
}栈戳;
test() //2
//如果不是匿名函數(shù)的表達(dá)式,該函數(shù)名只能在函數(shù)里有效难裆,以便在函數(shù)體內(nèi)部調(diào)用自身
var test = function test1() {
console.log(typeof test1)子檀; // function
};
console.log(test1)乃戈;// error : test1 is not defined
//new Funciton 構(gòu)造函數(shù)(arg1,arg2褂痰,...,函數(shù)內(nèi)容)
var test = new Function(a症虑,console.log(a))
test(1) // 1
//箭頭函數(shù)
var test = (x,y) => x + y ;
test(1,3) // 4
函數(shù)額外知識(shí)點(diǎn):
1.每個(gè)javascrit中函數(shù)有一個(gè)內(nèi)置的對(duì)象arguments對(duì)象缩歪,它是一個(gè)類數(shù)組,我們可以用它達(dá)到函數(shù)重載的效果
function sum() {
var sum=0;
for(var i = 0 ;i< arguments.length;i++) {
sum+=arguments[i]
}
return sum
}
sum(1,2,3) //6
2.函數(shù)的this指向個(gè)人理解:函數(shù)在執(zhí)行時(shí)谍憔,會(huì)在函數(shù)體內(nèi)部自動(dòng)生成一個(gè)this指針匪蝙。誰(shuí)直接調(diào)用產(chǎn)生這個(gè)this指針的函數(shù),this就指向誰(shuí)习贫。
var a = 1
function global(){
var a = 2;
console.log(this) //window
console.log(this.a) //1
console.log(a) //2
}
global() 等價(jià)于 window.global() //得出全局函數(shù)的this都是指向的window逛球,調(diào)用可以省略window
//如果函數(shù)作為對(duì)象的屬性被調(diào)用
var obj = {
name: "師父",
say: function() {
console.log("大師兄," + this.name + "被妖怪抓走了")
},
action: {
name: "二師兄",
me: "我老沙呀",
say: function() {
console.log("大師兄苫昌," + this.name + "被妖怪抓走了");
function whoSay() {
console.log(this) // window
console.log(this.me) //undefined
}
whoSay();
// 因?yàn)槭菍儆谄胀ê瘮?shù)調(diào)用颤绕,但是并沒有通過 obj.action.say 直接調(diào)用,
//只是自執(zhí)行,這個(gè)時(shí)候奥务,whoSay就是一個(gè)全局函數(shù)物独,因此該函數(shù)的
//this指向的就是window,在window上找不到me故所以是undefined
}
}
}
obj.say(); //大師兄汗洒,師父被妖怪抓走了
obj.action.say(); //大師兄议纯,二師兄被妖怪抓走了
var elseObj=obj.say;
elseObj(); // 注意此時(shí)的this指向?yàn)閣indow 大師兄,被妖怪抓走了
3.我們可以利用call溢谤,apply調(diào)用函數(shù)瞻凤,并改變了函數(shù)的this指向問題,他們區(qū)別傳參不同
function Animal (name){
this.name = name
this.showName = function() {
console.log(this.name)
}
}
function Cat(name) {
Animal.call(this,name) //this指代函數(shù)Cat
}
function Dog(name) {
Animal.apply(this,[name]) // 也可以寫成Animal.apply(this,[arguments])
}
var cat = new Cat('miaomiao');
console.log(cat.name) // miaomiao
console.log(cat.showName()) //miaomiao
var dog = new Dog('wangwang');
console.log(dog.name) //wangwang
console.log(dog.showName()) //wangwang
- 函數(shù)閉包 :在《JavaScript高級(jí)程序設(shè)計(jì)(第3版)》:閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)。創(chuàng)建閉包的常見方式世杀,就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)阀参。
作用:一個(gè)是可以讀取函數(shù)內(nèi)部的變量,另一個(gè)就是讓這些變量的值始終保持在內(nèi)存中瞻坝。
看個(gè)阮一峰老師最簡(jiǎn)單的閉包列子:
function f1(){
var n=999;
nAdd = function(){n+=1}
function f2(){
return n
}
return f2;
}
console.log(n) // 報(bào)錯(cuò)蛛壳,想要拿到函數(shù)內(nèi)部的n,那么用過一個(gè)函數(shù)返回
var result=f1();
result(); // 999
nAdd();
result(); // 1000 原因就在于f1是f2的父函數(shù)所刀,而f2被賦給了一個(gè)全局變量衙荐,這導(dǎo)致f2始終在內(nèi)存中,而f2的存在依賴于f1浮创,因此f1也始終在內(nèi)存中忧吟,不會(huì)在調(diào)用結(jié)束后,被垃圾回收機(jī)制(garbage collection)回收斩披。
好吧溜族,這東西呢仔細(xì)琢磨琢磨就行,現(xiàn)在我們拿一個(gè)列子來(lái)加深印象
function A(){
var funs=[];
for(var i=0;i<10;i++){
funs[i]=function(){
return i;
}
}
return funs;
}
var funs = A();//定義funs[0]-funs[9]垦沉,10個(gè)函數(shù)
console.log(funs[0]());//10
console.log(funs[1]());//10
console.log(funs[6]());//10
按理說我們腦袋反應(yīng)的應(yīng)該是0 煌抒,1 ,6 怎么全是10呢厕倍?
因?yàn)閳?zhí)行環(huán)境和變量對(duì)象在運(yùn)行函數(shù)時(shí)生成寡壮,在funs[0] () 的時(shí)候才產(chǎn)生真正的環(huán)境變量i,那么此時(shí)i是什么绑青,看return的時(shí)候诬像,顯然是10,所以返回的都是10了
我們用閉包改一下,j 加一個(gè)立即執(zhí)行函數(shù)正應(yīng)對(duì)了我們剛提到的執(zhí)行環(huán)境和變量對(duì)象在運(yùn)行函數(shù)時(shí)生成闸婴,那么此時(shí)的環(huán)境變量已經(jīng)變成了1坏挠,2,3....
function A(){
var funs=[];
for(var i=0;i<10;i++){
funs[i]=function B(num){
return function() {
console.log(num)
}
}(i)
}
return funs;
}
- 函數(shù)遞歸:字面就是在函數(shù)里面調(diào)用自己邪乍,在滿足遞歸條件時(shí)需要多次調(diào)用降狠,比較著名的斐波那契數(shù)列
fibonacci
fibonacci數(shù)列為:[1, 1, 2, 3, 5, 8, 13, 21, 34 …],就是后一項(xiàng)等于強(qiáng)兩項(xiàng)的和对竣,看下代碼實(shí)現(xiàn)
function fibonacci(n) {
if(n <= 1) {
return 1
}
return fibonacci(n-1) + fibonacci(n-2)
}
// 我們可以用閉包遞推來(lái)實(shí)現(xiàn)然這里也是使用了遞歸,
//但是閉包大大減少了遞歸的次數(shù)典型的用空間換時(shí)間的例子榜配。
var fibonacci = (function(){
var arr = [0,1,1];
return function(n) {
var res = arr[n];
if(res) {
return res;
} else {
arr[n]=fiba(n-1)+fiba(n-2);
return arr[n];
}
}
}(n)
//遞推法就是循環(huán)嘛
function fibonacci(n){
var one = 1;
var two = 1;
for (var i = 3;i<=n; i++) {
var three = one + two;
one = two;
two = three;
}
if(n==1 || n==2) {
return one
}
return three
}
遞推法的效率最高否纬,其次是閉包,而遞歸法效率最低蛋褥。
閉包效率雖然相對(duì)遞歸法高了不少临燃,但是這種方法,如果使用不當(dāng)會(huì)造成內(nèi)存泄露烙心,
遞歸算法一般用于解決三類問題:
- 數(shù)據(jù)的定義是按遞歸定義的膜廊。(Fibonacci函數(shù))
- 問題解法按遞歸算法實(shí)現(xiàn)。(回溯)
- 數(shù)據(jù)的結(jié)構(gòu)形式是按遞歸定義的淫茵。(樹的遍歷仍侥,圖的搜索)
但是遞歸算法解題的運(yùn)行效率較低端逼。在遞歸調(diào)用的過程當(dāng)中系統(tǒng)為每一層的返回點(diǎn)、局部量等開辟了棧來(lái)存儲(chǔ)初橘。遞歸次數(shù)過多容易造成棧溢出等沼溜。
如果使用循環(huán)不能解決,再考慮用遞歸