JavaScript 函數(shù)用法詳解【函數(shù)定義、參數(shù)、綁定、作用域、閉包等】
本文實(shí)例講述了JavaScript 函數(shù)用法。分享給大家供大家參考,具體如下:
初始函數(shù)
Function類型,即函數(shù)的類型。
典型的JavaScript函數(shù)定義:
function 函數(shù)名稱(參數(shù)表){
//函數(shù)執(zhí)行部分
return ;
}
//注意:參數(shù)列表直接寫形參名即可
return語(yǔ)句:return返回函數(shù)的返回值并結(jié)束函數(shù)運(yùn)行
函數(shù)也可以看做數(shù)據(jù)來進(jìn)行傳遞
參數(shù)列表相當(dāng)于函數(shù)入口,return 語(yǔ)句相當(dāng)于函數(shù)出口
函數(shù)可以作為參數(shù)來傳遞。
function test ( a ) {
a();
}
test(function () {
alert(1);
});
函數(shù)可以嵌套定義
function test2(){
function test3(){
alert(1);
}
test3();
}
test2();
定義函數(shù)
三種定義函數(shù)的方式:
function語(yǔ)句形式
函數(shù)直接量形式
通過Function構(gòu)造函數(shù)形式定義函數(shù)
//1 function 語(yǔ)句式
function test1 () {
alert(1);
}
//2 函數(shù)直接量 (ECMAScript 推薦)
var test2 = function () {
alert(2);
}
//3 function 構(gòu)造函數(shù)式
var test3 = new Function('a','b','return a+b;'); //最后一個(gè)參數(shù)是函數(shù)結(jié)構(gòu)體
test3(10,20);
| function語(yǔ)句 | Function構(gòu)造函數(shù) | 函數(shù)直接量 | |
|---|---|---|---|
| 兼容 | 完全 | js1.1以上 | js1.2以上版本 |
| 形式 | 句子 | 表達(dá)式 | 表達(dá)式 |
| 名稱 | 有名 | 匿名 | 匿名 |
| 性質(zhì) | 靜態(tài) | 動(dòng)態(tài) | 靜態(tài) |
| 解析時(shí)機(jī) | 優(yōu)先解析 | 順序解析 | 順序解析 |
| 作用域 | 具有函數(shù)的作用域 | 頂級(jí)函數(shù)(頂級(jí)作用域) | 具有函數(shù)作用域 |
靜態(tài)動(dòng)態(tài)的區(qū)別
var d1 = new Date();
var t1 = d1.getTime();
for ( var i=0; i<10000000; i++ ) {
// function test1 () {} // 342ms //在函數(shù)體只會(huì)被聲明一次 ,其它地方并不再解析,就可以調(diào)用 。 //靜態(tài) //只會(huì)編譯一次,放入內(nèi)存中。
var test2 = function () {} //354ms
// var test3 = new Function(); //8400ms //每次執(zhí)行,都是重新new一個(gè) 函數(shù)。 //new 完之后, 就銷毀了。不占用內(nèi)存。動(dòng)態(tài)創(chuàng)建一次。
}
var d2 = new Date();
var t2 = d2.getTime();
console.log( t2 - d1 );
解析順序
function f () {
return 1;
} // 函數(shù)1
alert( f() ); //返回值為4 說明第1個(gè)函數(shù)被第4個(gè)函數(shù)覆蓋
var f = new Function("return 2;"); // 函數(shù)2
alert( f() ); //返回值為2 說明第4個(gè)函數(shù)被第2個(gè)函數(shù)覆蓋
var f = function () {
return 3;
} // 函數(shù)3
alert( f() ); //返回值為3 說明第2個(gè)函數(shù)被第3個(gè)函數(shù)覆蓋
function f () {
return 4;
} // 函數(shù)4
alert(f()); //返回值為3 說明第4個(gè)函數(shù)被第3個(gè)函數(shù)覆蓋
var f = new Function("return 5"); // 函數(shù)5
alert(f()); //返回值為5 說明第3個(gè)函數(shù)被第5個(gè)函數(shù)覆蓋
var f = function(){
return 6;
} // 函數(shù)6
alert(f()); //返回值為6 說明第5個(gè)函數(shù)被第6個(gè)函數(shù)覆蓋
函數(shù)作用域
var k = 1 ;
function t1(){
var k = 2 ;
// function test(){return k ;} //2
// var test = function(){ return k}; //2
var test = new Function('return k;'); //1 //new Function(); 是會(huì)有頂級(jí)作用域
alert(test());
}
t1();
函數(shù)的參數(shù)arguments
arguments對(duì)象,是實(shí)參的副本
//js 中 函數(shù)分為 : 形參,實(shí)參
function test ( a,b,c,d ) {
// console.log( test.length ); //獲取形參的個(gè)數(shù) 4
//函數(shù)的實(shí)際參數(shù) 內(nèi)部就是使用一個(gè)數(shù)組去接收實(shí)際參數(shù)。 類數(shù)組對(duì)象
//arguments 對(duì)象,只能在函數(shù)內(nèi)部使用。
//arguments 對(duì)象, 可以訪問函數(shù)的實(shí)際參數(shù) (實(shí)參的副本)
// console.log( arguments.length ); //2
// console.log( arguments[0] ); //10
// // 第一種方式:
// if ( test.length === arguments.length ) {
//
// return a + b;
//
// }
//使用第二種 方式:
if ( arguments.callee.length === arguments.length ) {
return a + b;
}
//arguments對(duì)象, 使用得最多的還是使用遞歸操作
// arguments.callee; // 指向函數(shù)本身,函數(shù)體
}
test(10,20);
this對(duì)象的簡(jiǎn)單理解
this對(duì)象是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境綁定的
在全局函數(shù)中,this等于window,而當(dāng)函數(shù)被作為某個(gè)對(duì)象的方法調(diào)用時(shí),this等于那個(gè)對(duì)象。
也就是說this關(guān)鍵字總是指代調(diào)用者(誰(shuí)調(diào)用了我,就指向誰(shuí))。
//this: this對(duì)象是指在運(yùn)行時(shí)期基于執(zhí)行環(huán)境所綁定的
var k = 10;
function test () {
this.k = 20;
}
test();
console.log( test.k ); //undefined
call和apply方法
每一個(gè)函數(shù)都包含兩個(gè)非繼承而來的方法:call、apply。這倆個(gè)方法的用途都是在特定的作用域中調(diào)用函數(shù),實(shí)際上等于設(shè)置函數(shù)體內(nèi)this對(duì)象的值。
call、apply的用途之一就是傳遞參數(shù),但事實(shí)上,它們真正強(qiáng)大的地方式能夠擴(kuò)充函數(shù)賴以運(yùn)行的作用域(使你的作用域不斷的去變化)。
使用call()、aplly()來擴(kuò)充作用域的最大好處就是對(duì)象不需要與方法有任何耦合關(guān)系。
fn.call(obj);
讓fn以運(yùn)行,并且fn中的this以obj身份運(yùn)行
將一個(gè)函數(shù)綁定到一個(gè)特定的作用域中,然后傳遞特定作用域中的參數(shù)。
//call, apply 簡(jiǎn)單 用法: 綁定一些函數(shù) 用于傳遞參數(shù) 調(diào)用
function sum ( x,y ) {
return x + y;
}
function call1 ( num1,num2 ) {
return sum.call(this,num1,num2);
}
function apply1 ( num1,num2 ) {
return sum.apply(this,[num1,num2])
}
//將一個(gè)函數(shù)綁定到一個(gè)特定的作用域中,然后傳遞特定作用域中的參數(shù)。
console.log( call1(10,10) );
console.log( apply1(20,10) );
//擴(kuò)充作用域,底層也經(jīng)常使用這兩個(gè)方法,用于綁定不同的作用域。
//把一個(gè)函數(shù)賦給一個(gè)對(duì)象, 賦完之后,還可以重用,賦給另外一個(gè)對(duì)象。
window.color = 'pink';
var obj = {
color: 'tan'
}
function showColor () {
console.log( this.color );
}
showColor.call(window);
// showColor.call(this);
showColor.apply(obj);
call方法簡(jiǎn)單的實(shí)現(xiàn)
function test1 ( a,b ) {
return a + b;
}
//自定義對(duì)象
function Obj ( x,y ) {
return x * y;
}
var obj = new Obj();
//掛載到對(duì)象上
obj.method = test1;
//執(zhí)行該函數(shù)
obj.method(10,20);
//執(zhí)行完后刪除
delete obj.method;
bind
ES5中提供一個(gè)bind()方法。
為函數(shù)綁定一個(gè)執(zhí)行時(shí)候的作用域。
將該方法綁定Function的原型上,因此定義一個(gè)function就有該方法,bind()添加作用域的時(shí)候,方法沒有執(zhí)行,在方法執(zhí)行的時(shí)候,作用域會(huì)變成綁定兌現(xiàn)的作用域。
function b () {
console.log(this.title);
}
function Book ( title ) {
this.title = title;
}
var book = new Book('javascript');
// apply , call 特點(diǎn):調(diào)用就執(zhí)行
// b.call(book);
// b.apply(book);
// 當(dāng)執(zhí)行一個(gè)函數(shù)的時(shí)候更改作用域
var bBindfn = b.bind(book);
// 執(zhí)行更改作用域
bBindfn();
執(zhí)行環(huán)境和作用域鏈概念
執(zhí)行環(huán)境(execution context)是javascript中最為重要的一個(gè)概念。執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了它們各自的行為。每一個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對(duì)象,環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對(duì)象中。雖然我們的代碼無法訪問這個(gè)對(duì)象,但是解析器在處理數(shù)據(jù)時(shí)會(huì)在后臺(tái)執(zhí)行它。

