JS中函數的本質,定義、調用,以及函數的參數和返回值

要用面向對象的方式去編程,而不要用面向過程的方式去編程css


 

對象是各類類型的數據的集合,能夠是數字、字符串、數組、函數、對象……html

對象中的內容以鍵值對方式進行存儲jquery

對象要賦值給一個變量編程

var cat={ "name":"喵1", "age":4, "family":["喵爸","喵媽"], "speak":function(){ console.log("喵喵~"); }, "friend":{ "name":"喵2", "age":5 } }

上面是對象的字面量的建立方式,簡單直接數組

除此以外,還有對象的構造函數的建立方式瀏覽器

var cat=new Object();

還有JavaScript5新增的一種方式app

該方式在老版本的瀏覽器中存在兼容性問題函數

Object.create();

獲取、設置、添加、修改對象的值:this

對象.屬性名=屬性值spa

對象[ 屬性名 ]=屬性值

var cat={ "name":"喵1", "age":4, "family":["喵爸","喵媽"], "speak":function(){ console.log("喵喵~"); }, "friend":{ "name":"喵2", "age":5 } } cat.name="喵喵1"; cat.age=6; cat.type="英短"; console.log(cat.name);//喵喵1
    console.log(cat["age"]);//6
    console.log(cat.type);//英短

刪除對象的屬性:

delete 對象.屬性

var cat={ "name":"喵1", "age":4, "family":["喵爸","喵媽"], "speak":function(){ console.log("喵喵~"); }, "friend":{ "name":"喵2", "age":5 } } cat.type="英短"; console.log(cat.type);//英短

    delete cat.type; console.log(cat.type);//undefined

檢測對象是否擁有某個屬性:

屬性名 in 對象

var cat={ "name":"喵1", "age":4, "family":["喵爸","喵媽"], "speak":function(){ console.log("喵喵~"); }, "friend":{ "name":"喵2", "age":5 } } console.log("name" in cat);//true
    console.log("type" in cat);//false

對象的枚舉,遍歷對象中的各個屬性

var cat={ "name":"喵1", "age":4, "family":["喵爸","喵媽"], "speak":function(){ console.log("喵喵~"); }, "friend":{ "name":"喵2", "age":5 } } for(var p in cat){ console.log(p); //name age family speak friend
    }
var cat={ "name":"喵1", "age":4, "family":["喵爸","喵媽"], "speak":function(){ console.log("喵喵~"); }, "friend":{ "name":"喵2", "age":5 } } for(var p in cat){ console.log(p+": "+cat[p]); console.log(p);//獲取屬性名
        //console.log(cat.p);// 寫法錯誤
        console.log(cat[p]);//獲取屬性值 寫法正確
 console.log(cat["n"+"ame"]);//喵1 []中能夠添加字符串的拼接等操做
    }

匿名函數,如:

window.onload=function(){ }

函數一次執行完畢以後,會將局部做用域和局部變量銷燬,所以外部沒法調用到

但函數自己並無被銷燬,能夠進行屢次調用執行


 

爲何要使用函數:

代碼複用(本身的代碼和別人的代碼,如jquery)

統一修改和維護

增長程序的可讀性


 

函數的本質:對象

定義方式:字面量定義、構造函數定義

//字面量定義
function add(n1,n2){ } //構造函數定義
new Function("n1","n2","....");

函數和對象同樣,能夠添加屬性和方法

function person(){ console.log("cyy"); } //添加屬性
person.age=25; //添加方法
person.speak=function(words){ console.log(words); } console.log(person.age);//25
person.speak("hh~");//hh~
person();//cyy

函數能夠做爲數據值使用:

做爲數據值保存在一個變量中

var fn=function(){ return "這是一個函數"; } console.log(fn());//這是一個函數
console.log(fn); /* ƒ (){ return "這是一個函數"; } */

此時fn打印出來的就是函數本體

函數也能夠做爲參數來使用:

