92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线

JavaScript進階教程(4)-函數內this指向解惑call(),apply(),bind()的區別

2020-9-7    前端達人

目錄

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 函數的定義方式

定義函數的方式有三種:

  1. 函數聲明
  2. 函數表達式
  3. new Function(一般不用)

1.1 函數聲明


  1. // 函數的聲明
  2. function fn() {
  3. console.log("我是JS中的一等公民-函數!!!哈哈");
  4. }
  5. fn();

1.2 函數表達式

函數表達式就是將一個匿名函數賦值給一個變量。函數表達式必須先聲明,再調用。


  1. // 函數表達式
  2. var fn = function() {
  3. console.log("我是JS中的一等公民-函數!!!哈哈");
  4. };
  5. fn();

1.3 函數聲明與函數表達式的區別

  1. 函數聲明必須有名字。
  2. 函數聲明會函數提升,在預解析階段就已創建,聲明前后都可以調用。
  3. 函數表達式類似于變量賦值。
  4. 函數表達式可以沒有名字,例如匿名函數。
  5. 函數表達式沒有變量提升,在執行階段創建,必須在表達式執行之后才可以調用。

下面是一個根據條件定義函數的例子:


  1. if (true) {
  2. function f () {
  3. console.log(1)
  4. }
  5. } else {
  6. function f () {
  7. console.log(2)
  8. }
  9. }

以上代碼執行結果在不同瀏覽器中結果不一致。我們可以使用函數表達式解決上面的問題:


  1. var f
  2. if (true) {
  3. f = function () {
  4. console.log(1)
  5. }
  6. } else {
  7. f = function () {
  8. console.log(2)
  9. }
  10. }

函數聲明如果放在if-else的語句中,在IE8的瀏覽器中會出現問題,所以為了更好的兼容性我們以后最好用函數表達式,不用函數聲明的方式。

1.4 構造函數Function(了解即可,一般不用)

在前面的學習中我們了解到函數也是對象。注意:函數是對象,對象不一定是函數,對象中有__proto__原型,函數中有prototype原型,如果一個東西里面有prototype,又有__proto__,說明它是函數,也是對象。


  1. function F1() {}
  2. console.dir(F1); // F1里面有prototype,又有__proto__,說明是函數,也是對象
  3. console.dir(Math); // Math中有__proto__,但是沒有prorotype,說明Math不是函數

對象都是由構造函數創建出來的,函數既然是對象,創建它的構造函數又是什么呢?事實上所有的函數實際上都是由Function構造函數創建出來的實例對象。

所以我們可以使用Function構造函數創建函數。

語法:new Function(arg1,arg2,arg3..,body);
arg是任意參數,字符串類型的。body是函數體。


  1. // 所有的函數實際上都是Function的構造函數創建出來的實例對象
  2. var f1 = new Function("num1", "num2", "return num1+num2");
  3. console.log(f1(10, 20));
  4. console.log(f1.__proto__ == Function.prototype);
  5. // 所以,函數實際上也是對象
  6. console.dir(f1);
  7. console.dir(Function);

2 函數的調用方式

  1. 普通函數
  2. 構造函數
  3. 對象方法

  1. // 普通函數
  2. function f1() {
  3. console.log("我是普通函數");
  4. }
  5. f1();
  6. // 構造函數---通過new 來調用,創建對象
  7. function F1() {
  8. console.log("我是構造函數");
  9. }
  10. var f = new F1();
  11. // 對象的方法
  12. function Person() {
  13. this.play = function() {
  14. console.log("我是對象中的方法");
  15. };
  16. }
  17. var per = new Person();
  18. per.play();

3 函數內 this 的指向

函數的調用方式決定了 this 指向的不同:

調用方式 非嚴格模式 備注
普通函數調用 window 嚴格模式下是 undefined
構造函數調用 實例對象 原型方法中 this 也是實例對象
對象方法調用 該方法所屬對象 緊挨著的對象
事件綁定方法 綁定事件對象  
定時器函數 window  

  1. // 普通函數
  2. function f1() {
  3. console.log(this); // window
  4. }
  5. f1();
  6. // 構造函數
  7. function Person() {
  8. console.log(this); // Person
  9. // 對象的方法
  10. this.sayHi = function() {
  11. console.log(this); // Person
  12. };
  13. }
  14. // 原型中的方法
  15. Person.prototype.eat = function() {
  16. console.log(this); // Person
  17. };
  18. var per = new Person();
  19. console.log(per); // Person
  20. per.sayHi();
  21. per.eat();
  22. // 定時器中的this
  23. setInterval(function() {
  24. console.log(this); // window
  25. }, 1000);

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參數:

  • thisArg
  • argsArray

