资讯专栏INFORMATION COLUMN

前端教学讲义:闭包、高阶函数、原型链

meislzhua / 3360人阅读

摘要:上一章前端教学讲义基础闭包高阶函数闭包是一种打通两个作用域的能力,在和中都有类似的功能,中的闭包和他们没有太大的差别。在中任何函数都可以当作构造函数并搭配关键词使用。再将作为运行构造函数。

上一章:前端教学讲义:JS基础

闭包、高阶函数

闭包是一种打通两个作用域的能力,在 Java 和 OC 中都有类似的功能,JS 中的闭包和他们没有太大的差别。

不过因为 JS 的函数是一等公民,所以使用起来会更加灵活。

在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:

接受一个或多个函数作为输入

输出一个函数

下面的例子就是个典型的高阶函数

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]
call,apply,bind

在 JS 中经常会遇到 this 指向错乱的问题:

var obj = {
    value: 1234,
    log: function() {
      console.log(this.value)
    }
}
var log = obj.log;
log(); // 这个函数执行的时候没有上下文,this 会默认为 window 或者 global
 
// undefined

JS 提供了两个方法,call 和 apply,能够指定一个函数执行时候的上下文

log.call(obj);
 
// 1234
 
log.apply(obj);
 
// 1234

call 和 apply 同时还可以指定传给函数的参数是什么,他们的区别就是后面的传参数形式不一样;

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 中,「类」就是构造函数。

instanceof

如何判断一个对象是否是一个类的实例呢?

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 中对类的改进

在 ES6 中新增了 class 关键词,使得声明一个类更加简单,但只是写法上有改变,运行机制还是一样。

class A {
    constructor(value) {
        this.value = value;
    }
    log() {
        console.log(this.value);
    }
}
var a = new A(1234);
a.log();
 
// 1234

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/96139.html

相关文章

  • 2019春招前端实习面经总结

    摘要:春招前端实习面试记录从就开始渐渐的进行复习,月末开始面试,到现在四月中旬基本宣告结束。上海爱乐奇一面盒模型除之外的面向对象语言继承因为是视频面试,只记得这么多,只感觉考察的面很广,前端后端移动端都问了,某方面也有深度。 春招前端实习面试记录(2019.3 ~ 2019.5) 从2019.1就开始渐渐的进行复习,2月末开始面试,到现在四月中旬基本宣告结束。在3月和4月经历了无数次失败,沮...

    atinosun 评论0 收藏0
  • JS笔记

    摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。异步编程入门的全称是前端经典面试题从输入到页面加载发生了什么这是一篇开发的科普类文章,涉及到优化等多个方面。 TypeScript 入门教程 从 JavaScript 程序员的角度总结思考,循序渐进的理解 TypeScript。 网络基础知识之 HTTP 协议 详细介绍 HTT...

    rottengeek 评论0 收藏0
  • JavaScript设计模式

    摘要:可能因为先入为主,在编程之中,往往不由自主地以的逻辑编程思路设计模式进行开发。这是原型模式很重要的一条原则。关于闭包与内存泄露的问题,请移步原型模式闭包与高阶函数应该可以说是设计模式的基础要领吧。在下一章,再分享一下的几种常用设计模式。 前 在学习使用Javascript之前,我的程序猿生涯里面仅有接触的编程语言是C#跟Java——忽略当年在大学补考了N次的C与VB。 从静态编程语言,...

    keke 评论0 收藏0
  • 前端教学讲义:JS基础

    讲义内容:JS 诞生的背景、基本类型、运算符 以下内容只涉及 ES5 标准,ES6 增加的新内容可以在网上查找到。 JS 诞生的背景 上世纪 90 年代网景公司开发的浏览器独步天下 一个叫做 Brendan Eich 的工程师受命于开发一款脚本语言,来增强浏览器的功能。 这名工程师花费了 10 天时间设计出了第一个版本,名叫 LiveScript。 后来因为当时 Java 正红,公司将其改名为 J...

    walterrwu 评论0 收藏0
  • 【进阶 6-1 期】JavaScript 高阶函数浅析

    摘要:引言本期开始介绍中的高阶函数,在中,函数是一种特殊类型的对象,它们是。简单来说,高阶函数是一个接收函数作为参数传递或者将函数作为返回值输出的函数。我们来看看使用它们与不使用高阶函数的方案对比。引言 本期开始介绍 JavaScript 中的高阶函数,在 JavaScript 中,函数是一种特殊类型的对象,它们是 Function objects。那什么是高阶函数呢?本节将通过高阶函数的定义来展...

    yiliang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<