function fn(){ alert(1); } setTimeout(fn,1000);//此處須要傳函數本體 //此處不能加括號,若是加了括號,會馬上調用,而不是等到1秒以後

函數能夠做爲返回值使用:

function fn(){ return function(){ console.log("fn中的fn"); } } //調用
var newFn=fn(); newFn();//fn中的fn

// 或者
 fn()();//fn中的fn

函數的三種定義方式

// 字面量方式

    // function 聲明
    function add(){ } // var 賦值表達式
    var add=function(){ }; //構造函數
    var add=new Function("num1","num2","return num1+num2"); add();

區別:

字面量方式比構造函數方式簡潔

最重要的是預解析的區別

funtion聲明的函數,能夠先調用,再建立

函數預解析的時候會提早定義

 add(); function add(){ return 1; }

用var賦值表達式建立的函數,若是先調用,再建立,會報錯

由於var在預解析時賦值爲undefined

 add(); var add=function(){ return 1; };

function聲明和var賦值表達式聲明,這兩種都是很好的選擇

構造函數過於複雜,不推薦使用


 

函數定義的位置

全局做用域下的函數,在哪裏都能調用

add(); function add(){ add(); } add(); function fn(){ add(); }

局部做用域下的函數

//fn(); 沒法調用
    function add(){ fn(); function fn(){ fn(); function fn3(){ fn(); } } function fn2(){ fn(); } } //fn(); 沒法調用

裏層能夠訪問外層的函數,外層不能訪問裏層的函數

代碼塊中定義的函數:

因爲js中沒有塊級做用域,因此依然是處於全局做用域中

都會出現預解析中函數被提早聲明

if(true){ function fn1(){ } }else{ function fn2(){ } }

改爲下面這樣就能夠實現按照條件進行聲明,也是由於預解析的機制

if(true){ var fn1=function (){ } }else{ var fn2=function fn2(){ } }

對象中的函數

使用對象.函數名進行調用

var person={ name:"cyy", setAge:function(age){ this.age=age;//this指向當前對象
 } } person.setSex=function(sex){ this.sex=sex; } person.setAge(25); person.setSex("girl"); console.log(person.age);//25
console.log(person.sex);//girl

普通函數的調用:

命名函數的調用

function add(){ } add();

匿名函數的調用:

若是直接在匿名函數後面加上括號進行調用,會報錯

function(){ alert(1); }();//Uncaught SyntaxError: Unexpected token (

解決方法是,將這段匿名函數執行的代碼,賦值給一個變量

var fn=function(){ alert(1); }();//1

第二種解決方法:

將函數用括號括起來,實現匿名函數自執行

(function(){ alert(1); })();//1

括號把總體括起來也能實現同樣的效果

(function(){ alert(1); }());//1

或者在function前加上合法的字符也能夠,如!+-~

!function(){ alert(1); }();//1

或者放在console.log裏面

console.log(function(){ alert(1); }());

以上這些方式的共同目的,就是不讓匿名函數的function在開頭位置出現


 

遞歸調用:

本身調用本身

實現階乘

function fn(num){ if(num<=1) return 1; return num*fn(num-1); } console.log(fn(5)); /* return 5*fn(4) return 5*4*fn(3) return 5*4*3*fn(2) return 5*4*3*2*fn(1) return 5*4*3*2*1 */

匿名函數也是函數,當它自執行的時候,會建立本身的函數內部做用域,在執行完畢以後會被銷燬,所以在外部沒法訪問到自執行的匿名函數內部

//此處建立函數內部做用域
(function add(n1,n2){ return n1+n2; })(); console.log(add(3,4));//在全局沒法訪問到函數內部的函數add

方法的調用:

對象中的方法,使用對象.方法名進行調用

var operation={ add:function(n1,n2){ return n1+n2; }, sub:function(n1,n2){ return n1-n2; } } console.log(operation.add(3,4));//7

如下這種也是方法,是點擊瀏覽器時瀏覽器自動幫咱們完成調用;

也可使用方法調用的方式來進行調用