apply() 與 call() 相似,不同之處在于提供參數的方式。
apply() 使用參數數組而不是一組參數列表。例如:

fun.apply(this, ['eat', 'bananas']) 

4.1.1 新的函數調用方式apply和call方法


  1. function f1(x, y) {
  2. console.log("結果是:" + (x + y) + this);
  3. return "666";
  4. }
  5. f1(10, 20); // 函數的調用
  6. console.log("========");
  7. // apply和call方法也是函數的調用的方式
  8. // 此時的f1實際上是當成對象來使用的,對象可以調用方法
  9. // apply和call方法中如果沒有傳入參數,或者是傳入的是null,那么調用該方法的函數對象中的this就是默認的window
  10. f1.apply(null, [10, 20]);
  11. f1.call(null, 10, 20);
  12. // apply和call都可以讓函數或者方法來調用,傳入參數和函數自己調用的寫法不一樣,但是效果是一樣的
  13. var result1 = f1.apply(null, [10, 20]);
  14. var result2 = f1.call(null, 10, 20);
  15. console.log(result1);
  16. console.log(result2);

4.1.2 apply和call可以改變this的指向


  1. // 通過apply和call改變this的指向
  2. function Person(name, sex) {
  3. this.name = name;
  4. this.sex = sex;
  5. }
  6. //通過原型添加方法
  7. Person.prototype.sayHi = function(x, y) {
  8. console.log("您好啊:" + this.name);
  9. return x + y;
  10. };
  11. var per = new Person("小三", "男");
  12. var r1 = per.sayHi(10, 20);
  13. console.log("==============");
  14. function Student(name, age) {
  15. this.name = name;
  16. this.age = age;
  17. }
  18. var stu = new Student("小舞", 18);
  19. var r2 = per.sayHi.apply(stu, [10, 20]);
  20. var r3 = per.sayHi.call(stu, 10, 20);
  21. console.log(r1);
  22. console.log(r2);
  23. console.log(r3);

4.2 call,apply使用

apply和call都可以改變this的指向。調用函數的時候,改變this的指向:


  1. // 函數的調用,改變this的指向
  2. function f1(x, y) {
  3. console.log((x + y) + ":===>" + this);
  4. return "函數的返回值";
  5. }
  6. //apply和call調用
  7. var r1 = f1.apply(null, [1, 2]); // 此時f1中的this是window
  8. console.log(r1);
  9. var r2 = f1.call(null, 1, 2); // 此時f1中的this是window
  10. console.log(r2);
  11. console.log("=============>");
  12. //改變this的指向
  13. var obj = {
  14. sex: "男"
  15. };
  16. // 本來f1函數是window對象的,但是傳入obj之后,f1的this此時就是obj對象
  17. var r3 = f1.apply(obj, [1, 2]); //此時f1中的this是obj
  18. console.log(r3);
  19. var r4 = f1.call(obj, 1, 2); //此時f1中的this是obj
  20. console.log(r4);


調用方法的時候,改變this的指向:


  1. //方法改變this的指向
  2. function Person(age) {
  3. this.age = age;
  4. }
  5. Person.prototype.sayHi = function(x, y) {
  6. console.log((x + y) + ":====>" + this.age); //當前實例對象
  7. };
  8. function Student(age) {
  9. this.age = age;
  10. }
  11. var per = new Person(10); // Person實例對象
  12. var stu = new Student(100); // Student實例對象
  13. // sayHi方法是per實例對象的
  14. per.sayHi(10, 20);
  15. per.sayHi.apply(stu, [10, 20]);
  16. 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:


  1. function Person(name) {
  2. this.name = name;
  3. }
  4. Person.prototype.play = function() {
  5. console.log(this + "====>" + this.name);
  6. };
  7. function Student(name) {
  8. this.name = name;
  9. }
  10. var per = new Person("人");
  11. var stu = new Student("學生");
  12. per.play();
  13. // 復制了一個新的play方法
  14. var ff = per.play.bind(stu);
  15. ff();

