前端點滴(JS基礎)(一)----傾盡所有

前端點滴(JS基礎)(一)----傾盡所有

js基礎

一、js的組成

ECMAScript(ES) : 負責翻譯,爲js的核心,解釋器。

DOM (Document Object Model 文檔對象模型):賦予js操作HTML的能力,document。

BOM (Browser Object Modal 瀏覽器對象模型):賦予js操作瀏覽器的能力,window,不兼容不建議使用。

二、js放置位置

js放置的位置可以在head中,body中,body外,js文件中。
但是需要考慮js阻塞問題:
除了一些在頁面執行前必須加載的js以外,大多數js應該放置在body標籤內的尾部。這主要是因爲js會阻塞其後內容的加載和呈現,導致白屏。此外,js的執行過程可能作用於dom tree內的元素,如果放置在前面會報錯。
爲了避免js阻塞其後內容,可以根據具體需求使用參數async或defer,使JS異步加載。
async,加載和渲染後續元素的過程將和js文件的加載與執行並行進行,async的js元素下載完後立刻執行,不同js的先後執行順序很可能與頁面中的出現次序不同。
defer,加載後續文檔元素的過程將和js的加載並行進行,但js的執行要等到所有元素解析完成之後,DOMContentLoaded事件觸發之前完成。

三、js輸出方式

1、document.write(輸出的內容); //這種輸出的內容會顯示在瀏覽器頁面上
2、alert(輸出的內容); //這種方法輸出的內容會以提示框的形式顯示
3、console.log(輸出的內容); //輸出的內容會顯示在瀏覽器的控制檯。(推薦使用)。

四、js語句與表達式

JavaScript程序的執行單位爲行(line),也就是一行一行地執行。一般情況下,每一行就是一個語句。
JS語言中,一條語句結束,可以不用寫分號,直接用回車即可。但是建議每條語句後面都加分號。

五、js變量

1. 什麼是變量

  • 什麼是變量
    變量是計算機內存中存儲數據的標識符,根據變量名稱可以獲取到內存中存儲的數據
  • 爲什麼要使用變量
    使用變量可以方便的獲取或者修改內存中的數據
  • 變量分爲
    全局變量,局部變量(函數中)
    注意:修改全局變量可以不用再次聲明
    例如:
var a = 1;
a = 2;// var a = 2;
console.log(a);   //=> 2
  • 變量的生命週期
    JavaScript 變量的生命期從它們被聲明的時間開始。
    局部變量會在函數運行以後被刪除。
    全局變量會在頁面關閉後被刪除。

2. 如何使用變量

(1)變量的聲明

var、let(es6)、const

var:最常用的聲明變量關鍵字。 定義的變量的時候,若沒有初始化,不報錯,會輸出undefined。其值之後可以修改。
var可以用來聲明全局變量,也可以聲明局部變量,依據它們聲明的位置:

  • 全局變量:在函數外定義的變量;(注意:若沒有使用關鍵字聲明的變量,默認爲全局變量。)作用域是整個代碼文件。
  • 局部變量:在函數內定義的變量。作用域是當前的函數內部。

let:塊級作用域 。在塊級{}裏面用let定義的變量,離開當前的塊{}之後,就不能使用(有點像局部變量,但作用域不一樣)。
注意:{…}一對花括弧就就是一個特定的代碼塊,包括直接的{},流程語句的{},函數的{},循環的{}…。函數聲明時本身就帶有{},也是屬於一個代碼塊。

const:用於聲明常量。注意:定義的變量的時候,必須同時初始化,且其值之後不可以修改。

var、let 聲明變量的區別

var 聲明變量,具有全局作用域

四大特性:
1.全局作用域
2.同級作用域下,可以存在多個var 聲明的變量
3.會發生變量提升
4.設置i僅爲循環數組,但循環後,殘留一個變量i。

let 塊級變量,聲明的對象具有塊級作用域