document.onclick=function(){ alert(1); } document.onclick();//等同於點擊屏幕的效果

關於對象中的屬性,何時加引號,何時不加引號

對於合法的標識符,加不加引號均可以;

不合法的標識符,必須加引號,不然會引發報錯

var operation={ add:function(n1,n2){ return n1+n2; },//合法的屬性名能夠不加引號
    sub:function(n1,n2){ return n1-n2; }, "@":function(){ }//不合法的屬性名,會引發報錯,必須加引號
}

合法的標識符,調用時使用對象.方法名便可

非法的標識符,調用時使用對象[ " 方法名 " ]

var operation={ add:function(n1,n2){ return n1+n2; },//合法的屬性名能夠不加引號
    sub:function(n1,n2){ return n1-n2; }, "@":function(word){ alert(word); }//不合法的屬性名,會引發報錯,必須加引號
} console.log(operation.add(2,5));//7
console.log(operation["@"]("hh~"));//hh~

[ ] 加引號和不加引號的區別

var operation={ add:function(n1,n2){ return n1+n2; },//合法的屬性名能夠不加引號
    sub:function(n1,n2){ return n1-n2; }, "@":function(word){ return word; },//不合法的屬性名,會引發報錯,必須加引號
    key:function(n1,n2){ return "key~"; } } var key="add"; console.log(operation.key(2,3));//key~
console.log(operation["key"](2,3));//key~
console.log(operation[key](2,3));//5

方法的鏈式調用

如jquery

$("p").html("html").css("color","red")....

對象中要使用鏈式調用,則方法中須要返回當前對象

var operation={ add:function(n1,n2){ console.log(n1+n2); return this; }, sub:function(n1,n2){ console.log(n1-n2); return this; } } operation.add(5,3).sub(4,2); //要保證operation.add(5,3)可以返回operation對象 //就須要添加return this

 構造函數的調用:

構造函數命名時通常首字母大寫

調用時用new+函數名,返回值是一個對象

function Person(){ } var obj=new Person();

js中內置的構造函數,常見的有:

Object() new Object() Array() new Array()

經過new關鍵字來調用

用構造函數的方式定義對象和數組,並添加內容

var person=new Object(); person.name="cyy"; var arr=new Array(); arr[0]=1;

函數的間接調用

.call   第一個參數是改變this的指向,後面傳遞參數的方式就是一個一個傳

.apply   第一個參數是改變this的指向,後面傳遞參數的方式是經過數組來傳遞(或者類數組)

var name="cyy"; var person={}; person.name="cyy2"; person.getName=function(){ return this.name;//此處的this指向person對象
} console.log(person.getName());//直接調用 cyy2
 console.log(person.getName.call(window));//間接調用,此時this被指向了window,返回的是window.name cyy
console.log(person.getName.apply(window));//間接調用 cyy
function add(n1,n2){ return n1+n2; } console.log(add(1,2));//直接調用 3
console.log(add.call(window,1,2));//間接調用 3
console.log(add.apply(window,[1,2]));//間接調用 3
function add(n1,n2){ return n1+n2; } var arr=[4,6]; console.log(add.apply(window,arr));//10

只有函數擁有call和apply方法,二者惟一的區別在於它們的傳參方式


 

函數的參數

參數傳遞的本質是將實參賦值給形參

參數的個數

一、形參個數=實參個數

function add(n1,n2){ return n1+n2; } console.log(add(3,5));

二、實參個數 < 形參個數

多用於有可選參數的狀況

function pow(base,pow=2){ return Math.pow(base, pow); } console.log(pow(3));//9
console.log(pow(3,3));//27

三、實參個數 > 形參個數

若是沒法得知有多少個實參,可使用arguments

arguments是一個類數組,用於保存實參的信息

經過arguments[index] 獲取某一個參數

arguments.length 實參的個數

function add(){ if(arguments.length==0) return; var sum=0; for(var i=0,len=arguments.length;i<len;i++){ sum+=arguments[i]; } return sum; } console.log(add());//undefined
console.log(add(1,2,3,4,5));//15

