程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> 2016.3.23__ JavaScript基礎_3__第十四天

2016.3.23__ JavaScript基礎_3__第十四天

編輯:JAVA綜合教程

2016.3.23__ JavaScript基礎_3__第十四天


最近感冒了,身體太疲憊,同時我也發現每天更新一篇確實不能保證我的文章的質量,與其每天寫一些水文,不如靜下心來把一些知識梳理好再呈現給大家。

所以很抱歉的通知大家,這個系列從今天起,更新日期由每日一更改為3~5日一更,實際時間只會更短,不會更長。

同時也很感謝很多小伙伴這麼長時間的陪伴,謝謝大家。

1. 定時器

在我們的日常開發中,經常會需要讓某一個效果等待多少秒之後才去觸發,這也就引出了我們今天要學習的內容,定時器

1.1 setTimeout()

setTimeout() 方法用於在指定的毫秒數後調用函數或計算表達式。

提示: 1000 毫秒 = 1 秒.

setTimeout(code,millisec,lang)

參數 描述 code 必需。要調用的函數後要執行的 JavaScript 代碼串。 millisec 必需。在執行代碼前需等待的毫秒數。 lang 可選。腳本語言可以是:JScript
    
    

點擊按鈕,在等待 3 秒後彈出 "Hello"。

<script> function myFunction() { setTimeout(function(){alert("Hello")},3000); } </script>

清除定時器:cleanTimeout

    cleanTimeout(myFunction);

1.2 setInterval()

setInterval() 方法可按照指定的周期(以毫秒計)來調用函數或計算表達式。

setInterval() 方法會不停地調用函數,直到 clearInterval() 被調用或窗口被關閉。

由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的參數。

提示: 1000 毫秒= 1 秒。

setInterval(code,millisec,lang)

參數 描述 code 必需。要調用的函數後要執行的 JavaScript 代碼串。 millisec 必需。在執行代碼前需等待的毫秒數。 lang 可選。腳本語言可以是:JScript
    
<script type="text/javascript"> var int=self.setInterval("clock()",1000); function clock() { var d=new Date(); var t=d.toLocaleTimeString(); document.getElementById("clock").value=t; } </script>

清除定時器:clearInterval

    clearInterval(clock);

1.3 setTimeout 和 setInterval 的區別?

在上方的兩個模塊中,大家都看見了,這兩個方法全部都是定時器,但是咱們發現兩個除了名字不同,貌似沒什麼區別,那為什麼還要有兩個方法呢?

因為setTimeout(表達式,延時時間)在執行時,是在載入後延遲指定時間後,去執行一次表達式,記住,次數是一次 。

而setInterval(表達式,交互時間)則不一樣,它從載入後,每隔指定的時間就執行一次表達式 。

所以,完全是不一樣的

很多人習慣於將setTimeout包含於被執行函數中,然後在函數外再次使用setTimeout來達到定時執行的目的 。

這樣,函數外的setTimeout在執行函數時再次觸發setTimeout從而形成周而復始的定時效果 。

使用的時候各有各的優勢,使用setInterval,需要手動的停止tick觸發。

而使用方法中嵌套setTimeout,可以根據方法內部本身的邏輯不再調用setTimeout就等於停止了觸發。

其實兩個東西完全可以相互模擬,具體使用那個,看當時的需要而定了。

就像for可以模擬所有的循環包括分支,而還提供了do、while一樣。

2.類型轉換

JS 中類型轉換分為強制轉換和隱式轉換。

2.1 強制轉換

    var a = "50";       //字符串 String
    var b = 10;     //數字 Number

    //字符串-->數字
    var a = Number(a);

    //提示 Number
    alert(typeof a);

    //數字-->字符串
    var b = String(b);

    //提示 String
    alert(typeof b);

補充:

parsetInt() : 從左到右提取字符串中的整數,遇到非數字結束。(14.2312 取出來就是14) parsetFloat(): 提取字符串中的小數 Number() : 將字符串轉化為數字 String() : 將數字轉換為字符串

2.2 隱式轉換

    //隱式類型轉換
    var a = "5"; //字符串類型
    var b = 10 ; //數字類型
    alert(a-b); //隱式轉換 將字符串“5”轉換為數字 5

3. 函數

函數是由事件驅動的或者當它被調用時執行的可重復使用的代碼塊。

或者說,計算機編程語言中的函數是指可以完成某些功能的一段固定代碼。

3.1 如何定義函數