四大特性:
1.塊級作用域:{}下起作用
2.同級作用域下,只能存在一個(let聲明的變量不能重複聲明)
3.不能變量提升
4.let 在循環中經常被使用(重點),i變量只在for()內有效,不污染其他區域

const 常量聲明

特性:常量的作用域,是常量所在的塊。(塊可以理解爲大括號)
在這裏插入圖片描述

(2)變量的賦值

這裏的值可以是null,string類型的值,number類型的值,boolean類型的值,object(Function、Array、Object)…
也可以不賦值,當輸出後會顯示undefined
在這裏插入圖片描述

3. 變量在內存中的形式

let a1 = 0; // 棧內存
let a2 = "this is string" // 棧內存
let a3 = null; // 棧內存
let b = { x: 10 }; // 變量b存在於棧中,{ x: 10 }作爲對象存在於堆中
let c = [1, 2, 3]; // 變量c存在於棧中,[1, 2, 3]作爲對象存在於堆中

在這裏插入圖片描述
當我們要訪問堆內存中的引用數據類型時

  1. 從棧中獲取該對象的地址引用
  2. 再從堆內存中取得我們需要的數據

詳細請參考:https://www.jianshu.com/p/80bb5a01857a

4. 變量相關問題

(1)只聲明變量沒有賦初值

在這裏插入圖片描述

(2)重新聲明變量

在這裏插入圖片描述

(3)變量提升

在這裏插入圖片描述
在同一個作用域{}中,都會發生變量提升,比如函數中。
在這裏插入圖片描述

六、js數據類型

ECMAScript變量可能包含兩種不同數據類型的值:基本類型值和引用類型值。基本類型值指的是簡單的數據段,而引用類型值指那些可能由多個值構成的對象。

1. 原始類型和引用類型

基本類型和引用類型這裏可稱爲: 原始類型(值類型)和對象類型(引用數據類型)

原始類型、值類型、基本類型:
null: 只包含一個值: null
undefined: 只包含一個值:undefined
boolean: 包含兩個值: true和false
number:整數或浮點數,還有一些特殊值(-Infinity、+Infinity、NaN)
string: 一串標識文本值的字符序列
symbol: 一種實例是唯一且不可改變的數據類型(es6新增)

對象類型、引用數據類型:
Object: 除了常用的Object, Array,Function等都屬於特殊的對象

繼續細分則是:
5種基本類型:null,undefined,boolean,number,string
1種複雜類型:object
5種引用數據類型:Array,Object,Function,Date,RegExp
3種基本包裝類型:boolean,number,string
2種單體內置對象:global(全局屬性和函數),math

它們可以使用typeof、instanceof 來查看變量類型,一個變量應只存一個類型的數據。

<script type="text/javascript">
console.log(typeof 1)		//=> number
console.log(typeof "1")		//=> string		
console.log(typeof true)	//=> boolean
console.log(typeof {})		//=> object
console.log(typeof [])		//=> object
console.log(typeof function(){})		//=> function
console.log(typeof Symbol(1))		//=> symbol
var a;
console.log(typeof a);		//=> undefined
console.log(typeof undefined)		//=> undefined
console.log(typeof null)		//=> object
</script>

爲了徹底分清數據類型,封裝一個判斷:

var a = 123;
var b = 3.14;
var c = 'hello';
var d = true;
var e;
var f = null;
var g = [];
var h = {};
function m(){}

function panduan(x){
        if(typeof(x) != 'object')
        {
            return typeof(x);
        }
        else
        {
            if(x instanceof Array)
            {
                return 'array';
            }else if(x instanceof Object){
                return 'object';
            }else{
                return 'null';
            }
        }
    }
console.log(panduan(a));  //=> number
console.log(panduan(b));  //=> number
console.log(panduan(c));  //=> string
console.log(panduan(d));  //=> boolean
console.log(panduan(e));  //=> undefined
console.log(panduan(f));  //=> null
console.log(panduan(g));  //=> array
console.log(panduan(h));  //=> object
console.log(panduan(m));  //=> function

