「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)

文章目录

  • 1 手写 Promise 上
    • 1.1 手写 Promise
    • 1.2 使用 chai
    • 1.3 异步测试
    • 1.4 使用 sinon 测试函数
    • 1.5 完成 Promise A+ (上)
    • 1. 6 then 做了什么
    • 1.7 如何通过 pull request 完成作业
  • 2 手写 Promise 下
  • 3 async await 全解
  • 4 EventHub
    • 4.1 源码书写过程
    • 4.2 改进 TypeScript
  • 5 EventLoop
    • 5.1 EventLoop的三个阶段和三个API
    • 5.2 宏任务和微任务
    • 5.3 总结(必看)
  • async await 全解
    • Promise 精讲
    • Promise 串行面试题
    • 面试题解
    • await 基本用法
    • 问答
    • 题外话
  • 手写深拷贝
    • JSON 序列化
    • 递归深克隆
    • 环检测(遇 BUG )
    • 解决 BUG,考虑爆栈。
    • 拷贝 RegExp 和 Date
    • 总结
  • 手写bind
    • bind
    • 支持 new 的 bind
  • 函数全解上
    • 函数与闭包
    • this 超级变态题
    • 递归、记忆化与 React 优化
    • 测试题
  • 函数全解下
    • 柯里化 Currying
    • 高阶函数
  • 继承和组合
    • 类知识简介
    • 继承与组合
    • 复习一下组合
    • Vue 的源码中使用了组合
    • 组合更占内存吗

1 手写 Promise 上

1.1 手写 Promise

面试答题方法论:

  • 该技术要解决什么问题–why
  • 该技术是怎么解决这个问题的–how
  • 该技术有什么优点(对比其他技术)–pros (优点)
  • 该技术有什么缺点–cons (缺点)
  • 如何解决这些缺点–more

Promise 要解决什么问题–why
答:Promise 要解决的就是回调地狱的问题。回调过多导致代码复杂,不清晰。

如下图所示,该代码用 Node.js 实现了调整文件夹中图片的宽高比例。回调地狱就是不断的进行回调,这里进行了 5次。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第1张图片

回调地狱的出现可能是这个程序员水平不行造成的。

补充:重构上图代码。因此,出现回调地狱首先考虑的是不是因为自己的水平问题,代码没有写好。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第2张图片
该技术有什么优点(对比其他技术)–pros (优点)
答:
优点一,减少缩进,把函数里的函数变成 then 下面的 then。

fn1(xxx, function fn2(a){
  fn3(yyy, function fn4(b){
    // fn4 是函数里的函数
    fn5(a+b, function fn6(){
      // code
    })
  })
})

fn(xxx)
  .then(fn2) // fn2 里面调用fn3
  .then(fn4) // fn4 里面调用fn5
  .then(fn6)
// 提问:fn5 怎么得到 a 和 b
// fn2 的输出作为 fn4 的输入

优点二,消灭if(err),错误处理单独放到一个函数里,如果不处理就一直等到往后抛。

fn(xxx)
  .then(fn2, error1) // fn2 里面调用fn3
  .then(fn4, error2) // fn4 里面调用fn5
  .then(fn6, error3)
  .then(null, errorAll)
// 最后一句可以写成 .catch

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第3张图片
Promise/ A+ 标准文档:JavaScript 的Promise 的公开标准,有中文翻译,但可能不准确。

1.2 使用 chai

mocha 是提供 describe、it 和 漂亮输出的库。chai 是提供 assert 的库。

  • yarn --dev add ts-node mocha 局部安装
  • 创建目录 demo(名字随意)
  • yarn init -ynpm init -y
  • yarn --dev add chai mocha
  • yarn --dev @types/chai @types/mocha
  • 创建 test/index.ts
  • mocha 只支持 JavaScript,不支持 TypeScript。因此,用 mocha -r ts-node/register test/index.ts 运行代码

describe 和 it 的含义,如下图所示。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第4张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第5张图片
出现上图错误,是默认在本地环境下运行找不到该 module 。因此要通过 --dev 安装到本地环境下。

1.3 异步测试

1.4 使用 sinon 测试函数

安装

yarn --dev add sinon sinon-chai
yarn --dev add @types/sinon @types/sinon-chai

1.5 完成 Promise A+ (上)

1. 6 then 做了什么

1.7 如何通过 pull request 完成作业

2 手写 Promise 下

3 async await 全解

4 EventHub

eventhub(也叫发布订阅模式、eventbus 等)

4.1 源码书写过程

面试解题思路:

  • 确定API
  • 添加测试用例
  • 让测试用例通过
  • 重构代码

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第6张图片
Notice:# 表示对象的属性(class 中)。