3.1.1 有名函數(別問我這個是什麼鬼,我也不知道,23333)

函數就是包裹在花括號中的代碼塊,前面使用了關鍵詞 function:

    function functionname()
    {
    這裡是要執行的代碼
    }

當調用該函數時,會執行函數內的代碼。

可以在某事件發生時直接調用函數(比如當用戶點擊按鈕時),並且可由 JavaScript 在任何位置進行調用。

提示:JavaScript 對大小寫敏感。關鍵詞 function 必須是小寫的,並且必須以與函數名稱相同的大小寫來調用函數。

3.1.2 匿名函數

    var MR_LP = function(x){
        if(x > 0){
            return x;
        }
        else{
            return -x;
        }
    };

咱們發現上面的函數,並沒有具體的函數名,但是它將函數賦值給了變量 MR_LP,所以我們的 MR_LP就可以調用該函數。

3.1.3 總結

這兩種定義實際上完全等價,只是需要注意第二種方式按照完整的語法,需要在函數體末尾加一個 ” ; “.表示賦值語句結束。

3.2 調用帶參數的函數

在調用函數時,您可以向其傳遞值,這些值被稱為參數。

這些參數可以在函數中使用。

您可以發送任意多的參數,由逗號 (,) 分隔:

    myFunction(argument1,argument2)

    //當您聲明函數時,請把參數作為變量來聲明:
    function myFunction(var1,var2)
    {
    這裡是要執行的代碼
    }

變量和參數必須以一致的順序出現。第一個變量就是第一個被傳遞的參數的給定的值,以此類推。

3.3 帶有返回值的函數

有時,我們會希望函數將值返回調用它的地方。

通過使用 return 語句就可以實現。

在使用 return 語句時,函數會停止執行,並返回指定的值。

    function myFunction()
    {
    var x=5;
    return x;
    }

上面的函數會返回值 5。

注釋:整個 JavaScript 並不會停止執行,僅僅是函數。JavaScript 將繼續執行代碼,從調用函數的地方。

3.4 如何調用函數

我們調用函數只需要通過 函數名 + 參數即可完成調用,注意當函數有參數的時候,一定要按照順序傳入參數。

大家還記得剛才的匿名函數麼?我們可以直接通過調用MR_LP,就可以直接調用函數,例如這樣:

    MR_LP(100); //返回100
    MR_LP(-99); //返回99,函數內取反了

而且由於 JavaScript 允許傳入任意個參數而不影響調用,因此傳入的參數比定義的參數多也沒有問題,雖然函數內部並不需要這些參數。

    MR_LP(100,'13115235');  //返回100
    MR_LP(-99,'asdasd','aqweqvx');  //返回99,函數內取反了

當然,傳入的參數比定義的要少也沒有問題。

    MR_LP(); 返回 NaN

此時函數的參數將受到 undefined,計算結果為 NaN (Not a Number)。

所以我們在日常的開發中,一定要進行參數的預判斷操作。

    var MR_LP = function(x){

        //判斷參數是否是 Number 類型
        if(typeof x !== 'number'){
            //拋出異常
            throw 'Not a number';
        }
        if(x > 0){
            return x;
        }
        else{
            return -x;
        }
    };

3.5 return 的大坑

在 JavaScript 引擎中有一個在行末自動添加分號的機制,但是這個機制也有可能會讓你栽一個大跟頭。(參考蘋果公司的那個好多年的 return 大坑)

    function lol(){
        return{ name : 'lol'};
    }

    lol();  //{name : 'lol'}

但如果我們如果把 return 語句分成了兩句。

    function lol(){
        return;         //return 的結尾被自動添加上了分號,相當於 return undefined;
        { name : 'lol'};    //永遠不會被執行到
    }

所以我們平常書寫的時候,可以采用這種寫法:

    function lol(){
        return{     //這時候不會自動添加分號,因為 {} 代表語句未結束
            name : 'lol'
        };
    }

4. arguments

JavaScript 還有一個免費贈送的關鍵字 arguments,它只在函數內部起作用,並且永遠指向當前函數的調用者傳入的所有參數。arguments 類似 Array,但實際並不是。

function foo(x){
    alert(x);   //10
    for(var i = 0 ; i < arguments.length ; i ++){
        alert(arguments[i]);    //10 , 20 , 30
    }
}
foo(10, 20, 30);

利用 arguments,你可以獲得調用者傳入的所有參數。

