前端點滴(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]作爲對象存在於堆中
當我們要訪問堆內存中的引用數據類型時
- 從棧中獲取該對象的地址引用
- 再從堆內存中取得我們需要的數據
詳細請參考: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. 清空數組
清空數組的兩種方式:
- 使數組 = [];
- 數組.length = 0;
十、 js對象
1. 創建對象
JS中要得到(定義)一個對象有很多種方式:
- 直接量語法得到(定義)對象
- 構造函數方式得到(定義)對象
- 原型對象方式得到(定義)對象
- 混合方式得到(定義)對象(推薦使用)
(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){
當條件 1 爲 true 時執行的代碼
}else if (condition2){
當條件 2 爲 true 時執行的代碼
}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 條件1 和 case 條件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)立即執行函數是什麼
立即執行函數就是
- 聲明一個匿名函數
- 馬上調用這個匿名函數
(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