示例2:


  1. //通過對象,調用方法,產生隨機數
  2. function ShowRandom() {
  3. //1-10的隨機數
  4. this.number = parseInt(Math.random() * 10 + 1);
  5. }
  6. //添加原型方法
  7. ShowRandom.prototype.show = function() {
  8. //改變了定時器中的this的指向了
  9. window.setTimeout(function() {
  10. //本來應該是window, 現在是實例對象了
  11. //顯示隨機數
  12. console.log(this.number);
  13. }.bind(this), 1000);
  14. };
  15. //實例對象
  16. var sr = new ShowRandom();
  17. //調用方法,輸出隨機數字
  18. sr.show();

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
    • 函數的名字,name屬性是只讀的,不能修改

  1. function fn(x, y, z) {
  2. console.log(fn.length) // => 形參的個數
  3. console.log(arguments) // 偽數組實參參數集合
  4. console.log(arguments.callee === fn) // 函數本身
  5. console.log(fn.caller) // 函數的調用者
  6. console.log(fn.name) // => 函數的名字
  7. }
  8. function f() {
  9. fn(10, 20, 30)
  10. }
  11. f()

6 高階函數

函數可以作為參數,也可以作為返回值。

6.1 作為參數

函數是可以作為參數使用,函數作為參數的時候,如果是命名函數,那么只傳入命名函數的名字,沒有括號。


  1. function f1(fn) {
  2. console.log("我是函數f1");
  3. fn(); // fn是一個函數
  4. }
  5. //傳入匿名函數
  6. f1(function() {
  7. console.log("我是匿名函數");
  8. });
  9. // 傳入命名函數
  10. function f2() {
  11. console.log("我是函數f2");
  12. }
  13. f1(f2);


作為參數排序案例:


  1. var arr = [1, 100, 20, 200, 40, 50, 120, 10];
  2. //排序---函數作為參數使用,匿名函數作為sort方法的參數使用,此時的匿名函數中有兩個參數,
  3. arr.sort(function(obj1, obj2) {
  4. if (obj1 > obj2) {
  5. return -1;
  6. } else if (obj1 == obj2) {
  7. return 0;
  8. } else {
  9. return 1;
  10. }
  11. });
  12. console.log(arr);

6.2 作為返回值


  1. function f1() {
  2. console.log("函數f1");
  3. return function() {
  4. console.log("我是函數,此時作為返回值使用");
  5. }
  6. }
  7. var ff = f1();
  8. ff();

作為返回值排序案例: 


  1. // 排序,每個文件都有名字,大小,時間,可以按照某個屬性的值進行排序
  2. // 三個文件,文件有名字,大小,創建時間
  3. function File(name, size, time) {
  4. this.name = name; // 名字
  5. this.size = size; // 大小
  6. this.time = time; // 創建時間
  7. }
  8. var f1 = new File("jack.avi", "400M", "1999-12-12");
  9. var f2 = new File("rose.avi", "600M", "2020-12-12");
  10. var f3 = new File("albert.avi", "800M", "2010-12-12");
  11. var arr = [f1, f2, f3];
  12. function fn(attr) {
  13. // 函數作為返回值
  14. return function getSort(obj1, obj2) {
  15. if (obj1[attr] > obj2[attr]) {
  16. return 1;
  17. } else if (obj1[attr] == obj2[attr]) {
  18. return 0;
  19. } else {
  20. return -1;
  21. }
  22. }
  23. }
  24. console.log("按照名字排序:**********");
  25. // 按照名字排序
  26. var ff = fn("name");
  27. // 函數作為參數
  28. arr.sort(ff);
  29. for (var i = 0; i < arr.length; i++) {
  30. console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time);
  31. }
  32. console.log("按照大小排序:**********");
  33. // 按照大小排序
  34. var ff = fn("size");
  35. // 函數作為參數
  36. arr.sort(ff);
  37. for (var i = 0; i < arr.length; i++) {
  38. console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time);
  39. }
  40. console.log("按照創建時間排序:**********");
  41. // 按照創建時間排序
  42. var ff = fn("time");
  43. // 函數作為參數
  44. arr.sort(ff);
  45. for (var i = 0; i < arr.length; i++) {
  46. console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time);
  47. }

日歷

鏈接

個人資料

藍藍設計的小編 http://www.skdbbs.com

存檔