也就是說函數不定義任何參數,還是可以拿到參數的值。

    function MR_LP(){
        if(arguments.length === 0 ){
            return 0;
        }
        var x = arguments[0];
        return x >= 0 ? x : -x;
    }
    MR_LP(0);       //0
    MR_LP(100); //100
    MR_LP(-99); //99

實際上 arguments 最常用於判斷傳入參數的個數。你可能會看見這樣的寫法:

    // foo(a,[b],c)
    //接收2~3個參數,b 是可選參數,如果只傳入2個參數,b 默認為 null;
    function foo(a,b,c){
        if(arguments.length === 2){
            //實際拿到的參數是 a 和 b,c 為 undefined
            c = b;  //把 b 賦值給 c
            b = null;   //b 變為默認值
        }
    }

要把中間的參數 b 變為“可選”參數,就只能通過 arguments 判斷,然後重新調整參數並賦值。

5. rest 參數

由於 JavaScript 函數允許接收任意個參數,於是我們就不得不用 arguments 來獲取所有參數:

    function foo(a,b){
        var i,rest = [];


        if(arguments.length > 2){
            for(i = 2; i < arguments.length; i++){
                rest.push(arguments[i]);
            }
        }
        console.log('a = ' + a);
        console.log('b = ' + b);
        console.log(rest);
    }

為了獲取除了已定義參數 a , b 之外的參數,我們不得不用 arguments , 並且循環要從索引2 開始以便排除前兩個參數,這種寫法很別扭,只是為了獲得額外的 rest 參數,那我們有沒有更好的寫法?

ES6 標准引入的 rest 參數,上面的函數可以改寫為:

    function foo(a, b ... rest){
        console.log('a = ' + a);
        console.log('b = ' + b);
        sonsole.log(rest);
    }

    foo(1,2,3,4,5);
    //結果:
    //  a = 1
    //  b = 2
    //  Array [ 3, 4, 5]

    foo(1);
    //結果:
    //  a = 1
    //  b = undefined
    //  Array []

rest 參數只能寫在最後,前面用 ... 標識,從運行結果可知,傳入的參數先綁定 a , b,多余的參數以數組的形式交給變量 rest, 所以不再需要 arguments 我們就獲取了全部參數。

如果傳入的參數連正常定義的參數都沒填完,也不要緊,rest 參數會接收一個空數組(注意:不是 undefined)。

因為 rest 參數是 ES6 新標准,所以我們在使用時需要注意兼容性的問題。

6.變量及作用域

在 JavaScript 中,用 var 聲明的變量實際上是有作用域的。

如果一個變量在你函數體內部申明,則該變量的作用域為整個函數體,在函數體外不可以用該變量。

    function foo(){
        var x = 1;
        x = x + 1;
    }

    x = x + 2;  //referrenceError ! 無法再函數體外引用變量 x

如果兩個不同的函數各自申明了同一個變量,那麼該變量只在各自的函數體內起作用。

換句話說,不同函數內容的同名變量相互獨立,互不影響:

    function foo(){
        var x = 1;
        x = x + 1;
    }

    function bar(){
        var x = 'A';
        x = x + 'B';
    }

由於 JavaScript 的函數可以嵌套,此時內部函數可以訪問外部函數定義的變量,反過來卻不行。

    function foo(){
        var x = 1;

        function bar (){
            var y = x + 1;  //bar 可以訪問 foo 的變量 x!
        }
        var z = y + 1;  //ReferenceError! foo 不可以訪問 bar 的變量 y !
    }

如果內部函數和外部函數的變量名重名怎麼辦?

    function foo(){
        var x = 1;

        function bar(){
            var x = 'A';
            alert(' x in bar() = ' + x);    //'A'
        }
        alert('x in foo() = ' + x); // 1
        bar();
    }

這說明 JavaScript 的函數在查找變量時從自身函數定義開始,從“內”向“外”依次查找。

如果內部函數定義的函數名與外部函數有重名的變量,則內部函數的變量將“屏蔽”外部函數的變量。

6.1 變量提升

JavaScript 的函數定義有個特點,它會先掃描整個函數體中的語句,把所有申明的變量“提升”至函數的頂部;

    function foo(){
        var x = 'hello, ' + y;
        alert(x);
        var y = 'larra';
    }

    foo();

語句 var x = 'hello' + y; 並不會報錯,原因是變量 y在稍後直接聲明了,但是我們的 alert 會提示 hello, undefined ,這說明我們的變量 y 的值是 undefined。

這正是因為 JavaScript 引擎自動提升了變量 y 的聲明,但是不會提升變量 y 的賦值。

