1. 題目
說一下對變量提升的理解
說明this的幾種不同使用場景
創建10個a標籤,點擊的時候彈出來相應的序號
如何理解作用域
實際開發中閉包的應用
2. 知識點
2.1 執行上下文
範圍:一段script或者一個函數
全局:變量定義、函數聲明 script
函數:變量定義、函數聲明、this、arguments (執行之前)
函數聲明和函數表達式的區別:
a(); //報錯 函數表達式 變量聲明 會提前。
var a = function(){}
b(); // 不報錯 函數聲明
function b(){}
變量定義時會默認把他的變量聲明提升:(僅限於他的執行上下文,比如一段script和一個函數中)
console.log(a);
var a = 0;
實際上是
var a;
console.log(a);
a = 0;
2.2 this
this要在執行時才能確認,定義時無法確認。
var a = {
name:'a',
fn:function(){
console.log(this.name);
}
}
a.fn(); // a
a.fn.apply({name:'b'}); // b a.fn.call({name:'b'});
var fn1 = a.fn();
fn1(); // undefined
this的使用場景
構造函數中(指向構造的對象)
function Fun(name){
this.name = name;
}
var f = new Fun('a');
console.log(f.name);
對象屬性中(指向該對象)
普通函數中(指向window)
call apply bind
var fun = function (name){
console.log(this);
console.log(name);
}.bind({a:1});
fun("name");
arguments中的this:
var length = 10;
function fn(){
alert(this.length)
}
var obj = {
length: 5,
method: function(fn) {
arguments[0]()
}
}
obj.method(fn)//輸出1
這裏沒有輸出5,也沒有輸出10,反而輸出了1,有趣。這裏arguments是javascript的一個內置對象(可以參見mdn:arguments - JavaScript),是一個類數組(就是長的比較像數組,但是欠缺一些數組的方法,可以用slice.call轉換,具體參見上面的鏈接),其存儲的是函數的參數。也就是說,這裏arguments[0]指代的就是你method函數的第一個參數:fn,所以arguments[0]()的意思就是:fn()。
不過這裏有個疑問,爲何這裏沒有輸出5呢?我method裏面用this,不應該指向obj麼,至少也會輸出10呀,這個1是鬧哪樣?
實際上,這個1就是arguments.length,也就是本函數參數的個數。爲啥這裏的this指向了arguments呢?因爲在Javascript裏,數組只不過使用數字做屬性名的方法,也就是說:arguments[0]()的意思,和arguments.0()的意思差不多(當然這麼寫是不允許的),你更可以這麼理解:
arguments = {
0: fn, //也就是 functon() {alert(this.length)}
1: 第二個參數, //沒有
2: 第三個參數, //沒有
...,
length: 1 //只有一個參數
}
所以這裏alert出來的結果是1。
如果要輸出5應該咋寫呢?直接 method: fn 就行了。
2.3 作用域
沒有塊級作用域
if(true){
var name = "test"
}
console.log(name);
儘量不要在塊中聲明變量。
只有函數級作用域
2.4 作用域鏈
自由變量 當前作用域沒有定義的變量 即爲自由變量。
自由變量會去其父級作用域找。是定義時的父級作用域,而不是執行。
var a = 100;
function f1(){
var b = 200;
function f2(){
var c = 300;
console.log(a); //自由變量
console.log(b); //自由變量
console.log(c);
}
f2();
};
f1();
2.5 閉包
一個函數中嵌套另外一個函數,並且將這個函數return出去,然後將這個return出來的函數保存到了一個變量中,那麼就創建了一個閉包。
閉包的兩個使用場景
1.函數作爲返回值
function fun(){
var a = 0;
return function(){
console.log(a); //自由變量,去定義時的父級作用域找
}
}
var f1 = fun();
a = 1000;
f1();
2.函數作爲參數
function fun(){
var a = 0;
return function(){
console.log(a); //自由變量,去定義時的父級作用域找
}
}
function fun2(f2){
a = 10000
f2();
}
var f1 = fun();
fun2(f1);
具體解釋看 高級-閉包中的說明
閉包的兩個作用:
能夠讀取其他函數內部變量的函數
可以讓函數內部的變量一直保存在內存中
實際應用場景1:
閉包可以將一些不希望暴露在全局的變量封裝成“私有變量”。
假如有一個計算乘積的函數,mult函數接收一些number類型的參數,並返回乘積結果。爲了提高函數性能,我們增加緩存機制,將之前計算過的結果緩存起來,下次遇到同樣的參數,就可以直接返回結果,而不需要參與運算。這裏,存放緩存結果的變量不需要暴露給外界,並且需要在函數運行結束後,仍然保存,所以可以採用閉包。
上代碼:
function calculate(param){
var cache = {};
return function(){
if(!cache.parame){
return cache.param;
}else{
//緩存計算....
//cache.param = result
//下次訪問直接取
}
}
}
實際應用場景2
延續局部變量的壽命
img 對象經常用於進行數據上報,如下所示:
var report = function( src ){
var img = new Image();
img.src = src;
};
report( 'http://xxx.com/getUserInfo' );
但是通過查詢後臺的記錄我們得知,因爲一些低版本瀏覽器的實現存在 bug,在這些瀏覽器
下使用 report 函數進行數據上報會丟失 30%左右的數據,也就是說, report 函數並不是每一次
都成功發起了 HTTP 請求。
丟失數據的原因是 img 是 report 函數中的局部變量,當 report 函數的
調用結束後, img 局部變量隨即被銷燬,而此時或許還沒來得及發出 HTTP 請求,所以此次請求
就會丟失掉。
現在我們把 img 變量用閉包封閉起來,便能解決請求丟失的問題:
var report = (function(){
var imgs = [];
return function( src ){
var img = new Image();
imgs.push( img );
img.src = src;
}
})();
閉包缺點:浪費資源!
3. 題目解答
3.1 說一下對變量提升的理解
變量定義和函數聲明
注意函數聲明和函數表達式的區別
變量定義時會默認把他的變量聲明提升:(僅限於他的執行上下文,比如一段script和一個函數中)
console.log(a);
var a = 0;
實際上是
var a;
console.log(a);
a = 0;
3.2 說明this的幾種不同使用場景
- 構造函數中(指向構造的對象)
- 對象屬性中(指向該對象)
- 普通函數中(指向window)
- call apply bind
3.3 創建10個a標籤,點擊的時候彈出來相應的序號
實現方法1:用let聲明i
var body = document.body;
console.log(body);
for (let i = 0; i < 10; i++) {
let obj = document.createElement('i');
obj.innerHTML = i + '<br>';
body.appendChild(obj);
obj.addEventListener('click',function(){
alert(i);
})
}
實現方法2 包裝作用域
var body = document.body;
console.log(body);
for (var i = 0; i < 10; i++) {
(function (i) {
var obj = document.createElement('i');
obj.innerHTML = i + '<br>';
body.appendChild(obj);
obj.addEventListener('click', function () {
alert(i);
})
})(i)
}
3.4 實際開發中閉包的應用
能夠讀取其他函數內部變量的函數
可以讓函數內部的變量一直保存在內存中
封裝變量,權限收斂
應用1
var report = (function(){
var imgs = [];
return function( src ){
var img = new Image();
imgs.push( img );
img.src = src;
}
})();
用於防止變量銷燬。
應用2
function isFirstLoad() {
var arr = [];
return function (str) {
if (arr.indexOf(str) >= 0) {
console.log(false);
} else {
arr.push(str);
console.log(true);
}
}
}
var fun = isFirstLoad();
fun(10);
fun(10);
將arr封裝在函數內部,禁止隨意修改,防止變量銷燬。