链表及二叉树

数据结构是计算机存储、组织数据的方式,为了高效获取和修改数据

线性结构: 顺序表(数组)和链表 …

  1. 为什么要发明链表? => 弥补数组的不足

  2. 数组的优缺点?优点查询方便,缺点增删改复杂

  3. 链表是如何进行增删改操作的?链表的操作,修改指针域的指向(改变锁链连接的对象)

    数组相关代码:V8 引擎 array.js 源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    const start_i = 2
    const del_count = 1
    const arr = [10, 24, 17, 18, 13, 14, 15, 12, 16, 15, 19, 11]

    console.log('原始的数组:', arr);
    SimpleMove(arr, start_i, del_count); // 298 line 部分代码
    console.log('删除后结果:', arr)

    function SimpleMove(array, start_i, del_count) {
    const len = arr.length

    // 循环数组[17, ..., 11]的部分
    for (var i = start_i; i < len - del_count; i++) {
    var from_index = i + del_count;

    // 从 3号下标数值开始 依次往前移动
    array[i] = array[from_index];
    }

    // delete 操作符 删除多余的部分
    for (var i = len; i > len - del_count; i--) {
    delete array[i - 1];
    }

    // 最后修改数组的 length
    arr.length = len - del_count
    }
  4. 可以直接 修改数组的 length 从而达到 ArrayPop() 方法的效果吗? 395 line

  5. array[array.length] = val; 和使用 ArrayPush() 差别? 415 line

树结构: 二叉树

前端基础知识回顾

一段html代码

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<a href='https://www.baidu.cn/'>点击取消跳转</a>
<ul>
<li>红灯</li>
<li>绿灯</li>
<li>黄灯</li>
</ul>
<script>
var nodeList = document.querySelectorAll('ul li')
var colorList = ['red', 'green', 'orange']
</script>
</body>

一、数组

  1. slice() 和 splice()

    colorList.slice(1) –> [‘green’, ‘orange’]
    colorList.slice(-2) –> [‘green’, ‘orange’]
    colorList.slice(1, 2) –> [‘green’]
    colorList.splice(2, 1) –> [“orange”]; colorList –> [‘red’, ‘green’]

    nodeList.slice(1) –> Uncaught TypeError: nodeList.slice is not a function
    Array.prototype.slice.call(nodeList, 1) –> [li, li]

  2. isArray 及 Polyfill

    Array.isArray(colorList) –> true
    Array.isArray(nodeList) –> false

    1
    2
    3
    4
    5
    if (!Array.isArray) {  
    Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
    };
    }
  3. ArrayLike => Array 几种方式

    var arr = Array.from(nodeList); Array.isArray(arr) –> true
    var arr = […nodeList]; Array.isArray(arr) –> true
    var arr = Array.prototype.slice.call(nodeList); Array.isArray(arr) –> true

二、事件监听

  1. DOM0、DOM2、DOM3

    DOM0 注意 onclick 大小写 document.querySelector(‘ul’).onclick = function () { alert(‘hello world’) }
    DOM2是通过addEventListener绑定的事件, 还有IE下的DOM2事件通过attachEvent绑定

  2. addEventListener第三个参数如果是 useCapture: true 和 false 分别代表什么?事件传播方式(事件冒泡、事件捕获),分别讲述两者不同

    target.addEventListener(type, listener, useCapture);

  3. 两种事件传播方式的执行顺序,console.log() 执行顺序 (默默的思念旭明1秒钟)

    1:事件捕获, 2:处于目标阶段, 3:事件冒泡阶段

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    var ulEventTarget = document.querySelector('ul')

    ulEventTarget.addEventListener('click', function (e) {
    console.log('ul', '捕获')
    }, true)

    ulEventTarget.addEventListener('click', function (e) {
    console.log('ul', '冒泡')
    }, false)

    nodeList[0].addEventListener('click', function (e) {
    console.log('li', '冒泡')
    }, false)

    nodeList[0].addEventListener('click', function (e) {
    console.log('li', '捕获')
    }, true)
  4. 点击穿透 stopPropagation() 取消冒泡或者捕获 及 取消跳转:preventDefault() 取消默认事件;

三、作用域 – 最小访问

