- 函數(shù)在javascript里面是對(duì)象。每個(gè)函數(shù)在創(chuàng)建時(shí)會(huì)附加兩個(gè)屬性:函數(shù)的上下文和實(shí)現(xiàn)函數(shù)行為的代碼。
- 每個(gè)函數(shù)在創(chuàng)建的時(shí)候會(huì)有一個(gè)prototype屬性,它的值是一個(gè)擁有constructor屬性且值為該函數(shù)的對(duì)象。
- 函數(shù)是對(duì)象,可以像值一樣被使用。函數(shù)可以保存在變量、對(duì)象和數(shù)組中。函數(shù)可以當(dāng)做參數(shù)傳遞給其他函數(shù),函數(shù)也可以返回函數(shù)。因?yàn)楹瘮?shù)是對(duì)象所以函數(shù)也可以擁有方法。
- 函數(shù)的不同之處在于它可以被調(diào)用。
函數(shù)字面量
- 保留字 function
- 函數(shù)名,可以被省略。函數(shù)可以利用名字來遞歸調(diào)用自己。如果沒有名字,則稱之為匿名函數(shù)。
- 參數(shù)。
- 函數(shù)體,在函數(shù)被調(diào)用的時(shí)候執(zhí)行。
函數(shù)可以被定義在其他函數(shù)中,同事它就能訪問把它嵌套在中的父函數(shù)的屬性。通過函數(shù)字面量創(chuàng)建的函數(shù)對(duì)象包含一個(gè)鏈接到外部的上下文。這被稱之為閉包。
調(diào)用
- 調(diào)用一個(gè)函數(shù)時(shí),會(huì)暫停當(dāng)前函數(shù)的執(zhí)行,傳遞控制權(quán)和參數(shù)給當(dāng)前函數(shù)。
- 除了傳遞的參數(shù)之外,每個(gè)函數(shù)還接受兩個(gè)附加的參數(shù):this和 arguments。
- this的值取決于調(diào)用的模式。在js中有四種調(diào)用模式:
- 方法調(diào)用模式
當(dāng)一個(gè)函數(shù)被保存為一個(gè)對(duì)象屬性時(shí),我們稱之為方法。當(dāng)一個(gè)方法被調(diào)用時(shí),this被綁定到該對(duì)象。如果表達(dá)式包含一個(gè) . 的動(dòng)作,那么它就是被當(dāng)做一個(gè)方法來調(diào)用,方法調(diào)用模式的this指向?qū)ο蟆?/li>
var myObject = {
value: 0,
increment: function (inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
};
myObject.increment(1);
document.wirteln(myObject.value);
myObject.increment(2);
document.wirteln(myObject.value);
- 函數(shù)調(diào)用模式
當(dāng)一個(gè)函數(shù)并非一個(gè)對(duì)象屬性時(shí),那么它就是被當(dāng)做函數(shù)調(diào)用。
var sum = add(3, 4);
這時(shí),this被綁定到全局對(duì)象上。如果語言設(shè)計(jì)正確,那么 當(dāng)內(nèi)部函數(shù)被調(diào)用時(shí),this 應(yīng)該任然綁定到外部函數(shù)的this變量。
但是
function a(){
var b = function() {
console.info(1231231, this);
}
b();
}
a();// 輸出windows this 被綁定到外部去了
// 解決方法
var myObject = {};
myObject.fn = function(){
var that = this;
var helper = function () {
that.value = add(that.value, this.value);
}
helper();
}
//解決方法2 箭頭函數(shù)的this指向 外部函數(shù)的this
var myObject = {};
myObject.fn = function() {
var b = () =>{
console.info(1231231, this);
}
b();
}
- 構(gòu)造器調(diào)用模式
構(gòu)造器調(diào)用模式this將會(huì)指向new出來的對(duì)象。
var Quo = function (string){
this.status = string;
}
Quo.prototype.get_status = function () {
return this.status;
}
var myQuo = new Quo("confused");
document.writeln(myQuo.get_status());
- apply調(diào)用模式(還有call調(diào)用模式)
構(gòu)造一個(gè)參數(shù)數(shù)組傳遞給調(diào)用的函數(shù),允許我們選擇this的值。apply接受兩個(gè)值 一個(gè)是this,另外一個(gè)是參數(shù)數(shù)組。
這些模式在初始化關(guān)鍵參數(shù)this上存在差異。
參數(shù)
當(dāng)函數(shù)被調(diào)用時(shí),會(huì)有一個(gè)arguments數(shù)組??梢跃帉懸粋€(gè)無需指定參數(shù)個(gè)數(shù)的函數(shù)。
var sum = function (){
var i, sum = 0;
for (i = 0; i < arguments.length; i += 1) {
sum += arguments[i];
}
return sum;
}
arguments并不是一個(gè)數(shù)組 只是一個(gè)類似數(shù)組的對(duì)象。只有l(wèi)ength屬性,沒有其他數(shù)組方法。
返回
一個(gè)函數(shù)總會(huì)有一個(gè)返回值,如果沒有那么返回undefined。
如果調(diào)用時(shí)加了new,且返回值不是對(duì)象那么返回新對(duì)象即this。
異常
var add = function (a, b) {
if (typeof a !== 'number' || typeof b !== 'number' ) {
throw{
name: 'TypeError',
message: 'add needs number'
};
}
return a + b;
}
var try_it = function () {
try {
add("seven");
} catch(e) {
document.writeln(e.name + ':' + e.message);
}
}
擴(kuò)充類型的功能
Function.prototype.method = function(name, func){
this.prototype[name] = func;
return this;
}
Number.method('integer', function(){
return Math[this < 0? 'ceil' : 'floor'](this);
})
document.writeln((-10/3).integer());
String.method('trim', function () {
return this.replace(/^\s+|\s+$/g, '');
})
document.writeln('"' + " neat ".trim() + '"');
符合條件時(shí)才添加方法
Function.prototype.method = function (name, func) {
if (!this.prototype[name]) {
this.prototype[name] = func;
}
return this;
}
遞歸 todo
作用域
var foo = function () {
var a = 3, b = 5;
var bar = function () {
var b = 7, c = 11;
a += b + c;
};
//a為3 b 為5
bar();
//a為21 b 為5
}
了解js奇怪的作用域。
閉包
函數(shù)可以訪問它被創(chuàng)建時(shí)所處的上下文環(huán)境。這杯稱之為閉包。
var fade = function(node) {
var level = 1;
var step = function () {
var hex = level.toString(16);
node.style.backgroundColor = '#FFFF' + hex + hex;
console.info('#FFFF' + hex + hex);
if(level < 15){
level += 1;
setTimeout(step, 1000);
}
}
setTimeout(step, 1000);
}
fade(document.body);
只要step還需要父函數(shù)里的變量那么這個(gè)變量就不會(huì)釋放。
回調(diào)
模塊
我們可以使用函數(shù)和閉包來構(gòu)造模塊,模塊是一個(gè)提供接口卻隱藏狀態(tài)與實(shí)現(xiàn)的函數(shù)或?qū)ο?。通過函數(shù)產(chǎn)生模塊,我們可以摒棄全局變量的使用,從而緩解js的糟糕特性。
定義一個(gè)方法替換尋找字符串中的HTML實(shí)體并把他們替換為對(duì)應(yīng)的字符。
String.method('deentityify', function() {
//需要替換的字符實(shí)體保存在全局變量里太污染,保留在函數(shù)里每次需要求值,但是保留在閉包里很理想
var entity = {
quot: '"',
lt: '<',
gt: '>'
};
return function() {
return this.replace(/&([^&;]+);/g,
function (a, b) {
var r = entity[b];
return typeof r === 'string' ? r : a;
}
}
})
模塊的一般形式就是利用閉包創(chuàng)建可以訪問的私有變量和函數(shù)的特權(quán)函數(shù),最后返回這個(gè)特權(quán)函數(shù),或者把他們保存到一個(gè)可以訪問到的地方。
使用模塊摒棄了全局變量的使用。它促進(jìn)了信息隱藏和其他優(yōu)秀的設(shè)計(jì)實(shí)踐。對(duì)于應(yīng)用的封裝或者構(gòu)建其他單例對(duì)象,模塊模式非常有效。
模塊也可以產(chǎn)生安全的對(duì)象,假定我們要構(gòu)造一個(gè)用來生產(chǎn)序列的對(duì)象。
var serial_maker = function () {
var prefix = '';
var seq = 0;
return {
set_prefix: function (p) {
prefix = String(p);
},
set_seq: function (s) {
seq = s;
},
gensym: function () {
var result = prefix + seq;
seq += 1;
return result;
}
};
};
var seqer = serial_maker();
seqer.set_prefix('Q');
seqer.set_seq(1000);
var unique = seqer.gensym();
級(jí)聯(lián)
一些方法沒有返回值,一些這只或修改對(duì)象的某個(gè)狀態(tài)卻不返回任何值得方法就是經(jīng)典的例子。如果我們讓這些方法返回this而不是undefinded那么就可以啟動(dòng)級(jí)聯(lián)。
getElement('myBoxDiv')
.move(350, 150)
.width(100)
.height(100)
.color('red')
柯里化
函數(shù)也是值,從而我們可以利用有趣的方式操作函數(shù)值??吕锘试S我們把函數(shù)與傳遞給它的參數(shù)相結(jié)合,產(chǎn)生出一個(gè)新的函數(shù)。
Function.method('curry', function() {
var slice = Array.prototype.slice,
args = slice.apply(arguments),
that = this;
return function () {
return that.apply(null, args.concat(slice.apply(arguments)));
}
})
記憶 todo
函數(shù)可以將先前操作的結(jié)果記錄在某個(gè)對(duì)象里,從而避免無所謂的重復(fù)運(yùn)算。