引出問題:
1、console.log(null instanceof Object); //=> false
2、console.log(typeof null); //=> object (使用typeof判斷得來)
3、console.log(panduan(f)); //=> null (使用instanceof判斷得來)

爲什麼會矛盾?????
看似二者有些矛盾,但null返回object這個其實是最初JavaScript的實現的一個錯誤,然後被ECMAScript沿用了,成爲了現在的標準,不過我們把null可以理解爲尚未存在的對象的佔位符,這樣就不矛盾了 。對於Null類型的值(只有null),規範就是定義返回"object"這個字符串。但是本質上Null和Object不是一個數據類型,null值並不是以Object爲原型創建出來的。所以null instanceof Object是false。但從這裏也可以看到,null確實是javascript中用來表示空引用的一個特殊值。使得它不是instanceof Ojbect,而typeof null是“object”。在語義上也是可以理解的。

個人理解(道家學說):
無物不屬於萬物,卻似於萬物。

詳細可以參考:
https://blog.csdn.net/weixin_42049636/article/details/81565461
https://www.jianshu.com/p/659cbcdcebfd

2. 原始類型和引用類型的區分

引用類型指對象,指的是狹義上的對象。數組不應該算引用類型。
區分是否是引用類型,關鍵看內存圖。
對象在內存的棧區只保存它的地址,在堆區保存它的實際內容,這種類型的變量就是引用類型。
在這裏插入圖片描述
引用類型的一些應用:引用類型可以作爲函數的參數

例如:
參數是一個數組

var arr = [1,2,3]
function change(arr){
arr.push(4);
console.log(arr);
}
change(arr);    //=>[1, 2, 3, 4]

參數是一個對象

var obj = {name:'chenjianer',age:20}
function change(o){
o.name = 'yaodao'
console.log(o);
}
change(obj);      //=>{age: 20, name: "yaodao"}

參數是一個函數 function(稱爲回調函數),後期經常使用

var callback = function(a){
console.log(a);
};
function change(callback){
var a = 2;
callback (a);
}
change(callback);

3. undefined的產生

js中undefined的幾種情況:(一變一對象兩函數)
1、變量聲明且沒有賦值;(或者發生變量提升)
2、獲取對象中不存在的屬性時;
3、函數需要實參,但是調用時沒有傳值,形參是undefined;
4、函數調用沒有返回值或者return後沒有數據,接收函數返回的變量是undefined。

4. null的產生

1、作爲函數的參數,表示該函數的參數不是對象
2、作爲對象原型鏈的終點

5. undefined與null之間的區別

1、null和undefined的類型不同

console.log(typeof(undefined)); //undefined
console.log(typeof(null)); //object

null是一個具有有效不存在值的對象,並且它是不可變的,而undefined的對象類型是本身未定義的

此外任何具有null值的算術運算都將產生整數值,而任何帶有undefined的算術運算都會導致變量值變爲NaN

2、轉換爲原始類型的方式不同
null和undefined之間的主要區別在於它們被轉換爲原始類型的方式。在null上執行算術轉換時,確定的值爲0可以使用以下代碼片段驗證此轉換。

var v1= 5+ null;
console.log(v1)

輸出結果爲5

但是undefined不執行任何此類轉換,如果將undefined添加到數字中得出的結果將爲NaN

var v2= 5+ undefined;
console.log(v2)

輸出結果爲NaN

七、js數據類型的轉換

1. 轉換成字符串(String)類型

  • 使用字符串方法toString([進制])轉換,但是null和undefined無法轉換。
  • 使用頂層函數String()可以將任何數據類型轉換成字符串,包括null和undefined。
  • 在其他數據類型和字符串類型進行連接 (+操作) 操作時,會自動對其他數據類型使用String()強制轉換成字符串,然後在相加(連)(隱性轉換)