1
2
3
4
5
for (var i = 0; i < nodeList.length; i++) {
nodeList[i].addEventListener('click', function (e) {
e.target.style.color = colorList[i]
})
}
  1. i –> 3

  2. colorList[i] –> undefined

  3. 如何解决

    3.1 简单方便的绑定一次,复制两次,改一改,优点:缓解脱发

    1
    2
    3
    4
    5
    6
    7
    8
    9
    nodeList[0].addEventListener('click', function (e) {
    e.target.style.color = colorList[0]
    })
    nodeList[1].addEventListener('click', function (e) {
    e.target.style.color = colorList[1]
    })
    nodeList[2].addEventListener('click', function (e) {
    e.target.style.color = colorList[2]
    })

    3.2 发现代码冗余后,抽取一个公共方法出来,一个莫名其妙的闭包就诞生了

    1
    2
    3
    4
    5
    6
    7
    8
    var handleEvent = function (color) {
    return function (e) {
    e.target.style.color = color
    }
    }
    nodeList[0].addEventListener('click', handleEvent(colorList[0]))
    nodeList[1].addEventListener('click', handleEvent(colorList[1]))
    nodeList[2].addEventListener('click', handleEvent(colorList[2]))

    3.3 发现可以帅帅的写个循环,发现代码行数竟然没啥变化,好沮丧啊!
    闭包 密闭的空间,块级作用域
    执行方法的时候,可以创建一个新方法,同时开辟一块空间
    此时新函数,保留了创建函数时的所需要参数 color 的值
    (录制了案发现场,储存 color 路过时的痕迹)

    console.log(this) –> <li>红灯</li>
    console.log(this) –> <li>绿灯</li>
    console.log(this) –> <li>黄灯</li>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var handleEvent = function (color) {
    return function (e) {
    e.target.style.color = color
    console.log(this)
    }
    }

    for (var i = 0; i < nodeList.length; i++) {
    nodeList[i].addEventListener('click', handleEvent(colorList[i]))
    }

四、ES6

1
2
3
4
5
6
7
const len = nodeList.length
for (let i = 0; i < len; i++) {
nodeList[i].addEventListener('click', e => {
console.log(this)
e.target.style.color = colorList[i]
})
}
  1. 箭头函数

    console.log(this) –> Window (指向外层 this)

  2. 阐述 let & const & var 区别、作用.

    let、const 块级作用域, const/let 不/允许重新赋值.
    colorList[i] –> ‘red’
    colorList[i] –> ‘green’
    colorList[i] –> ‘orange’

  3. 细节 避免重复查询数组长度(length)

    const len = nodeList.length

  4. for of 及 array.entries() 的应用

1
2
3
4
5
6
const handleEvent = color => e => e.target.style.color = color
const iterator = nodeList.entries();

for (let [i, node] of iterator) {
node.addEventListener('click', handleEvent(colorList[i]))
}

