1. 程式人生 > >js學習(4) 函數

js學習(4) 函數

私有 判斷函數 def 覆蓋 執行 速度 width 返回函數 理解

JavaScript有三種聲明函數的方法

(1)function命令

function print(s) {
  console.log(s);
}

(2)函數表達式

1.var print = function(s) {
  console.log(s);
};

2.var print = function x(){
  console.log(typeof x);
};

x
// ReferenceError: x is not defined

print()
// function

3.var f = function f() {};

將匿名函數賦予變量,如果不匿名,也只在內部有效

函數表達式定義函數最後要加上分號代表結束

(3)Function構造函數

可以傳遞任意數量的參數給Function構造函數,但只有最後一個參數被當做函數體

Function構造函數可以不使用new命令,返回結果完全一樣

var add = new Function(
  ‘x‘,
  ‘y‘,
  ‘return x + y‘
);

// 等同於
function add(x, y) {
  return x + y;
}

此方法比較不常用  第一種方法倒更熟悉

如果一個函數被多次聲明,後面的聲明就會覆蓋前面的聲明

調用函數時和其他語言大致相同,使用圓括號運算符

第一等公民 

Js把函數看成一種值,與其他值(數值,字符串,布爾值等)地位相同

凡是可以使用值的地方,就能使用函數,函數只是一個可以執行的值

function add(x, y) {
  return x + y;
}

// 將函數賦值給一個變量
var operator = add;

// 將函數作為參數和返回值
function a(op){
  return op;
}
a(add)(1, 1)
// 2

函數提升

技術分享圖片 

函數的屬性和方法

name屬性:返回函數名字 如果采用表達式定義函數,且function後有名字,則返回那個名字

length屬性:返回函數預期傳入的參數個數

toString():返回一個字符串,內容是函數的源碼,以及註釋

對於var命令來說,局部變量只能在函數內部聲明,在其他區塊中聲明,一律都是全局變量

函數內部也有變量提升,var命令聲明的對象,不管在什麽位置,變量聲明都會被提升到函數體的頭部

函數本身的作用域

函數本身作用域就是聲明時所在的作用域,與運行時所在作用域無關

在定義時綁定作用域

var a = 1;
var x = function () {
  console.log(a);
};

function f() {
  var a = 2;
  x();
}

f() // 1

函數的參數  

可以省略,但沒辦法只省略靠前的參數,而保留靠後的參數,如果一定要省略靠前的參數,只有顯式傳入undefined

參數傳遞對原值的影響

如果是原始類型的值,無影響,只是一份拷貝

如果傳入的是對象,則修改其屬性的話,原值會受到影響

但把對象整個替換掉,不會影響原始值,因為相當於指向其他地址

同名參數

如果有同名參數,則取最後出現的那個值

function f(a, a) {
  console.log(a);  //a為最後一個a
}

f(1) // undefined 相當於f(1,undefined)

arguments對象

由於js允許函數提供不定數目的參數,所以需要一種機制,可以在函數體內部讀取所有參數,這就是arguments對象的由來

arguments對象包含函數運行時所有參數,使用類似數組

如果在非嚴格模式下還可以在函數內部修改參數的值,嚴格模式下只讀

通過它的length屬性,可以判斷函數調用時到底帶了幾個參數

與數組關系:

是對象,不能使用數組方法,除非轉換為數組

var args = Array.prototype.slice.call(arguments);

// 或者
var args = [];
for (var i = 0; i < arguments.length; i++) {
  args.push(arguments[i]);
}

arguments自帶一個callee屬性1,返回它所對應的原函數

函數的閉包

閉包即能夠讀取其他函數內部變量的函數,可以簡單理解為“定義在一個函數內部的函數”

用處:1.讀取函數內部變量 2.記住誕生的環境

function createIncrementor(start) {
  return function () {
    return start++;
  };
}

var inc = createIncrementor(5);

inc() // 5
inc() // 6
inc() // 7

 3.封裝對象的私有屬性和方法,感覺像類 

function Person(name) {
  var _age;
  function setAge(n) {
    _age = n;
  }
  function getAge() {
    return _age;
  }

  return {
    name: name,
    getAge: getAge,
    setAge: setAge
  };
}

var p1 = Person(‘張三‘);
p1.setAge(25);
p1.getAge() // 25

立即調用的函數表達式,聲明時就調用(IIFE)

(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();

 所以以下寫法都行

var i = function(){ return 10; }();
true && function(){ /* code */ }();
0, function(){ /* code */ }();
!function () { /* code */ }();
~function () { /* code */ }();
-function () { /* code */ }();
+function () { /* code */ }();

通常只對匿名函數使用

目的:1.不必命名,避免汙染全局變量 2。在IIFE內部形成了一個單獨的作用域,可以封裝一些外部無法讀取的私有變量

eval命令

eval命令接受一個字符串作為參數,並將這個字符串當做語句執行

eval(‘var a = 1;‘);
a // 1

如果無法執行就報錯

如果eval的參數不是字符串,那麽會原樣返回

eval的作用域是當前作用域,因此小心修改當前作用域變量的值

eval本質是在當前作用域中註入代碼,由於安全風險和不利於js引擎優化執行速度,一般不推薦使用

最常用的場合是解析json數據的字符傳你,不過正確做法應該使用原生的JSON.parse方法

js規定,凡是使用別名執行eval,eval內部一律是全局作用域

var a = 1;

function f() {
  var a = 2;
  var e = eval;
  e(‘console.log(a)‘);
}

f() // 1

  

 

  

 

  

  

  

js學習(4) 函數