记一次闭包问题

前言

在使用 promise.ify 包进行如下操作:

1
2
3
4
5
6
7
const promiseify = require('promise.ify');
const request = require('superagent');
const Request = request.Request;

// 使用 const req = yield req.endAsync();
// 配合 co 使用
Request.prototype.endAsync = promiseify(Request.prototype.end);

进行 n 此request, 发现每次 url 都是第一次的url.
promiseify@0.1.0 https://github.com/magicdawn/promise.ify/blob/v0.1.0/index.js#L13

示例

多次调用 makeFn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var makeFn = function(m, ctx){
return function(){
ctx = ctx || this;
m.call(ctx);
}
};

var foo = {
name: 'foo',
fn: function(){
console.log(this.name);
}
};
foo._fn = makeFn(foo.fn);
foo._fn(); // foo

var bar = {
name: 'bar',
fn: function(){
console.log(this.name);
}
};
bar._fn = makeFn(bar.fn);
bar._fn(); // bar

单次调用 makeFn

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
'use strict';

class T{
constructor(name){
this.name = name;
}

fn(){
console.log(this.name);
}
}

var makeFn = function(m, ctx){
return function(){
ctx = ctx || this;
m.call(ctx);
}
};

T.prototype._fn = makeFn(T.prototype.fn);

var foo = new T('foo');
var bar = new T('bar');

foo._fn(); // foo
bar._fn(); // foo

可以看到:

  1. 多次调用 makeFn, 是每次都为 makeFn 创建一个 execute contect, 这样即使修改这个执行上下文中的 ctx 都没有关系, 因为在 foo._fnbar._fn 中引用的 ctx 不是同一个
  2. 为 prototype 方法生成了一个 _fn, 调用了一次 makeFn, 为 makeFn 方法创建了一个执行上下文, 后续的实例的 _fn 引用的 ctx 都是一个, 于是 ctx = ctx || this, 将 ctx 修改为了第一个 this

解决办法

不要去修改公共的 ctx 就行

1
2
3
4
5
6
var makeFn = function(m, ctx){
return function(){
var _ctx = ctx || this;
m.call(_ctx);
}
};

资料

上述基本为之前知识积累 + 猜想. 还需要读读标准才行:

//TODO

  • execute context 执行上下文
  • lexical scope 词法作用域