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://m.skdbbs.com

存檔

92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线
激情婷婷亚洲| 欧美日韩无遮挡| 久久99精品久久久久| 欧美在线视频全部完| 国产精品久久久久一区二区三区共| 影音先锋亚洲电影| 粉嫩91精品久久久久久久99蜜桃| 蜜臀久久久99精品久久久久久| 免费国产自久久久久三四区久久| 99精品久久只有精品| 精品视频全国免费看| 国产精品成人**免费视频| 国产99久久久国产精品免费看| 精品久久久久99| 亚洲欧美日本国产有色| 国产精品一区二区你懂的| 性欧美亚洲xxxx乳在线观看| 成人v精品蜜桃久久一区| av福利导福航大全在线| 欧美日本二区| 国产精品激情偷乱一区二区∴| 精品日韩一区| freemovies性欧美| 国产欧美日韩综合精品二区| 亚洲青色在线| 亚洲国产精品一区二区久久恐怖片| 精品午夜一区二区| 欧美猛男做受videos| 99久久精品费精品国产风间由美| 日韩在线免费视频| 欧美大片1688| 午夜在线精品| 5月丁香婷婷综合| 国产美女视频一区| 精品成人一区二区三区| 日韩高清人体午夜| 欧美日韩成人精品| 国产成人免费在线| 日韩欧美国产三级| 精品国产3级a| 亚洲精品影院在线观看| rebdb初裸写真在线观看| 捆绑变态av一区二区三区| 亚洲欧美日韩精品久久亚洲区| 欧美一区二区三区四区在线观看| 亚洲全黄一级网站| 免费av在线| 欧美一级免费视频| 伊人久久久久久久久久| 国产欧美日韩在线观看视频| 一区免费视频| 99r国产精品| 影音先锋男人资源在线| 亚洲国产一区二区三区在线| 日韩三区免费| 亚洲天堂免费视频| 欧美日本在线看| 欧美国产高潮xxxx1819| 99国产精品久久久久久久成人热| 8x8ⅹ拨牐拨牐拨牐在线观看| 成黄免费在线| 欧美国产美女| 中文在线播放一区二区| 中文字幕中文字幕中文字幕亚洲无线| 欧美高清视频一区二区三区在线观看| 91在线高清免费观看| 精品国产综合| 日韩三级一区| 欧美婷婷久久| 日韩最新在线| 久久综合久久鬼色| 国内精品模特av私拍在线观看| 91精品高清| h视频在线免费| 91国在线精品国内播放| 欧美综合国产| 1024成人| 激情五月婷婷综合| 欧美日韩中文字幕一区| 国产一区二区三区的电影| 久久99精品久久久久久园产越南| 午夜精品福利一区二区三区av| 偷拍自拍亚洲色图| 在线视频观看日韩| 26uuu亚洲国产精品| 天堂资源在线| 成人免费网站在线| 国产精品黄色片| 一区一区三区| 欧美激情精品在线| 国产黑人绿帽在线第一区| 国产视频久久网| 在线观看福利电影| 色狠狠色狠狠综合| 成人短视频app| 欧美亚洲视频在线观看| 国产一区亚洲一区| 国内成人精品视频| 日韩美女视频一区二区| 日韩aaa久久蜜桃av| 亚洲国产精品久久不卡毛片| 天天综合网天天综合色| 夜夜嗨av一区二区三区| 日本午夜大片a在线观看| 国产精品一区二区三区在线| 欧美日韩电影免费看| 久久亚洲国产精品日日av夜夜| 日韩西西人体444www| 久久青草精品视频免费观看| www.com.cn成人| 国产亚洲美女久久| 影音先锋日韩精品| 久久精品国产精品亚洲精品| 国产精品久久久久久久久久10秀| 国产成人福利片| 国产99精品在线观看| 国产精品久久久久毛片软件| 国产aⅴ精品一区二区四区| 91精品观看| 国产区一区二| 国产午夜精品一区二区三区| 欧美性理论片在线观看片免费| 中文字幕av一区二区三区人| 成人精品在线视频| 国产精品一区免费在线观看| 日本欧美中文字幕| xxx性欧美| 中文字幕日韩欧美精品高清在线| 欧美多人乱p欧美4p久久| 日韩最新av| 欧美成人免费观看| 日韩欧美国产精品一区| 99久久婷婷国产综合精品电影| 欧美一区中文字幕| 亚洲精品电影网站| 日韩五码电影| 国产一区欧美二区三区| 久久99精品久久久久久野外| 成人在线视频www| 久久99久久精品| 日韩av毛片网| 日韩av在线播放资源| 狠狠做六月爱婷婷综合aⅴ| 色婷婷av一区二区三区在线观看| 精品国产污网站| 中文字幕在线播放不卡一区| 亚洲激情国产精品| 在线观看av一区二区| 日韩欧美国产一区二区| 久热国产精品视频| 色综合久久一区二区三区| 国产精品久久久久久久岛一牛影视| 日韩国产在线播放| 日本精品免费一区二区三区| 国产成人亚洲综合色影视| 2021年精品国产福利在线| 亚洲人体大胆视频| 婷婷激情久久| 九九热这里只有在线精品视| 日韩a**中文字幕| 色婷婷精品视频| 久久人体大尺度| 一区二区激情| 天堂一区二区三区|