前端基礎(三):函式

大司馬愛學習發表於2018-06-21

字數:1685

閱讀時間:5分鐘

函式定義

在最新的ES規範中,宣告函式有4中方法:

-函式宣告

-函式表示式

-建構函式Function

-生成器函式

1.函式宣告

語法:

function name([param[, param2 [...]]]){

	[statements]

}
複製程式碼

name:函式名稱

param:需要傳遞給函式的引數的名稱。有最大引數數量限制,不同引擎限制不同。

statements:包含函式體的語句

函式的最基本用法,注意會有宣告提升機制。

2.函式表示式

語法:

var name = function(param [,param [...]]){

        [statements]

}

或

let name = function(param [,param [...]]){

        [statements]

}

複製程式碼

用法同函式宣告,使用let時,是塊級作用域。

如下程式碼,我們就能看出函式宣告與函式表示式的差異:

testFun(); //testFun
function testFun () {
    console.log('testFun');
}

console.log(typeof testFun1); //undefined
var testFun1 = function () {
    console.log('testFun1');
};
複製程式碼

以第一個是函式申明提升,所以我們可以在宣告之前使用函式。第二個是var變數宣告提升,在宣告之前它的值是undefined,所以無法直接使用。

3.Funcion建構函式

語法:

new Function([arg1 [ ,arg2 [,arg3 [...]]] ,] functionBody);
複製程式碼

引數:

arg1, arg2, ... argN

​ 函式使用的引數列表

functionBody

​ 一個包含函式定義的語句字串

JS中,所有的函式都是Function建構函式的例項。Object內建物件實質也是一個函式,所以,Object也是繼承自Function的。而且,Function本身也是一個函式,因此,它自己也繼承自己。

Function.prototype === Function.__proto__;	//true
複製程式碼

使用這種方法建立函式有以下三點缺點:

1.使用Function建立的函式只有在執行到 new Function()語句時,才會解析函式。上面兩種方式建立的函式,是與JS程式碼一起解析的。所以,使用Function函式建立的函式效率更低。

2.函式都是在全域性作用域中建立的,無法使用建立時的上下文作用域。

let msg = 'testFun3 global';
function testFun3 () {
    let msg = 'testFun3 inner';
    let testFun4 = new Function('console.log(msg);');
    testFun4();
}
testFun3(); //testFun3 global
複製程式碼

3.程式碼書寫在字串中,不利於維護。

所以,儘量不要使用該方式定義函式。

4.function*

語法:

function* name([param[, param[, ... param]]]) { statements }
複製程式碼

引數同 1

定義一個生成器函式。

基礎用法:

function * generator (i) {
    yield i;
    yield i + 1;
    let y = yield i + 2;
    yield y;
}
let gen1 = generator(0);
console.log(gen1.next()); //{value: 0, done: false}
console.log(gen1.next()); //{value: 1, done: false}
console.log(gen1.next()); //{value: 2, done: false}
console.log(gen1.next(100)); //{value: 100, done: false}
console.log(gen1.next()); //{value: undefined, done: true}
複製程式碼

呼叫next函式時,傳入的值是賦予上一個yield的返回值。

有瀏覽器相容性問題,IE所有版本都不支援該用法。

函式呼叫

###直接呼叫

語法:

funName([arg1 [,arg2 , [arg3 [...]]]]);
複製程式碼

最常用方式,不予贅述。

apply

語法:

func.apply(thisArg [ ,argsArray])
複製程式碼

引數:

thisArg

​ 可選引數,將引數作為函式執行的this值使用。

​ 如果處於非嚴格模式下,傳值為null或者undefined時,就將全域性物件繫結到函式的this上。如果傳值為原始值(數字、字串、布林),則會將this指向原始值的包裝物件(Number、String、Boolean)

argsArray

​ 可選引數,傳入一個陣列或者類陣列的值,作為函式的引數列表。

一般情況下,我們直接呼叫函式即可,在一些特殊的應用場景下,我們才會需要用到apply函式。例:

let Obj = {
    name: 'obj',
    print: function () {
        console.log(this.name);
    }
};
function success (callback) {
    this.name = 'success obj';
    callback(); 	//success obj
    callback.apply(Obj); 	//obj
}
success(Obj.print);
複製程式碼

如上,如果我們將函式作為引數傳遞時,為了保證this的指向,就可能需要使用apply方法。

call

語法:

fun.call(thisArg, arg1, arg2, ...)
複製程式碼

用法同apply,只是引數陣列改為了引數列表。

作為建構函式呼叫

語法:

new constructor[([args])]
複製程式碼

引數:

constructor

​ 一個類或者函式。

args

​ 類或者函式的引數列表,new constructor 等同於 new constructor().

當使用new Foo(...)來呼叫一個函式時,其實發生了如下三件事。

①建立一個繼承自Foo.prototype的新物件。

②呼叫Foo函式,並將新建的物件作為this繫結到當前函式的執行上下文中。

③如果函式有返回值,就返回該值;否則,直接返回第一步建立的物件。

舉個栗子:

function Foo (name) {
    this.name = name;
}

var foo1 = new Foo('Tom');
var foo2 = Object.create(Foo.prototype);
Foo.call(foo2, 'Tom');
console.log(foo1 instanceof Foo); //true
console.log(foo2 instanceof Foo); //true
複製程式碼

物件方法呼叫

語法:

obj.foo([args])

var pFoo = obj.foo;
pFoo([args]);
複製程式碼

這種方式就是物件的屬性值為函式的時候,我們呼叫函式的情況。

用法基本和直接呼叫一致,其實通過第一種方式呼叫,實質就是在全域性物件上宣告瞭一個函式變數,然後通過全域性物件來呼叫這個函式,所以他們的表象和使用方法類似。

但是這裡,我們需要注意一點,就是我們通過物件屬性呼叫和宣告一個變數來獲取函式值而後呼叫有使用上的區別。

舉個栗子:

var obj = {
    foo: function (name) {
        this.name = name;
        console.log(this);
    }
};

obj.foo('Tom'); //obj
var pFoo = obj.foo;
pFoo(); //window
複製程式碼

如上栗子,直接呼叫,this指向的是 obj 物件,但如果通過變數賦值後,this指向的是當前函式呼叫語句的上下文環境,這裡我是瀏覽器環境,並且在頂層作用域中執行,所以this指向的是window物件。

總結

函式定義有函式宣告、函式表示式、Function建構函式、function*四種方式。前面兩種最常用,但是要注意this的指向問題(由於函式宣告的作用域問題,我建議儘量使用函式表示式)。Function建構函式官方推薦不要使用,funtion*生成器函式是ES2015出現的新特性,使用時需要注意相容性。

函式呼叫有函式直接呼叫、apply、call、作為建構函式呼叫、物件方法呼叫。他們各有針對的使用場景,需要注意的是物件方法呼叫中的this指向問題。

歡迎關注我的微信公眾號:

前端基礎(三):函式

相關文章