对知识的学习应当保持一种敬畏之心和不为艰难之心 --前言bash
var a = {n:1};
var b = a;
a.x = a = {n:2};
console.log(a); // {n:2}
console.log(a.x); //{undefined}
console.log(b); //{n: 1, x: {n:2}}
console.log(b.x); // {n:2}
复制代码
产生式 AssignmentExpression : LeftHandSideExpression = AssignmentExpression 按照下面的过程执行 :
1.令 lref 为解释执行 LeftH 和 SideExpression 的结果 .
2.令 rref 为解释执行 AssignmentExpression 的结果 .
3.令 rval 为 GetValue(rref).
4.抛出一个 SyntaxError 异常,当如下条件都成立 :
4.1Type(lref) 为 Reference
4.2IsStrictReference(lref) 为 true
4.3Type(GetBase(lref)) 为环境记录项
4.4GetReferencedName(lref) 为 "eval" 或 "arguments"
5.调用 PutValue(lref, rval).
6.返回 rval.
复制代码
也就是说,赋值操做是一个递归的操做。也就是说先执行LeftHandSideExpression(简记左值表达式),执行完以后在执行AssignmentExpression(最后的赋值操做)。讲下lref,lval,rref,rval的理解app
lref是等号左边表达式的计算结果,
lval是lref Getvalue以后的结果,也就是左值,
rref是等号右边表达式的计算结果,
rval是rref Getvalue以后的结果,也就是右值。
复制代码
再来看看左值表达式的定义:ide
Syntax
MemberExpression[Yield] :
PrimaryExpression[?Yield]
MemberExpression[?Yield] [ Expression[In, ?Yield] ]
MemberExpression[?Yield] . IdentifierName
MemberExpression[?Yield] TemplateLiteral[?Yield]
SuperProperty[?Yield]
MetaProperty
new MemberExpression[?Yield] Arguments[?Yield]
SuperProperty[Yield] :
super [ Expression[In, ?Yield] ]
super . IdentifierName
MetaProperty :
NewTarget
NewTarget :
new . target
NewExpression[Yield] :
MemberExpression[?Yield]
new NewExpression[?Yield]
CallExpression[Yield] :
MemberExpression[?Yield] Arguments[?Yield]
SuperCall[?Yield]
CallExpression[?Yield] Arguments[?Yield]
CallExpression[?Yield] [ Expression[In, ?Yield] ]
CallExpression[?Yield] . IdentifierName
CallExpression[?Yield] TemplateLiteral[?Yield]
SuperCall[Yield] :
super Arguments[?Yield]
Arguments[Yield] :
( )
( ArgumentList[?Yield] )
ArgumentList[Yield] :
AssignmentExpression[In, ?Yield]
... AssignmentExpression[In, ?Yield]
ArgumentList[?Yield] , AssignmentExpression[In, ?Yield]
ArgumentList[?Yield] , ... AssignmentExpression[In, ?Yield]
LeftHandSideExpression[Yield] :
NewExpression[?Yield]
CallExpression[?Yield]
复制代码
简记左值表达式有类型约束,只能为:函数
构造函数 :new F(),supper()
函数调用:a.f(), f(),f(1,2)
属性访问:a.i a.j
普通:i,j,1,"abc",this等变量,字面量,this等。
复制代码
再来看下左值表达式的属性访问的定义:学习
属性是经过 name 来访问的,可使用点表示法访问,
···省略一些
CallExpression . IdentifierName
是等同于下面的行为
CallExpression [ <identifier-name-string> ]
是一个字符串字面量,它与 Unicode 编码后的 IdentifierName 包含相同的字符序列。
·产生式 MemberExpression : MemberExpression [ Expression ] is evaluated as follows:
1.令 baseReference 为解释执行 MemberExpression 的结果 .
2.令 baseValue 为 GetValue(baseReference).
3.令 propertyNameReference 为解释执行 Expression 的结果 .
4.令 propertyNameValue 为 GetValue(propertyNameReference).
5.调用 CheckObjectCoercible(baseValue).
6.令 propertyNameString 为 ToString(propertyNameValue).
7.若是正在执行中的语法产生式包含在严格模式代码当中,令 strict 为 true, 不然令 strict 为 false.
8.返回一个值类型的引用,其基值为 baseValue 且其引用名为 propertyNameString, 严格模式标记为 strict.
9.产生式CallExpression : CallExpression [ Expression ] 以彻底相同的方式执行,除了第1步执行的是其中的CallExpression。
复制代码
也就是说若是左值表达式左边说经过属性访问,好比a.x它会执行上述操做。ui
在多=号操做时,好比 a = b = c.x = ····,因为左值表达式的特性,它会对等号的左侧的全部表达式进行lref和lval迭代操做,执行到最后右边时开始真正的赋值操做,也就是把右边的值挨个赋给左边,若是左侧的表达式不符合规范,则抛出错误,不然继续下一个迭代。 这里有一点就是若是计算出的lref是undefined,则赋值操做会被忽略,再赋值其值仍为undefined。如何理解呢,以下:this
let a = 'singleDog';
console.log(a.n); // undefined
a.n = 'i wanna have a girlfriend';
console.log(a.n) // undefined
let a1 = 13;
console.log(a1.n); // undefined
a1.n = 14;
console.log(a1.n) // undefined
let undefined = 111;
console.log(undefined) // undefined
复制代码
但有一个特例,这个特例对咱们解答原题有重要的辅助做用:编码
let a = {n:'I am a happy single dog'};
console.log(a.m) // undefined;
a.m = {q:'I have a lovely girlfriend'};
console.log(a.m) //{q:'I have a lovely girlfriend'};
复制代码
再看一下这样的内容:lua
let a = 1;
let b = a;
a =2;
console.log(a); //2
console.log(b); //1
let c = undefined;
c = 2;
console.log(c)
复制代码
至于第一段代码结果相信你们都能了解,这里就再也不赘述,写此题是为了方便接下来的题目理解。再来看下第二段代码,若是把一个undefined赋给c,在给c从新赋值,c能够从新获取值。这点很重要spa
再来看看 a.x = a = {n:2}的赋值过程:
a.x的lref → a的lref
↓ 计算rval
a.x调用PutValue(lref, rval) ← a调用PutValue(lref, rval) ←
复制代码
这里a.x的lref有两个:一个是对象a的属性引用获取即 a.x的lref => undefined
复制代码
另外一个a.x的lref计算过程能够抽象理解为b.x的lref求值。由于b是对a的引用 第一步a.x 的lref =>undefined,第二步b对a的引用,即b.x = undefined,此时b.x的lref为x,即第二个a.x的lref => x;
复制代码
最终执行到a.x的赋值:对对象a来讲:PutValue(undefined, {n:2}) ;
对对象b来讲:PutValue(x, {n:2})
复制代码
这也就解释了 为何的打印a.x为undefined,打印b.x为{n:2};
复制代码
当左侧表达式为结构表达式时,结构函数的变量名不会再函数初始化的时候,提早声明。只要运行表达式计算后才什么,并且是全局变量
console.log(j)//Uncaught ReferenceError: j is not defined
var i = {j} ={i:1,j:2}
console.log(j)//2
console.log(j)//Uncaught ReferenceError: j is not defined
var i = {j} ={i:1,k:2}
console.log(j)//undefine
function A(){
var i = { k} ={i:1,k:2}
console.log(k)//2
}
A()
console.log(k)//2
复制代码
看到这里咱们再来一道题目复习下前面所讲:
var a=1
function A(){
a={i:0,c:2}
return a
}
var b={c}=(A()).i=a.d=2
console.log(a) //结果请自行思考
console.log(b)
console.log(c)
复制代码
趁发际线还没那么高的时候!!!争取早日脱单把!!!!!其它都是浮云