目錄
1 函數的定義方式
1.1 函數聲明
1.2 函數表達式
1.3 函數聲明與函數表達式的區別
1.4 構造函數Function(了解即可,一般不用)
2 函數的調用方式
3 函數內 this 的指向
4 call、apply、bind
4.1 call,apply
4.1.1 新的函數調用方式apply和call方法
4.1.2 apply和call可以改變this的指向
4.2 call,apply使用
4.3 bind
4.4 總結
5 函數的其它成員(了解)
6 高階函數
6.1 作為參數
6.2 作為返回值
7 總結
1 函數的定義方式
定義函數的方式有三種:
-
函數聲明
-
函數表達式
-
new Function
(一般不用)
1.1 函數聲明
-
-
-
console.log("我是JS中的一等公民-函數!!!哈哈");
-
-
1.2 函數表達式
函數表達式就是將一個匿名函數賦值給一個變量。函數表達式必須先聲明,再調用。
-
-
-
console.log("我是JS中的一等公民-函數!!!哈哈");
-
-
1.3 函數聲明與函數表達式的區別
-
函數聲明必須有名字。
-
函數聲明會函數提升,在預解析階段就已創建,聲明前后都可以調用。
-
函數表達式類似于變量賦值。
-
函數表達式可以沒有名字,例如匿名函數。
-
函數表達式沒有變量提升,在執行階段創建,必須在表達式執行之后才可以調用。
下面是一個根據條件定義函數的例子:
-
-
-
-
-
-
-
-
-
以上代碼執行結果在不同瀏覽器中結果不一致。我們可以使用函數表達式解決上面的問題:
-
-
-
-
-
-
-
-
-
-
-
函數聲明如果放在if-else的語句中,在IE8的瀏覽器中會出現問題,所以為了更好的兼容性我們以后最好用函數表達式,不用函數聲明的方式。
1.4 構造函數Function(了解即可,一般不用)
在前面的學習中我們了解到函數也是對象。注意:函數是對象,對象不一定是函數,對象中有__proto__原型,函數中有prototype原型,如果一個東西里面有prototype,又有__proto__,說明它是函數,也是對象。
-
-
-
-
-
對象都是由構造函數創建出來的,函數既然是對象,創建它的構造函數又是什么呢?事實上所有的函數實際上都是由Function構造函數創建出來的實例對象。
所以我們可以使用Function構造函數創建函數。
語法:new Function(arg1,arg2,arg3..,body);
arg是任意參數,字符串類型的。body是函數體。
-
-
var f1 = new Function("num1", "num2", "return num1+num2");
-
-
console.log(f1.__proto__ == Function.prototype);
-
-
-
-
2 函數的調用方式
-
普通函數
-
構造函數
-
對象方法
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
3 函數內 this
的指向
函數的調用方式決定了 this
指向的不同:
調用方式
|
非嚴格模式
|
備注
|
普通函數調用
|
window
|
嚴格模式下是 undefined
|
構造函數調用
|
實例對象
|
原型方法中 this 也是實例對象
|
對象方法調用
|
該方法所屬對象
|
緊挨著的對象
|
事件綁定方法
|
綁定事件對象
|
|
定時器函數
|
window
|
|
-
-
-
-
-
-
-
-
-
-
-
this.sayHi = function() {
-
-
-
-
-
Person.prototype.eat = function() {
-
-
-
-
-
-
-
-
-
-
-
4 call、apply、bind
了解了函數 this 的指向之后,我們知道在一些情況下我們為了使用某種特定環境的 this 引用,需要采用一些特殊手段來處理,例如我們經常在定時器外部備份 this 引用,然后在定時器函數內部使用外部 this 的引用。
然而實際上 JavaScript 內部已經專門為我們提供了一些函數方法,用來幫我們更優雅的處理函數內部 this 指向問題。這就是接下來我們要學習的 call、apply、bind 三個函數方法。call()、apply()、bind()這三個方法都是是用來改變this的指向的。
4.1 call,apply
call()
方法調用一個函數, 其具有一個指定的 this
值和分別地提供的參數(參數的列表)。
apply()
方法調用一個函數, 其具有一個指定的 this
值,以及作為一個數組(或類似數組的對象)提供的參數。
注意:call()
和 apply()
方法類似,只有一個區別,就是 call()
方法接受的是若干個參數的列表,而 apply()
方法接受的是一個包含多個參數的數組。
call語法:
fun.call(thisArg[, arg1[, arg2[, ...]]])
call參數:
-
thisArg
-
在 fun 函數運行時指定的 this 值
-
如果指定了 null 或者 undefined 則內部 this 指向 window
-
arg1, arg2, ...
apply語法:
fun.apply(thisArg, [argsArray])
apply參數:
apply()
與 call()
相似,不同之處在于提供參數的方式。
apply()
使用參數數組而不是一組參數列表。例如:
fun.apply(this, ['eat', 'bananas'])
4.1.1 新的函數調用方式apply和call方法
-
-
console.log("結果是:" + (x + y) + this);
-
-
-
-
-
-
-
-
-
-
f1.apply(null, [10, 20]);
-
-
-
-
var result1 = f1.apply(null, [10, 20]);
-
var result2 = f1.call(null, 10, 20);
-
-
4.1.2 apply和call可以改變this的指向
-
-
function Person(name, sex) {
-
-
-
-
-
Person.prototype.sayHi = function(x, y) {
-
console.log("您好啊:" + this.name);
-
-
-
var per = new Person("小三", "男");
-
var r1 = per.sayHi(10, 20);
-
-
console.log("==============");
-
-
function Student(name, age) {
-
-
-
-
var stu = new Student("小舞", 18);
-
var r2 = per.sayHi.apply(stu, [10, 20]);
-
var r3 = per.sayHi.call(stu, 10, 20);
-
-
-
-
4.2 call,apply使用
apply和call都可以改變this的指向。調用函數的時候,改變this的指向:
-
-
-
console.log((x + y) + ":===>" + this);
-
-
-
-
var r1 = f1.apply(null, [1, 2]);
-
-
var r2 = f1.call(null, 1, 2);
-
-
console.log("=============>");
-
-
-
-
-
-
var r3 = f1.apply(obj, [1, 2]);
-
-
var r4 = f1.call(obj, 1, 2);
-

調用方法的時候,改變this的指向:
-
-
-
-
-
Person.prototype.sayHi = function(x, y) {
-
console.log((x + y) + ":====>" + this.age);
-
-
-
-
-
-
var per = new Person(10);
-
var stu = new Student(100);
-
-
-
per.sayHi.apply(stu, [10, 20]);
-
per.sayHi.call(stu, 10, 20);
總結
apply的使用語法:
1 函數名字.apply(對象,[參數1,參數2,...]);
2 方法名字.apply(對象,[參數1,參數2,...]);
call的使用語法
1 函數名字.call(對象,參數1,參數2,...);
2 方法名字.call(對象,參數1,參數2,...);
它們的作用都是改變this的指向,不同的地方是參數傳遞的方式不一樣。
如果想使用別的對象的方法,并且希望這個方法是當前對象的,就可以使用apply或者是call方法改變this的指向。
4.3 bind
bind() 函數會創建一個新函數(稱為綁定函數),新函數與被調函數(綁定函數的目標函數)具有相同的函數體(在 ECMAScript 5 規范中內置的call屬性)。當目標函數被調用時 this 值綁定到 bind() 的第一個參數,該參數不能被重寫。綁定函數被調用時,bind() 也可以接受預設的參數提供給原函數。一個綁定函數也能使用new操作符創建對象:這種行為就像把原函數當成構造器。提供的 this 值被忽略,同時調用時的參數被提供給模擬函數。
bind方法是復制的意思,本質是復制一個新函數,參數可以在復制的時候傳進去,也可以在復制之后調用的時候傳入進去。apply和call是調用的時候改變this指向,bind方法,是復制一份的時候,改變了this的指向。
語法:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
參數:
-
thisArg
-
當綁定函數被調用時,該參數會作為原函數運行時的 this 指向。當使用new 操作符調用綁定函數時,該參數無效。
-
arg1, arg2, ...
-
當綁定函數被調用時,這些參數將置于實參之前傳遞給被綁定的方法。
返回值:
返回由指定的this值和初始化參數改造的原函數的拷貝。
示例1:
-
-
-
-
Person.prototype.play = function() {
-
console.log(this + "====>" + this.name);
-
-
-
-
-
-
var per = new Person("人");
-
var stu = new Student("學生");
-
-
-
var ff = per.play.bind(stu);
-
示例2:
-
-
-
-
this.number = parseInt(Math.random() * 10 + 1);
-
-
-
ShowRandom.prototype.show = function() {
-
-
window.setTimeout(function() {
-
-
-
console.log(this.number);
-
-
-
-
var sr = new ShowRandom();
-
-
4.4 總結
-
call 和 apply 特性一樣
-
都是用來調用函數,而且是立即調用
-
但是可以在調用函數的同時,通過第一個參數指定函數內部
this
的指向
-
call 調用的時候,參數必須以參數列表的形式進行傳遞,也就是以逗號分隔的方式依次傳遞即可
-
apply 調用的時候,參數必須是一個數組,然后在執行的時候,會將數組內部的元素一個一個拿出來,與形參一一對應進行傳遞
-
如果第一個參數指定了
null
或者 undefined
則內部 this 指向 window
-
bind
-
可以用來指定內部 this 的指向,然后生成一個改變了 this 指向的新的函數
-
它和 call、apply 最大的區別是:bind 不會調用
-
bind 支持傳遞參數,它的傳參方式比較特殊,一共有兩個位置可以傳遞
-
在 bind 的同時,以參數列表的形式進行傳遞
-
在調用的時候,以參數列表的形式進行傳遞
-
那到底以 bind 的時候傳遞的參數為準呢?還是以調用的時候傳遞的參數為準呢?
-
兩者合并:bind 的時候傳遞的參數和調用的時候傳遞的參數會合并到一起,傳遞到函數內部。
5 函數的其它成員(了解)
-
arguments
-
caller
-
length
-
name
-
-
-
-
console.log(arguments.callee === fn)
-
-
-
-
-
-
-
-
-
6 高階函數
函數可以作為參數,也可以作為返回值。
6.1 作為參數
函數是可以作為參數使用,函數作為參數的時候,如果是命名函數,那么只傳入命名函數的名字,沒有括號。
-
-
-
-
-
-
-
-
-
-
-
-
-
-

作為參數排序案例:
-
var arr = [1, 100, 20, 200, 40, 50, 120, 10];
-
-
arr.sort(function(obj1, obj2) {
-
-
-
} else if (obj1 == obj2) {
-
-
-
-
-
-
6.2 作為返回值
-
-
-
-
console.log("我是函數,此時作為返回值使用");
-
-
-
-
-
-
作為返回值排序案例:
-
-
-
function File(name, size, time) {
-
-
-
-
-
var f1 = new File("jack.avi", "400M", "1999-12-12");
-
var f2 = new File("rose.avi", "600M", "2020-12-12");
-
var f3 = new File("albert.avi", "800M", "2010-12-12");
-
-
-
-
-
return function getSort(obj1, obj2) {
-
if (obj1[attr] > obj2[attr]) {
-
-
} else if (obj1[attr] == obj2[attr]) {
-
-
-
-
-
-
-
console.log("按照名字排序:**********");
-
-
-
-
-
for (var i = 0; i < arr.length; i++) {
-
console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time);
-
-
-
console.log("按照大小排序:**********");
-
-
-
-
-
for (var i = 0; i < arr.length; i++) {
-
console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time);
-
-
-
console.log("按照創建時間排序:**********");
-
-
-
-
-
for (var i = 0; i < arr.length; i++) {
-
console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time);
-
-