运行 TypeScript 代码用 ts-node

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第7张图片
注意:数组的 indexOf 对 IE 的兼容性不好,仅支持 Edge。

打注释方法 /** + enter

留坑给面试官问,别写的太完美了。

unknown 是一个安全的 any,只要一旦确定就不能修改了。

4.2 改进 TypeScript

申明类型。

5 EventLoop

5.1 EventLoop的三个阶段和三个API

EventLoop 是 C++写的,是 Node.js 的概念不是浏览器的。

EventLoop 是循环的一个过程,表示不同状态。

与前端相关的就 3 个(timers、poll 和 check):
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第8张图片
setImediate() 只有 Node.js 有。

5.2 宏任务和微任务

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第9张图片
如果用 await 语法糖,那就将 await 转化为 Promise 的 then 的方式。

Promise 中加入队列看 then 不需要看 resolveresolve 是确定用那个函数。

5.3 总结(必看)

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第10张图片

async await 全解

Promise 精讲

Promise 串行面试题

感觉不好

面试题解

感觉不好

await 基本用法

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第11张图片
因为,官网的 await 在发布之前,就有第三方写过await 了。因此需要区分,就在 funciton 前面加 async 来以示区分。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第12张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第13张图片
await 和 Promise 要配合使用。

function ajax(){
  return new Promise((resolve, reject) => {
    reject({
      response: {
        status: 403
      }
    })
    // resolve({
    //   date:{name: 'Jonathan Ben'}
    // })
  })
}

const error = (e) => {
  console.log(e)
  console.log('提示用户没有权限')
  throw e
}

async function fn(){
  const response = await ajax().then(null, error)
  console.log(response)
}

fn()

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第14张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第15张图片

await 在 for 里是串行的,在 forEach 里才是并行的,并行做网络请求(浏览器做的网络请求)。setTimeOut 是浏览器的接口。

async function runPromiseByQueue(myPromises) {
  for (let i = 0; i < myPromises.length; i++) {
    await myPromises[i]();
  }
}

const createPromise = (time, id) => () =>
  new Promise((resolve) =>
    setTimeout(() => {
      console.log("promise", id);
      resolve();
    }, time)
  );

runPromiseByQueue([
  createPromise(3000, 4),
  createPromise(2000, 2),
  createPromise(1000, 1)
]);
// promise 4
// promise 2
// promise 1

async function runPromiseByQueue(myPromises) {
  myPromises.forEach(async (task) => {
    await task();
  });
}

const createPromise = (time, id) => () =>
  new Promise((resolve) =>
    setTimeout(() => {
      console.log("promise", id);
      resolve();
    }, time)
  );
runPromiseByQueue([
  createPromise(3000, 4),
  createPromise(2000, 2),
  createPromise(1000, 1)
]);
// promise 1
// promise 2
// promise 4

问答

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第16张图片

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第17张图片

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第18张图片

表达式前面的 , 不影响任何。

var a = 1

var b = (console.log(a), a) + 2

console.log('b' + b)

将同步的代码放到异步代码的前面,因为 await 和 Promise 后的代码会有传染性(同步变异步)。

题外话

一般情况下还是用Promise来写,在 ajax 错误处理部分可以是用 await 小技巧。

手写深拷贝

JSON 序列化

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第19张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第20张图片
最简易的方法:JSON 序列化反序列化

let a = {
  b: 1,
  c: [1, 2, 3],
  d: {d1: 'ddd1', d2: 'ddd2'}
}

let a2 = JSON.parse(JSON.stringify(a))

a2.b = 2
console.log(a.b)
a2.c[1] = 2222
console.log(a.c[1])
a2.d.d2 = 'ccccc'
console.log(a.d.d2)

缺点:对 JavaScript 来说,
JSON 不支持函数(直接忽略了)

let a = {
  b: 1,
  c: function (){return 1}
}

let a2 = JSON.parse(JSON.stringify(a))
console.log(a2)

不支持 undefined(和函数一样直接忽略了)
不支持正则(和函数一样直接忽略了)
不支持引用,JSON 不支持环装引用,只支持树状引用。

let a = {
  b: 1,
  c: function (){return 1}
}

a.self = a

let a2 = JSON.parse(JSON.stringify(a))
console.log(a2)
// TypeError: Converting circular structure to JSON

对 Date 支持不好,会把 Date 对象转为 String

let a = {
  b: 1,
  c: new Date()
}

let a2 = JSON.parse(JSON.stringify(a))
console.log(a2)
// { b: 1, c: '2021-03-29T15:54:34.691Z' }

递归深克隆

用最少的代码,来完成测试用例。

var a = Symbol()
var b = a 
a === b // true

环检测(遇 BUG )

因为递归的对象是没有形成环的,所有会死循环。

解决 BUG,考虑爆栈。

函数调用栈会爆栈,如果对象很深。解决方式就是:把递归代码改为循环代码,存入数组中。也就是把纵向的,改为横向的。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第21张图片

拷贝 RegExp 和 Date

正则有 source 和 flags。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第22张图片

总结

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第23张图片
存在的问题就是,每次复制一次之后,cache 变量没有清空。可以通过用面向对象来每次实例化,也就是把 cache 放到 constructor 来解决。

手写bind

bind

nodejs 是没有模块化的。

支持 new 的 bind

JavaScript 的 new 其实就是一个语法糖:

var fn = function(a){
	this.a = a
}

new fn(a)

// 等价于上面的 fn,相当于JavaScript 帮你做这几步骤。
// var fn = function (a){
//   1. var temp = {}
//   2. temp.__proto__ = fn.prototype
//   3. fn.call(fn, a)
//   4. return this
// }

当指定 this,没有传。则 this 指向全局:

function fn(){
    console.log(this)
}

fn.call(undefined) 
// Window {0: global, window: Window, self: Window, document: document, name: "", location: Location, …}

通过 new 4步过程中的第二步: 临时变量.__proto__ === Fn.prototype 来鉴别是否用了 new:

var fn = function(){
    console.log(this)
    console.log(this.__proto__ === fn.prototype)
}

fn()
// Window {0: global, window: Window, self: Window, document: document, name: "", location: Location, …}
// false

fn.call({name: 'Jonathan Ben'})
// {name: "Jonathan Ben"}
// false

new fn()
// fn {}
// true

__proto__ 是几个浏览器自己定义的,因为很火,导致后面官方不得不加到标准,但是官方内心还是坚持不推荐使用的。

this.__proto__ === Fn.prototype 最好的写法是:this instanceof FnFn.prototype.isPrototypeOf(this)(后者用的少)。

this.constructor === Fn 可能会间接继承。@@ 试验一下 @@

mdn 上的可以 new 的 bind 存在的问题:多了一层原型。

 var ArrayPrototypeSlice = Array.prototype.slice;
 // 将 mdn bind 改为 bind2,防止与原生 bind 冲突
  Function.prototype.bind2 = function(otherThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var baseArgs= ArrayPrototypeSlice.call(arguments, 1),
        baseArgsLength = baseArgs.length,
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          baseArgs.length = baseArgsLength; // reset to default base arguments
          baseArgs.push.apply(baseArgs, arguments);
          return fToBind.apply(
                 fNOP.prototype.isPrototypeOf(this) ? this : otherThis, baseArgs
          );
        };

    if (this.prototype) {
      // Function.prototype doesn't have a prototype property
      fNOP.prototype = this.prototype;
    }
    fBound.prototype = new fNOP();

    return fBound;
  };

let fn = function (a){
    this.a = a
}

fn.prototype.sayHi = function(){}

let o1 = new fn('fn1')
console.log(fn.prototype.isPrototypeOf(o1)) // true

let fn2 = fn.bind(undefined, 'fn2')
let o2 = new fn2()
console.log(o2 instanceof fn) // true

let fn3 = fn.bind2(undefined, 'fn3')
let o3 = new fn3()
console.log(o3.__proto__ === fn.prototype) // true
console.log(o3.__proto__.__proto__ === fn.prototype) // false

通过打印出对象看也是多了一层原型,和 JavaScript 的原生 bind 出现差别。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第24张图片

TDD (Test-Driven development,测试驱动开发)
三个版本:

  • es6 语法版本
  • 兼容版本(不能 new)
  • 兼容版本(可以new)

函数全解上

函数与闭包

JavaScript 中,只有函数和方法,没有过程。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第25张图片

数学函数和编程的函数?

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第26张图片

JavaScript 是默认支持闭包的。有些语言并不默认支持,比如 Ruby,需要吧 def 关键字改为 lambda
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第27张图片

闭包只能位置变量这种状态,变量的值可以变化的。

对象和闭包都维持了变量的状态。

var obj = {
  _i:0,
  fn(){
    console.log(this._i)
  }
}

const handle = function (){
  var i = 0
  return function (){
    console.log(i)
  }
}

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第28张图片

如果不支持对象,用闭包来代替。

function createPerson(name, age) {
  return function (key) {
    if (key === 'name') return name
    if (key === 'age') return age
  }
}

var person = createPerson('Jonathan ben', 25)

console.log(person('name'))
console.log(person('age'))

this 超级变态题

4 中申明函数的方式如图。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第29张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第30张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第31张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第32张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第33张图片
方方,自编题:
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第34张图片
this,关注调用时候的传递的参数(call等)。
如果是点击的触发的话,那么一定就是 button
如果是

var fn = button.onclick()
fn()
// 等价于 fn.call(undefined)

那么 this 就是 window。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第35张图片
一般来说 this 只的是 vm 对象。但是如果特意指定的话,就说不定了。

变态面试题:

let length = 10
function fn(){
  console.log(this.length)
}

let obj = {
  length: 5,
  method(fn){
    fn()
    // fn.call(undefined)
    arguments[0]()
    // arguments.0.call(arguments)
    // fn.call(arguments)
  }
}

obj.method(fn, 1)

// 3
// 2

考点一:let 不会绑定在 window
考点二:window.length 在浏览器指的是 的个数
考点三:method 的隐式对象确实是 obj
考点四:arguments 是实参。确定形参(函数定义时的参数)的是 fn.length 属性

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第36张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第37张图片

自由变量:不是自己本身的变量。

递归、记忆化与 React 优化

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第38张图片

console.time ('看时间')
// 代码
console.timeEnd('看时间')

这两个方法用于计时,可以算出一个操作所花费的准确时间。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第39张图片
迭代基本就是尾递归(迭代递归)。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第40张图片

JavaScript 是没有尾递归优化的,因此还是压栈(理论是是不用压栈的)。「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第41张图片
所有的递归都可以变为循环

在 JavaScript 中,递归有时候很差,性能差可读性低。因此解决办法之一是:记忆化(Memorize)

代码执行了,可能 DOM 也没更新。因此 React 中用了 memo ,来记忆代码执行过了。

新的问题 onClick 每次都会重新创建。

测试题

const memo = (fn) => {
  let hashMap = new Map()
  return function (key){
    if(!hashMap.get(key)) {
      hashMap.set(key, fn(key))
    }
    return hashMap.get(key)
  }
}

const x2 = memo((x) => {
  console.log('执行了一次')
  return x * 2
})


// 第一次调用 x2(1)
console.log(x2(1)) // 打印出执行了,并且返回2
// 第二次调用 x2(1)
console.log(x2(1)) // 不打印执行,并且返回上次的结果2
// 第三次调用 x2(1)
console.log(x2(1)) // 不打印执行,并且返回上次的结果2

函数全解下

柯里化 Currying

函数式
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第42张图片

JavaScript 中,柯里化里面一般都是用闭包多余对象。

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第43张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第44张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第45张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第46张图片
== 不要用外面传的参数 ==
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第47张图片

第一:形参 params 数组,递归是传的地址进去的,因此是同一个地址的数组。
第二:因为每次需要 push。第一次 newAddTwo(1),已经通过 push 改变了 params 里面有一个 1了。然后第二次调用 newAddTwo(1)(2) 其实在第一个(1) 时候,就满足 params.length === fn.length 因此直接返回了结果 2。在通过 2(2) 显然会报不是函数的错误。

正常版本:

const addTwo = (a, b) => a + b

const currying = (fn, params = []) =>
  (arg) => {
    const newParams = params.concat(arg)
    return newParams.length === fn.length
      ? fn(...newParams)
      : currying(fn, newParams)
  }


const newAddTwo = currying(addTwo)
console.log(newAddTwo(1)(5))

优化版:

const addTwo = (a, b) => a + b

const currying = (fn, params = []) =>
  (...args) => {
    return params.length + args.length === fn.length
      ? fn(...params, ...args)
      : currying(fn, [...params, ...args])
  }


const newAddTwo = currying(addTwo)
console.log(newAddTwo(1)(5))
console.log(newAddTwo(1, 3))

三元运算符用 return:需要将return放在三元运算符最前面。不是放在后面 return 两次。

let fn = () => {
    return 1 ?  22 :  33
}
fn()

高阶函数

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第48张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第49张图片

var bind = Function.prototype.bind

var f1 = function (){
  console.log('this')
  console.log(this)
  console.log('arguments')
  console.log(arguments)
  console.log('-------')
}
var newF1 = f1.bind({name: 'Jonathan Ben'}, 1, 2, 3)
var newF2 = bind.call(f1,{name: 'Jonathan Ben'}, 1, 2, 3)

newF1()
newF2()
// 假定
// obj.method(a, b, c, d)
// obj.method.call(obj, a, b, c, d)

// 设 obj = f1
// 设 method = bind

// 代入
// f1.bind(a, b, c, d)
// f1.bind.call(f1, a, b, c, d)

// 代入参数
// f1.bind({name: 'Jonathan Ben'}, 1, 2, 3)
// f1.bind.call(f1, {name: 'Jonathan Ben'}, 1, 2, 3)

// f1.bind === Function.prototype.bind
// var bind = Function.prototype.bind
// 所以 f1.bind 就是 bind

// bind.call(f1, {name: 'Jonathan Ben'}, 1, 2, 3)
// bind.call 接收一个函数 fn, this, 其他参数
// 返回一个新的函数,会调用 fn,并传入 this 和其他参数
var apply = Function.prototype.apply

var f1 = function (){
  console.log('this')
  console.log(this)
  console.log('arguments')
  console.log(arguments)
  console.log('-------')
}
f1.apply({name: 'Jonathan Ben'}, [1, 2, 3])
apply.call(f1,{name: 'Jonathan Ben'}, [1, 2, 3])

// 假定
// obj.method(a, b, c, d)
// obj.method.call(obj, a, b, c, d)

// 设 obj = f1
// 设 method = apply

// 代入
// f1.apply(a, b, c, d)
// f1.apply.call(f1, a, b, c, d)

// 代入参数
// f1.apply({name: 'Jonathan Ben'}, [1, 2, 3])  // 理解这个。。
// f1.apply.call(f1, {name: 'Jonathan Ben'}, [1, 2, 3])

// f1.apply === Function.prototype.apply
// var apply = Function.prototype.apply
// 所以 f1.apply 就是 apply

// apply.call(f1, {name: 'Jonathan Ben'}, [1, 2, 3])
// apply.call 接收一个函数 fn, this, 数组
// 返回一个新的函数,会调用 fn,并传入 this 和数组
var call = Function.prototype.call

var f1 = function (){
  console.log('this')
  console.log(this)
  console.log('arguments')
  console.log(arguments)
  console.log('-------')
}
f1.call({name: 'Jonathan Ben'}, 1, 2, 3)
call.call(f1,{name: 'Jonathan Ben'}, 1, 2, 3)

// 假定
// obj.method(a, b, c, d)
// obj.method.call(obj, a, b, c, d)

// 设 obj = f1
// 设 method = call

// 代入
// f1.call(a, b, c, d)
// f1.call.call(f1, a, b, c, d)

// 代入参数
// f1.call({name: 'Jonathan Ben'}, 1, 2, 3)  // 理解这个。。
// f1.call.call(f1, {name: 'Jonathan Ben'}, 1, 2, 3)

// f1.call === Function.prototype.call
// var call = Function.prototype.call
// 所以 f1.call 就是 call

// call.call(f1, {name: 'Jonathan Ben'}, 1, 2, 3)
// call.call 接收一个函数 fn, this, 其他参数
// 返回一个新的函数,会调用 fn,并传入 this 和其他参数

bind.call 接收一个 函数,然后也返回一个函数。apply.call 接收一个函数。call.call 接收一个函数。因此都是高阶函数。

var array = [1, 5, 2, 3, 4]
var sort = Array.prototype.sort

console.log(array.sort((a, b) => a - b))
console.log(sort.call(array, (a, b) => a - b))
var array = [1, 5, 2, 3, 4]
var map = Array.prototype.map

console.log(array.map(item => item * 2))
console.log(map.call(array, item => item * 2))

函数组合 JavaScript 程序员用的比较少。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第50张图片

使用 pipe 来进行函数组合
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第51张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第52张图片

单参数在高级的函数值是很重要的。

继承和组合

类知识简介

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第53张图片

类和继承都是为了解决代码重复。

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第54张图片

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第55张图片
直接谷歌 tsconfig.json 复制代码。


需要定义类自己的方法:

class Person{
	// 类自己的方法需要用 =
	mySayHi = () => {}
}

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第56张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第57张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第58张图片

继承与组合

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第59张图片

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第60张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第61张图片
JavaScript 和 Java 中的继承是单继承。C++中的继承是多继承。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第62张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第63张图片
因此,组合的最大缺点就是太灵活了。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第64张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第65张图片
组合的思想:你要什么东西,我就复制给你。

for… in 遍历不到 class 的方法。
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第66张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第67张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第68张图片
「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第69张图片
组合是优于继承的。

复习一下组合

Vue 的源码中使用了组合

「前端进阶」方方-前端体系课阶段二:前端精进(JavaScript 专精)_第70张图片

组合更占内存吗

面向对象虽然省了函数的内存(共有函数),但是原型链创建的对象省不了。

自由组合,虽然函数多了内存,但是都是复制过来的,因此原型链这部分的对象省了些。

因此,两种方式对内存的开销应该是差不多的。

你可能感兴趣的:(前端)