將其他數據類型的值轉換成字符串,可以使用toString(),也可以使用String()。區別在於String可以轉換任何類型的值爲字符串,toString()不能轉換undefined和null。區別二是語法不同。

使用語法:
使用toString() : 待轉換的變量.toString();
使用String() : String(待轉換的變量);

(1)使用toString()轉換

在這裏插入圖片描述

(2)使用String()轉換

在這裏插入圖片描述

(3)隱性轉換

隱式轉換:在其他數據類型和字符串類型進行連接(+操作)操作時或者比較(==操作),會自動對其他數據類型使用String()強制轉換成字符串,然後在相加(連)。除此之外,還有(-操作),但是此操作會自動對其他數據類型使用parseFloat()強制轉換成浮點型,然後再數學相減。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
上述的[object Object] 第一個object表示是何種數據類型,第二個Object表示是那種類型的對象

2. 轉換成數值(Num)類型

(1)parseInt() 轉換成整型

parseInt() 方法首先查看開頭位置的字符,判斷它是否是個有效數字;如果不是,該方法將返回 NaN(not a number),不再繼續執行其他操作。但如果該字符是有效數字,該方法將查看下一位置的字符,進行同樣的測試。這一過程將持續到發現非有效數字的字符爲止,此時 parseInt() 將把該字符之前的字符串轉換成數字。

例如,如果要把字符串 “12345red” 轉換成整數,那麼 parseInt() 將返回 12345,因爲當它檢查到字符 r 時,就會停止檢測過程。

注意的是:字符串中包含的數字字面量會被正確轉換爲數字,比如 “0xA” 會被正確轉換爲數字10。原因parseInt 按照進制數(基數)解析,0x表示16進制,A在16進制下表示10,parseInt 的正確語法是 parseInt(string, radix(進制數))。不過,字符串 “22.5” 將被轉換成22,因爲對於整數來說,小數點是無效字符。
在這裏插入圖片描述
在這裏插入圖片描述

(2)parseFloat() 轉換成浮點型

道理和轉換成整型道理一樣,只不過浮點型允許有一個小數點出現。
在這裏插入圖片描述

(3)顯性轉換(Number() 強制轉換)

Number() 函數的強制類型轉換與 parseInt() 和 parseFloat() 方法的處理方式相似,只是它轉換的是整個值,而不是部分值。

用 Number() 進行強制類型轉換,“1.2.3” 將返回 NaN,因爲整個字符串值不能轉換成數字。如果字符串值能被完整地轉換,Number() 將判斷是調用 parseInt() 方法還是 parseFloat() 方法。
在這裏插入圖片描述

3. 轉換成布爾(boolean)類型

顯示的轉換是使用Boolean()函數,對需要轉換的內容進行轉換。
以下內容在轉換成布爾值時會被轉換成false:

  • 數值型的 0
  • 數值型的 0.0
  • 布爾型的 false
  • 空字符串 “”
  • 非數字 NaN
  • undefined
  • null

其他所有值都會轉換成true,包括 “0”、空數組 [] 和空對象 {} 。

八、js運算符

1. 賦值運算符

給定 x=10 和 y=5

運算符 例子 等同於 運算結果
= x=y x=y x=5
+= x+=y x=x+y x=15
-= x-=y x=x-y x=5
*= x*=y x=x*y x=50
/= x/=y x=x/y x=2
%= x%=y x=x%y x=0

2. 比較運算符

在這裏插入圖片描述
在這裏插入圖片描述

3. 算數運算符

+加-減*乘%除(取餘)
在這裏插入圖片描述

4. 邏輯運算符

與、或、非

與(&& )關係,先看左邊,左邊爲true,直接等於右邊;左邊爲false,直接等於左邊。
或(|| )關係,先看左邊,左邊爲true,則直接等於左邊;左邊爲false,直接等於右邊。
非(!)

