zishu's blog

zishu's blog

一个热爱生活的博主。https://zishu.me

javascript——函數、變量和方法

當代碼出現有規律的重複之後,可以利用函數,定義變量,調用方法,不用去重複的改動代碼,只需要進行函數的修改。基本上所有的高級語言都支持函數,javascript 也不例外,它可以像變量一樣被使用,方便且強大,因此本文對 js 函數進行系統的學習,並在學習過程中做了詳細的筆記以及範例。

一、函數的定義和調用#

1. 定義函數#

function abs(x) {
    if(x = 0) {
        return x;
    } else {
        return -x;
    }
}
  1. function()指出這是一個函數定義
  2. abs是函數的代碼
  3. (x)裡面的內容是函數的參數
  4. {...}的內容是函數體,可以包括若干語句,甚至可以沒有任何語句

函數體中,必須以return結尾,才可以把結果返回,如果不用 return 結尾的話,就會返回 undefined

也可以直接定義一個對象,這個對象也可以寫成函數的方式

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

function(x)就是一個匿名函數,這個函數被賦值給了變量abs,所以可以直接通過abs調用該函數

這兩種定義函數的方式完全一致,但使用變量定義的時候需要注意,要用;結尾,代表函數語句結束

2. 調用函數#

調用函數時,直接傳參即可
abs (10),根據函數定義,將 10 代入進去即可,返回的結果是 x,即 10

3. 檢查參數#

可以對參數進行檢查,看看是否是自己想要的參數類型

如果傳入對參數abs(x)中非數字,控制台返回結果this is not number,如果傳參為數字,則進行條件判斷

function abs(x) {
    // 檢查參數x是否為數字
    if (typeof x !== 'number') {
        console.log('this is not number')
    }else{
        if (x >= 0) {
            return x
        }else {
            return -x
        }
    }
}

4.arguments#

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

arguments代表傳入的參數,arguments.length代表傳入參數的長度

console.log(arguments.length)
// 這行代碼寫在函數中,控制台就可以輸出出來

先寫一個循環,把參數輸出的函數方法,函數寫完之後,傳入參數,控制台隨之打印出傳入的參數

function str() {
    var s
    for(var i = 0; i<arguments.length; i++) {
        // 返回傳入的參數
        console.log(arguments[i]);
        s += arguments[i] + ",";
    }
    return s;
};
// 傳入參數
str("name", "age");
//控制台輸出:name, age

5.return#

返回 true 時,點擊鏈接直接跳轉,返回 false 時,會忽略 a 鏈接的地址,跳轉到 window.location.href 後的地址

<a href="https:www.baidu.com" onclick="return myfun()">baidu</a>
<input type="text" id="test" value="click">
<script>
    function myfun() {
    window.location.href = 'https://www.bilibili.com';
    var test = document.getElementById('test').value;
    console.log(test);
    
    return false;
}
</script>

return 需要注意的地方:函數會自動在行尾添加;,所以在寫 return 的時候一定要注意,不要單純的拆分為兩行,很容易報錯

return 
    { name: 'foo' }
// 上面這種寫法就是有問題的,js的機制會自動將其渲染為
return;     //return undefined
    { naem: 'foo' };

// 正確的寫法應該是:
return {
    name: 'foo'
};

6.rest#

把傳入的參數,多餘的部分,以數組的形式保存起來,為了獲得額外的參數,需要 i = 2 開始,把已有的 a,b 排除掉

function arr(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);
};

arr(1,2,3,4);

控制台打印:
image

可以看到多餘的部分被打印到Array中了

這種寫法略顯麻煩,下面是更簡單的寫法

直接在函數裡定義參數rest,並且在前面加上...標識,多餘的參數直接以數組的形式交給變量rest,不需要arguments就可以獲取全部參數

如果傳參數量還沒有超過定義參數的數量,函數就會返回一個空數組

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

foo(1,2,3,4)
// a = 1
// b = 2
// Array [3,4]

foo(1)
// a = 1
// b = undefined
// Array [ ] 

7. 計算#

對傳入的參數求和

// forEach可以返回數組中所有的元素

function sum(...rest) {
    var sum = 0;
    rest.forEach(function(x) {
        sum += x;
    });
    return sum;
};

//sum(1,2)
//控制台輸出 3。求和成功

計算圓的面積

