上一章:前端教學講義:JS基礎前端
閉包是一種打通兩個做用域的能力,在 Java 和 OC 中都有相似的功能,JS 中的閉包和他們沒有太大的差異。express
不過由於 JS 的函數是一等公民,因此使用起來會更加靈活。segmentfault
在數學和計算機科學中,高階函數是至少知足下列一個條件的函數:數組
接受一個或多個函數做爲輸入閉包
輸出一個函數app
下面的例子就是個典型的高階函數函數
const multiple = function(a, b) { return a * b; } const plus = function(a, b) { return a + b; } const express = function(operator, a, b) { return operator(a)(b); } [ [plus, 1, 2], [multiple, 3, 4], ].map(function(formula) { return express.apply(null, formula) }); // [3, 12]
在 JS 中常常會遇到 this 指向錯亂的問題:this
var obj = { value: 1234, log: function() { console.log(this.value) } } var log = obj.log; log(); // 這個函數執行的時候沒有上下文,this 會默認爲 window 或者 global // undefined
JS 提供了兩個方法,call 和 apply,可以指定一個函數執行時候的上下文prototype
log.call(obj); // 1234 log.apply(obj); // 1234
call 和 apply 同時還能夠指定傳給函數的參數是什麼,他們的區別就是後面的傳參數形式不同;code
function fn(arg1, arg2){} fn.call(obj, arg1, arg2) fn.apply(obj, [arg1, arg2]) // apply 是將參數看成數組傳入
bind 函數經過閉包的形式,來強制的將某個上下文綁定到函數上:
log = bind(log, obj); // 返回一個新函數,此函數的 this 強制被綁定爲 obj log() 1234
bind 的實現大概以下(省略傳參的功能):
function bind(fn, ctx) { return function() { return fn.apply(ctx); } }
和上面舉例有點區別的是 JS 提供的 bind 方法是 Function 類的一個方法,調用方法以下:
function log() {} log = log.bind(obj)
JS 的原型鏈(prototype chain)是爲了實現面向對象而存在的,當訪問一個對象的方法的時候,首先會遍歷對象自己,是否存在這個鍵,而後在查找父類是否有這個鍵,再一直追溯到頂層。
「父類」中包含的方法和屬性,都存放在對象的 「__proto__」對象上(在舊的 JS 規範中,「__proto__」是隱藏屬性,可是 ES6 將它標準化並暴露出來了)。
var c = { somekey: 1, } var b = { __proto__: c, } var a = { __proto__: b } a.somekey // 1
當訪問 「a.somekey」 的時候,會沿着原型鏈一直向上查找,至關於作了以下計算:
function getValue(obj, key) { while (obj) { if (key in obj) { return obj[key] } if (obj.__proto__) { obj = obj.__proto__; } else { return undefined; } } } getValue(a, 'somekey')
在最先期的 JS 版本中是沒有類的,後來加入了原型鏈和 new 關鍵詞才實現了類。
function SubClass(value) { this.value = value; } SubClass.prototype = { log: function() { console.log(this.value); } } var sub = new SubClass(1234); sub.log(); // 1234
在 JS 中任何函數均可以看成構造函數並搭配 new 關鍵詞使用。
new 關鍵詞的做用就是新建一個空對象 a,而後把構造函數上的 prototype 值掛載到 a.__proto__ 上。
再將 a 做爲 context 運行構造函數。
若是咱們將 new 做爲一個函數,它就是這樣:
function new(class, ...params) { var instance = {}; instance.__proto__ = class.prototype; var result = class.apply(instance, params); if (typeof result !== 'undefined') { return result; } return instance; } var sub = new(SubClass, 1234);
從上面的代碼能夠看出,在構造函數中,能夠指定 new 關鍵詞返回的類型,也就是說 new A() 返回的結果不必定就是 A 的實例,這要看 A 的構造函數內部是如何實現的。
function A() { return 1234; } var a = new A(); console.log(a) // 1234
JS 用原型鏈的方式,曲線的實現了類的行爲和寫法,因此在 JS 中,「類」就是構造函數。
如何判斷一個對象是不是一個類的實例呢?
JS 實現的方法很粗糙,就是判斷實例的原型鏈上是否存在類的原型。
function A() { } var a = new A(); a instanceof A true
咱們甚至可讓任意一個類成爲實例的父類
function C() {} function A() {} var a = new A(); C.prototype = A.prototype; a instanceof A // true a instanceof C // true
咱們甚至也可讓一個父類不是實例的父類
function A() {} var a = new A(); A.prototype = {}; // 對原型從新賦值 a instanceof A false
總之就是很神奇
在 ES6 中新增了 class 關鍵詞,使得聲明一個類更加簡單,但只是寫法上有改變,運行機制仍是同樣。
class A { constructor(value) { this.value = value; } log() { console.log(this.value); } } var a = new A(1234); a.log(); // 1234