js預解析+做用域+this指向

預解析一

var name = 'World!';

(function() {

    if (typeof name === 'undefined') {
      var name = 'Jack';
      console.log('Goodbye ' + name);
    } else {
      console.log('Hello ' + name);
    }
})();
複製代碼

答案:Goodbye Jackwindows

註釋bash

1.代碼執行前,先預解析。函數

2.var變量提高,提高到當前做用域最前面。ui

3.首先給全局的變量name賦值爲'World!',而後匿名函數自調用。this

4.把匿名函數裏的變量name提高到當前做用域最前面。spa

5.匿名函數裏的name聲明但未賦值,因此name爲undefined。typeof name === undefined爲true。prototype

6.因此執行結果爲Goodbye Jack。code

下面是預解析代碼過程對象

var name;

(function() {
    
    var name;   變量聲明未賦值==>undefined
    if (typeof name === 'undefined') {
      name = 'Jack';
      console.log('Goodbye ' + name);
    } else {
      console.log('Hello ' + name);
    }
})();

name = 'World!';
複製代碼

詞法做用域

詞法做用域規則:函數的做用域在聲明的時候就已經決定了,與調用位置無關。原型鏈

var a = 10;
  
function aaa() {

    alert(a);
}
  
function bbb() {

    var a = 20;
    aaa();
}
  
bbb();
複製代碼

答案:10。

分析:

1.預解析,把a的聲明和函數aaa() 和函數bbb() 聲明提高到當前做用域最前面。

2.給全局的a賦值爲10,而後調用bbb()。

3.函數執行前要預解析,將函數bbb裏的變量a提高到當前做用域,而後執行函數bbb。並給a賦值爲20.再調用函數aaa()。

4.由於函數aaa的做用域在聲明的時候就已經在全局了,因此aaa內部訪問變量a的時候,按照做用域鏈的查找規則,找到全局的a。並alert(a)爲10。

下面是預解析代碼過程

var a;

function aaa(){

    alert(a);
}

function bbb(){
    
    var a;
    a = 20;
    aaa();
}

a = 10;

bbb();
複製代碼

做用域

題目一:

var scope = 'global scope';
      
function checkScope() {
        
    var scope = 'local scope';
    
    function f() {

        return scope;
  
    }

    return f;
}

var fn = checkScope();

var a = fn();

console.log(a);
複製代碼

答案:local scope

解析:

1.調用checkScope()函數。並將返回值賦值給fn,在checkScope做用域裏定義了一個變量scope,還有f函數。並把f函數返回賦值給fn。

fn = function() {

    return scope;
  
}
複製代碼

2.調用fn函數,並將返回值賦值給a。因爲fn的函數做用域是在checkScope做用域內部,因此在調用函數fn時,訪問到的scope是checkScope函數做用域內的scope。

3.最終a的值爲local scope

題目二:

var scope = 'global scope';
function checkScope() {
    var scope = 'local scope';
    function f(scope) {
      return scope;
    }
    return f;
}
var fn = checkScope();
var a = fn(123);
console.log(a)
複製代碼

答案:123

解析:

1.調用checkScope函數,並將返回值賦值給fn,在checkScope內部定義了一個scope變量,還有一個f函數。把f函數返回並賦值給fn。

fn = function(scope) {
    return scope;
}
複製代碼

2.調用函數fn,並將返回值賦值給a,因爲fn的函數做用域在checkScope函數內部。因此調用fn函數時,進行參數傳遞。至關於在scope做用域內部聲明var scope = 123。雖然fn函數做用域是在checkScope函數內部。可是fn函數內部就有變量scope,因此訪問到的是自身scope。

3.因此a的值爲123.

題目三

var scope = 'global scope';
function checkScope() {
    var scope = 'local scope';
    function f() {
      scope = 233;
      return scope;
    }
    return f;
}
var fn = checkScope();
var a = fn();
console.log(a);
複製代碼

答案:233

解析:

1.調用函數checkScope,並把返回值賦值給fn。函數checkScope內部定義了一個變量scope,並聲明瞭f函數。將f返回並賦值給fn。

fn = function(){
    scope = 233;
    return scope;
}
複製代碼

2.調用fn函數,並賦值給a。fn函數做用域在checkScope函數內部。因此在調用fn函數時,訪問到的scope是checkScope函數做用域內的scope。

3.因此a的值爲233.

new關鍵字

1.建立了一個空對象

2.讓this指向了這個空對象

3.執行構造函數裏的代碼

4.返回this指向的這個對象

題目一:

function A() {
    console.log('A');
    return 'aaa';
}
var obj1 = new A();
console.log(obj1);
複製代碼

答案:

A     A{}
複製代碼

解析: 若是函數的返回值爲基本數據類型(string,number,boolean,null,undefined) 則JS引擎會忽略該返回值,並建立一個新對象。

題目二

function B() {
    console.log('B');
    return { b: 'bbb' };
}
var obj2 = new B();
console.log(obj2);
複製代碼

答案:

B       { b: 'bbb' }
複製代碼

解析:若是函數的返回值爲引用類型,則實際返回值爲這個引用數據類型。

題目三

function C() {
    console.log('C');
}
var obj3 = new C();
console.log(obj3);
複製代碼

答案:

C       C{}
複製代碼

解析:若是函數沒有返回值,則返回this指向的對象。