// r 表示圓的半徑
// pi 如果沒有參數,默認為3.14
function area_of_circle(r, pi){
    var area;
    if(arguments.length == 1) {
        // 當傳入的參數只有一位時,計算3.14*r的平方
        area = 3.14*r*r;
    }else{
        area = pi*r*r;
    }
    return area;
}

二、變量和作用域#

1. 聲明變量#

在 js 中,通常使用var來聲明變量,而聲明的變量實際上是有作用域的

  1. 在函數體內聲明的變量,只能在函數體內生效,在函數體外是無法識別的
function fun() {
    var a = 1;
};
a = a + 1;  // err  這行代碼直接報錯,因為全局中沒有a這個變量
  1. 如果兩個函數體中各自聲明了變量a,互不干擾,在自己的函數體內可以正常作用,出了函數體都沒有作用了

  2. js 函數可以嵌套,內部函數可以訪問外部函數,外部函數不能訪問內部函數

function par() {
    var x = 1;
    function son() {
        var y = x + 1;
    };
    var z = x + y;      // Error:
}

所以var z = x + y會報錯,因為變量yson()中,根據函數外部無法訪問函數內部y無法被訪問,因此var z = x + y報錯

  1. 兩個嵌套的函數體,各有一個重名變量,js 函數在查找變量的時候,優先從自身開始,如果自身有這個變量就獲取,如果沒有,有內向外,由下層到上層的查找
function par() {
    var num = 1;
    function son() {
        var num = 2;
        console.log("son() = " + num);
    };
    console.log("par() = " + num);
    son();
};
par();

函數必須經過調用之後才能生效 son()par()

2. 變量提升#

JavaScript 的函數定義有個特點,它會先掃描整個函數體的語句,把所有申明的變量 “提升” 到函數頂部,但是並不會將賦值一起提升,很容易產生代碼的報錯

因此,針對這一問題,我們在聲明變量的時候,要將其統一放置在函數的起始位置,嚴格遵守在函數內部首先聲明所有變量的原則

3. 全局作用域#

不在任何函數內部定義的變量就叫做全局變量,也就是window下,它也被稱作全局作用域,全局作用域下的變量實際上被綁定到window

var course = 'learn js';
console.log(course);        // learn js
console.log(window.course)  // learn js

直接訪問全局變量或者在前面加上window,結果都是一樣的

整個 js 文件只有一個全局作用域,就是window,如果在某一個函數作用域內查找變量,沒有查找到,就會由內到外一層層查找,如果最後在全局作用域中也沒有查找到,就會ReferenceError報錯

4. 局部作用域#

在函數內部就是局部作用域,這個代碼的名字只在函數的內部起作用

for循環等語句中,無法定義具有局部作用域的變量

5. 全局變量和局部變量的區別#

  1. 全局變量:在任何一個地方都可以使用,全局變量只有在瀏覽器關閉的時候才會銷毀,比較佔用內存資源

  2. 局部變量:只能在函數內部使用,當其所在代碼塊被執行時,會被初始化;當代碼塊執行完畢就會銷毀,因此更節省節約內存空間

  3. 當在函數作用域中操作一個變量的時候,會先在自身作用域中查找,如果有就直接使用,如果沒有就向上級作用域中尋找。如果全局作用域中也沒有,那麼就報錯

6. 常量#

varlet聲明的是一個變量,在 ES6 之間,用大寫的變量名,表示定義一個常量

// ES5
var NAME = 'xiaoming'

ES6 新增一個關鍵字const來定義常量

// ES6
const name = 'xiaoming'

三、解構賦值#

1. 可以把一個數組的元素分別賦值給不同的變量

var array = ['hello', 'javascript', 'ES6'];
var x = array[0];
var y = array[1];
var z = array[2];

 // x = 'hello'
// y = 'javascript'
// z = 'ES6'

2. 如果數組本身還有嵌套,也可以進行解構賦值,但是要注意嵌套的層次和數組保持一致

let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];

x; // 'hello'
y; // 'JavaScript'
z; // 'ES6'

3. 解構賦值時可以忽略元素

let [, , z] = ['hello', 'JavaScript', 'ES6'];

z;  // ES6 

4. 還可以對對象進行解構賦值

var person = {
    name: 'xiaoming',
    age: 22,
    gender: 'male',
    email: '[email protected]',
    school: 'zyg'
}
// 定義了三個變量,分別對應三個屬性
var {name, age, email} = person;
console.log(name, age, email);

控制台就可以打印出我們想要的內容了