所以我們在函數內部定義變量的時候,請嚴格遵守函數內部首先聲明所有變量的原則,最常見的就是用一個 var 聲明函數內部所有用到的變量。

    function foo(){
        var
            x = 1;          //x 初始值為 1
            y = x + 1;  //y 初始值為 2
            z,i;            //z 和 i 為 undefined

        //其他語句
        for(i = 0; i<100; i++){
            ...
        }
    }

6.2 全局變量

不在任何函數內定義的變量就具有全局作用域。

實際上,JavaScript 默認有一個全局對象 window (也就是浏覽器對象),全局作用域的變量實際上被綁定到 window 的一個屬性:

    var course = 'learn JavaScript';
    alert(course);
    alert(window.course);

因此直接訪問全局變量 course 和訪問 window.course 是完全一樣的。

由於函數的定義有兩種方式,以變量的方式 var foo = function(){}定義的函數實際上也是一個全局變量,因此,頂層函數的定義也被視為一個全局變量,並綁定到 window 對象:

    'use strict';

    function foo(){
    alert('foo');
    }

    foo();              //直接調用 foo();
    window.foo();       //通過 window.foo()調用

由此我們也可以做一個更大膽的猜測,我們每次調通的 alert() 函數其實也應該是 window 的一個變量。

注意:

JavaScript 實際上之喲偶一個全局作用域。任何函數(函數也視為變量),如果沒有在當前函數作用域中找到,那麼就會繼續向上層去查找,如果最後在全局中也沒有找到,就直接報 ReferenceError 的錯誤。

6.3 局部變量

由於 JavaScript 的變量作用域實際上是函數內部,,我們在 for 循環等語句塊中是無法定義具有局部作用域的變量。

    function foo(){
        for(var i = 0;i < 100; i++){
            //
        }
        i +=100;    //仍然可以引用變量 i
    }

所以為了解決塊級作用域,ES6引入了新的關鍵字 let,用 let 替代 var 可以聲明一個塊級元素作用域的變量:

    function foo(){
        var sum = 0;
        for(let i = 0; i< 100 ; i++){
            sum +=i;
        }
        i +=1;  //SyntaxError
    }

一個坑

    
    
我是按鈕1 我是按鈕2 我是按鈕3 我是按鈕4

6.4 常量

變量最明顯的特征就是裡面儲存的值是可以隨意改變的,常量則與之相反,常量一旦確定,則不能發生改變。

var PI = 3.14;

ES6標准出台之後,我們可以使用新的關鍵字 const 來定義常量,const 與 let 都具有塊級作用域。

const PI = 3.14;
PI = 3; //某些浏覽器不會報錯,但是無實際效果
PI;         //3.14

7.閉包

7.1 函數作為返回值

高階函數除了可以接受函數作為參數外,還可以把函數作為返回結果返回。

我們來實現一個對 Array 的求和,通常,求和的函數是這樣定義的:

    function sum(arr){
        return arr.reduce(function(x,y){
            return x + y;
        });
    }
    sum([1,2,3,4,5]);   //15

但是如果不需要立刻求和,而是在後面的代碼中,根據需要再計算,那我們可以直接返回求和的函數。

    function lazy_sum(arr){
        var sum = function(){
            return arr.reduce(function (x,y){
                return x + y;
            });
        }
        return sum;
    }

當我們調用lazy_sum()返回的並不是求和的結果,而是求和的函數:

var f = lazy_sum([1,2,3,4,5]); //function sum()

調用函數f 的時候才是真正計算求和的結果,而是求和函數:

f(); //15

在這個例子中,我們在函數 lazy_sum 中又定義了函數 sum,並且內部函數 sum 可以引用外部函數 lazy_sum 的參數和局部變量,當 lazy_sum 返回函數 sum 時,相關參數和變量都保存在返回的函數中,這種我們稱之為“閉包”

需要注意,當我們調用 lazy_sum()時,每次調用都會返回一個新的函數,即使傳入相同的參數

    var f1 = lazy_sum([1,2,3,4,5]);
    var f2 = lazy_sum([1,2,3,4,5]);
    f1===f2;    //false

f1()和 f2() 的調用結果互不影響。

注意到返回的函數在其定義內部引用了局部變量 arr,所以,當一個函數返回了一個函數後,其內部的局部變量還被新函數引用,所以,閉包用起來容易,做起來可不容易。