var a = 1, b = 2, c = 0, d = false;
var e = a||b; // e = a;
console.log(a||b); // 1
console.log(a||c); // 1
console.log(c||a); // 1
console.log(c||d); // false
// || 關係,先看左邊,左邊爲true,則直接等於左邊;左邊爲false,直接等於右邊

console.log(a&&b); // 2
console.log(a&&c); // 0
console.log(c&&a); // 0
console.log(c&&d); // 0
// && 關係,先看左邊,左邊爲true,直接等於右邊;左邊爲false,直接等於左邊。
console.log(10||20); // 10
/*var x = 10&&20;
console.log(x||30); // 20*/
//console.log(0&&5); // 0

5. 字符串運算符

PHP中用 點(.) 連接兩個字符串。
JS中用 加號(+) 連接兩個字符串。
如果使用+的是兩個數字,則表示加法運算;如果使用+的有一方是字符串,則表示字符串連接。

console.log(2+3);    // 5,表示加法運算,因爲參與運算的兩個值都是數值型
console.log(2+'hello');    // 2hello,表示字符串相連
console.log('hello ' + 'world');    //hello world

6. 條件(三元)運算符

元,表示參與運算的參數個數。三元意思就是參與運算的有三個值。

var a = (b>c) ? x : y;

上述代碼整體意思是給a賦值,值可能是x,也可能是y。關鍵取決於b是否大於c。如果b>c,則將x賦值給a;如果b不大於c,則將y賦值給a。

7. 一元運算符

var a = 2, b = 3;
var c = a++;    // var c = a; a = a+1
var d = ++b;    // b = b+1; var d=b;
console.log(a, b , c, d);    // a=3, b=4, c=2, d=4

九、 js數組

1. 數組的聲明

在JS中,數組也是一種特殊的對象。屬於 typeof [1,2,3] == object,但實際上[1,2,3] instanceof Object 爲 false。因爲數組屬於 Array 類型。

三種聲明方式:
① var arr = [‘apple’, ‘pear’]; // 推薦使用
② var arr = new Array(‘apple’, ‘pear’); // 一般推薦
③ var arr = new Array(3); //表示數組中有三個單元

2. 獲取數組的元素

使用“數組[下標]”可以獲取到數組中的值。JS中數組的下標一定是數字類型的。
在這裏插入圖片描述

3. 判斷下標是否存在

使用 in 來判斷下標是否存在。
語法: 下標 in 數組 如果存在返回true,不存在返回false。
在這裏插入圖片描述

4. 清空數組

清空數組的兩種方式:

  1. 使數組 = [];
  2. 數組.length = 0;

在這裏插入圖片描述

十、 js對象

1. 創建對象

JS中要得到(定義)一個對象有很多種方式:

  1. 直接量語法得到(定義)對象
  2. 構造函數方式得到(定義)對象
  3. 原型對象方式得到(定義)對象
  4. 混合方式得到(定義)對象(推薦使用)

(1)直接量語法得到對象

語法:

var obj = {}; //空對象
var obj = {成員名:, 成員名:}; 
var obj = {
	成員名:,
	成員名:};

(2)構造函數得到對象

這種方式的弊端是,會爲每一個對象開闢一個內存,比較佔空間,好辦法是將p1和p2相同部分放到原型對象上
語法:

function people(){
            this.name = 'chenjianer';
            this.age = 20;
            this.say = function(){
                console.log('帥哥一枚!');
            }
        }   
var p1 = new people();
console.log(p1);

在這裏插入圖片描述

(3)原型對象方式得到對象

這種方式的弊端是,如果原型對象中有一個引用類型(數組,對象…)的值,則修改其中一個實例對象,另外一個也會修改

function person(){

}
//原型對象
person.prototype.name = 'chenjianer';
person.prototype.age = 20;
person.prototype.sanwei = ['100cm','90cm']
person.prototype.say = function(){
      console.log(123);
}
var p1 =new person();
console.log(p1);

在這裏插入圖片描述

(4)混合方式得到對象(推薦使用)

實際開發中,推薦使用這種方式,減少內存的消耗

function person(n,a){
            this.name = n;
            this.age = a;
            this.sanwei = ['100cm','90cm','110cm'];
        }
        //原型對象(函數類型)
        person.prototype.say = function(){
            console.log(123);
        } 
        person.prototype.cook = function(){
            console.log('能煮一手好飯菜');
        }
        //實例化得到對象
        var p1 = new person('chenjianer',20);
        console.log(p1);

在這裏插入圖片描述

2. 獲取對象的成員

點語法用於對象調用裏面的成員
在這裏插入圖片描述
對象內部的 this 表示對象本身
在這裏插入圖片描述

3. 判斷對象中的成員

使用 in 來判斷屬性是否存在與對象中。
語法:對象成員 in 對象,存在返回true,不存在返回false。

4. 添加對象的成員

使用點語法添加對象成員。
在這裏插入圖片描述

5. 刪除對象的成員

使用delete關鍵字來刪除對象的成員。
在這裏插入圖片描述

6. 修改對象成員

還是使用點語法修改對象成員,先找到對象成員再重新賦值(此處省略)。

十一、 js流程控制

1. 順序結構

js默認的流程結構。按照書寫順序從上至下執行每一條語句。需要注意的是js不存在等待的說法。

2. 分支結構

(1)條件 (if…else…) 語句

if 語句

語法:

if (condition){
    當條件爲 true 時執行的代碼
}
if…else… 語句

語法:

if (condition){
    當條件爲 true 時執行的代碼
}else{
    當條件不爲 true 時執行的代碼
}
if…else if…else 語句
if (condition1){
    當條件 1true 時執行的代碼
}else if (condition2){
    當條件 2true 時執行的代碼
}else{
  當條件 1 和 條件 2 都不爲 true 時執行的代碼
}

(2)switch語句

工作原理:首先設置表達式 n(通常是一個變量)。隨後表達式的值會與結構中的每個 case 的值做比較(比較規則是 n === 條件,不僅值要相等,類型也要相同)。如果存在匹配,則與該 case 關聯的代碼塊會被執行。請使用 break 來阻止代碼自動地向下一個 case 運行。
語法:

var n = ...   // switch參數
switch(n){
    case 條件1:
        執行代碼塊 1
        break;
    case 條件2:
        執行代碼塊 2
        break;
    default:case 條件1case 條件2 不同時執行的代碼
}

3. 循環結構

(1)for 循環

for 循壞

循環(遍歷)對象:Num數組,String數組,Object數組
語法:

for (語句 1; 語句 2; 語句 3){
    被執行的代碼塊
}

語句 1: (代碼塊)開始前執行
語句 2: 定義運行循環(代碼塊)的條件
語句 3: 在循環(代碼塊)已被執行之後執行

形式一:

for (var i=0; i<5; i++){
      被執行的代碼塊
}

形式二:

var i=0,len=arr.length;
for (; i<len; i++){ 
    被執行的代碼塊
}

形式三:

var i=0,len=arr.length;
for (; i<len; ){ 
    被執行的代碼塊
    i++;           /*在這++*/
}

其中 i 的聲明使用的聲明方法請看上述的變量聲明

for in 循環

語法:

for (var i in arr){     
    被執行的代碼塊
}

實例:

var arr = ['關羽', '張飛', '趙雲', '馬超', '黃忠'];
    for(var b in arr){
        //b 表示數組的下標
        //arr[b] 表示數組中的每個值
        console.log(b, arr[b]);
    }

(2)while 循環

while 循環

while 循環會在指定條件爲真時循環執行代碼塊。
語法:

while (條件){
    需要執行的代碼;
    條件自增
}
do while循環

該循環會在檢查條件是否爲真之前執行一次代碼塊,然後如果條件爲真的話,就會重複這個循環。

do{
    需要執行的代碼
    條件自增
}
while (條件);

區別說明:

/*while循環*/
var a = 1;
    while(a < 1){
        console.log(a);       //=> 沒有輸出
        a++;
    }

/*do while 循環*/
var a = 1;
    do{
        console.log(a);     //=> 輸出爲 1 
        a++
    }while(a < 1);

說明 while 是判斷後執行,do while 是執行後判斷 。

(3)break 和 continue 語句

break 語句用於跳出循環。(終止)
continue 用於跳過循環中的一個迭代。

break 語句

使用break語法的 for 循環

var i=0;
    for( ; ; ){
        if(i == 10){
            //終止循環
            break;
        }
        需要執行的代碼;
        i++;
    }

簡寫:

var i=0;
    for( ; ; ){
        if(i == 10)  break;   //終止循環
        需要執行的代碼;
        i++;
    }
continue 語句
var i=0;
    for( ; ; ){
        if(i == 2){
            // 跳過當前迭代
            continue;
        }
        需要執行的代碼;
        i++;
    }

簡寫:

var i=0;
    for( ; ; ){
        if(i == 2)  continue; 	 	// 跳過當前迭代
        需要執行的代碼;
        i++;
    }

十二、js函數基礎

1. 函數定義與調用

定義語法:

function 函數名(參數列表) {
	//函數體
	//return xxx;
}

實例:

/**************** 使用function聲明函數,並調用 *******************/

//定義函數,參數爲一個大於0 的數字n,要求返回1+2+3+....+n 的和
function sum(n){
    //定義一個和
    var s = 0; //默認是0
    for(var i=1; i<=n; i++){
        s = s + i; // s += i;
    }
    //循環結束之後,就得到一個和
    return s;
}
//調用函數
console.log(sum(100));

這種方式定義的函數可以先調用,後定義,也就是函數預加載。

2. 函數表達式

將其看做是一個值,並賦值給一個變量。

var a = sum;
console.log(a(100));   //得到的結果相同

既然函數可以看做是變量,那麼就可以像定義變量一樣來定義函數,這就是函數表達式的形式:

var a = function sum(x){
	console.log('總數爲'+x);
}
a(5050);   // 調用函數

3. 函數的預加載

什麼是函數的預加載?
就是先調用,後聲明。
函數預加載指的是哪種方式定義的函數呢?
在這裏插入圖片描述
原因:變量的提升
函數預加載指定是在同一個script代碼段中,由“function xxx(){}”這種方式定義的函數,可以先調用函數,再聲明函數。
注意不要在非函數的代碼塊中聲明函數。

4. 立即調用模式

(1)立即執行函數是什麼

立即執行函數就是

  1. 聲明一個匿名函數
  2. 馬上調用這個匿名函數

(2)爲什麼使用匿名函數

爲的就是營造私密環境,使得變量不被污染,變量不丟失

(3)匿名函數的作用

只有一個作用:
創建一個獨立的作用域。
這個作用域裏面的變量,外面訪問不到(即避免「變量污染」)。

以一個著名的面試題爲例:

var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
  liList[i].onclick = function(){
    alert(i) // 爲什麼 alert 出來的總是 6,而不是 0、1、2、3、4、5
  }
}

爲什麼 alert 的總是 6 呢,因爲 i 是貫穿整個作用域的,而不是給每個 li 分配了一個 i,或者可以這麼理解:就是for循環瞬間執行完,函數都還沒開始執行 i 就已經丟掉了,因此需要== 用()將 i保存起來== 這是關鍵,所以如下:

(4)立即調用與回調函數

那麼怎麼解決這個問題呢?用立即執行函數給每個 li 創造一個獨立作用域即可(當然還有其他辦法):
方法一:(立即調用模式)

var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
  (function(j){
    liList[j].onclick = function(){
      alert(j) // 0、1、2、3、4、5
    }
  })(i)
}

方法二:(回調函數)

var liList = ul.getElementsByTagName('li');
function callback(i){
    liList[i].onclick = function(){
        alert(i);
    };
};
function click(callback){
    for(var i=0; i<6; i++){
        callback(i);
    };
};
click(callback);

在立即執行函數執行的時候,i 的值被賦值給 j,此後 j的值一直不變。

i 的值從 0 變化到 5,對應 6 個立即執行函數,這 6 個立即執行函數裏面的 j 「分別」是 0、1、2、3、4、5,起到了保存作用。

5. 函數中的this指向

<script type="text/javascript">
(function test(){
	console.log(this);    //=> this指向全局對象
})();

function test(){
	this.name = 'chenjianer'    //=> this指向本身實例化對象
};
var test = new test();   // 實例化得到對象
console.log(test.name);
</script>

在這裏插入圖片描述

6. 函數的參數

形參:定義函數時,約定的參數
實參:調用函數時,傳遞給函數的實際值(實際值包括普通值,引用類型中的值)。
JS函數,參數傳遞非常靈活,定義的形參和實際傳入的實參個數可以不一樣。
在這裏插入圖片描述
ES5中,函數的參數不可以用默認值。ES6中,函數的參數可以有默認值的。目前IE11只支持部分ES6的內容。
那麼在ES5中,如何實現形參有默認值的寫法?
在這裏插入圖片描述

7. 函數作用域

1. 作用域分類

作用域指的是變量起作用的範圍。
分爲全局作用域和局部作用域。其中局部作用域也叫做函數作用域。

2. 作用域規則

(1)規則一:函數可以使用函數以外的變量

在這裏插入圖片描述

(2)規則二:函數內部,優先使用函數內部的變量

在這裏插入圖片描述
函數內部也會發生變量提升:
在這裏插入圖片描述

(3)規則三:函數內部沒有用var聲明的變量,也是全局變量

在這裏插入圖片描述

3. 作用域鏈

在內部函數中查找變量的時候,優先從函數內部自身查找,如果沒有查到,則向外層查找,如果外層還沒有,則繼續向上一層查找,一直查詢到全局作用域。這種鏈式的查找方式就是作用域鏈。
注意:在函數中的var 聲明的變量稱爲局部變量,只能在函數內部訪問,全局下是不能被訪問的。詳細請看:函數內的變量說明
在這裏插入圖片描述

8. 函數內的變量說明

(1)局部 JavaScript 變量

在 JavaScript 函數內部聲明的變量(使用 var)是局部變量,所以只能在函數內部訪問它。(該變量的作用域是局部的)。
您可以在不同的函數中使用名稱相同的局部變量,因爲只有聲明過該變量的函數才能識別出該變量。
只要函數運行完畢,本地變量就會被刪除。

(2)全局 JavaScript 變量

在函數外聲明的變量是全局變量,網頁上的所有腳本和函數都能訪問它。
自調用模式下的全局變量聲明,網頁上的所有腳本和函數都能訪問它。

(3)JavaScript 變量的生存期

JavaScript 變量的生命期從它們被聲明的時間開始。
局部變量會在函數運行以後被刪除。
全局變量會在頁面關閉後被刪除。

實例:

/*自調用匿名函數---立即調用模式下*/
var name = 'zhangsan';
(function(){
	console.log(name)//這裏因爲變量提升,name === undefined,所以結果是undefined
	var name = 'lisi'
	console.log(name)//這裏執行的是 name = lisi ,所以自然就是lisi了
})();
console.log(name)//函數的作用域,在全局中無法訪問,這裏結果是張三

var name = 'zhangsan';
(function(){
	console.log(name)//函數內部沒有name 這個變量,所以像全局查找,全局有一個name,那麼結果就是zhangsan
	name = 'lisi'
	console.log(name)//同理,這裏結果是lisi,因爲name被賦值成lisi
})()
console.log(name)//lisi
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章