image

對對象進行解構賦值時,也可以進行嵌套

5. 可以通過屬性名賦值的時候,重新定義一個變量名

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school'
};

// 把passport屬性賦值給變量id:
let {name, passport:id} = person;
console.log(name);
console.log(age);
console.log(id);
console.log(email);

控制台輸出結果:

image

可以看到,name,age,id 都打印出來了,而 email 報錯,因為 email 的內容賦值給了新變量id,而email沒有任何內容,所以報錯

6. 可以使用默認值 true,避免不存在的屬性返回 undefined

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678'
};

// 如果person對象沒有single屬性,默認賦值為true:
var {name, single=true} = person;
name;   // '小明'
single; // true

要注意,賦值的時候不能以{開頭,避免 js 將其渲染失敗

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678'
};
// 聲明變量
var x;
var y;

// 解構賦值
{x, y} = { name: '小明', x: 100, y: 200} // Error:

在這裡 {x, y} = person會報一個錯誤,=不合法,所以正確的寫法是,在賦值語句外部包裹一層()小括號

({x, y} = { name: '小明', x: 100, y: 200});

7. 解構賦值的使用場景

交換兩個變量的值

var a = 1;
var b = 2;
[a, b] = [b, a]

四、對象的方法#

綁定到對象上的函數被稱為方法

在一個對象中綁定函數,稱為這個對象的方法

1.this#

下面段代碼返回的是(今年的年份-出生年份)

var xm = {
    name: 'xiaoming',
    birth: 1998,
    age: function() {
        var year = new Date().getFullYear();
        return year - this.birth
    }
};
// 在對象xm中,調用方法age()
xm.age();   // 22

這裡引入了一個新的關鍵詞this

在方法內部,this是一個特殊的變量,它始終指向當前對象,也就是xm這個變量

所以this.birth指的就是變量xm的birth屬性

this存在於方法中,想在方法中調用對象的屬性,必須通過this

如果在方法寫在對象外部時,this的指向問題就要好好分析了,比如:

function getage() {
    var year = new Date().getFullYear();
    return year - this.birth;
}

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: getage
};

image

單獨調用getage()的時候,這時的getage()指的是一個方法,同時這個方法處於全局作用域下,此時this指向的是全局對象window,所以返回NaN

只有xiaoming.age()調用的才是對象xiaoming下面的方法getage()

因此:要保證this的指向正確,必須使用obj.xxx()的形式調用
如果沒有使用這種方法,全部報錯,如果是在 'use strict' 模式下,this會指向 undefined

2.that#

如果在對象裡面的方法,又套了一層事件,此時this指向又有問題了,它指向第一層方法,而不是方法對應的對象

所以,在寫方法的時候,直接先聲明一個var that = this,這個that指向對象裡面的屬性,接下來,在方法裡面調用屬性的時候,直接在前面加上that.即可,它直接指向到對象下面的屬性

var xm = {
    name: 'xiaoming',
    birth: 1998,
    age: function() {
        var that = this;
        function getbirthage() {
            var y = new Date().getFullYear();
            return y - that.birth;
        }
        return getbirthage();
    }
};

// xm.age();

image

可以看到,通過定義var that = this,然後在方法裡面使用that.指向屬性,不管套了幾層方法,都不會報錯,直接指向對象下面的屬性

通過var that = this,可以放心的在方法裡面定義其他函數,不用擔心獲取不到對象屬性的問題

但是有一個需要注意的地方,每個方法結束後,都要返回一下結果,return getbirthage()

3.apply#

除了var that = this,還可以通過apply屬性控制this的指向

apply是函數本身的方法,它擁有兩個參數

function getage() {
    var y = new Date().getFullYear();
    return y - this.birth;
}

var xm = {
    name: 'xiaoming',
    birth: 1998,
    age: getage
}

// xm.age();
// getage.apply(xm, []);

寫法就是getage.apply(xm, [])apply的第一個參數代表this指向,即對象,第二個參數,表示函數本身的參數

image

4.apply () 和 call ()#

call()是與apply類似的方法,區別是:

  1. apply()將參數打包成Array
  2. call()直接將參數按順序傳入

調用math.max(1,2,3),分別採用兩種方式

math.max.apply(null, [1,2,3]);  // 3
math.max.call(null, [1,2,3]);   // 3

兩者的結果是一樣的,調用普通函數時,把this綁定為null

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。