摘要:在定義函數的作用域外調用,得到的返回仍然是函數創建時所在的作用域的局部變量。這是因為所在的匿名函數的閉包中存放的是第一行的,而不是在循環中獲得的的當前值。
原文: http://pij.robinqu.me/JavaScript_Core/JavaScript_Basics/Function.html
源代碼: https://github.com/RobinQu/Programing-In-Javascript/blob/master/chapters/JavaScript_Core/JavaScript_Basics/Function.md
本文存在批注,但該網站的Markdown編輯器不支持,所以無法正常展示,請到原文參考。
函數Javascript中,要記住函數是first-class citizen。
定義
函數聲明語句
function plus(x ,y) { }
函數定義表達式
var plus = function (x, y) { }函數調用
作為函數調用
function a(){}; a();
作為方法調用
a={}; a.x = function(){}; a.x();
通過call和apply間接調用函數(改變this)
call 和 apply帶有多個參數,call和apply把當前函數的this指向第一個參數給定的函數或對象中,并傳遞其余所有的參數作為當前函數的參數。
var O = function () { this.foo = "hello"; this.hello = function () { return "world"; } }; var fn = function () { console.log("call", this); }; var o = new O(); fn.call(o);//此時fn的this指向o
call和apply的不同之處,在于call傳遞的參數是作為arguments依次傳入的,例如
fn.call(o, 1, 2, 3);
而apply傳遞的參數是以一個數組的方式傳入的,例如
fn.apply(o, [1, 2, 3]);
當傳入參數少于函數聲明的參數時,留空的參數的值是undefined。
Javascript允許傳入參數的個數大于聲明時制定的參數個數。可以用arguments來訪問這些參數
function f(){ var i; for( i = 0; i < arguments.length ; i++) { console.log(arguments[i]); } } f(1,2,3,4,5,6);
函數通過取得arguments的長度得到傳入參數的個數,使用一個循環獲取每一個參數。
arguments還有兩個屬性,callee和caller
callee表示正在執行的function對象,
caller表示調用當前function的function
例如
function f(){ console.log(arguments.callee);//[Function: f] console.log(arguments.callee.caller);[Function: g] var i; for( i = 0; i < arguments.length ; i++) { console.log(arguments[i]); } } function g(){ f(1,2,3,4,5,6); } g();
callee 的重要用法之一是在匿名函數中實現遞歸
var result = function (x) { if (x <= 1) return 1; return x * arguments.callee(x - 1); }(3); console.log(result);
上例使用了一個匿名函數和callee實現了一個階乘。
作為值的函數javascript中的函數可以作為值來傳遞
function square(x) { return x * x; } var s = square; s(4);作為命名空間的函數
(function() { }());閉包
Javascript函數對象的內部狀態不僅包含著函數的代碼邏輯,還引用當前的作用域鏈。函數對象通過作用域鏈相互關聯起來,函數體內部變量包含在函數作用域內,這就叫閉包。
例如
var scope = "global scope"; function checkscope() { var scope = "local scope"; function f() { return scope; } return f; } checkscope()();
這段checkscope聲明了一個局部變量,定義了一個函數f,函數f返回了這個局部變量的值,最后返回了這個函數f。在定義函數f的作用域外調用f,得到的返回仍然是函數f創建時所在的作用域的局部變量scope。
又例如
var counter = (function() { var count = 0; return function () { return count++ ; } }());
代碼定義了一個立即執行函數并返回給counter,這個函數定義了一個局部變量count,返回了一個子函數,該子函數每次調用,都會吧count加一并返回。
閉包的注意事項
觀察下面的示例:
var add_the_handlers = function (nodes) { var i; for (i = 0; i < nodes.length; i += 1) { nodes[i].onclick = function (e) { alert(i); }; } };
這個函數期望的結果,是在運行的時候為每個node在onclick的時候alert出各自的序號,但是實際運行的結果卻不同:所有的node在單擊的時候alert出來的數字總是同一個。
這是因為alert所在的匿名函數的閉包中存放的i是第一行的i,而不是在循環中獲得的i的當前值。
所以如果希望達到預期結果,應該在循環中創建多個閉包,在閉包中存放當前循環的i的值:
var add_the_handlers = function (nodes) { var i; for (i = 0; i < nodes.length; i += 1) { nodes[i].onclick = function (i) { return function(e){ alert(e); }; }(i); } };
這里使用一個立即執行函數并傳遞當前的i的值,返回一個新生成的函數。在這個新生成的函數的閉包中就保存了當前的i的值。
函數中的this對象在一個對象中的this始終引用當前對象,但是在函數中,特別是在閉包中,this有一些特殊的行為。
函數中的this對象始終綁定在函數運行時的上下文環境上。所以在普通模式下調用一個全局函數,this始終指向window(客戶端),在嚴格模式下調用一個全局函數,this始終是undefined
示例
var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { return function () { return this.name; }; }, getName : function () { return this.name; } }; console.log(object.getNameFunc()()); console.log(object.getName());
getNameFunction()返回了一個匿名函數,這個匿名函數在調用的時候,上下文是window(瀏覽器中),所以在瀏覽器中輸出的是the Window
而getName()調用的時候上下文是object,所以成功輸出object的name
其實以上代碼中
object.getNameFunc()()
等效于
var fnc = object.getNameFunc();//這時候的fnc已經脫離了object對象
fnc();
所以如果想要getNameFunction()正確返回Object的Name,需要在返回的匿名函數的閉包中保存在函數聲明時的this,
getNameFunc: function () { var that = this; return function () { return that.name; }; },
這樣就可以了。。
函數柯里化函數柯里化是指,把接受多個參數的函數轉換成接受一個單一參數的函數,并且返回接受余下的參數而且返回結果的新函數的技術。
示例
var add1 = add.curry(1); console.log(add1(2));
其中,add是接受兩個參數的函數,add調用了curry返回一個只接受一個參數的新函數,之后調用add1便等效于調用add(1, 2);
javascript并不原生支持curry,可以用prototype來模擬
Function.prototype.curry = function () { var slice = Array.prototype.slice, args = slice.apply(arguments), that = this; return function () { return that.apply(null, args.concat(slice.apply(arguments))); }; }; function add(n1, n2) { return n1 + n2; } var add1 = add.curry(1); console.log(add1(2));
curry創建了一個新函數,在新函數的閉包中保存了原先傳遞的參數。
函數的屬性和方法length 函數的length表示函數實參的數量,是只讀的
prototype 指向一個該函數的原型對象的引用
toString 返回一個字符串
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78111.html
摘要:基礎語法區分大小寫是一種區分大小寫的語法,意味著的關鍵字變量名函數名以及其他一切的字符表示都要使用一致的大小寫形式。化物語化物語空格和換行會忽略代碼中出現的空格換行制表符。如果不用花括號獨立獨立編寫一個語句,語法并不報錯,但不推薦。 JavaScript基礎語法 區分大小寫 JavaScript是一種區分大小寫的語法,意味著JavaScript的關鍵字、變量名、函數名以及其他一切的字符...
摘要:數組創建數組數組字面量使用構造函數數組本質上是所以要判斷是不是數組,需要通過判斷。數組長度使用屬性獲取元素的個數。例如函數的對象就是這樣 原文: http://pij.robinqu.me/JavaScript_Core/JavaScript_Basics/Array.html 源代碼: https://github.com/RobinQu/Programing-In-...
摘要:創建對象對象直接量構造函數原型繼承類繼承對象擁有自有屬性和繼承屬性。遍歷順序是以廣度優先遍歷所以使用便可以判斷是否是對象自有的屬性。可執行對象通過如下方法可以創建一個可執行對象既可以當作對象來使用有原型鏈,也可以當作函數來直接調用 原文: http://pij.robinqu.me/Javascript_Core/Javascript_Basics/Objects.html ...
摘要:多數運算符都是由標點符號表示,比如和。通常會根據需要對操作數進行類型轉換左值是一個古老的屬于,它是指表達式只能出現在賦值運算符的左側。也稱為嚴格相等運算符,它用來檢測兩個操作數是否嚴格相等。運算符的檢測規則是和運算符的求反。 源代碼: https://github.com/RobinQu/Programing-In-Javascript/blob/master/chapters/...
摘要:瀏覽器只是實現的宿主環境之一,其他宿主環境包括和。年月,版發布,成為國際標準。事件定義了事件和事件處理的接口。對于已經正式納入標準的來說,盡管各瀏覽器都實現了某些眾所周知的共同特性,但其他特性還是會因瀏覽器而異。 JavaScript 是面向 Web 的編程語言,絕大多數現代網站都使用了 JavaScript,并且所有的現代 Web 瀏覽器(電腦,手機,平板)均包含了 JavaScri...
閱讀 2434·2021-11-15 11:36
閱讀 1172·2019-08-30 15:56
閱讀 2243·2019-08-30 15:53
閱讀 1038·2019-08-30 15:44
閱讀 649·2019-08-30 14:13
閱讀 997·2019-08-30 10:58
閱讀 476·2019-08-29 15:35
閱讀 1293·2019-08-29 13:58