你晓得援用这个货色吗?

eval('x = 100');
(0, eval)('x = 100');
首先先抛出一个问题,这两者有什么区别?

delete这个运算符

var obj = { x: 1 };delete obj.x;

置信大家应用delete的时候基本上是下面代码片段的场景,删除一个对象的一个属性。
那么以下这些表达式,他们又在删些什么?

delete 0;delete x;delete undefined;delete null;

下面几个语句,都能够执行,并且不会报错。
那么这个delete到底是什么神仙,在隐秘的角落里耍些什么花里胡哨的法术呢?

delete在Javascript1.2的时候就有了,那时候还没有try catch呢。
所以不论删除是否有问题,都不会报错,删除胜利或者没有作删除动作都会返回true,删除失败返回false

delete x;

下面这个语句,单纯在语法分析是不晓得它在删除什么。
实际上,delete运算符前面跟着一个芋圆表达式,不论前面是0, null, x,都视为一个表达式,并尝试删除它的后果。

一元表达式的后果,可能是,可能是援用

如果它是值,则依照约定间接返回true,并不做任何实际行动;
如果它是援用,则删除它(除非它不能被删除)。

这里说的援用不是咱们日常说的指针,这里说的援用是ECMAScript中的标准。

在ECMAScript标准里,有这些类型:
援用Reference 、 列表List 、 完结Completion 、 属性形容式 Property Descriptor、 属性标示Property Identifier 、 词法环境(Lexical Environment)、 环境纪录(Environment Record)

x = x;

实际上是

x = GetValue(x);

当一个表达式作为左手端的时候,它是一个援用,当一个表达式作为右手端的时候,它实际上会被GetValue变成一个值。

回到delete x;
delete是为数不多能间接操作援用的操作符,那么当初你们晓得为什么delete/typeof前面跟一个不存在的变量不会报错了吗?
因为它们都是间接操作援用,并没有作GetValue的操作,所以不会取值,就不会报错。

当初来考考大家

// case 1var x = 1;delete x;// case 2window.x = 1;delete x;// case 3with ({ x: 1 }) { delete x; }

这以上3个case,它们能删除掉x吗?

-----------上面是答案----------
当应用var申明的变量,会放在window上,大家都晓得,然而它理论是

Object.defineProperty(window, 'x', {    configurable: false,    value: 1});

并不能删除,所以case 1会返回false,示意删除失败。

case 2就不多解释了,删除胜利返回true。

case 3中,在with语句当中,会产生一个{ x: 1 }的词法环境,所以删除胜利并返回true。

援用长什么样

讲了这么久,援用长什么样子都不晓得。
它长这个样子

{    [[BaseValue]]: Env Record或者携带这个援用的对象    [[ReferencedName]]: 名字    [[StrictReference]]: 严格模式?}

函数的this是怎么确认的?

  1. 假如ref是一个办法
  2. 如果 Type(ref) 为 Reference,那么 如果 IsPropertyReference(ref) (如果ref是Env Record则返回false,如果是被其余援用携带,则返回true)为 true,那么 令 thisValue 为 GetBase(ref)(返回[[BaseValue]]).
  3. 否则 , ref 的基值是一个环境记录项(Env Record),令 thisValue 为调用 GetBase(ref) 的 ImplicitThisValue 具体方法的后果(ImplicitthisValue始终返回undefined)
  4. 否则 , 如果 Type(ref) 不是 Reference. 令 thisValue 为 undefined.
  5. 在非严格模式下,如果thisValue是undefined,则会变成全局对象。
var value = 1;var foo = {    value: 2,    bar: function() {        return this.value;    }};// 1foo.bar();// 2(foo.bar)();// 3(foo.bar = foo.bar)();// 4(false || foo.bar)();// 5(foo.bar, foo.bar)();

套用下面的步骤,大家本人答复一下答案是什么,如果能正确答复进去,那么基本上就弄懂了。

最初讲回eval这个办法
eval分为间接调用和间接调用
间接调用:作为Reference并且[[BaseValue]]是全局对象
间接调用:总是执行在全局环境中,总是执行在非严格模式下

eval环境是惟一一个将变量环境指向了与它本身的词法环境不同地位的环境。
啥意思?

eval('let x = 1;');console.log(x); // 报错,eval有本人的词法环境
eval('var x = 1;');console.log(x); // 1

下面两个代码片段,置信大家曾经看进去了,应用let申明的变量,是在eval外部的词法环境中,然而应用var申明的变量,是申明在了内部的变量环境。

当初大家能答复出

eval(‘x = 100’);(0, eval)(‘x = 100’);eval(‘this.x = 100’);(0, eval)(‘this.x = 100’);

他们的区别了吗?^_^

参考
极客工夫《JavaScript外围原理剖析》