從零開始學 Web 之 JS 高級(三)apply與call,bind,閉包和沙箱
大家好,這裏是「 從零開始學 Web 系列教程 」,並在下列地址同步更新......
- github:https://github.com/Daotin/Web
- 微信公眾號:Web前端之巔
- 博客園:http://www.cnblogs.com/lvonve/
- CSDN:https://blog.csdn.net/lvonve/
在這裏我會從 Web 前端零基礎開始,一步步學習 Web 相關的知識點,期間也會分享一些好玩的項目。現在就讓我們一起進入 Web 前端學習的冒險之旅吧!
一、apply 和 call 方法
apple 和 call 都可以改變調用其的函數或方法中的 this 指向。
不同的是傳入參數時,apple有兩個參數,第二個參數是數組;call 從第二個參數開始是調用其的函數的所有參數。
使用方法:
1、apply的使用語法:
函數名.apply(對象,[參數1, 參數2,... ]);
方法名.apply(對象,[參數1, 參數2,... ]);
2、call的使用語法:
函數名.call(對象,參數1, 參數2,... );
方法名.call(對象,參數1, 參數2,... );
1、函數調用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
2、方法調用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.
問題:我們知道函數也是對象,函數可以調用 apple 和 call 方法,但是這兩個方法並不在這個函數這個對象的實例函數中,那麽在哪裏呢?
解答:所有的函數都是 Function 的實例對象,而 apply 和 call 就在 Function 構造函數的原型對象中。
二、bind方法
bind 是復制的意思,也可以改變調用其的函數或方法的 this 指向,參數可以在復制的時候傳進去,也可以在復制之後調用的時候傳進去。
使用語法:
1、函數名.bind(對象, 參數1, 參數2, ...); // 返回值是復制的這個函數
2、方法名.bind(對象, 參數1, 參數2, ...); // 返回值是復制的這個方法
1、函數調用 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);
2、方法調用 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
三、閉包
1、閉包的概念
有一個函數 A 中有一個函數或者對象 B,那麽函數或者對象 B 可以訪問函數 A 中的數據,那麽函數 A 的作用域就形成了閉包。
2、閉包的模式
函數模式的閉包:函數中包含函數。
對象模式的閉包:函數中包含對象。
3、閉包的作用
緩存數據,延長作用域鏈。
4、閉包的優缺點
也是緩存的數據,導致在閉包的範圍內一直起作用。
5、閉包的應用
緩存數據,函數中的數據,外面可以使用。
如果想要緩存數據,就把這個數據放在外層的函數和裏層的函數之間。這樣不停的調用裏層函數,相當於外層函數裏的數據沒有得到及時釋放,就相當於緩存了數據。
// 函數閉包
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);
四、沙箱
沙箱:一小塊的真實環境,裏面發生的事情不會影響到外面。相同的操作,相同的數據都不會和外面發生沖突。
作用:避免命名沖突。
比如:自調用函數裏面就相當於一個沙箱環境。
(function (){
}());
五、區分偽數組和真數組
// 真數組
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]);
}
方法一、使用 length 來區分
這樣看起來,真數組和偽數組就沒法區別了。
但是真數組的長度 length 可以改變,偽數組不可以,貌似可以區分了。
但是,你還記得有個 arguement 這個偽數組(對象)的 length 是可以改變的,方法一區分失敗。
方法二、使用數組的方法 forEach 來鑒別
因為每個數組都是 Array 的實例對象,而 forEach 在 Array 的原型對象中,所以其他的偽數組是不能使用的。方法二成功。
從零開始學 Web 之 JS 高級(三)apply與call,bind,閉包和沙箱