摘要:概述函數(shù)的聲明命令函數(shù)表達(dá)式變量賦值命令后面不帶有函數(shù)名。如果加上函數(shù)名,該函數(shù)名只在函數(shù)體內(nèi)部有效,在函數(shù)體外部無效。同樣的,函數(shù)體內(nèi)部聲明的函數(shù),作用域綁定函數(shù)體內(nèi)部。可以通過,達(dá)到調(diào)用函數(shù)自身的目的。
函數(shù)
函數(shù)是一段可以反復(fù)調(diào)用的代碼塊。函數(shù)還能接受輸入的參數(shù),不同的參數(shù)會返回不同的值。
1.概述
1.1函數(shù)的聲明
1.2函數(shù)的重復(fù)聲明
1.3圓括號運算符,return 語句和遞歸
1.4第一等公民
1.5函數(shù)名的提升
2函數(shù)的屬性和方法
2.1name 屬性
2.2length 屬性
2.3toString()
3函數(shù)作用域
3.1定義
3.2函數(shù)內(nèi)部的變量提升
3.3函數(shù)本身的作用域
4參數(shù)
4.1概述
4.2參數(shù)的省略
4.3傳遞方式
4.4同名參數(shù)
4.5arguments 對象
5.函數(shù)的其他知識點
5.1閉包
5.2立即調(diào)用的函數(shù)表達(dá)式(IIFE)
6eval 命令
6.1基本用法
6.2eval 的別名調(diào)用
函數(shù)
函數(shù)是一段可以反復(fù)調(diào)用的代碼塊。函數(shù)還能接受輸入的參數(shù),不同的參數(shù)會返回不同的值。
1.概述
1.1函數(shù)的聲明
(1)function 命令
(2)函數(shù)表達(dá)式 變量賦值
var print = function(s) {}
function命令后面不帶有函數(shù)名。如果加上函數(shù)名,該函數(shù)名只在函數(shù)體內(nèi)部有效,在函數(shù)體外部無效。
var print = function x(){
console.log(typeof x);
};
x
// ReferenceError: x is not defined
print()
// function
用處有兩個,一是可以在函數(shù)體內(nèi)部調(diào)用自身,二是方便除錯(除錯工具顯示函數(shù)調(diào)用棧時,將顯示函數(shù)名,而不再顯示這里是一個匿名函數(shù))。
(3)Function 構(gòu)造函數(shù)
var add = new Function(
"x",
"y",
"return x + y"
);除了最后一個參數(shù)是add函數(shù)的“函數(shù)體”,其他參數(shù)都是add函數(shù)的參數(shù)
// 等同于
function add(x, y) {
return x + y;
}
1.2函數(shù)的重復(fù)聲明
由于函數(shù)名的提升
后面的聲明就會覆蓋前面的聲明
1.3圓括號運算符,return 語句和遞歸
圓括號用于調(diào)用函數(shù)a()
遇到return后面句子都不執(zhí)行,沒有就返回undefined
函數(shù)可以調(diào)用自身,這就是遞歸(recursion)。下面就是通過遞歸,計算斐波那契數(shù)列的代碼。
function fib(num) {
if (num === 0) return 0;
if (num === 1) return 1;
return fib(num - 2) + fib(num - 1);
}
fib(6) // 8
1.4第一等公民
函數(shù)只是一個可以執(zhí)行的值
函數(shù)只是一個可以執(zhí)行的值
function add(x, y) {
return x + y;
}
// 將函數(shù)賦值給一個變量
var operator = add;
// 將函數(shù)作為參數(shù)和返回值
function a(op){
return op;
}
a(add)(1, 1)
// 2
1.5函數(shù)名的提升
視為變量名,所以采用function命令聲明函數(shù)時,整個函數(shù)會像變量聲明一樣
表達(dá)式形式是匿名函數(shù)
f();
var f = function (){};
// TypeError: undefined is not a function
上面的代碼等同于下面的形式。
var f;
f();
f = function () {};
上面代碼第二行,調(diào)用f的時候,f只是被聲明了,還沒有被賦值,等于undefined,所以會報錯。因此,如果同時采用function命令和賦值語句聲明同一個函數(shù),最后總是采用賦值語句的定義。 因為兩個都是聲明 ,然后賦值 function形式是聲明 var聲明 最后賦值
var f = function () {
console.log("1");
}
function f() {
console.log("2");
}
f() // 1
2函數(shù)的屬性和方法
2.1name 屬性
2.1.1function f1() {}
f1.name // "f1"
如果是通過變量賦值定義的函數(shù),那么name屬性返回變量名。
2.2.2var f2 = function () {};
f2.name // "f2"
如果變量的值是一個具名函數(shù),那么name屬性返回function關(guān)鍵字之后的那個函數(shù)名。
2.2.3var f3 = function myName() {};
f3.name // "myName"
f3.name返回函數(shù)表達(dá)式的名字。注意,真正的函數(shù)名還是f3,而myName這個名字只在函數(shù)體內(nèi)部可用
2.2.4name屬性的一個用處,就是獲取參數(shù)函數(shù)的名字。
var myFunc = function () {};
function test(f) {
console.log(f.name);
}
test(myFunc) // myFunc
2.2length 屬性
函數(shù)定義之中的參數(shù)個數(shù)。
2.3toString()
返回一個字符串,內(nèi)容是函數(shù)的源碼。
Math.sqrt.toString()
// "function sqrt() { [native code] }
利用這一點,可以變相實現(xiàn)多行字符串。
var multiline = function (fn) {
var arr = fn.toString().split("n");
return arr.slice(1, arr.length - 1).join("n");
};
function f() {/*
這是一個
多行注釋
*/}
multiline(f);
// " 這是一個
// 多行注釋"
3函數(shù)作用域
3.1定義
3.1.1全局 一直存在在內(nèi)存 到處可讀取
3.1.2 函數(shù)作用域 變量只在函數(shù)種存在
對于var命令來說,局部變量只能在函數(shù)內(nèi)部聲明,在其他區(qū)塊中聲明,一律都是全局變量。
3.2函數(shù)內(nèi)部的變量提升
函數(shù)作用域內(nèi)部也會產(chǎn)生“變量提升”現(xiàn)象。var命令聲明的變量,不管在什么位置,變量聲明都會被提升到函數(shù)體的頭部
3.3函數(shù)本身的作用域
作用域只與聲明的地方有關(guān)
函數(shù)本身也是一個值,也有自己的作用域。它的作用域與變量一樣,就是其聲明時所在的作用域,與其運行時所在的作用域無關(guān)
var a = 1;
var x = function () {
console.log(a);
};
function f() {
var a = 2;
x();
}
f() // 1
函數(shù)x是在函數(shù)f的外部聲明的,所以它的作用域綁定外層,內(nèi)部變量a不會到函數(shù)f體內(nèi)取值,所以輸出1,而不是2。
3.3.1同樣的,函數(shù)體內(nèi)部聲明的函數(shù),作用域綁定函數(shù)體內(nèi)部。
function foo() {
var x = 1;
function bar() {
console.log(x);
}
return bar;
}
var x = 2;
var f = foo();
f() // 1
上面代碼中,函數(shù)foo內(nèi)部聲明了一個函數(shù)bar,bar的作用域綁定foo。當(dāng)我們在foo外部取出bar執(zhí)行時,變量x指向的是foo內(nèi)部的x,而不是foo外部的x。正是這種機制,構(gòu)成了下文要講解的“閉包”現(xiàn)象。
4參數(shù)
4.1概述
4.2參數(shù)的省略
4.1.1無論提供多少個參數(shù)(或者不提供參數(shù)),JavaScript 都不會報錯。省略的參數(shù)的值就變?yōu)閡ndefined。需要注意的是,函數(shù)的length屬性與實際傳入的參數(shù)個數(shù)無關(guān)
4.1.2沒有辦法只省略靠前的參數(shù),而保留靠后的參數(shù)。如果一定要省略靠前的參數(shù),只有顯式傳入undefined。
function f(a, b) {
return a;
}
f( , 1) // SyntaxError: Unexpected token ,(…)
f(undefined, 1) // undefined
上面代碼中,如果省略第一個參數(shù),就會報錯。
4.3傳遞方式
4.3.1函數(shù)參數(shù)如果是原始類型的值(數(shù)值、字符串、布爾值),傳遞方式是傳值傳遞。在函數(shù)里面的是復(fù)制的另外一個值了
在函數(shù)體內(nèi)修改參數(shù)值,不會影響到函數(shù)外部
var p = 2;
function f(p) {
p = 3;
}
f(p);
p // 2
上面代碼中,變量p是一個原始類型的值,傳入函數(shù)f的方式是傳值傳遞。因此,在函數(shù)內(nèi)部,p的值是原始值的拷貝,無論怎么修改,都不會影響到原始值。
4.3.2如果函數(shù)參數(shù)是復(fù)合類型的值(數(shù)組、對象、其他函數(shù)),傳遞方式是傳址傳遞
傳入函數(shù)的原始值的地址,因此在函數(shù)內(nèi)部修改參數(shù),將會影響到原始值。
var obj = { p: 1 };
function f(o) {
o.p = 2;
}
f(obj);
obj.p // 2
上面代碼中,傳入函數(shù)f的是參數(shù)對象obj的地址。因此,在函數(shù)內(nèi)部修改obj的屬性p,會影響到原始值。(外面和里面指向同一個地址)
4.3.3注意,如果函數(shù)內(nèi)部修改的,不是參數(shù)對象的某個屬性,而是替換掉整個參數(shù),這時不會影響到原始值。
var obj = [1, 2, 3];
function f(o) {
o = [2, 3, 4];
}
f(obj);
obj // [1, 2, 3]
形式參數(shù)(o)的值實際是參數(shù)obj的地址,重新對o賦值導(dǎo)致o指向另一個地址,保存在原地址上的值當(dāng)然不受影響。(外面和里面指向不同地址了)
4.4同名參數(shù)
如果有同名的參數(shù),則取最后出現(xiàn)的那個值
function f(a, a) {
console.log(a);
}
f(1, 2) // 2
取值的時候,以后面的a為準(zhǔn),即使后面的a沒有值或被省略,也是以其為準(zhǔn)。
function f(a, a) {
console.log(a);
}
f(1) // undefined
function f(a, a) {
console.log(arguments[0]);
}
f(1) // 1
4.5arguments 對象
4.5.1只能在函數(shù)體內(nèi)用
正常可以修改 嚴(yán)格無效
有l(wèi)ength屬性
var f = function(a, b) {
arguments[0] = 3;
arguments[1] = 2;
return a + b;
}
f(1, 1) // 5
4.5.2與數(shù)組的關(guān)系
數(shù)組專有的方法(比如slice和forEach),不能在arguments對象上直接使用
兩種常用的轉(zhuǎn)換方法:slice方法和逐一填入新數(shù)組。
var args = Array.prototype.slice.call(arguments);
// 或者
var args = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
4.5.3callee屬性
arguments對象帶有一個callee屬性,返回它所對應(yīng)的原函數(shù)。
var f = function () {
console.log(arguments.callee === f);
}
f() // true
可以通過arguments.callee,達(dá)到調(diào)用函數(shù)自身的目的。這個屬性在嚴(yán)格模式里面是禁用的,因此不建議使用。
5.函數(shù)的其他知識點
5.1閉包
定義在函數(shù)內(nèi)的函數(shù)
用處 1.讀取函數(shù)內(nèi)的變量
2.讓這些變量始終保存在內(nèi)存中
function createIncrementor(start) {
return function () {
return start++;
};
}
var inc = createIncrementor(5);
inc() // 5
inc() // 6
inc() // 7
上面代碼中,start是函數(shù)createIncrementor的內(nèi)部變量。通過閉包,start的狀態(tài)被保留了,每一次調(diào)用都是在上一次調(diào)用的基礎(chǔ)上進(jìn)行計算。從中可以看到,閉包inc使得函數(shù)createIncrementor的內(nèi)部環(huán)境,一直存在。所以,閉包可以看作是函數(shù)內(nèi)部作用域的一個接口。
為什么會這樣呢?原因就在于inc始終在內(nèi)存中,而inc的存在依賴于createIncrementor,因此也始終在內(nèi)存中,不會在調(diào)用結(jié)束后,被垃圾回收機制回收。
因為inc是全局的,使得閉包可以一直存在同時訪問內(nèi)部變量
3封裝私有變量
function Person(name) {
var _age;
function setAge(n) {
_age = n;
}
function getAge() {
return _age;
}
return {
name: name, getAge: getAge, setAge: setAge
};
}
var p1 = Person("張三");
p1.setAge(25);
p1.getAge() // 25
上面代碼中,函數(shù)Person的內(nèi)部變量_age,通過閉包getAge和setAge,變成了返回對象p1的私有變量
注意,外層函數(shù)每次運行,都會生成一個新的閉包,而這個閉包又會保留外層函數(shù)的內(nèi)部變量,所以內(nèi)存消耗很大。因此不能濫用閉包,否則會造成網(wǎng)頁的性能問題
5.2立即調(diào)用的函數(shù)表達(dá)式(IIFE)
()是運算符 表達(dá)式
function(){ / code / }();
// SyntaxError: Unexpected token (
產(chǎn)生這個錯誤的原因是,function這個關(guān)鍵字即可以當(dāng)作語句,也可以當(dāng)作表達(dá)式。
如果function關(guān)鍵字出現(xiàn)在行首,一律解釋成語句。因此,JavaScript 引擎看到行首是function關(guān)鍵字之后,認(rèn)為這一段都是函數(shù)的定義,不應(yīng)該以圓括號結(jié)尾
// 語句
function f() {}
// 表達(dá)式
var f = function f() {}
5.2.1最簡單的處理,就是將其放在一個圓括號里面。
(function(){ / code / }());
// 或者
(function(){ / code / })();
上面兩種寫法都是以圓括號開頭,引擎就會認(rèn)為后面跟的是一個表示式,而不是函數(shù)定義語句,所以就避免了錯誤。這就叫做“立即調(diào)用的函數(shù)表達(dá)式
任何讓解釋器以表達(dá)式來處理函數(shù)定義的方法,都能產(chǎn)生同樣的效果,比如下面三種寫法。
var i = function(){ return 10; }();
true && function(){ / code / }();
0, function(){ / code / }();
甚至像下面這樣寫,也是可以的。
!function () { / code / }();
~function () { / code / }();
-function () { / code / }();
+function () { / code / }();
5.2.2目的
1.不必為函數(shù)命名,避免污染全局變量
2.封裝私有變量
// 寫法一
var tmp = newData;
processData(tmp);
storeData(tmp);
// 寫法二
(function () {
var tmp = newData;
processData(tmp);
storeData(tmp);
}());
上面代碼中,寫法二比寫法一更好,因為完全避免了污染全局變量
6eval() 命令
6.1接受一個字符串當(dāng)參數(shù)并當(dāng)語錄執(zhí)行 否則報錯
eval("var a = 1;");
a // 1
eval("3x") // Uncaught SyntaxError: Invalid or unexpected token
6.2
放在eval中的字符串,應(yīng)該有獨自存在的意義,不能用來與eval以外的命令配合使用。舉例來說,下面的代碼將會報錯。
eval("return;"); // Uncaught SyntaxError: Illegal return statement
6.3如果eval的參數(shù)不是字符串,那么會原樣返回。
eval(123) // 123
6.4作用域
var a = 1;
eval("a = 2");
a // 2
上面代碼中,eval命令修改了外部變量a的值。由于這個原因,eval有安全風(fēng)險
如果使用嚴(yán)格模式,(自己聲明的不會影響,上面的問題依然有)eval內(nèi)部聲明的變量,不會影響到外部作用域。
(function f() {
"use strict";
eval("var foo = 123");
console.log(foo); // ReferenceError: foo is not defined
})()
(function f() {
"use strict";
var foo = 1;
eval("foo = 2");
console.log(foo); // 2
})()
eval最常見的場合是解析 JSON 數(shù)據(jù)的字符串,不過正確的做法應(yīng)該是使用原生的JSON.parse方法
6.1基本用法
6.2eval 的別名調(diào)用
1.eval不利于引擎優(yōu)化執(zhí)行速度
2.引擎在靜態(tài)代碼分析的階段,根本無法分辨執(zhí)行
的是eval。
var m = eval;
m("var x = 1");
x // 1
上面代碼中,變量m是eval的別名。靜態(tài)代碼分析階段,引擎分辨不出m("var x = 1")執(zhí)行的是eval命令
為了保證eval的別名不影響代碼優(yōu)化改進(jìn):用別名的話,里面都是全局變量
var a = 1;
function f() {
var a = 2;
var e = eval;
e("console.log(a)");
}
f() // 1
上面代碼中,eval是別名調(diào)用,所以即使它是在函數(shù)中,它的作用域還是全局作用域,因此輸出的a為全局變量。這樣的話,引擎就能確認(rèn)e()不會對當(dāng)前的函數(shù)作用域產(chǎn)生影響,優(yōu)化的時候就可以把這一行排除掉。
eval的別名調(diào)用的形式五花八門,只要不是直接調(diào)用,都屬于別名調(diào)用,因為引擎只能分辨eval()這一種形式是直接調(diào)用。
eval.call(null, "...")
window.eval("...")
(1, eval)("...")
(eval, eval)("...")
上面這些形式都是eval的別名調(diào)用,作用域都是全局作用域。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/105995.html
摘要:詞法作用域的查找規(guī)則是閉包的一部分。因此的確同閉包息息相關(guān),即使本身并不會真的使用閉包。而上面的創(chuàng)建一個閉包,本質(zhì)上這是將一個塊轉(zhuǎn)換成一個可以被關(guān)閉的作用域。結(jié)合塊級作用域與閉包模塊這個模式在中被稱為模塊。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡單易用的語言,又是一門具有許多復(fù)雜微妙技術(shù)的語言,即使是經(jīng)驗豐富的 Jav...
摘要:中函數(shù)是一等公民。小明小明調(diào)用函數(shù)時,傳遞給函數(shù)的值被稱為函數(shù)的實參值傳遞,對應(yīng)位置的函數(shù)參數(shù)名叫作形參。所以不推薦使用構(gòu)造函數(shù)創(chuàng)建函數(shù)因為它需要的函數(shù)體作為字符串可能會阻止一些引擎優(yōu)化也會引起瀏覽器資源回收等問題。 函數(shù) 之前幾節(jié)中圍繞著函數(shù)梳理了 this、原型鏈、作用域鏈、閉包等內(nèi)容,這一節(jié)梳理一下函數(shù)本身的一些特點。 javascript 中函數(shù)是一等公民。 并且函數(shù)也是對象,...
摘要:盡可能的使用局部變量,少用全局變量。正確的實現(xiàn)就是在函數(shù)體內(nèi)部使用將聲明成局部變量。在新特性中,引入了塊級作用域這個概念,因此還可以使用,來聲明局部變量。它們共享外部變量,并且閉包還可以更新的值。 變量作用域 作用域,對于JavaScript語言來說無處不在,變量作用域,函數(shù)作用域(運行時上下文和定義時上下文),作用域污染等等都跟作用域息息相關(guān),掌握J(rèn)avaScript作用于規(guī)則,可以...
閱讀 1259·2021-09-22 15:18
閱讀 2589·2021-09-22 15:17
閱讀 2218·2019-08-30 15:55
閱讀 1567·2019-08-30 15:54
閱讀 1032·2019-08-30 13:12
閱讀 619·2019-08-30 13:12
閱讀 1673·2019-08-29 11:33
閱讀 1433·2019-08-26 17:04