摘要:函數使用它們實參的值來計算返回值,稱為該函數調用表達式的值。通常函數名的第一個字符為小寫。在函數內部定義的變量,外部無法讀取,稱為局部變量。注意對于命令來說,局部變量只能在函數內部聲明,在其他區塊中聲明,一律都是全局變量。
函數 1.函數簡介
通過函數可以封裝任意多條語句,而且可以在任何地方、任何時候調用。
ECMAScript中的函數使用function關鍵字來聲明,后跟一組參數以及函數體,這些參數在函數體內像局部變量一樣工作。
function functionName(arg0, arg1....argN) { statements }
函數調用會為形參提供實參的值。函數使用它們實參的值來計算返回值,稱為該函數調用表達式的值。
function test(name){ return name; } test("tg");
在上面的例子中,name就是形參,調用時的"tg"就是實參。
除了實參之外,每次調用還會擁有另一個值---本次調用的上下文---這就是this關鍵字的值。
我們還可以通過在函數內添加return語句來實現返回值。
注意:遇到return語句時,會立即退出函數,也就是說,return語句后面的語句不再執行。
function test(){ return 1; alert(1); //永遠不會被執行 }
一個函數中可以包含多個return語句,而且return語句可以不帶有任何返回值,最終將返回undefined。
function test(num){ if(num > 2){ return num; }else{ return ; } } test(3); // 3 test(1); //undefined
如果函數掛載在一個對象上,將作為對象的一個屬性,就稱它為對象的方法。
var o = { test: function(){} }
test()就是對象o的方法。
2、函數定義(聲明)JavaScript有三種方法,可以定義一個函數。
(1)function命令
function name() {}
name是函數名稱標識符。函數名稱是函數聲明語句必需的部分。不過對于函數表達式來說,名稱是可選的:如果存在,該名字只存在于函數體內,并指向該函數對象本身。
圓括號:圓括號內可放置0個或多個用逗號隔開的標識符組成的列表,這些標識符就是函數的參數名稱。 花括號:可包含0條或多條JavaScript語句。這些語句構成了函數體。一旦調用函數,就會執行這些語句。
(2)函數表達式
var f = function(x){ console.log(x); }
采用函數表達式聲明函數時,function命令后面不帶有函數名。如果加上函數名,該函數名只在函數體內部有效,在函數體外部無效。
(3)Function()
Function()函數定義還可以通過Function()構造函數來定義
var f=new Function("x","y","return x+y");
等價于
var f=function(x,y){ return x+y; }
除了最后一個參數是函數體外,前面的其他參數都是函數的形參。如果函數不包含任何參數,只須給構造函數簡單的傳入一個字符串---函數體---即可。 不過,Function()構造函數在實際編程中很少會用到。
注意點:如果同一個函數被多次定義(聲明),后面的定義(聲明)就會覆蓋前面的定義(聲明)
function f(){ console.log(1); } f() //1 function f(){ console.log(2); } f() //2
函數可以調用自身,這就是遞歸(recursion)
function f(x){ if(x>2){ console.log(x); return f(x-1); }else{ return 1; } } f(4); // 4 //3
不能在條件語句中聲明函數 (在ES6中的塊級作用域是允許聲明函數的。)
3、函數命名任何合法的JavaScript標識符都可以用做一個函數的名稱。
函數名稱通常是動詞或以動詞為前綴的詞組。 通常函數名的第一個字符為小寫。當函數名包含多個單詞時,可采取下劃線法,比如:like_this();也可以采取駝峰法,也就是除了第一個單詞之外的單詞首字母使用大寫字母,比如:likeThis();
4、被提前就像變量的“被提前”一樣,函數聲明語句也會“被提前”到外部腳本或外部函數作用域的頂部,所以以這種方式聲明的函數,可以被在它定義之前出現的代碼所調用。
f() function f(){}
其實JavaScript是這樣解釋的:
function f(){} f()
注意:以表達式定義的函數并沒有“被提前”,而是以變量的形式“被提前”。
f(); var f = function (){}; // TypeError: f is not a function
變量其實是分為聲明,賦值兩部分的,上面的代碼等同于下面的形式
var f; f(); f = function() {};
調用f的時候,f只是被聲明了,還沒有被賦值,等于undefined,所以會報錯。
5、嵌套函數JavaScript的函數可以嵌套在其他函數中定義,這樣它們就可以訪問它們被定義時所處的作用域中的任何變量,這就是JavaScript的閉包。
function test(){ var name = "tg"; function test2(){ var age = 10; console.log(name); // "tg" } console.log(age); // Uncaught ReferenceError: age is not defined } test();
從上面的例子可得,test2()可以訪問name,但是如果在test()內,test2()外訪問age,就會報錯。
6、函數調用構成函數主體的JavaScript代碼在定義時并不會執行,只有調用該函數,它們才會執行。有4種方式調用JavaScript函數:
作為函數
作為方法
作為構造函數
通過它們的call()和apply()方法間接調用
6.1函數調用
函數可以通過函數名來調用,后跟一對圓括號和參數(圓括號中的參數如果有多個,用逗號隔開)
function test(){} test()
6.2方法調用
var o = { f: function(){} } o.f();
6.3構造函數調用
如果函數或者方法調用之前帶有關鍵字new,它就構成構造函數調用。 凡是沒有形參的構造函數調用都可以省略圓括號(但不推薦)。
var o=new Object();
var o=new Object;
7.1 可選形參
在ECMAScript中的函數在調用時,傳遞的參數可少于函數中的參數,沒有傳入參數的命名參數的值是undefined。
為了保持好的適應性,一般應當給參數賦予一個合理的默認值。
function go(x,y){ x = x || 1; y = y || 2; }
注意:當用這種可選實參來實現函數時,需要將可選實參放在實參列表的最后。那些調用你的函數的程序員是沒法省略第一個參數并傳入第二個實參的。
7.2 實參對象
當調用函數時,傳入的實參個數超過函數定義時的形參個數時,是沒有辦法直接獲得未命名值的引用。 這時,標識符arguments出現了,其指向實參對象的引用,實參對象是一個類數組對象,可以通過數字下標來訪問傳入函數的實參值,而不用非要通過名字來得到實參。
function go(x){ console.log(arguments[0]); console.log(arguments[1]); } go(1,2); //1 //2
arguments有一個length屬性,用以標識其所包含元素的個數。
function f(x){ console.log(arguments.length); } f(1,2) // 2
注意:arguments并不是真正的數組,它是一個類數組對象。每個實參對象都包含以數字為索引的一組元素以及length屬性。
通過實參名字來修改實參值的話,通過arguments[]數組也可以獲取到更改后的值。
在函數體內,我們可以通過arguments對象來訪問這個參數類數組,我們可以使用方括號語法訪問它的每一個參數(比如arguments[0]),它還有一個length屬性,表示傳遞進來的參數個數。
arguments類數組中每一個元素的值會與對應的命名參數的值保持同步,這種影響是單向的,也可以這樣說,如果是修改arguments中的值,會影響到命名參數的值,但是修改命名參數的值,并不會改變arguments中對應的值。
function f(x){ console.log(x); // 1 arguments[0]=null; console.log(x); // null } f(1);
在上面的例子中,arguments[0]和x指代同一個值,修改其中一個的值會影響到另一個。 注意:如果有同名的參數,則取最后出現的那個值。
function f(x,x){ console.log(x); } f(1,2) // 2
callee和caller屬性
arguments對象帶有一個callee屬性,返回它所對應的原函數。
在一個函數調用另一個函數時,被調用函數會自動生成一個caller屬性,指向調用它的函數對象。如果該函數當前未被調用,或并非被其他函數調用,則caller為null。
再次提醒,arguments并不是真正的數組,它只是類數組對象(有length屬性且可使用索引來訪問子項)。但我們可以借助Array類的原型對象的slice方法,將其轉為真正的數組:
Array.prototype.slice.call(arguments, 0);
//更簡潔的寫法
[].slice.call(arguments, 0);
7.3 按值傳參
ECMAScript中所有函數的參數都是按值傳遞的。也就是說,把函數外部的值復制給函數內部的參數,就和把值從一個變量復制到另一個變量一樣。
在向參數傳遞基本類型的值時,被傳遞的值會被復制給一個局部變量(即命名參數,或者用ECMAScript的概念來說,就是arguments對象中的一個元素。)
例子:
var num = 1; function test(count){ count += 10; return count; } var result = test(num); console.log(num); // 1 console.log(result); // 11
在上面的例子中,我們將num作為參數傳給了test()函數,即count的值也是1,然后在函數內將count加10,但是由于傳遞的只是num的值的一個副本,并不會影響num,count和num是獨立的,所以最后num的值依舊是1.
在向參數傳遞引用類型的值時,會先把這個值在內存中的地址復制給一個局部變量,若局部變量變化,則局部變量和復制給局部變量路徑的全局變量也會發生改變。
function test(obj){ obj.name = "tg"; } var person = new Object(); test(person); console.log(person.name); // "tg"
但是,如果局部變量指向了一個新的堆內地址,再改變局部變量的屬性時,不會影響全局變量。
看下面的例子:
function test(obj){ obj.name = "tg"; obj = new Object(); obj.name = "tg2"; } var person = new Object(); test(person); console.log(person.name); // "tg"
在上面的例子中,全局的person和函數內局部的obj在初始傳遞時,兩者指向的是內存中的同一個地址,但是,當在函數內創建了一個新的對象,并賦值給obj(賦值的是新對象的地址)。這個時候,obj指向的就不在是全局對象person,而是指向了新對象的地址,所以給obj添加屬性name時,全局對象person的屬性不會被改變。
對于上面的例子中的obj,也可以這樣說,一旦obj的值發生了變化,那么它就不再指向person在內存中的地址了。
8、將對象屬性用做實參當一個函數包含超過三個形參時,要記住調用函數中實參的正確順序是件讓人頭疼的事。不過,我們可以通過名/值對的形式傳入參數,這樣就不需要管參數的順序了。
function f(params){ console.log(params.name); } f({name:"a"})9、作為值的函數
在JavaScript中,我們可以將函數賦值給變量。
function f(){} var a=f;10、函數作用域
作用域(scope)指的是變量存在的范圍。
Javascript只有兩種作用域:一種是全局作用域,變量在整個程序中一直存在,所有地方都可以讀取;另一種是函數作用域,變量只在函數內部存在。 在函數外部聲明的變量就是全局變量(global variable),它可以在函數內部讀取。
var a=1; function f(){ console.log(a) } f() //1
上面的代碼中,函數f內部可以讀取全局變量a。
在函數內部定義的變量,外部無法讀取,稱為“局部變量”(local variable)。
function f(){ var a=1; } v //ReferenceError: v is not defined
上面代碼中,變量v在函數內部定義,所以是一個局部變量,函數之外就無法讀取。
函數內部定義的變量,會在該作用域內覆蓋同名全局變量。
var a=1; function f(){ var a=2; console.log(a); } f() //2 a //1
注意:對于var命令來說,局部變量只能在函數內部聲明,在其他區塊中聲明,一律都是全局變量。
函數的執行依賴于變量作用域,這個作用域是在函數定義時決定的,而不是函數調用時決定的。
11、函數內部的變量提升與全局作用域一樣,函數作用域內部也會產生“變量提升”現象。
var命令聲明的變量,不管在什么位置,變量聲明都會被提升到函數體的頭部。
function f(x){ if(x>10){ var a = x -1; } }
//等同于
function f(x){ var a; if(x>10){ a = x - 1; } }12、沒有重載
ECMAScript函數沒有重載的定義。
重載是指為一個函數編寫兩個定義,只要這兩個定義的簽名(接受的參數的類型和數量)不同即可。
對于ECMAScript函數,如果定義了兩個同名的,后定義的函數會覆蓋先定義的函數。
13、函數屬性、方法和構造函數13.1 函數的屬性、方法
(1)name屬性
name屬性返回緊跟在function關鍵字之后的那個函數名。
function f(){} f.name //f
(2)length屬性
函數的length屬性是只讀屬性,代表函數形參的數量,也就是在函數定義時給出的形參個數。
function f(x,y){} f.length //2
(3)prototype屬性
每一個函數都包含一個prototype屬性,這個屬性指向一個對象的引用,這個對象稱做“原型對象”(prototype object)。
(4)call()
語法:
call([thisObj[,arg1[, arg2[, [,.argN]]]]])
定義:調用一個對象的一個方法,以另一個對象替換當前對象。
說明: call 方法可以用來代替另一個對象調用一個方法。call 方法可將一個函數的對象上下文從初始的上下文改變為由 thisObj 指定的新對象。
(5)apply()
語法:
apply([thisObj[,argArray]])
定義:應用某一對象的一個方法,用另一個對象替換當前對象。
說明: 如果 argArray 不是一個有效的數組或者不是 arguments 對象,那么將導致一個 TypeError。 如果沒有提供 argArray 和 thisObj 任何一個參數,那么 Global 對象將被用作 thisObj, 并且無法被傳遞任何參數。 bind()方法 bind()方法是在ECMAScript 5中新增的方法。 toString()方法
函數的toString方法返回函數的源碼。
function f(){ return 1; } f.toString() //function f(){ // return 1; //}(6)bind()
bind()方法會創建一個新函數,稱為綁定函數,當調用這個綁定函數時,綁定函數會以創建它時傳入 bind()方法的第一個參數作為 this,傳入 bind() 方法的第二個以及以后的參數加上綁定函數運行時本身的參數按照順序作為原函數的參數來調用原函數。
var bar=function(){ console.log(this.x); } var foo={ x:3 } bar(); bar.bind(foo)(); /*或*/ var func=bar.bind(foo); func(); 輸出: undefined 3
注意:bind()返回的是函數。
13.2 構造函數構造函數和普通函數的定義并沒有太大區別,不過我們使用new關鍵字來生成構造函數的實例對象。
function Test(){} var t = new Test();
對于構造函數,一般首字母大寫,便于和普通函數區別開來。
定義每個函數都會主動獲取一個prototype屬性,該屬性擁有一個對象--該函數的原型,該原型有一個constructor屬性,指向其當前所屬的函數。
14、閉包JavaScript的函數可以嵌套在其他函數中定義,這樣它們就可以訪問它們被定義時所處的作用域中的任何變量,這就是JavaScript的閉包。
閉包會保存函數作用域中的狀態,即使這個函數已經執行完畢。
閉包的最大用處有兩個,一個是可以讀取函數內部的變量,另一個就是讓這些變量始終保持在內存中,即閉包可以使得它誕生環境一直存在。
閉包的創建依賴于函數。
function f(a){ return function(){ return a++; }; } var c=f(1); console.log(c()); //1 console.log(c()); //2 console.log(c()); //3
閉包的另一個用處,是封裝對象的私有屬性和私有方法。
15、立即調用的函數表達式(IIFE)在Javascript中,一對圓括號()是一種運算符,跟在函數名之后,表示調用該函數。
(function(){ statement }())
上面的函數會立即調用。
注意:上面代碼的圓括號的用法,function之前的左圓括號是必需的,因為如果不寫這個左圓括號,JavaScript解釋器會試圖將關鍵字function解析為函數聲明語句。而使用圓括號,JavaScript解釋器才會正確地將其解析為函數定義表達式。
當然,下面的方法也會以表達式來處理函數定義的方法。
!function(){}(); ~function(){}(); -function(){}(); +function(){}();
通常情況下,只對匿名函數使用這種“立即執行的函數表達式”。它的目的有兩個:
一是不必為函數命名,避免了污染全局變量;
二是IIFE內部形成了一個多帶帶的作用域,可以封裝一些外部無法讀取的私有變量。
16、eval命令eval命令的作用是,將字符串當作語句執行。
eval("var a=1"); a //1
eval沒有自己的作用域,都在當前作用域內執行
JavaScript規定,如果使用嚴格模式,eval內部聲明的變量,不會影響到外部作用域。
(function(){ "use strict"; eval("var a=1"); console.log(a); //ReferenceError: a is not defined })();17、嚴格模式下的函數
不能把函數命名為eval或arguments
不能把參數命名為eval或arguments
不能出現兩個命名參數同名的情況
如果出現上面三種情況,都會導致語法錯誤,代碼無法執行。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/81988.html
摘要:專題系列共計篇,主要研究日常開發中一些功能點的實現,比如防抖節流去重類型判斷拷貝最值扁平柯里遞歸亂序排序等,特點是研究專題之函數組合專題系列第十六篇,講解函數組合,并且使用柯里化和函數組合實現模式需求我們需要寫一個函數,輸入,返回。 JavaScript 專題之從零實現 jQuery 的 extend JavaScritp 專題系列第七篇,講解如何從零實現一個 jQuery 的 ext...
摘要:設計模式是以面向對象編程為基礎的,的面向對象編程和傳統的的面向對象編程有些差別,這讓我一開始接觸的時候感到十分痛苦,但是這只能靠自己慢慢積累慢慢思考。想繼續了解設計模式必須要先搞懂面向對象編程,否則只會讓你自己更痛苦。 JavaScript 中的構造函數 學習總結。知識只有分享才有存在的意義。 是時候替換你的 for 循環大法了~ 《小分享》JavaScript中數組的那些迭代方法~ ...
摘要:理解的函數基礎要搞好深入淺出原型使用原型模型,雖然這經常被當作缺點提及,但是只要善于運用,其實基于原型的繼承模型比傳統的類繼承還要強大。中文指南基本操作指南二繼續熟悉的幾對方法,包括,,。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。 怎樣使用 this 因為本人屬于偽前端,因此文中只看懂了 8 成左右,希望能夠給大家帶來幫助....(據說是阿里的前端妹子寫的) this 的值到底...
摘要:然后將構造函數的原型設為,便實現了對象繼承。首先,我們定義一個構造函數,并在其中定義一個局部變量。這里的是局部變量,其作用域仍然存在是閉包現象,而非對象屬性。 Javascript是動態的,弱類型的,解釋執行的程序設計語言。 Javascript極其靈活,支持多種程序設計范式:面向對象、指令式、函數式。JavaSCript最初被用于瀏覽器腳本,現在已經是所有主流瀏覽器的默認腳本語言。瀏...
摘要:和類在開始時遇到類組件,只是需要有關類的基礎。畢竟,中的條件呈現僅再次顯示大多數是而不是特定的任何內容。 在我的研討會期間,更多的材料是關于JavaScript而不是React。其中大部分歸結為JavaScript ES6以及功能和語法,但也包括三元運算符,語言中的簡寫版本,此對象,JavaScript內置函數(map,reduce,filter)或更常識性的概念,如:可組合性,可重用...
閱讀 1903·2021-11-24 09:39
閱讀 2566·2021-10-14 09:43
閱讀 3331·2021-10-08 10:10
閱讀 2348·2021-09-22 15:54
閱讀 2350·2019-08-29 17:20
閱讀 1584·2019-08-28 18:14
閱讀 2384·2019-08-26 13:28
閱讀 1125·2019-08-26 12:16