1.对象属性名相关知识:
练习题:写出下列代码的输出结果
//example 1
var a={}, b='0', c=0;
a[b]='你好';
a[c]='世界';
console.log(a[b]);
---------------------
//example 2
var a={}, b=Symbol('1'), c=Symbol('1');
a[b]='你好';
a[c]='世界';
console.log(a[b]);
---------------------
//example 3
var a={}, b={n:'1'}, c={m:'2'};
a[b]='你好';
a[c]='世界';
console.log(a[b]);
obj['name'] 获取成员为name的属性值 obj[name] 把name变量存储的值作为成员获取其属性值 对象的属性名可以是:
- 字符串
- Symbol;
- 数字/布尔/null/undefined等基本数据类型值(用这些值直接处理和基于他们的字符串格式处理结果是一样的)
也有人认为属性名除了Symbol只能是字符串格式的
对于普通对象来讲,属性名不能是引用数据类型值(比如对象),设置为对象,也会转换为字符串;ES6中新增Map数据结构,这个结构中可以允许属性名是一个对象;

var obj = {
name: 'xxx',
0: 100,
true: 11,
null:10,
'null':12,
undefined:99
};
obj[Symbol('AA')] = 100;
var o1 = {
name: 'xxx'
};
obj[o1] = 100; // obj["[object Object]"]=100
console.log(obj);
/*//输出结果
{
'0': 100,
name: 'xxx',
true: 11,
null: 12,
undefined: 99,
'[object Object]': 100,
[Symbol(AA)]: 100
}
// Symbol类型不能直接写对象属性
var obj={
a:1,
Symbol('a'):10
}
//要写出下面这样
obj[Symbol('a')]=10;
现在分析例题:
//example 1
var a={}, b='0', c=0;
a[b]='你好'; //=>a['0']='你好'
a[c]='世界'; //=>a[0]='世界';
console.log(a[b]); //=>'世界'
//example 2
var a={}, b=Symbol('1'), c=Symbol('1'); //b!==c唯一值
a[b]='你好';
a[c]='世界';
console.log(a[b]);//=>'你好'
//example 3
var a={}, b={n:'1'}, c={m:'2'};
a[b]='你好'; //=>a['object Object']='你好'
a[c]='世界'; //=>a['object Object']='世界'
console.log(a[b]); //=>'世界'
2.JS底层运行机制之堆(Heap)栈(Stack)
- ECStack(Execution [ˌeksɪˈkjuːʃn] Context Stack)和 EC(Execution Context )
- GO(Global Object)
- VO(Varibale Object)
练习题:写出下列代码的输出结果
var a = {
n: 1
};
var b = a;
a.x = a = {
n: 2
};
console.log(a.x);
console.log(b);
此题的图解:
图解分析:
- 浏览器为了代码运行提供环境即栈内存【执行环境栈ECStack】,栈内存就是在计算机中分配出来的一块内存;
- 全局代码执行前形成全局执行上下文EC(G),全局代码都在EC(G)中执行,而EC(G)也要进入浏览器提供的这个执行环境栈ECStack中。在这个全局执行上下文中创建的
变量总会存储在当前上下文中指定的**全局变量对象[VO (G)]**中。浏览器默认创建的一个全局变量是window(在nodeJS中默认创建的全局变量是global) - 浏览器最开始加载代码的时候,不仅提供了一个栈内存供代码执行;而且还默认开辟了一个堆内存,存储一些内置的属性和方法,即全局对象Global Object(GO)
- 全局对象GO!==全局变量对象VO(G),两者的关系只是:VO(G)中的一个变量window是属性名,属性值是GO,两者相互关联。
- “在全局上下文中”,基于“var/function”声明的全局变量,也会给GO(window)中新增一个对应的私有属性,并且和全局的变量有“映射机制”:一个修改,另外一个也会跟着修改
// 1.声明一个全局变量a=10 2.给window新增一个私有属性 window.a=10
var a = 10;
console.log(a);
//首先看a是否为全局变量,如果是按照全局变量处理,如果不是全局变量,再看是否为window的一个属性,如果也不是window的属性则报错:a is not defined
console.log(window.a);
//直接访问对象的成员
window.a = 20; //“映射机制” 全局变量a=20
console.log(a);
//-----------------------------------------------
// 基于“let/const”声明的全局变量和window没有关系
let a = 10; //全局变量a
console.log(a); //10
console.log(window.a); //undefined
//----------------------------------------------
// 在全局上下文中
a = 10; //不是全局变量,window.a=10,相当于省略了window(前提:确定之前没有声明过)
==带声明的关键词和不带是不一样的机制,所以真实项目中请大家不要省略== 6. 连等赋值 在MDN网站查看“运算符优先级”,赋值等号的优先级是3,从右到左,所以【var a=b=10】=>【先b=10;再var a=10;】。【a.x=b={}】,因为a.x的优先级高(点成员访问的优先级是19),所以【先a.x={},再b={}】
3.函数底层运行机制
全面剖析函数的底层处理机制
- EC(Execution Context )
- AO(Active Object)
- SCOPE
- SCOPE-CHAIN
练习题:写出下列代码的输出结果
var x = [12, 23];
function fn(y) {
y[0] = 100;
y = [100];
y[1] = 200;
console.log(y);
}
fn(x);
console.log(x);
此题的图解:
由此题所引申的知识点:
创建函数:
1.开辟堆内存(16进制地址)
2.把函数体内部的代码,当作“字符串”存储到堆中[当然也有键值对,函数也是对象]
3.把地址存到栈中,供变量(函数名)调用
=>创建函数的时候,就声明了函数的“作用域”,值是当前函数创建时所处的上下文
创建函数但不执行,毛用都没有,存储的一-对破字符串
函数执行 目的:是把之前创建函数时候,在函数体中存储的代码字符串去执行的
- 形成一个全新的私有上下文
- 有存储当前私有上下文中声明的私有变量的空间AO,AO(ActiveObject)他是VO的一种
- 把这个私有上下文进栈执行
- 代码执行之前要做好多事情
- 初始化作用域链
- 初始化this指向
- 初始化arguments
- 形参赋值
- 变量提升
- 代码执行
- 一般情况下,函数执行完成后,为了优化栈内存,会把形成的私有上下文出栈释放掉“GC浏览器垃圾回收机制”
- 作用域链查找机制:当前私有上下文下代码执行,遇到一个变量,首先看是否为私有变量,如果是私有的,则接下来操作,都是操作自己私有的,和其他上下文没有任何直接的关系;如果不是自己私有变量,则按照作用链,向上级_上下文中查找...如果上级也没有,一直找到全局上下文;如果全局也没有设置变量值:相当于给window设置属性值。如果是获取这个值:报错。
- 闭包:函数执行,会形成一个私有的上下文,保护里面的私有变量不受外界的干扰,我们把函数执行的这种保护机制,称之为“闭包”
- 作用域链scope--chain:<EC(fn),EC(G)> 左端EC(fn):当前上下文 右端EC(G):函数的作用域(函数创建时候所处的上下文)
- 进栈执行(栈结构:进栈执行的内容都在栈的顶端)
- 函数中的私有变量:形参是私有变量;当前上下文中声明过的变量是私有变量。