arguments 是類數組,實質上仍是對象

索引是數組下標,數字開頭的變量名不合法,所以須要加引號

{ '0': 1, '1': 2, '3': 4, length: 3 }

能夠經過arguments來修改參數的值

function speak(m){ arguments[0]=""; return m; } console.log(speak("hh"));//

arguments是每一個函數中獨有的,不會跨函數

function fn1(){ console.log(arguments);//Arguments [1, callee: ƒ, Symbol(Symbol.iterator): ƒ]
    function fn2(){ console.log(arguments);//Arguments [2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
 } fn2(2); } fn1(1);

arguments.callee  指代函數自己

function add(){ console.log(arguments.callee); } add();

arguments.callee 經常使用於遞歸中

function factorial(num){ if(num==1) return 1; return num*factorial(num-1); } console.log(factorial(5));//120


function factorial(num){ if(num==1) return 1; return num*arguments.callee(num-1); } console.log(factorial(5));//120

不過在嚴格模式下,不容許使用arguments.callee(也不容許不使用var聲明變量)

此時的解決方法就是將函數賦值給一個變量,這樣函數自己的名字不會影響調用

"use strict"; var myfn=function factorial(num){ if(num==1) return 1; return num*factorial(num-1); } console.log(myfn(5));//120

實參的個數 arguments.length

形參的個數 函數名.length 或者arguments.callee.length

function add(n1,n2){ if(arguments.length != add.length) throw new Error("請傳入"+add.length+"個參數!"); } console.log(add(5));//Uncaught Error: 請傳入2個參數!

什麼作參數

一、沒有參數

二、數字作參數

三、字符串( 如選擇DOM節點,$("p") )

四、布爾值(保持函數的純潔性,建議一個函數只作一件事情)

五、undefined(可選參數必須放在最後)

六、null

七、數組 

$.each(["a","b","c"],function(index,item)){ console.log(index);//0 1 2
    console.log(item);//a b c
}

八、對象

$.each({name:"cyy",age:24},function(index,item)){ console.log(index);//name age
    console.log(item);//cyy 24
}

使用對象做爲參數的好處(能夠自由調換順序)

function fn(obj){ var person={}; person.name=obj.name||"cyy"; person.age=obj.age||24; person.tel=obj.tel||110, person.addr=obj.addr||"China"; return person; } var cyy={ name: "cyy1", age:25 } console.log(fn(cyy));//{name: "cyy1", age: 25, tel: 110, addr: "China"}

九、函數

回調函數,如 setTimeout(fn, time);


 

函數的返回值

return:

表示函數結束

將值返回

什麼能夠作返回值:

直接return ,返回值是undefined

數字

字符串 :alert() 輸出的都是字符串,會默認調用.toString() 方法

布爾值:經常使用於表單驗證

null 和 undefined

數組

function add(n1,n2){ return [n1,n2,n1+n2]; } console.log(add(5,6));//(3) [5, 6, 11]

對象

function fn(){ return { name:"cyy", age:25 } }

注意return後面不要換行,不然默認是分號,到此結束;因而後面的會報錯

function fn(){ //return會默認後面是分號,結束
    return { name:"cyy", age:25 } }

函數

須要用()()來調用


 

document.write() 執行時會調用.toString() 方法,嘗試將結果轉換爲字符串形式

document.write([1,2,3]);//1,2,3
document.write({ name:"cyy" });//[object Object]
document.write({ name:"cyy", toString:function(){ return "hh~"; } });//hh~
function count(){ var num=1; return function(){ return num++; } } //每次調用count()時,num都會被初始化 //而且return num++ 是先返回num,再執行++
console.log(count()());//1
console.log(count()());//1
console.log(count()());//1 //count()只執行了一次,所以num只初始化一次 //後面可以每次都進行遞增+1
var fn=count(); console.log(fn());//1
console.log(fn());//2
console.log(fn());//3
相關文章
相關標籤/搜索