題目四

function D() {
    console.log('D');
    return this;
}
var obj4 = new D();
console.log(obj4);
複製代碼

答案:

D    D{}
複製代碼

解析:若是函數返回值是this,那麼與沒有返回值結果是同樣的。

題目五

function obj5() {
    console.log('E');
}
new obj5();
複製代碼

答案:E

解析:由於沒有變量接收這個實例對象。因此new四步操做最後一步不執行。不返回this指向的新對象。因此打印E。

this的指向

1.函數調用模式
2.方法調用模式
3.構造函數調用模式
4.上下文調用模式
複製代碼

題目一

var name = 'windowsName';
function a() {
    var name = 'Cherry';
    console.log(this.name); 
    console.log(this); 
}
a();
複製代碼

答案:

windowsName     window
複製代碼

解析:函數調用模式,this指向window

題目二

var name = 'windowsName';
var a = {
    name: 'Cherry',
    fn: function() {
        console.log(this.name);
    }
};
a.fn();
複製代碼

答案:Cherry

解析:方法調用模式,this指向的a

題目三

var name = 'windowsName';
var a = {
    fn: function() {
      console.log(this.name);
    }
};
a.fn();
複製代碼

答案:undefined 解析: 方法調用模式。this指向的window.a ==> a.name =>undefined

題目三

var name = 'windowsName';
var a = {
    name: 'Cherry',
    fn: function() {
      console.log(name);
      console.log(this.name);
    }
};
var f = a.fn;
f();
複製代碼

答案:windowsName windowsName

解析:函數調用模式。this指向的window。

題目四

var name = 'windowsName';
function fn() {
    var name = 'Cherry';
    innerFunction();
    function innerFunction() {
      console.log(this.name);
    }
}
fn();
複製代碼

答案:windowsName

解析:函數調用模式,this指向的window。

靜態成員和實例成員

什麼是實例?

實例就是由構造函數建立出來的對象

什麼是成員?

成員是屬性和方法的統稱

什麼是實例成員?

由構造函數建立出來的對象能直接訪問的屬性和方法,包括:對象自己,以及原型中的全部屬性和方法。

什麼是靜態成員?

由構造函數直接訪問到的屬性和方法。注意是直接訪問的屬性和方法,間接獲取就不是了。

function Person(name) {
    this.name = name;
}
Person.age = 18;
Person.run = function() {
    console.log('run');
};
Person.prototype.sayHi = function() {
    console.log('hi');
};

var p = new Person('xf');
console.log(p.name);
p.sayHi();

// 實例對象不能夠訪問到構造函數上的成員
console.log(p.age);
// p.run();
console.log(Person.age);
Person.run();
複製代碼

答案:

xf  hi  undefined   18  run     
複製代碼

題目一

function Foo() {
    getName = function() {
      console.log(1);
    };
    console.log(this);
    return this;
}
Foo.getName = function() {
    console.log(2);
};
Foo.prototype.getName = function() {
    console.log(3);
};
var getName = function() {
    console.log(4);
};
function getName() {
    console.log(5);
}

// 請寫出一下輸出結果
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
複製代碼

答案:

2   4   window   1   1   2  Foo{}   3
複製代碼

解析:

先預解析代碼以下:

1.變量和函數聲明提高。併合寫爲表達式。

function Foo() {
    getName = function() {
      console.log(1);
    };
    console.log(this);
    return this;
}
`var getName;
function getName() {  
    console.log(5);
}`
上面的寫下面表達式這種
var getName = function() {  
    console.log(5);
}

Foo.getName = function() {
    console.log(2);
};
Foo.prototype.getName = function() {
    console.log(3);
};
getName = function() {
    console.log(4);
};
複製代碼

這個打印的4會覆蓋上面的表達式5

getName = function() {
    console.log(4);
};
複製代碼

因此最後解析成下面這樣

function Foo() {
    getName = function() {
      console.log(1);
    };
    console.log(this);
    return this;
}
var getName = function() {  
    console.log(4);
}

Foo.getName = function() {
    console.log(2);
};
Foo.prototype.getName = function() {
    console.log(3);
};
複製代碼

開始打印解析:

Foo.getName() ==> 構造函數.方法 =>由於構造函數直接訪問方法和屬性是靜態成員。因此打印的是

Foo.getName = function() {
    console.log(2);
};
複製代碼

getName(); =>

var getName = function() {  
    console.log(4);
}
複製代碼

Foo().getName() ==> 先執行Foo()方法 因此會打印window,而後再調用getName() ,打印1,此處Foo()裏的

getName = function() {
  console.log(1);
};
```會覆蓋外面的

複製代碼

var getName = function() {
console.log(4); }

因此會再打印一個1

此時代碼變成了
function Foo() {
    getName = function() {
      console.log(1);
    };
    console.log(this);
    return this;
}
var getName = function() {  
    console.log(4); 改爲=>console.log(1);
}

Foo.getName = function() {
    console.log(2);
};
Foo.prototype.getName = function() {
    console.log(3);
};


getName(); => 打印1
new Foo.getName(); => new (Foo.getName)()  ==> 2
new Foo().getName(); ==> (new Foo()).getName(); 調用實例的getName()方法,
沿着原型鏈查找。根據屬性查找原則。打印的是3.

複製代碼
相關文章
相關標籤/搜索