OLOO in JavaScript

在 Js 里 "OLOO" (objects-linked-to-other-objects) 较 OO (object-oriented):

  • 心智模型更直接
  • 语法更简洁
  • 生成的对象更少
  • 而 ES6中对 Class 的支持,只是可读性更好,本质上还是走原型委托。但是引用的 super 是静态绑定。而且还是会动态改变。

所以如果建模的话,选择 OLOO 。

refer https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/objects-classes/ch6.md#review-tldr

JavaScript 普通函数、箭头函数和this

  • 作为对象属性的普通函数,this 绑定为该对象。

  • 作为回调函数的普通函数,this 绑定为被触发对象。

  • 除了 call、apply、bind 以外能指定 this 参数,其余调用普通函数的地方,this 都绑定为全局对象。

  • 箭头函数没有自己的 this,也就是说没有自己的定义域。进一步说,就是会绑定定义时上层代码块的 this。

function Timer() {
    this.s1 = 0;
    this.s2 = 0;
    // 箭头函数
    setInterval(() => this.s1++, 1000);
    // 普通函数
    setInterval(function () {
        this.s2++;
    }, 1000);
}

假如 var timer = new Timer();
此时 timer.s1 可以正常绑定,.s2 被绑定到了全局对象。

再假如,直接调用函数 Timer(),此时 s1 也被绑定到了全局对象。
  • 即使作为对象方法,箭头函数内部的 this 也是指向的全局对象。所以箭头函数不适合作为函数方法。
  • 作为回调函数的箭头函数,this 绑定为全局对象。所以箭头函数不适合动态指定 this 的场合。
var x = 10;

let foo = {
  x: 20,

  // Dynamic `this`.
  bar() {
    return this.x;
  },

  // Lexical `this`.
  baz: () => this.x,

  qux() {
    // Lexical this within the invocation.
    let arrow = () => this.x;

    return arrow();
  },
};

console.log(
  foo.bar(), // 20, from `foo`
  foo.baz(), // 10, from global
  foo.qux(), // 20, from `foo` and arrow
);

一道有意思的Js 题,sum(1,2)(3,4).sumof()…

看到一道题有点意思:

题目:编写函数方法实现
  • 形式1:sum(1).sumof()
  • 形式2:sum(1,2).sumof()
  • 形式3:sum(1)(2, 3).sumof()
  • 最终求得的结果,需要是各个出现的数字的和。
目前想到的写法:
function sum(...lastArgs) {
    var callback = function(...args) {
        return sum(...[ ...lastArgs, ...args ]);
    };
    callback.sumof = function() {
        return lastArgs.reduce((aggregated, number) => aggregated + number, 0);
    };
    return callback;
}

为什么 promise 结合生成器使用更优雅?

promise 用来执行异步任务。

通过链式调用可以一步接一步的执行任务,那么为什么还要结合生成器呢?

比如这样的:

new promise(...).then(val1 => {resolve(val2)}).then(val2 => resolve()).catch();

变成这样的:

function* xx() {
    const val1 = yield new promise(...);
    const val2 = yield new promise(...);
}

最终产生了 async?
变成这样:

async function xx() {
    const val1 = await new promise(...);
    const val2 = await new promise(...);
    return
}

答案是用【同步写法】执行【异步任务】,这样看代码看的更清楚。而且使用了 async,还包括了自动执行,可以直接调用xx()来得到结果。

JavaScript 函数

  • 函数调用共有4种类型

    • 方法调用模式【作为对象属性], this 绑定为动态执行时的对象。因为方法可以被单独应用,所以this随着变化会变化。

      值得注意的是箭头函数,作为方法时 this 绑定到了 window 对象上了。

      因为内部箭头函数方法会在起初声明时就绑定不再变化。

    • 函数调用模式,this 绑定为 Window 对象或 undefined,取决于是否 “use strict”。

    • 构造器(构造函数)调用模式,this 绑定为动态运行时实际创建的对象。

      值得注意的是箭头函数,此时 this 绑定到了 新创建的这个对象上。

    • this,arguments 可被指定、每个函数都有的的 apply /call 调用模式,2者区别在于参数传递,前者传递一个参数数组,后者可传递多个参数,作为参数列表。

javascript 闭包与模块

闭包 closure :

Observational: closure is a function instance remembering its outer variables even as that function is passed around and invoked in other scopes.

<pre>
    <a href="">Visit the Apress website</a>
    <p>I like <span>apples</span> and oranges.</p>
    <a class="myclass1 myclass2" href="">Visit the W3C website</a>
</pre>

var pre = document.getElementsByTagName("pre")[0].children;
add_the_handlers(pre);
// error 
var add_the_handlers = function (nodes) {
    var i;
    console.log(nodes.length);
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = function (e) {
            alert(i);
        };
    }
};
nodes 节点可以绑定到 [0-1-2下标],但是 alert 执行时实际是对闭包环境变量的链接引用,此时 i 已经变成了3,因此最后点击时的 i 总会显示为3.
// ok
var add_the_handlers = function (nodes) {
    var helper = function (i) {
        return function (e) {
            alert(i);
        };
    };
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = helper(i);
    };
}
helper 创建了一个闭包,通过形参记录了每一个 i,同时 onclick 保持对该函数的引用,保持该词法环境一直存在。
onclick 触发时,每个执行函数都有自己对应的词法环境,每个环境里都有自己的 i 值。
// 更简单写法
var add_the_handlers = function (nodes) {
    console.log(nodes.length);
    for (let i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = function (e) {
            alert(i);
        };
    }
};
每次循环都会创建新的 i

模块,模块模式。利用函数作用域和闭包创建被绑定对象与私有成员的关联。

举例:

var serial_maker = function () {
    var prefix = '';
    var seq = 0;
    return {
        set_prefix: function (p) {
            prefix = String(p);
        },
        set_seq: function (s) {
            seq = s;
        },
        gensym: function () {
            var result = prefix + seq;
            seq += 1;
            return result;
        }
    }
}

var serialm = serial_maker()
serialm.set_prefix("qqqq")
serialm.set_seq(10000)
serialm.gensym()
->qqqq1000