函數就是封裝了一段可被重複調用執行的代碼塊。javascript
函數的使用分爲兩步:聲明函數和調用函數。java
function fn() { console.log("hi") }
注意:數組
fn()
注意:調用的時候必定要加小括號。瀏覽器
function f(x, y) { return x + y } console.log(f(1, 2)) // 3
var fn fn = function(x, y) { return x + y } console.log(fn(1, 2)) // 3
var x = function y(a, b) { return a + b } console.log(x(1, 2)) // 3 console.log(y) // y is not defined
var fn = new Function('x', 'y', 'return x+y') console.log(fn(1, 2)) // 3
var fn1 = x => n * n var fn2 = (x, y) => x + y var fn3 = (x, y) => { return x + y }
function.name 屬性返回函數實例的名稱。閉包
咱們來看看下面這幾種狀況:wordpress
function fn() {} console.log(fn.name) // fn
let fn1 = function fn2() {} console.log(fn1.name) // fn2
let fn = new Function('x', 'y', 'return x+y') console.log(fn.name) // anonymous
console.log((() => {}).name) // "" let fn = () => {} console.log(fn.name) // fn
函數就是一段能夠反覆調用的代碼塊。函數是一個對象,這個對象能夠執行一段代碼,能夠執行代碼的對象就是函數。函數
那爲何函數是一個對象呢?this
var f = {} f.name = 'f' f.params = ['x', 'y'] f.functionBody = 'console.log("1")' f.call = function() { return window.eval(f.functionBody) } console.log(f) // {name: "f", params: Array(2), functionBody: "window.runnerWindow.proxyConsole.log("1")", call: ƒ} f.call() // 1
函數的封裝是把一個或多個功能經過函數的方法封裝起來,對外只提供一個簡單的函數接口。lua
下面咱們來看看幾個簡單的例子:prototype
// 計算 1 ~ 100 之間的累加和 function getNum() { var sum = 0 for (let i = 1; i <= 100; i++) { sum += i } console.log(sum) } getNum() // 5050
// 求任意兩個數的和 function getSum(num1, num2) { console.log(num1 + num2) } getSum(1, 2) // 3
// 求任意兩個數之間的和 function getNum(start, end) { let sum = 0 for (let i = start; i <= end; i++) { sum += i } console.log(sum) } getNum(0, 10) // 55
this 就是 call 的第一個參數,能夠用 this 獲得。
arguments 就是 call 除了第一個之外的參數,能夠用 arguments 獲得。arguments 對象中存儲了傳遞的全部實參。
arguments 展現形式是一個僞數組。
僞數組具備如下這幾個特色:
在普通模式下,若是 this 是 undefined,瀏覽器會自動把 this 變爲 window。
在普通模式下:
let fn = function() { console.log(this) // window console.log(this === window) // true } fn.call(undefined)
在嚴格模式下:
let fn = function() { 'use strict' console.log(this) // undefined console.log(this === window) // false } fn.call(undefined)
arguments:
let fn = function() { console.log(arguments) } fn.call(undefined, 1, 2) // Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 求任意個數中的最大值 function fn() { let max = arguments[0] for (let i = 1; i < arguments.length; i++) { if (max < arguments[i]) { max = arguments[i] } } return max } console.log(fn(1, 2, 3, 4, 661, 434)) // 661
先進後出
柯里化(Currying),又稱部分求值(Partial Evaluation),是一種關於函數的高階技術。它不會調用函數,它只是對函數進行轉換。將 fn(a,b,c) 轉換爲能夠被以 fn(a)(b)(c) 的形式進行調用。它是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數並且返回結果的新函數的技術。
咱們先來看一個例子:
function add(a, b, c) { return a + b + c } console.log(add(1, 2, 3)) // 6
如今咱們把上面代碼修改爲柯里化版本:
function addCurry(a) { return function(b) { return function(c) { return a + b + c } } } console.log(addCurry(1)(2)(3)) // 6
咱們來把 addCurry(1)(2)(3) 換一個形式來表示:
let a = addCurry(1) // ƒ (b) { return function(c) { return a + b + c } } let b = a(2) // ƒ (c) { return a + b + c } let c = b(3) console.log(c) // 6
下面咱們再來看一個例子:
let handleBar = function(template, data) { return template.replace('{{name}}', data.name) } handleBar('<p>Hello,{{name}}</p>', { name: 'zww' }) // <p>Hello,zww</p> handleBar('<p>Hello,{{name}}</p>', { name: 'lq' }) // <p>Hello,lq</p>
上面這段代碼致使的問題就是,若是咱們常常要使用 template 模板,那麼每次像上面這樣寫將會致使十分繁瑣。咱們能夠將代碼修改成柯里化版本:
function handleBarCurry(template) { return function(data) { return template.replace('{{name}}', data.name) } } let h = handleBarCurry('<p>Hello,{{name}}</p>') h({ name: 'zww' }) // <p>Hello,zww</p> h({ name: 'lq' }) // <p>Hello,lq</p>
這樣就實現了 template 模板參數複用的效果了。
張鑫旭 - JS中的柯里化(currying)
現代JavaScript 教程 - 柯里化(Currying)
Currying 的侷限性
JavaScript函數柯里化
高階函數是至少知足下面一個條件的函數:
例以下面這些就是 JS 原生的高階函數:
咱們來實現找出數組中全部的偶數並相加:
let array = [1, 2, 3, 4, 5, 6, 7, 8, 9] let sum = 0 for (let i = 0; i < array.length; i++) { if (array[i] % 2 === 0) { sum += array[i] } } console.log(sum) // 20
下面咱們用高階函數來實現上面的功能:
let array = [1, 2, 3, 4, 5, 6, 7, 8, 9] let sum = array.filter(function(x) { return x % 2 === 0 }).reduce(function(p, n) { return p + n }, 0) console.log(sum) // 20
MDN 所描述的:被做爲實參傳入另外一函數,並在該外部函數內被調用,用以來完成某些任務的函數,稱爲回調函數。
簡單的說就是被看成參數的函數就是回調。
就像 array.sort(function() {})
、array.forEach(function() {})
這些都是回調函數。
function putMsg(msg, callback) { setTimeout(() => { console.log(msg) callback() }, 1000) } putMsg('hi', function() { console.log('msg') })
上面代碼將在 1 秒後打印 hi、msg。
簡單的說就是返回對象的函數就是構造函數,構造函數名字首字母通常大寫。
構造函數有兩個特色:
function Person(name, age) { this.name = name this.age = age } let person = new Person('zww', 18) console.log(person) // Person {name: "zww", age: 18}
做用域指的是您有權訪問的變量集合。
做用域決定了這些變量的可訪問性(可見性)。
函數內部定義的變量從函數外部是不可訪問的(不可見的)。
做用域分爲全局做用域、局部做用域。變量也能夠分爲全局變量與局部變量。
從執行效率來看全局變量與局部變量:
做用域鏈:內部函數訪問外部函數的變量,採起的是鏈式查找的方式來決定取哪一個值,這種結構稱爲做用域鏈。也就是所謂的就近原則。
咱們來看看幾個例子:
question one:
var a = 1 function f1() { var a = 2 f2.call() console.log(a) // 2 function f2() { var a = 3 console.log(a) // 3 } } f1.call() console.log(a) // 1
question two:
var a = 1 function f1() { f2.call() console.log(a) // undefined var a = 2 // 變量提高!!! function f2() { var a = 3 console.log(a) // 3 } } f1.call() console.log(a) // 1
question three:
var a = 1 function f1() { console.log(a) // undefined var a = 2 f2.call() } function f2() { console.log(a) // 1 } f1.call() console.log(a) // 1
question four:
var liTags = document.querySelectorAll('li') for (var i = 0; i < liTags.length; i++) { liTags[i].onclick = function() { console.log(i) // 點擊第二個li時,打印6 } }
以上代碼變量提高後可等價以下:
var liTags var i liTags = document.querySelectorAll('li') for (i = 0; i < liTags.length; i++) { liTags[i].onclick = function() { console.log(i) } }
閉包指有權訪問另外一個函數做用域中變量的函數,簡單的說就是,一個做用域能夠訪問另一個函數內部的局部變量。
閉包的主要做用:延伸了變量的做用範圍。
var a = 1 function fn() { console.log(a) }
這個函數 fn 與變量 a 就造成一個閉包。
function f1() { var num = 10 function f2() { console.log(num); } return f2 } var f = f1() f() // 10
ES6 中新增的定義函數的方式。
箭頭函數不綁定 this,箭頭函數中的 this,指向的是函數定義位置的上下文 this。
箭頭函數有如下這些特色:
let fn = () => { console.log(this) } fn() // window // 若是使用 call,仍是不會改變 this 指向。 fn.call({ name: 'zww' }) // window
咱們來看看下面這段代碼的 this 是什麼:
function fn() {} setTimeout(function(a) { console.log(this) // window }, 1000)
很明顯,上面代碼執行 1s 後將會打印 window,那麼咱們應該怎樣把 this 指向 fn 呢?
可使用 bind 來更改 this 的執行,代碼以下:
setTimeout(function(a) { console.log(this) // ƒ fn() {} }.bind(fn), 1000)
這樣 this 就指向了 fn 了,下面咱們再在 setTimeout 裏面添加個 setTimeout:
setTimeout(function(a) { console.log(this) // ƒ fn() {} setTimeout(function(a) { console.log(this) // window }, 1000) }.bind(fn), 1000)
裏面這個 setTimeout 所打印的 this 仍是指向的 window,那應該怎麼指向 fn 呢?
沒錯,仍是使用 bind,只不過裏面直接傳 this 便可:
setTimeout(function(a) { console.log(this) // ƒ fn() {} setTimeout(function(a) { console.log(this) // ƒ fn() {} }.bind(fn), 1000) }.bind(fn), 1000)
上面代碼,咱們還可使用箭頭函數來簡化:
setTimeout(function(a) { console.log(this) // ƒ fn() {} setTimeout((a) => { return console.log(this) // ƒ fn() {} }, 1000) }.bind(fn), 1000)