從零開始學 Web 之 JS 高級(三)apply與call,bind,閉包和沙箱

你們好,這裏是「 從零開始學 Web 系列教程 」,並在下列地址同步更新......前端

在這裏我會從 Web 前端零基礎開始,一步步學習 Web 相關的知識點,期間也會分享一些好玩的項目。如今就讓咱們一塊兒進入 Web 前端學習的冒險之旅吧!git

1、apply 和 call 方法

apply 和 call 均可以改變調用其的函數或方法中的 this 指向。github

不一樣的是傳入參數時,apply 有兩個參數,第二個參數是數組;call 從第二個參數開始是調用其的函數的全部參數。數組

使用方法:緩存

一、apply的使用語法:微信

函數名.apply(對象,[參數1, 參數2,... ]);閉包

方法名.apply(對象,[參數1, 參數2,... ]);app

二、call的使用語法:函數

函數名.call(對象,參數1, 參數2,... );學習

方法名.call(對象,參數1, 參數2,... );

一、函數調用apply和call

function f1(x, y) {
  console.log(x+y +this); // 這裏面的this是window
  return x+y;
}

var r1 = f1.apply(null, [10,20]); // 打印30 window,傳入的是null,因此this指向仍是window
console.log(r1); // 30
var r2 = f1.call(null, 10,20);// 打印30 window
console.log(r2); // 30
//函數改變 this 的指向
var obj = {};

var r1 = f1.apply(obj, [10,20]); // 打印30 window,傳入的是Obj,因此this指向是Obj
console.log(r1); // 30
var r2 = f1.call(obj, 10,20);// 打印30 Obj
console.log(r2); // 30

二、方法調用apply和call

// 方法改變 this 的指向
    function Person(age) {
        this.age = age;
    }
    Person.prototype.eat = function () {
        console.log(this.age); // this 指向實例對象
    };

    function Student(age) {
        this.age = age;
    }

    var per = new Person(18);
    var stu = new Student(20);

    per.eat.apply(stu); // 打印 20
    per.eat.call(stu); // 打印 20

因爲 eat 方法已經指向了 Student 了,因此打印 20,而不是 18.

問題:咱們知道函數也是對象,函數能夠調用 apply 和 call 方法,可是這兩個方法並不在這個函數這個對象的實例函數中,那麼在哪裏呢?

解答:全部的函數都是 Function 的實例對象,而 apply 和 call 就在 Function 構造函數的原型對象中。


2、bind方法

bind 是複製的意思,也能夠改變調用其的函數或方法的 this 指向,參數能夠在複製的時候傳進去,也能夠在複製以後調用的時候傳進去。

使用語法:

一、函數名.bind(對象, 參數1, 參數2, ...); // 返回值是複製的這個函數

二、方法名.bind(對象, 參數1, 參數2, ...); // 返回值是複製的這個方法

一、函數調用 bind

function f1(x, y) {
    console.log(x + y + this);
}
// 1.參數在複製的時候傳入
var ff = f1.bind(null,10,20); // 這只是複製的一份函數,不是調用,返回值纔是
ff();

// 2.參數在調用的時候傳入
var ff = f1.bind(null); // 這只是複製的一份函數,不是調用,返回值纔是
ff(10,20);

二、方法調用 bind

function Person(age) {
    this.age = age;
}

Person.prototype.eat = function () {
    console.log(this.age); // this 指向實例對象
};

function Student(age) {
    this.age = age;
}

var per = new Person(18);
var stu = new Student(20);

var ff = per.eat.bind(stu);
ff(); // 20

bind和call,apply的區別:[update:2018.07.26]

bind綁定this的指向以後,不會當即調用當前函數,而是將函數返回。
而call,apply綁定this指向後會當即調用。

若是咱們在不知道何時會調用函數的時候,須要改變this的指向,那麼只能使用bind。

好比:在定時器中,咱們想改變this的指向,可是又不能當即執行,須要等待2秒,這個時候只能使用bind來綁定this。

setInterval(function(){
    // ...
}.bind(this), 2000);

3、閉包

一、閉包的概念

有一個函數 A 中有一個函數或者對象 B,那麼函數或者對象 B 能夠訪問函數 A 中的數據,那麼函數 A 的做用域就造成了閉包。

二、閉包的模式

函數模式的閉包:函數中包含函數。

對象模式的閉包:函數中包含對象。

三、閉包的做用

緩存數據,延長做用域鏈。

四、閉包的優缺點

也是緩存的數據,致使在閉包的範圍內一直起做用。

五、閉包的應用

緩存數據,函數中的數據,外面可使用。

若是想要緩存數據,就把這個數據放在外層的函數和裏層的函數之間。這樣不停的調用裏層函數,至關於外層函數裏的數據沒有獲得及時釋放,就至關於緩存了數據。

// 函數閉包
function A() {
    var num = 10;
    return function () {
        return num++;
    }
}

var func = A();
console.log(func());
console.log(func());
console.log(func());
// 對象閉包
function A() {
    var num = 10;
    return {
        age: num++
    };
}
var func = A();
console.log(func.age);

4、沙箱

沙箱:一小塊的真實環境,裏面發生的事情不會影響到外面。相同的操做,相同的數據都不會和外面發生衝突。

做用:避免命名衝突。

好比:自調用函數裏面就至關於一個沙箱環境。

(function (){
        
}());

5、區分僞數組和真數組

// 真數組
    var arr = [10,20,30];
    // 僞數組
    var obj = {
        0:10,
        1:20,
        2:30,
        length: 3
    };
    // 真數組的訪問
    for(var i=0; i<arr.length; i++) {
        console.log("真數組的訪問:"+arr[i]);
    }
    // 僞數組的訪問
    for(var j=0; j<obj.length; j++) { // 錯誤:對象中沒有length方法
        console.log("僞數組的訪問:"+obj[j]);
    }

方法1、使用 length 來區分

這樣看起來,真數組和僞數組就無法區別了。

可是真數組的長度 length 能夠改變,僞數組不能夠,貌似能夠區分了。

可是,你還記得有個 arguement 這個僞數組(對象)的 length 是能夠改變的,方法一區分失敗。

方法2、使用數組的方法 forEach 來鑑別

由於每一個數組都是 Array 的實例對象,而 forEach 在 Array 的原型對象中,因此其餘的僞數組是不能使用的。方法二成功。

相關文章
相關標籤/搜索