函數(shù)
js
的函數(shù)時(shí)參數(shù)化的:
- 函數(shù)的定義會包括一個(gè)稱為形參的標(biāo)識符列表洛姑,這些參數(shù)會像局部變量一樣工作
- 函數(shù)調(diào)用會為形參提供實(shí)參的值
- 函數(shù)使用它們實(shí)參的值來計(jì)算返回值湃缎,成為該函數(shù)調(diào)用表達(dá)式的值
- 除了實(shí)參之外啦租,每次調(diào)用還會有另一個(gè)值——本次調(diào)用的上下文(就是
this
的值) - 如果函數(shù)掛載在一個(gè)對象上浪慌,作為對象的一個(gè)屬性趁桃,就稱它為對象的方法仑最,當(dāng)通過這個(gè)對象來調(diào)用函數(shù)時(shí)扔役,該對象就是此次調(diào)用的上下文,也就是該函數(shù)
this
的值
函數(shù)的定義
函數(shù)使用 function
關(guān)鍵字來定義:
1警医、函數(shù)定義表達(dá)式
2亿胸、函數(shù)聲明語句
// 函數(shù)聲明
function init () {
//do something
}
// 函數(shù)定義表達(dá)式
var init = function () {
// do something
}
-
函數(shù)聲明語句“被提前”
可以在定義之前調(diào)用
init();
function init () { // }
-
表達(dá)式定義
要使用一個(gè)表達(dá)式方式定義的函數(shù)之前,必須把它賦值給一個(gè)變量预皇,變量的聲明提前了侈玄,但是給變量賦值是不會提前的,所以定義之前是無法調(diào)用的
init(); // 報(bào)錯(cuò) init is not a function
var init = function () {};
init(); // 成功調(diào)用
函數(shù)命名
- 通常以動詞或以動詞為前綴的詞組
- 通常第一個(gè)字符為小寫(編程約定)
- 當(dāng)函數(shù)名包含多個(gè)參數(shù)時(shí)吟温,或以下劃線分割序仙,或者駝峰
- 內(nèi)部函數(shù)或私有函數(shù),通常以下劃線為前綴
- 經(jīng)常調(diào)用的函數(shù)指定短名稱
函數(shù)調(diào)用
定義函數(shù)并不會執(zhí)行鲁豪,只有調(diào)用該函數(shù)時(shí)潘悼,它們才會執(zhí)行律秃,有4種方式調(diào)用:
1、作為函數(shù)
2治唤、作為方法:保存在一個(gè)對象的屬性里的js
函數(shù)
3棒动、作為構(gòu)造函數(shù)
4、通過它們的call()
和apply()
方法間接調(diào)用
任何函數(shù)只要作為方法調(diào)用實(shí)際上都會傳入一個(gè)隱式的的實(shí)參——這個(gè)實(shí)參是一個(gè)對象宾添,方法調(diào)用的母體就是這個(gè)對象船惨。
-
this
是一個(gè)關(guān)鍵字,不是變量缕陕,也不是屬性名粱锐,不允許給它賦值 -
this
沒有作用域限制,嵌套的函數(shù)不會從調(diào)用它的函數(shù)中繼承this
- 如果嵌套函數(shù)作為方法調(diào)用扛邑,其
this
的值指向調(diào)用它的對象怜浅。 - 如果嵌套函數(shù)作為函數(shù)調(diào)用,
this
值不是全局對象(非嚴(yán)格模式)就是undefined
(嚴(yán)格模式下)
var o = {
m:function () { // 掛載在對象上的方法 m()
var self = this; // 將this的值保存至一個(gè)變量中
console.log(this === o); // => true this 就是當(dāng)前這個(gè)對象 o
f();
function f () { // 定義一個(gè)嵌套函數(shù)f()
console.log(this); // => Window
console.log(this === o); // => false this 的值是全局對象或者undefined
console.log(self === o); // => true self 變量保存的是外部函數(shù)的this
}
}
}
var w = function () {
console.log('w',this); // => 輸出對象o
console.log(o === this); // => true
}
o.n = w;
o.n();
o.m();
構(gòu)造函數(shù)調(diào)用
如果函數(shù)或者方法調(diào)用之前帶有關(guān)鍵字new
鹿榜,它就構(gòu)成構(gòu)造函數(shù)調(diào)用
- 凡是沒有形參的構(gòu)造函數(shù)調(diào)用都可以省略括號
var o = new Object();
var o = new Object; // 凡是沒有形參的構(gòu)造函數(shù)調(diào)用都可以省略括號
- 構(gòu)造函數(shù)調(diào)用創(chuàng)建一個(gè)新的空對象海雪,這個(gè)對象繼承自構(gòu)造函數(shù)的
prototype
屬性锦爵,構(gòu)造函數(shù)試圖初始化這個(gè)新創(chuàng)建的對象舱殿,并將這個(gè)對象用作其上下文,因此構(gòu)造函數(shù)可以使用this
來引用這個(gè)新創(chuàng)建的對象险掀。
var o = {
m:function (x) { // 掛載在對象上的方法 m()
var self = this; // 將this的值保存至一個(gè)變量中
console.log(this); // 輸出的是對象d
console.log(this.w); // => undefined 對象d在調(diào)用構(gòu)造函數(shù)的時(shí)候還未添加屬性 w
console.log(this === o); // => false 此時(shí)this是調(diào)用構(gòu)造函數(shù)創(chuàng)建的d
this.x = x; // => 調(diào)用構(gòu)造函數(shù)創(chuàng)建對象時(shí)添加的參數(shù)沪袭,給新創(chuàng)建對象指定 屬性 x 并為其復(fù)制
f();
function f () { // 定義一個(gè)嵌套函數(shù)f()
console.log(this === o); // => false this的值是全局對象或者undefined
console.log(self === o); // => false 對象 d
console.log(this); // => Window 對象
}
}
}
var d = new o.m(1); // 通過構(gòu)造函數(shù)創(chuàng)建一個(gè)新對象
d.w = "scp"
console.log(d.x); // => 1 調(diào)用構(gòu)造函數(shù)時(shí)添加的屬性值
實(shí)參列表(arguments)
- 標(biāo)識符
arguments
是指向?qū)崊ο蟮囊茫瑢?shí)參對象是一個(gè)類數(shù)組對象樟氢,可以通過數(shù)字下標(biāo)就能訪問傳入函數(shù)的實(shí)參值冈绊,而不用非要通過名字來得到實(shí)參。
init(1,2,3)
function init(){
console.log(arguments[0]); // => 1
console.log(arguments[1]); // => 2
console.log(arguments[2]); // => 3
}
-
arguments
包含一個(gè)length
屬性埠啃,用以標(biāo)識其所包含元素的個(gè)數(shù)死宣。
init(1,2,3);
function init () {
console.log(arguments.length); // => 3 傳入三個(gè)參數(shù)
}
- 實(shí)參對象讓函數(shù)可以操作任意數(shù)量的實(shí)參。
如下函數(shù)可以接收任意個(gè)數(shù)的實(shí)參碴开,稱為“不定實(shí)參函數(shù)”
max(1,2,3,100);
function max () {
var max = Number.NEGATIVE_INFINITY; // 負(fù)無窮
for (var i = 0; i < arguments.length; i ++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
//
Math.max(1,2,3,100);
-
callee
和caller
屬性
1毅该、ES5嚴(yán)格模式中,這倆屬性的讀寫操作都會產(chǎn)生類似錯(cuò)誤
2潦牛、非嚴(yán)格模式下眶掌,callee
屬性指代當(dāng)前正在執(zhí)行的函數(shù)
3、callee
屬性在某些時(shí)候回非常有用巴碗,比如在匿名函數(shù)中通過callee
來調(diào)用自身
var init = function () {
if (arguments.length < 2){
arguments.callee(1,2)
}
console.log(arguments.length); // => 1 2
}
init(1);
閉包
首先回顧一下變量 作用域 和 作用域鏈
- 全局變量擁有全局作用域
- 在函數(shù)內(nèi)聲明的變量只在函數(shù)體內(nèi)有定義朴爬,它們是局部變量,作用域是局部性的
- 函數(shù)參數(shù)也是局部比那里橡淆,它們只在函數(shù)體內(nèi)有定義
- 在函數(shù)體內(nèi)召噩,局部變量的優(yōu)先級高于同名的全局變量母赵,如果在函數(shù)體內(nèi)聲明的一個(gè)局部變量或者函數(shù)參數(shù)中帶有的變量和全局變量重名,那么全局變量就被局部變量所遮蓋具滴。
- 聲明局部變量必須使用
var
市咽,否則會認(rèn)為是什么一個(gè)全局變量或者修改全局變量
var a = 'global'; // 全局變量
function init () {
var a = 'local'; // 同名的局部變量
console.log(a); // => "local"
}
init();
- 函數(shù)定義是可以嵌套的,由于每個(gè)函數(shù)都有自己的作用域抵蚊,因此會出現(xiàn)幾個(gè)局部作用域嵌套的情況施绎。
var a = 'global'; // 全局變量
function init () {
var a = 'local'; // 同名的局部變量
console.log(a); // => local
function n (){
var a = 'n local';
console.log(a); // => n local
}
}
init();
函數(shù)作用域和聲明提前
-
js
使用函數(shù)作用域:變量在聲明它們的函數(shù)體以及這個(gè)函數(shù)體嵌套的任意函數(shù)體內(nèi)都是有定義的。 -
js
的函數(shù)作用域是指在函數(shù)內(nèi)聲明的所有變量在函數(shù)體內(nèi)始終是可見的贞绳,這意味著變量在聲明之前甚至已經(jīng)可用谷醉,js
的這個(gè)特性被非正式的稱為“聲明提前”
var a = 'global'; // 聲明一個(gè)全局變量
function f () {
console.log(a); // => "undefined" 并沒有輸出“global”
var a = 'local'; // 變量在這里聲明并賦值,但是變量在函數(shù)體內(nèi)任何地方都是有定義的
console.log(a); // => "local"
}
// 如上 等價(jià)于
function f () {
var a; // 在函數(shù)頂部聲明了局部變量
console.log(a); // 變量存在冈闭,但是未賦值俱尼,所以值是undefined
a = "local"; // 對局部變量進(jìn)行賦值
console.log(a); // => "local"
}
作用域鏈
變量取值是到創(chuàng)建這個(gè)變量的函數(shù)作用域中取值,如果當(dāng)前作用域沒有取到值萎攒,就會到上一級作用域去查遇八,直到查到全局作用域,這么一個(gè)查找的過程形成的鏈條就叫作用域鏈
- 最終沒有查到會拋出異常
var a = 1;
function i_a () {
var b = 2;
function i_b () {
var c = 3;
console.log(a + b + c); // => 6
}
i_b();
}
i_a();
-
a
在i_b
作用域中并不存在耍休,此時(shí)就會到上一級作用域i_a
中查到刃永,i_a
中也不存在變量a
,那么就再往上一級到全局中查到了a
取值成功羊精。
閉包
- 函數(shù)執(zhí)行依賴于變量作用域斯够,這個(gè)作用域是在函數(shù)定義時(shí)決定的
- 函數(shù)嵌套
- 一個(gè)函數(shù)訪問另一個(gè)函數(shù)的作用域
- 閉包無處不在,
js
編寫幾乎時(shí)刻都在發(fā)生一個(gè)函數(shù)訪問另一個(gè)函數(shù)的作用域
利用閉包實(shí)現(xiàn)私有屬性的讀寫
function counter(){
var n = 0;
return {
count:function(){
return n++;
},
reset:function(){
n = 0;
}
}
}
var c = counter();
console.log(c.count()); // => 0
console.log(c.count()); // => 1
c.reset(); // 重置變量
console.log(c.count()); // => 0
函數(shù)屬性喧锦、方法和構(gòu)造函數(shù)
length屬性
-
arguments.length
表示實(shí)參個(gè)數(shù) - 函數(shù)本身的
length
屬性是只讀屬性读规,代表函數(shù)的形參數(shù)量
function counter(x){
var arg_length = arguments.length;
var ex_length = arguments.callee.length;
console.log("arg_length:",arg_length); // => 2 實(shí)際傳入兩個(gè)參數(shù)
console.log("ex_length:",ex_length); // => 1 定義函數(shù)時(shí)定義了一個(gè)參數(shù)
}
counter(1,1);
prototype屬性
每個(gè)函數(shù)都包含一個(gè)prototype
屬性,這個(gè)屬性是指向一個(gè)對象的引用燃少,這個(gè)對象稱做“原型對象”束亏,每個(gè)函數(shù)都包含不同的原型對象,當(dāng)將函數(shù)做構(gòu)造函數(shù)的時(shí)候阵具,新創(chuàng)建的對象會從原型對象上繼承屬性碍遍。
function counter () {
}
var c = new counter();
call()方法和apply()方法
-
call()
和apply()
的第一個(gè)實(shí)參是要調(diào)用函數(shù)的母對象,它是調(diào)用上下文怔昨,在函數(shù)體內(nèi)通過this
來獲取對它的引用雀久。
通俗來講第一個(gè)參數(shù)是誰要調(diào)用函數(shù)
var o = {
name:"my name is o"
}
function f () {
console.log(this); // => {name: "my name is o"} 此時(shí)this指向調(diào)用者o
console.log(this.name); // => "my name is o"
}
f.call(o);
f.apply(o); // 倆個(gè)調(diào)用結(jié)果是一樣的
-
call()
的參數(shù)直接方進(jìn)去的料祠,第二第三第n
個(gè)參數(shù)全都用逗號分隔
var o = {
name:'my name is o'
}
function counter(){
console.log(arguments.length); // => 2 傳入兩個(gè)參數(shù)
console.log(this.name); // => 'my name is o'
console.log("my height is " + arguments[0] + 'cm'); // => 'my height is 180cm'
console.log("my weight is " + arguments[1] + 'kg'); // => 'my weight is 70kg'
}
counter.call(o,180,70);
-
apply()
的所有參數(shù)必須放在一個(gè)數(shù)組里傳入
counter.apply(o,[180,70]);
bind()方法
bind()
用來將函數(shù)綁定至某個(gè)對象丑瞧。
function f() {
return this.x + 1;
}
var o = {x:1};
f.bind(o)(); // => 輸入2 會把f當(dāng)做o的方法來調(diào)用