上一章:前端教学讲义: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