共计 2301 个字符,预计需要花费 6 分钟才能阅读完成。
你晓得援用这个货色吗?
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 1
var x = 1;
delete x;
// case 2
window.x = 1;
delete x;
// case 3
with ({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
是怎么确认的?
- 假如 ref 是一个办法
- 如果
Type(ref)
为 Reference,那么 如果IsPropertyReference(ref)
(如果 ref 是 Env Record 则返回 false,如果是被其余援用携带,则返回 true)为 true,那么 令 thisValue 为GetBase(ref)
(返回[[BaseValue]]). - 否则 , ref 的基值是一个环境记录项(Env Record),令 thisValue 为调用 GetBase(ref) 的 ImplicitThisValue 具体方法的后果(ImplicitthisValue 始终返回 undefined)
- 否则 , 如果 Type(ref) 不是 Reference. 令 thisValue 为 undefined.
- 在非严格模式下,如果 thisValue 是 undefined,则会变成全局对象。
var value = 1;
var foo = {
value: 2,
bar: function() {return this.value;}
};
// 1
foo.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 外围原理剖析》