另外需要注意,返回的函數並沒有立即執行,而是知道調用了 f() 才執行,我們再來看另外一個例子:

    function count(){
        var arr = [];
        for(var i = 1; i<=3;i++){
            arr.push(function(){
                return i * i;
            });
        }
        return arr;
    }

    var resullts = count();
    var f1 = results[0];
    var f2 = results[1];
    var f3 = results[2];

在上面的例子中,每次循環都創建了一個新的函數,然後把創建的三個函數都添加到一個 array 循環中並返回了。

你可能認為代用的 f1(),f2(),f3()的結果是 1,4,9.而實際情況是

    f1();   //16
    f2();   //16
    f3();   //16

他們全部都是16,原因在於返回的函數引用了變量 i,但它卻不是立即執行,等到三個函數全部執行完畢的時候,i 的值已經變成了 4 ,所以所有的結果都是 16.

所以在返回閉包的時候一定要注意一點: 返回函數不要引用任何循環變量,或者後續會發生變化的變量。

如果程序中必須使用的時候呢?

我們需要重新創建一個函數,用該函數的參數綁定循環變量當前的值,之後就不用管循環變量會怎麼變,我們已經綁定到函數的參數的值是不會變的。

    function count(){
        var aarr = [];
        for(var i=1;i<=3;i++){
            arr.push((function(n){
                return function(){
                    return n*n;
                }
            })(i));
        }
        return arr;
    }

    var results = count();
    var f1 = results[0];
    var f2 = results[1];
    var f3 = results[2];

    f1();   //1
    f2();   //4
    f3();   //9

注意在上面的程序中,我們創建了一個“匿名函數並立即執行”的方法。

    (function(x){
        return x*x;
    })(3)   

理論上講,創建一個匿名函數並立即執行可以這兒寫:

function (x){return x * x;} (3);

但是 JS 語法解析,會認為這個函數是錯誤的,會提示 SyntaxError。因此我們需要用括號吧整個函數定義括起來:

(function(x){return x*x;})(3);

而我們日常工作中,會將這個函數分開來寫

    (function(x){
        return x*x;
    })(3);

那我們之前說閉包功能非常強大,他強大在哪裡呢?

在面向對象的程序設計語言內,如 Java 和 C++ ,要在對象內部封裝一個私有變量,可以用 private 修飾一個成員變量。

在沒有 class 機制,只有函數的語言裡,借助閉包,同樣可以封裝一個私有變量。我們用 JavaScript 創建一個計數器:

    function create_counter(initial){
        var x = initial || 0;
        return{
            inc : function(){
                x += 1;
                return x;
            }
        }
    }

使用的時候:

    var c1 = create_counter();
    c1.inc();   //1
    c1.inc();   //2
    c1.inc();   //3

    var c2 = create_counter(10);
    c2.inc();   //11
    c2.inc();   //12
    c2.inc();   //13

在返回的對象中,實現了一個閉包,該閉包攜帶了局部變量 x , 並且,從外部代碼根本無法訪問到變量 x。換句話說,閉包就是攜帶狀態的函數,並且它的狀態可以完全對外隱藏起來。

閉包還可以把多參數的函數編程但參數的函數。

例如,要計算 x^y 可以使用 Math.pow(x,y) 函數,不過考慮到經常計算 x^2 或者 x^3 , 我們可以利用閉包創建新的函數 pow2pow3:

    function make_pow(n){
        return function (x){
            return Math.pow(x,n);
        }
    }

    //創建兩個新的函數
    var pow2 = make_pow(2);
    var pow3 = make_pow(3);

    pow2(5);    //25
    pow3(7);    //343

8.Match 函數

大家注意到,上面我們使用了一個新的函數,Match.pow(x,y) ,這個函數的作用是:返回底數(x)的指定次(y)冪,Match 本身就是一個龐大的數學函數庫,下面我們再來學習一個新函數 :隨機數 Math.random();,結果為 0 - 1 之間的一個隨機數 (包括0,不包括1)

例如我們現在來生產一個 1-10之間的隨機數:

<code class=" hljs xml">   
    </code>
<script type="text/javascript"> for (var i =0;i< 10;i++) { alert(parseInt(Math.random() * 10)); } </script>

9. eval 函數

eval();是 JS 中的函數,可以將字符轉化為可執行的 JS 代碼,下面我們來看幾個例子:

    var a = "5 + 10";
    alert(eval(a)); //15

    var str = "function(){alert('a')}";

    //直接變函數 等價 var str = function(){alert('a')};
    str = eval("("+str+")");
    str();

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved