army8735 / jsdc

Javascript Downcast (es6 to es5)
http://army8735.me/jsdc
93 stars 12 forks source link

Arrow Function 的实现不符合规范. #20

Closed lixiaoyan closed 9 years ago

lixiaoyan commented 10 years ago

this 与 arguments 需要处理

function test() {
  var fn = (a, b, c) => console.log(this, arguments, a, b, c);
  fn.call({name: "inner"}, 4, 5, 6);
}
test.call({name: "outer"}, 1, 2, 3);

// 实际应该输出
// {name: "outer"} [1, 2, 3] 4 5 6

// 你这个实现的输出
// {name: "inner"} [4, 5, 6] 4 5 6

对比实现:

参考链接:

var fn = (...arg) => console.log(arg);

上面代码应该正常解析.

hax commented 10 years ago

我不确定 arrow function 上是否可以调用 call ... 不过this应是lexical绑定这一点是无疑的。

lixiaoyan commented 10 years ago

@hax arrow function 上是可以 call 的, 上面代码只不过是证明 arguments 同 this 一样是 lexical 绑定的.

lixiaoyan commented 10 years ago

@hax 事实上, 无论是 call / apply / bind 在 arrow function 上都是工作的, 只不过 this 和 arguments 是 lexical 绑定的而已. 另外多说一句, chrome(v8) 目前为止似乎还不支持 arguments 的处理.

army8735 commented 10 years ago

这个应该好处理,在arrow function前记录下this和arguments的引用,替换掉执行的。

hax commented 10 years ago

对了,还有super。

army8735 commented 10 years ago

会出现super的场景么?

hax commented 10 years ago
class A {
  a() { return 'a' }
}
class B extends A {
  get b() { return x => super.a() + x + 'b' }
}
console.log(new B().b('x')) // => "axb"
army8735 commented 10 years ago
function A(){}
  A.prototype.a = function() { return 'a' }

function B(){A.call(this)}!function(){var _4=Object.create(A.prototype);_4.constructor=B;B.prototype=_4}();
  Object.defineProperty(B.prototype, "b", {get :function() { return function(x) {return A.prototype.a.call(this) + 'b'} }});
Object.keys(A).forEach(function(k){B[k]=A[k]});
console.log(new B().b())

是 ab

hax commented 10 years ago

题外话。B extends A要求B的[[prototype]]为A。从你贴的生成代码看是复制所有属性(但是没有复制原型上的属性,这可能是个bug),但是如果有__proto__时可以直接B.__proto__ = A

army8735 commented 10 years ago

没错吧? 复制所有属性那里,你说的是最后一句Object.keys那里吧,实际上应该说是“继承所有静态属性”。 一开始就是继承原型了啊,寄生组合式继承,Object.create(A.prototype)那里。

hax commented 10 years ago

Object.create(A.prototype)是子类的原型继承父类的原型,我说的是子类构造器继承父类构造器。B.__proto__ = A 之后你也不需要复制静态属性了,因为自动就继承了。

lixiaoyan commented 10 years ago

@hax __proto__ is a part of es6, not es5.

army8735 commented 10 years ago

一,一

army8735 commented 10 years ago
function test() {
  var fn = (a, b, c) => console.log(this, arguments, a, b, c);
  fn.call({name: "inner"}, 4, 5, 6);
}

编译成这样可否?

function test() {
  var temp1 = this, temp2 = arguments;
  var fn = (a, b, c) => { this = temp1; arguments = temp2; console.log(this, arguments, a, b, c); }
  fn.call({name: "inner"}, 4, 5, 6);
}
lixiaoyan commented 10 years ago

@army8735 你确认 this 能修改...?

() => { this, arguments }

替换成

var $_this = this, $_arguments = arguments;
function() { $_this, $_arguments }

类似这样的.

lixiaoyan commented 10 years ago

另外如果不考虑仅 strict mode 的话还得处理这种情况.

() => {
  {
    let arguments = {};
    console.log(arguments);
  }
}

这时这里的 arguments 不应该被替换.

army8735 commented 10 years ago

this不能改就麻烦了…… 第二个strict mode不用担心

lixiaoyan commented 10 years ago

@army8735 你是不是误解了什么... this 和 arguments 的 lexical 绑定只能通过替换 arrow function 里面的 this 和 arguments 实现. 后面那段评论的意思是并非所有情况下 arguments 都会被替换. 参考 traceur 的实现.

lixiaoyan commented 10 years ago
(function(){
  (()=>{
    console.log(arguments);
    {
      let arguments=["inner"];
      console.log(arguments);
    }
    console.log(arguments);
  })("outer");
})();

// 应该得到的输出:
// ["outer"]
// ["inner"]
// ["outer"]
// 测试浏览器: firefox 33.1

补充: 由于浏览器尚不支持 arguments 的 lexical 绑定, 所以参数调用放里面, 不影响当前这个测试.

lixiaoyan commented 10 years ago

无论是对 this 赋值还是对 arguments 赋值都不现实, 因为你无法保证代码运行环境是否在 strict mode 下, this 就不说了, 在 strict mode 下对 arguments 赋值会抛异常的. 另外就算用了变量替换, 上面那段代码也无法保证完美支持. 比如, 将上面的那个 let 套上一个 eval.

// ...
eval("let arguments=[\"inner\"];");
// ...

然后你会在 firefox 里的到一个非常坑的结果: outer / inner / inner. 我估计这大概也是 traceur 强制 strict mode 的一个原因吧.

补充: 上面两段代码均在非 strict mode 下运行.

hax commented 10 years ago

应该是只需考虑 strict。module下的代码自动都是strict。

hax commented 10 years ago

@army8735 __proto__ 很早就有了,几乎所有引擎都早已实现了(除了IE)。ES6标准化(实现细节有变化)之后,IE11也支持了。所以可以优先使用 __proto__。参见:https://github.com/hax/mmclass/blob/master/src/class.js#L76-L90

army8735 commented 10 years ago

只能用变量替换,忽视非strict了。

army8735 commented 10 years ago

https://github.com/hax/mmclass/blob/master/src/class.js#L78 pds没用到?

放弃ie11以下么,国内pc端还不行吧……

hax commented 10 years ago

嗯,78行好象是重构代码时漏删了。

79到87行就是处理IE11以下啊。其他浏览器只要第89行就结束了。