全局執(zhí)行環(huán)境是最外圍的一個(gè)執(zhí)行環(huán)境。根據(jù)ECMScript實(shí)現(xiàn)所在的宿主環(huán)境不同,表示執(zhí)行環(huán)境的對(duì)象也不一樣。
每一個(gè)函數(shù)都有自己的執(zhí)行環(huán)境。當(dāng)執(zhí)行流進(jìn)一個(gè)函數(shù)時(shí),函數(shù)的環(huán)境就會(huì)被推入一個(gè)環(huán)境棧中。而在函數(shù)執(zhí)行之后,棧將其環(huán)境彈出,把控制權(quán)返還給之前的執(zhí)行環(huán)境。當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí),會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈(scope chain)。作用域鏈的用途,是保證對(duì)執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問(控制代碼的訪問權(quán)限)。
var color1 = "blue";
function changeColor () {
var color2 = "red";
function swapColor () {
var color3 = color2; //color3 = 'red'
color2 = color1; //color2 = 'blue'
color1 = color3; //color1 = 'red'
console.log( color1,color2,color3 );
}
swapColor();
}
changeColor();
//環(huán)境變量 可以一層一層的向上進(jìn)行追溯 可以訪問它的上級(jí) 環(huán)境(變量和函數(shù))
// 作用域鏈 具有層級(jí)關(guān)系
//在大型程序中,全局變量,盡量少使用,因?yàn)槿肿兞靠偸亲詈笠淮嗡阉鳌?防止全局變量污染。//很少去定義全局變量,效率比較慢。
垃圾收集和塊級(jí)作用域的概念
垃圾收集
javascript是一門具有自動(dòng)垃圾收集機(jī)制的編程語(yǔ)言。開發(fā)人員不必關(guān)心內(nèi)存分配和回收問題。
垃圾回收器也是每隔一段時(shí)間去進(jìn)行回收。
離開作用域的值將被自動(dòng)標(biāo)記為可以回收,因此將在垃圾收集期間被刪除。標(biāo)記清除是目前主流的垃圾收集算法。這種思想是給當(dāng)前不使用的值加上標(biāo)記,然后回收其內(nèi)存。
//垃圾收集 ,標(biāo)記清除 (模擬)
function test () {
var a = 10; //mark - 被使用
var b = 20; //mark - 被使用
}
test(); //執(zhí)行完畢 之后 ,a,b又被標(biāo)記使用。 mark - 沒有被使用
//在間隔時(shí)間中 回收。 如果mark 沒有被使用, 則回收。
//引用計(jì)數(shù)(模擬)
//如果變量被引用 , count = 1;
function test2 () {
var a = 10; //count = 1;
var b = 20;
var c;
c = a; //count++ = 2; //a 被 c 所使用 ,引用。
a = 50; //count--; //重新被賦值 count-- //等待 count 為 0 的時(shí)候, 垃圾回收機(jī)制 就回收
}
塊級(jí)作用域
javascript里面沒有塊級(jí)作用域的概念,所以在使用if、for時(shí)候要格外的小心。
javascript模擬塊級(jí)作用域 (塊級(jí)作用域,相當(dāng)于內(nèi)部的執(zhí)行體,一個(gè)執(zhí)行環(huán)境)
利用 IIEF的特性
//當(dāng)函數(shù)執(zhí)行之后, 變量就被回收
function test () {
(function () { //函數(shù)有一個(gè)單獨(dú)的作用域,外面無法訪問到 i
for ( var i=0; i<=5; i++ ) {
console.log( i );
}
})();
console.log( i ); //報(bào)錯(cuò),無法找到
}
test();
閉包 Closure
閉包與函數(shù)有著緊密的關(guān)系,它是函數(shù)的代碼在運(yùn)行過程中的一個(gè)動(dòng)態(tài)環(huán)境,是一個(gè)運(yùn)行期的、動(dòng)態(tài)的概念。
所謂閉包,是指詞法表示包括不必計(jì)算的變量的函數(shù)。也就是說,該函數(shù)能夠使用函數(shù)外定義的變量。
在程序語(yǔ)言中,所謂閉包,是指語(yǔ)法域位于某個(gè)特定的區(qū)域,具有持續(xù)參照(讀寫)位于該區(qū)域內(nèi)自身范圍之外的執(zhí)行域上的非持久型變量值能力的段落。這些外部執(zhí)行域的非持久型變量神奇地保留它們?cè)陂]包最初定義(或創(chuàng)建)時(shí)的值
理解閉包,必須要對(duì)于作用域鏈的概念非常的清楚。
var name = "xiao A";
var obj = {
name : "xiao B",
getName: function(){
return function(){
return this.name;
}
}
};
console.log(obj.getName()()); //xiao A
//類似:
//var zf = obj.getName();//全局作用域
//zf();
var name = "xiao A";
var obj = {
name : "xiao B",
getName: function(){
var self = this;
return function(){
return self.name;
}
}
};
//console.log( obj.getName().call(obj) );
console.log( obj.getName()() );
//閉包: 一個(gè)函數(shù), 可以訪問另外一個(gè)作用域中的變量
//封閉性,(類似食品包裝袋一樣,封閉起來,保質(zhì)期延長(zhǎng),變量的訪問范圍的延長(zhǎng)) //private 起到一個(gè)保護(hù)變量的作用
//1 level
function f(x){ //2 level
var temp = x; //局部變量 //temp 標(biāo)記 已經(jīng)沒有被使用
return function(x){ //3 level (function 有一個(gè)執(zhí)行域)
temp += x; //temp 下一級(jí)作用域仍然被引用 , 標(biāo)記為 使用
alert(temp);
}
}
//js 垃圾回收機(jī)制,當(dāng)函數(shù)執(zhí)行完畢后,內(nèi)部所有的局部變量都集體的被回收。
var a = f(50);
a(5); //55
a(10); //65
a(20); //85
回調(diào)函數(shù)
-
回調(diào)函數(shù)執(zhí)行
-
回調(diào)函數(shù)中的this
-
回調(diào)函數(shù)的返回值
forEach
// forEach:用來遍歷數(shù)組中的每一項(xiàng)
// 1. 數(shù)組中有幾項(xiàng),那么傳遞進(jìn)去的匿名回調(diào)函數(shù)就需要執(zhí)行幾次。
// 2. 每一次執(zhí)行匿名函數(shù)的時(shí)候,還傳遞了三個(gè)參數(shù)值:數(shù)組中的當(dāng)前項(xiàng)item,當(dāng)前項(xiàng)的索引index,原始的數(shù)組input
// forEach方法中的this是arr,匿名函數(shù)回調(diào)函數(shù)的this默認(rèn)是window
var arr = [10, 234, 23, 76, 7666, 34];
arr.forEach(function(item, index, input) {
input[index] = item * 10; // 操作之后,修改了原數(shù)組
console.log(arguments);
});
var obj = {name: 'zf'};
// arr.forEach(function(item, index) {
// console.log(this);
// }.call(obj)); // 給forEach賦值的是時(shí)候,首先把匿名函數(shù)執(zhí)行,把匿名函數(shù)中的this變?yōu)閛bj,把匿名函數(shù)執(zhí)行的返回結(jié)果undefined賦值給foreach
arr.forEach(function(item, index) {
console.log(this, '---');
}.bind(obj)); // bind 只是預(yù)先處理,先把this轉(zhuǎn)為參數(shù)的對(duì)象,到后續(xù)該執(zhí)行的時(shí)候才執(zhí)行。
// 不管是forEach,還是map都支持第二個(gè)參數(shù),第二個(gè)參數(shù)表示:把匿名函數(shù)中的this進(jìn)行修改。
arr.forEach(function() {
console.log(this, 'this');
}, obj);
forEach兼容處理
// 兼容處理
Array.prototype._forEach = function(callback, context) {
content = content || window;
if ('forEach' in Array.prototype) {
this.forEach(callback, context);
return;
}
// IE6-8,執(zhí)行回調(diào)邏輯
for (var i=0; i<this.length; i++) {
callback || callback.call(context, this[i], i, this);
}
}
map
var arr = [10, 234, 23, 76, 7666, 34];
arr.map(function(item, index, input) { // 原有數(shù)組不變
console.log(arguments);
return item * 10;
});
// map和forEach非常相似,都是用來遍歷數(shù)組中的每一項(xiàng)的值
// 區(qū)別:map的回調(diào)函數(shù)中支持return返回值,return的是什么,相當(dāng)于把數(shù)組中的這一項(xiàng)改變?yōu)槭裁矗ǖ遣⒉挥绊懺瓉淼臄?shù)組,只是相當(dāng)于把原數(shù)組克隆一份,把克隆的這一份的數(shù)組中的對(duì)應(yīng)項(xiàng)改變)
map兼容處理
Array.prototype._map = function(callback, context) {
context = context || window;
if ('map' in Array.prototype) {
this.map(callback, context);
return;
}
// IE6-8,執(zhí)行回調(diào)邏輯
var resArr = [];
for (var i=0; i<this.length; i++) {
if (typeof callback === 'function') {
resArr[resArr.length] = callback.call(context, this[i], i, this);
}
}
return resArr;
}
var arr = [10, 234, 23, 76, 7666, 34];
arrMap = arr._map(function(item, index, input) { // 原有數(shù)組不變
// console.log(arguments, '_map');
return item * 100;
});
console.log(arrMap);
柯理化函數(shù)思想
柯理化函數(shù)思想:一個(gè)JS預(yù)處理思想
核心:利用函數(shù)執(zhí)行可以形成一個(gè)不銷毀的私有作用域的原理,把需要預(yù)先處理的內(nèi)容都存儲(chǔ)在這個(gè)不銷毀的作用域中,并且返回一個(gè)匿名函數(shù),執(zhí)行的都是匿名函數(shù),把匿名函數(shù)中,把之前的預(yù)先存儲(chǔ)的值進(jìn)行相關(guān)操作處理即可。
var obj = {name: 'zf'};
function fn(num1, num2) {
console.log(this, num1, num2)
}
// 把傳遞進(jìn)來的callback這個(gè)方法中的this預(yù)先處理為context
function bind(callback, context) {
context = context || window;
var outArg = Array.prototype.slice.call(arguments, 2);
return function(ev) {
var innerArg = Array.prototype.slice.call(arguments, 0);
callback.apply(context, outArg.concat(innerArg));
}
}
// document.body.onclick = fn; // fn 中的 this是document.body. num1是 MouseEven對(duì)象
document.body.onclick = fn.bind(obj, 100, 200); // 除了預(yù)先處理了this和需要手動(dòng)傳遞的參數(shù)值以外,把瀏覽器默認(rèn)給傳遞的鼠標(biāo)事件對(duì)象也進(jìn)行預(yù)先處理,傳遞到最后一個(gè)參數(shù)。
document.body.onclick = bind(obj, 100, 200)
// document.body.onclick = function() {
// console.log(this); // this --> document.body
// }
// window.setTimeout(fn.bind(obj), 0);
// window.setTimeout(bind(fn, obj, 100, 20), 0); // 給定時(shí)器綁定方法,然后定時(shí)器到達(dá)時(shí)間的時(shí)候,讓fn執(zhí)行,并且讓fn中的this變?yōu)閛bj
感興趣的朋友可以使用在線HTML/CSS/JavaScript代碼運(yùn)行工具:http://tools.jb51.net/code/HtmlJsRun測(cè)試上述代碼運(yùn)行效果。
更多關(guān)于JavaScript相關(guān)內(nèi)容可查看本站專題:《JavaScript常用函數(shù)技巧匯總》、《javascript面向?qū)ο笕腴T教程》、《JavaScript錯(cuò)誤與調(diào)試技巧總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》及《JavaScript數(shù)學(xué)運(yùn)算用法總結(jié)》
希望本文所述對(duì)大家JavaScript程序設(shè)計(jì)有所幫助。
相關(guān)文章
JavaScript實(shí)現(xiàn)256色轉(zhuǎn)灰度圖
本文主要介紹了JavaScript實(shí)現(xiàn)256色轉(zhuǎn)灰度圖的示例代碼,具有很好的參考價(jià)值,下面跟著小編一起來看下吧2017-02-02
微信小程序?qū)崿F(xiàn)的貪吃蛇游戲【附源碼下載】
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)的貪吃蛇游戲,結(jié)合實(shí)例形式分析了微信小程序?qū)崿F(xiàn)貪吃蛇游戲功能的相關(guān)界面布局與代碼邏輯操作技巧,并附帶源碼供讀者下載參考,需要的朋友可以參考下2018-01-01
JS點(diǎn)擊某個(gè)圖標(biāo)或按鈕彈出文件選擇框的實(shí)現(xiàn)代碼
本文給大家介紹js點(diǎn)擊點(diǎn)擊某個(gè)圖標(biāo)或按鈕彈出文件選擇框的實(shí)現(xiàn)代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),感興趣的朋友可以參考下2016-09-09
layui 解決form表單點(diǎn)擊無反應(yīng)的問題
今天小編就為大家分享一篇layui 解決form表單點(diǎn)擊無反應(yīng)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-10-10
JavaScript Dom 綁定事件操作實(shí)例詳解
這篇文章主要介紹了JavaScript Dom 綁定事件操作,結(jié)合實(shí)例形式詳細(xì)分析了JavaScript實(shí)現(xiàn)dom綁定事件的相關(guān)實(shí)現(xiàn)方法與操作注意事項(xiàng),需要的朋友可以參考下2019-10-10
JSON 數(shù)據(jù)詳解及實(shí)例代碼分析
這篇文章主要介紹了JSON 數(shù)據(jù)詳解及實(shí)例代碼分析的相關(guān)資料,需要的朋友可以參考下2017-01-01
javascript下查找父節(jié)點(diǎn)的簡(jiǎn)單方法
javascript下查找父節(jié)點(diǎn)的簡(jiǎn)單方法...2007-08-08
重構(gòu)Javascript代碼示例(重構(gòu)前后對(duì)比)
回顧頭來看看那些Javascript腳本,有寫得不太理想,過于復(fù)雜?,F(xiàn)抽取出來,重構(gòu)它們,想學(xué)習(xí)javascript重構(gòu)的朋友可以參考下啊,網(wǎng)本文可以幫助你的2013-01-01