五、类、oop

  1. 类的定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    function Light(dom, color = "grey") {
    this.color = color
    this.isLight = false
    this.dom = dom
    this.turnOn = function() {
    this.isLight = true
    this.dom.style.color = this.color
    }

    this.turnOff = () => {
    this.isLight = false
    this.dom.style.color = 'grey'
    }

    this.switch = () => {
    this.isLight ? this.turnOff() : this.turnOn()
    }
    }
  2. 事件代理 及 switch case 使用 && LampBox.red.switch === LampBox.green.switch ?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    var LampBox = {}

    colorList.forEach((color, index) => {
    LampBox[color] = new Light(document.querySelectorAll('li')[index], color)
    })

    // console.log(LampBox.red.switch === LampBox.green.switch) false

    document.querySelector('ul').addEventListener('click', e => {
    switch (e.target.innerText) {
    case '红灯':
    LampBox.red.switch()
    break;
    case '绿灯':
    LampBox.green.switch()
    break;
    case '黄灯':
    LampBox.orange.switch()
    break;
    default:
    console.warn('bulubulu')
    }
    })

    借助 prototype 修改 Light

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    function Light(dom, color = "grey") {
    this.color = color
    this.isLight = false
    this.dom = dom
    }

    Light.prototype.turnOn = function() {
    this.isLight = true
    this.dom.style.color = this.color
    }

    Light.prototype.turnOff = function() {
    this.isLight = false
    this.dom.style.color = 'grey'
    }

    Light.prototype.switch = function() {
    this.isLight ? this.turnOff() : this.turnOn()
    }
  3. Map 操作 get、set

    1
    2
    3
    4
    5
    6
    var map = new Map()

    for (var i = 0; i < nodeList.length; i++) {
    map.set(nodeList[i], new Light(nodeList[i], colorList[i]))
    }
    document.querySelector('ul').addEventListener('click', e => map.get(e.target).switch())
  4. css 动画、异步操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    var map = new Map()

    function doScaledTimeout(i, time, cb) {
    setTimeout(function() {
    cb && cb(i)
    }, time);
    }

    const createLight = (e, color) => {
    if (color !== 'orange') {
    return new Light(e, color)
    }
    var yellowL = new Light(e, color)

    yellowL.turnOn = function() {
    this.isLight = true

    // css 动画
    this.dom.style.transition = 'color'
    this.dom.style.transitionDuration = '.5s'

    // color: 1000 yellow 1500 grey 2000 yellow 2500 grey 3000 yellow 3500 grey s
    var i = 0
    while (i < 3) {
    // 异步操作
    setTimeout(() => {
    this.dom.style.color = this.color
    }, 1000)
    setTimeout(() => {
    this.dom.style.color = 'grey'
    }, 500)
    i++
    }
    }
    return yellowL
    }

    for (var i = 0; i < nodeList.length; i++) {
    map.set(nodeList[i], createLight(nodeList[i], colorList[i]))
    }

    document.querySelector('ul').addEventListener('click', e => map.get(e.target).switch())
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    while (i < 3) {
    // 需要闭包
    setTimeout(() => {
    this.dom.style.color = this.color
    }, i * 1000);
    setTimeout(() => {
    this.dom.style.color = 'grey'
    // i => 3
    if (i === 2) {
    this.isLight = false
    }
    }, i * 1000 + 500);
    i++
    }

    while (i < 3) {
    doScaledTimeout(i, i * 1000, () => {
    this.dom.style.color = this.color
    })
    doScaledTimeout(i, i * 1000 + 500, (i) => {
    this.dom.style.color = 'grey'
    if (i === 2) {
    this.isLight = false
    }
    })
    i++
    }
  5. Promise 解决方案

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function turnOn() {
    return new Promise(function(resolve, reject) {
    setTimeout(() => {
    this.dom.style.color = this.color
    resolve(500)
    }, 1000);
    }.bind(this))
    }

    function turnOff() {
    return new Promise(function(resolve, reject) {
    setTimeout(() => {
    this.dom.style.color = 'grey'
    resolve()
    }, 500);
    }.bind(this))
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    function doPromiseTimeout(i) {
    return new Promise((resolve, reject) => {
    setTimeout(() => {
    this.dom.style.color = this.color
    resolve(500)
    }, 1000);
    }).then(delayMS => {
    return new Promise((resolve, reject) => {
    setTimeout(() => {
    this.dom.style.color = 'grey'
    resolve(delayMS)
    }, delayMS);
    });
    });
    }

    const createLight = (e, color) => {
    if (color !== 'orange') {
    return new Light(e, color)
    }
    const yellowL = new Light(e, color)

    yellowL.turnOn = function() {
    this.isLight = true

    // css 动画
    this.dom.style.transition = 'color'
    this.dom.style.transitionDuration = '.5s'

    const doTurnOn = doPromiseTimeout.bind(this)
    doTurnOn(0)
    .then(() => doTurnOn(1))
    .then(() => doTurnOn(2))
    }
    return yellowL
    }

    var map = new Map()

    for (var i = 0; i < nodeList.length; i++) {
    map.set(nodeList[i], createLight(nodeList[i], colorList[i]))
    }

    document.querySelector('ul').addEventListener('click', e => map.get(e.target).switch())
  6. while promise

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    const createLight = (e, color) => {
    if (color !== 'orange') {
    return new Light(e, color)
    }
    const yellowL = new Light(e, color)

    yellowL.turnOn = function() {
    this.isLight = true

    // css 动画
    this.dom.style.transition = 'color'
    this.dom.style.transitionDuration = '.5s'

    const doTurnOn = doPromiseTimeout.bind(this)
    var blink = Promise.resolve()

    var i = 0;
    while (i < 3) {
    blink = blink.then(() => doTurnOn(i))
    i++
    }

    blink.then(() => {
    this.isLight = false
    })
    }
    return yellowL
    }
  7. 子类 继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    // 闪烁类
    function Blink(dom, color = "grey", size) {
    // 1. 构造器继承 缺点,修改 this 上的方法时,父类的方法也会被修改
    Light.call(this, dom, color)
    this.size = size
    }

    /**
    * 2. 继承 父类的 Light.prototype ,
    * 为什么不使用 new Light() 来进行继承?
    * 因为理论上 Light 类实例化的时候时 是需要参数的,这个时候仅仅是继承,传与不传都不太合适
    *
    */
    Blink.prototype = Object.create(Light.prototype);

    /**
    * Blink.prototype.constructor === Light true
    *
    * 3. new Blink(...size) 时,
    * size依旧会传进来,对this.size 进行赋值,
    * 所以,不写也不会出错,只是有点奇怪,
    * 因为指向了 父类的构造器,而不是自己
    */
    Blink.prototype.constructor = Blink;

    Blink.prototype.blinkOn = function(ms) {
    return new Promise((resolve, reject) => {
    setTimeout(() => {
    this.dom.style.color = this.color
    resolve()
    }, ms);
    })
    }

    Blink.prototype.blinkOff = function(ms) {
    return new Promise((resolve, reject) => {
    setTimeout(() => {
    this.dom.style.color = 'grey'
    resolve(ms)
    }, ms);
    })
    }

    Blink.prototype.doPromiseTimeout = function(ms) {
    return this.blinkOn(ms).then(() => this.blinkOff(500))
    }

    Blink.prototype.turnOff = function() {
    setTimeout(() => {
    this.isLight = false
    this.dom.style.color = 'grey'
    this.dom.style.fontSize = '12px'
    }, 500);
    }

    Blink.prototype.turnOn = function() {
    this.isLight = true
    this.dom.style.transition = 'color'
    this.dom.style.transitionDuration = '.5s'
    this.dom.style.fontSize = this.size

    let blink = Promise.resolve(1000)

    let i = 0;
    while (i < 3) {
    blink = blink.then((ms) => this.doPromiseTimeout(ms))
    i++
    }

    blink = blink.then(() => this.turnOff())
    }
    1
    2
    3
    4
    5
    6
    const createLight = (e, color) => {
    if (color !== 'orange') {
    return new Light(e, color)
    }
    return new Blink(e, color, '24px')
    }
  8. 如何实现一个 promise.all (待完善)

  9. generator yield next (待完善)

  10. async await (待完善)

  11. 实现 bind()

    const bar = foo.bind(this)
    bar()

    1
    2
    3
    4
    5
    6
    Function.prototype.bind = function (ctx) {
    var that = this;
    return function () {
    that.apply(ctx);
    }
    }

    const bar = foo.bind(this)
    bar(arg1, arg2)

    1
    2
    3
    4
    5
    6
    Function.prototype.bind = function(ctx) {
    var that = this;
    return function() {
    return that.apply(ctx, arguments);
    }
    }

    const bar = foo.bind(this, arg1, arg2)
    bar(arg3)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Function.prototype.bind = function (ctx) {
    var that = this;
    var args = Array.prototype.slice.call(arguments, 1);

    return function () {
    var bindArgs = Array.prototype.slice.call(arguments);
    that.apply(ctx, args.concat(bindArgs));
    }
    }

    当作为构造函数时,this 指向实例,that 指向绑定函数,因为下面一句 fbound.prototype = this.prototype;,已经修改了 fbound.prototype 为 绑定函数的 prototype,此时结果为 true,当结果为 true 的时候,this 指向实例。

    当作为普通函数时,this 指向 window,self 指向绑定函数,此时结果为 false,当结果为 false 的时候,this 指向绑定的 ctx。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Function.prototype.bind = function (ctx) {
    var that = this;
    var args = Array.prototype.slice.call(arguments, 1);
    var fNOP = function () {};

    var fbound = function () {
    var bindArgs = Array.prototype.slice.call(arguments);
    that.apply(this instanceof fNOP ? this : ctx, args.concat(bindArgs));
    }
    fNOP.prototype = this.prototype;
    fbound.prototype = new fNOP();
    return fbound;
    }

    e.g.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function Point(x, y) {
    this.x = x;
    this.y = y;
    this.getPoint = function() {
    console.log(this)
    }
    }

    var p = new Point(1, 2);
    p.getPoint(); // '1,2'

    var Point2 = Point.bind2(this, 0);

    var p2 = new Point2(5);
    p2.getPoint(); // '0,5'

    console.log(p2 instanceof Point, p2 instanceof Point2, p instanceof Point2);

    我们直接将 fbound.prototype = this.prototype,我们直接修改 fbound.prototype 的时候,也会直接修改函数的 prototype。so:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    Function.prototype.bind = Function.prototype.bind || function (ctx, ...formerArgs) {
    if (typeof this !== 'function') {
    throw new TypeError("NOT_A_FUNCTION -- this is not callable");
    }
    let that = this;
    let fNOP = function () {};
    let args = Array.prototype.slice.call(arguments, 1);

    let fbound = function (...laterArgs) {
    that.apply(this instanceof fNOP ? this : ctx, args.concat(laterArgs));
    };

    if (this.prototype) {
    fNOP.prototype = this.prototype;
    }

    fbound.prototype = new fNOP();
    return fbound;
    };

Javascript 作用域引发的一系列思考

apply & call & 闭包 & 块级作用域

题目:点亮三盏灯

1
2
3
4
5
6
7
8
<body>
<h3>点亮三盏灯</h3>
<ul>
<li>红灯</li>
<li>绿灯</li>
<li>黄灯</li>
</ul>
</body>

freshman
colorList.slice(1) –> [‘green’, ‘orange’]
colorList.slice(-2) –> [‘green’, ‘orange’]
colorList.slice(1, 2) –> [‘green’]
colorList.splice(2, 1) –> [“orange”]; colorList –> [‘red’, ‘green’]
Array.isArray(colorList) –> true
Array.isArray(nodeList) –> false
var arr = Array.from(nodeList); Array.isArray(arr) –> true
var arr = […nodeList]; Array.isArray(arr) –> true
var arr = Array.prototype.slice.call(nodeList); Array.isArray(arr) –> true
nodeList.slice(1) –> Uncaught TypeError: nodeList.slice is not a function
Array.prototype.slice.call(nodeList, 1) –> [li, li]

1
2
var nodeList = document.querySelectorAll('ul li')
var colorList = ['red', 'green', 'orange']
1
2
3
4
5
6
7
8
9
nodeList[0].addEventListener('click', function (e) {
e.target.style.color = colorList[0]
})
nodeList[1].addEventListener('click', function (e) {
e.target.style.color = colorList[1]
})
nodeList[2].addEventListener('click', function (e) {
e.target.style.color = colorList[2]
})

sophomore
i –> 3
colorList[i] –> undefined

1
2
3
4
5
for (var i = 0; i < nodeList.length; i++) {
nodeList[i].addEventListener('click', function (e) {
e.target.style.color = colorList[i]
})
}

junior
闭包

1
2
3
4
5
6
7
8
var handleEvent = function (color) {
return function (e) {
e.target.style.color = color
}
}
nodeList[0].addEventListener('click', handleEvent(colorList[0]))
nodeList[1].addEventListener('click', handleEvent(colorList[1]))
nodeList[2].addEventListener('click', handleEvent(colorList[2]))

senior
console.log(this) –> <li>红灯</li>
console.log(this) –> <li>绿灯</li>
console.log(this) –> <li>黄灯</li>

1
2
3
4
5
6
7
8
9
10
var handleEvent = function (color) {
return function (e) {
e.target.style.color = color
console.log(this)
}
}

for (var i = 0; i < nodeList.length; i++) {
nodeList[i].addEventListener('click', handleEvent(colorList[i]))
}

ES6 let const 块作用域 箭头函数
console.log(this) –> Window
colorList[i] –> ‘red’
colorList[i] –> ‘green’
colorList[i] –> ‘orange’
const len = nodeList.length 小细节

1
2
3
4
5
6
7
const len = nodeList.length
for (let i = 0; i < len; i++) {
nodeList[i].addEventListener('click', e => {
console.log(this)
e.target.style.color = colorList[i]
})
}

ES6 for of array.entries()

1
2
3
4
5
6
const handleEvent = color => e => e.target.style.color = color
const iterator = nodeList.entries();

for (let [i, node] of iterator) {
node.addEventListener('click', handleEvent(colorList[i]))
}

1
2
3
4
5
6
Function.prototype.bind = function (context) {
var that = this;
return function () {
that.apply(context);
}
}
1
2
3
4
5
6
Function.prototype.bind = Function.prototype.bind || function(context) {
var that = this;
return function() {
return that.apply(context, arguments);
}
}
1
2
3
4
5
6
7
8
9
Function.prototype.bind = function (context) {
var that = this;
var args = Array.prototype.slice.call(arguments, 1);

return function () {
var bindArgs = Array.prototype.slice.call(arguments);
that.apply(context, args.concat(bindArgs));
}
}
1
2
3
4
5
6
7
8
9
10
11
Function.prototype.bind = function (context) {
var that = this;
var args = Array.prototype.slice.call(arguments, 1);

var fbound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
that.apply(this instanceof that ? this : context, args.concat(bindArgs));
}
fbound.prototype = this.prototype;
return fbound;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Function.prototype.bind = Function.prototype.bind || function (context, ...formerArgs) {
let that = this;

if (typeof this !== 'function') {
throw new TypeError("NOT_A_FUNCTION -- this is not callable");
}
let fNOP = function () {};

let fbound = function (...laterArgs) {
that.apply(this instanceof that ? this : context, formerArgs.concat(laterArgs));
};

fNOP.prototype = this.prototype;

fbound.prototype = new fNOP();
return fbound;
};

Lesson 12 Goodbye and good luck

Our neighbour, Captain Charles Alison, will sail from Portsmouth tomorrow. We’ll meet him at the harbour early in the morning. He will be in his small boat, Topsail. Topsail is a famous little boat. It has sailed across the Atlantic many times. Captain Alison will set out at eight o’clock, so we’ll have plenty of time. We’ll see his boat and then we’ll say goodbye to him. He will be away for two months. We are very proud of him. He will take part in an important race across the Atlantic.

Lesson 11 One good turn deserves another

I was having dinner at a restaurant when Tony Steele came in. Tony worked in a lawyer’s office years ago, but he is now working at a bank. He gets a good salary, but he always borrows money from his friends and never pays it back. Tony saw me and came and sat at the same table. He has never borrowed money from me. While he was eating, I asked him to lend me twenty pounds. To my surprise, he gave me the money immediately. ‘I have never borrowed any money from you,’ Tony said, ‘so now you can pay for my dinner!’

Lesson 10 Not for jazz

We have an old musical instrument. It is called a clavichord. It was made in Germany in 1681. Our clavichord is kept in the living room. It has belonged to our family for a long time. The instrument was bought by my grandfather many years ago. Recently it was damaged by a visitor. She tried to play jazz on it! She struck the keys too hard and two of the strings were broken. My father was shocked. Now we are not allowed to touch it. It is being repaired by a friend of my father’s.

Lesson 9 A cold welcome

On Wednesday evening, we went to the Town Hall.
It was the last day of the year and a large crowd of people had gathered under the Town Hall clock. It would strike twelve in twenty minutes’ time. Fifteen minutes passed and then, at five to twelve, the clock stopped. The big minute hand did not move. We waited and waited, but nothing happened. Suddenly someone shouted, ‘It’s two minutes past twelve! The clock has stopped!’ I looked at my watch. It was true. The big clock refused to welcome the New Year. At that moment, everybody began to laugh and sing.

Lesson 8 The best and the worst

Joe Sanders has the most beautiful garden in our town. Nearly everybody enters for ‘The Nicest Garden Competition’ each year, but Joe wins every time. Bill Frith’s garden is larger than Joe’s. Bill works harder than Joe and grows more flowers and vegetables, but Joe’s garden is more interesting. He has made neat paths and has built a wooden bridge over a pool. I like gardens too, but I do not like hard work. Every year I enter for the garden competition too, and I always win a little prize for the worst garden in the town!

Lesson 7 Too late

The plane was late and detectives were waiting at the airport all morning. They were expecting a valuable parcel of diamonds from South Africa.
A few hours earlier, someone had told the police that thieves would try to steal the diamonds. When the plane arrived, some of the detectives were waiting inside the main building while others were waiting on the airfield. Two men took the parcel off the plane and carried it into the Customs House. While two detectives were keeping guard at the door, two others opened the parcel. To their surprise, the precious parcel was full of stones and sand!