92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线
在线观看亚洲视频| 国产精品久久久久久中文字| 亚洲精品福利在线观看| 精品亚洲一区二区三区在线观看| 中文字幕国产日韩| 久久国产精品影视| 色吧影院999| 久久艳片www.17c.com| 欧美国产第一页| 亚洲毛片在线看| 亚洲国产欧美在线成人app| 亚洲三级免费看| 91精品中文在线| 欧美日韩一区二区在线| 成人激情视频在线观看| 精品视频久久久久久久| 国产亚洲欧美日韩美女| 97精品一区二区三区| 国产成人极品视频| 久久午夜a级毛片| 国产欧美 在线欧美| 亚洲第一av网站| 日韩亚洲第一页| 精品久久久久久国产| 日本道色综合久久影院| 中文字幕免费精品一区高清| 91中文在线观看| 亚洲国产精品久久91精品| 久久九九免费视频| 欧美裸体xxxx极品少妇| 精品久久久久久久久久久久久久| 色av中文字幕一区| 欧美大尺度激情区在线播放| 91精品久久久久| 日韩人体视频一二区| 久久亚洲精品毛片| 国产成人精品视频| 欧美理论电影在线观看| 欧美日韩福利视频| 国产精品久久久久久久美男| 伊人青青综合网站| 亚洲最大成人在线| 久久精品国产96久久久香蕉| 日韩欧美视频一区二区三区| 中文字幕日韩精品有码视频| 91探花福利精品国产自产在线| 成人黄色激情网| 成人网址在线观看| 在线观看精品自拍私拍| 国产成人精品亚洲精品| 国产成人av在线播放| 久久久人成影片一区二区三区观看| 欧美理论电影在线播放| 91精品国产成人www| 国产精品久久久久久久美男| 日韩精品免费综合视频在线播放| 国产精品综合不卡av| 亚洲激情自拍图| 日韩电影中文字幕av| 欧美日韩日本国产| 97久久精品人搡人人玩| 亚洲自拍偷拍色片视频| 亚洲欧美综合另类中字| 亚洲男人的天堂在线| 精品国产91乱高清在线观看| 成人激情免费在线| 日本国产一区二区三区| 国产精品爽爽ⅴa在线观看| 亚洲综合在线播放| 丝袜一区二区三区| 亚洲字幕在线观看| 国内精品视频一区| 久久综合免费视频影院| 国产精品色午夜在线观看| 九九久久精品一区| 亚洲黄一区二区| 伊人亚洲福利一区二区三区| 久久99国产精品久久久久久久久| 69av视频在线播放| 亚洲最新在线视频| 国产精品视频久| 国内精品模特av私拍在线观看| 国产成人自拍视频在线观看| 欧美日韩国产一区中文午夜| 亚洲中国色老太| 日本高清视频精品| 国产精品一二区| 欧洲一区二区视频| 精品亚洲一区二区三区四区五区| 日韩免费观看视频| 日韩女在线观看| 日本高清+成人网在线观看| 精品国产成人在线| 91av免费观看91av精品在线| 亚洲高清免费观看高清完整版| 亚洲欧美日本精品| 亚洲美女动态图120秒| 麻豆成人在线看| 国产欧洲精品视频| 亚洲精品乱码久久久久久金桔影视| 精品国产91久久久| 久久影视电视剧免费网站清宫辞电视| 久久久国产一区二区| 色婷婷综合成人av| 亚洲国产第一页| 97热精品视频官网| 亚洲人精选亚洲人成在线| 欧美另类69精品久久久久9999| 91热精品视频| 国产日本欧美一区二区三区| 91在线高清视频| 日本精品中文字幕| 亚洲国产另类久久精品| 日韩欧美在线网址| 国产精品中文字幕在线观看| 国产日产亚洲精品| 欧美色播在线播放| 欧美丝袜美女中出在线| 国产一区二区三区在线免费观看| 欧美成人精品在线视频| 成人www视频在线观看| 欧美成人免费一级人片100| 欧美色道久久88综合亚洲精品| 北条麻妃一区二区三区中文字幕| xxxxx91麻豆| 黑人巨大精品欧美一区二区免费| 热久久视久久精品18亚洲精品| 精品国产一区二区三区久久久狼| 国产专区精品视频| 亚洲精品720p| 亚洲国产精品久久久| 久久久成人av| 国产欧美日韩中文字幕在线| 色妞在线综合亚洲欧美| 日本韩国欧美精品大片卡二| 91免费看视频.| 懂色av影视一区二区三区| 国产色视频一区| 精品av在线播放| 色悠悠国产精品| 国产99视频精品免视看7| 色噜噜狠狠狠综合曰曰曰| 国产亚洲xxx| 91麻豆国产语对白在线观看| 亚洲欧美精品中文字幕在线| 久久视频在线视频| 欧美黑人性生活视频| 亚洲日本中文字幕免费在线不卡| 欧美性生交xxxxx久久久| 一区二区亚洲精品国产| 91av在线精品| 亚洲精品成a人在线观看| 欧美日韩国产中文精品字幕自在自线| 色琪琪综合男人的天堂aⅴ视频| 日韩精品久久久久久福利| 日韩精品中文字幕在线播放| 欧美成人精品h版在线观看| 日韩av123| 中文字幕久精品免费视频| 国产精品久久久久久久久久小说| 久久精品久久精品亚洲人| 海角国产乱辈乱精品视频| 亚洲国产一区二区三区在线观看| 在线播放日韩欧美|