一.词法解析和变量提升
1.1 创建变量
第二:var可以先使用,后声明;let必须先声明后使用。
第三:var是允许在相同作用域内重复声明同一个变量的,而let与const不允许这一现象。
第四:在全局上下文中,基于let声明的全局变量和全局对象GO(window)没有任何关系 ; var声明的变量会和GO有映射关系;
第五:解决暂时性死区:
第六:let /const/function会把当前所在的大括号(除函数之外)作为一个全新的块级上下文,应用这个机制,在开发项目的时候,遇到循环事件绑定等类似的需求,无需再自己构建闭包来存储,只要基于let的块作用特征即可解决
1.2 变量提升
- 带var的只是提前声明,没有赋值,默认值是undefined。
- 带function的声明加赋值。
- 不带var的
a=3
表示给全局设置window.a=3
和在全局作用域下var a=3
是一样的;
//因为有变量提升机制,所以这段代码不会报错。
console.log(foo);//undefined
bar();//2
var foo = 1;
function bar() {
if (!foo) {
var foo = 2;
}
console.log(foo);
}
console.log(foo);//foo() {}
function foo() {}
console.log(foo);//undefined
{
function foo() {}
}
var a = 0;
if (true) {
a = 1;
function a() {}//这前面的对a的操作映射一份给全局,后面的留给自己
a = 11;
console.log(a);//11
}
console.log(a);//1
var a = 0;
if (true) {
a = 1;
function a() {}
a = 11;
function a() {}//这前面的对a的操作映射一份给全局,后面的留给自己
console.log(a); //11
}
console.log(a); //11
{
function foo() {
alert("1");
}
foo = 1;
function foo() {
alert("2");
} //把之前的对a的操作映射给全局一份(包括他自己也弄没了),后面的全是自己的
foo = 2;
console.log(foo); // => 2
}
console.log(foo); // => 1
二.堆栈内存及垃圾回收机制
堆内存:浏览器会把内置的属性和方法放到一个单独的内存中,
js 中存在多种作用域(全局,函数私有的,块级私有的),代码执行前首先会形成自己的执行上下文,然后把上下文进栈,进栈后,在当前上下文再依次执行代码; 全局执行器上下文(EC(G))进栈(ECStack)执行,执行完代码就会把形成的上下文释放(出栈),当页面关闭全局上下文出栈;
VO 变量对象:每一个执行上下文都会有自己的一个VO变量对象,用来存放在当前上下文中创建的变量和函数。(函数私有上下文叫 AO 活跃对象,但也是变量对象)。
GO 全局对象:他是一个堆内存(存储的都是浏览器内置的 api 属性方法),在浏览器端,让 window 指向它
VO(G)全局变量对象:全局上下文中用来存储全局变量的空间,他不是 GO=》只不过某些情况下 VO(G)中的东西会和 GO 中的东西有所关联而已;
-
函数执行的时候,形成一个全新的私有上下文
EC(FN)
,共字符串代码执行 -
进栈执行,从上面进去,把全局往下压
-
私有上下文有私有变量对象 AO(FN),在私有上下文中创建的对象会放到这里来;
-
代码执行之前还需要:
-
- 1.初始化作用域链(scopeChain):<EC(FN),EC(G)>
- 1.初始化作用域链(scopeChain):<EC(FN),EC(G)>
-
- 2.初始化 this 指向:window
- 2.初始化 this 指向:window
-
- 3.初始化实参集合:arguments
- 3.初始化实参集合:arguments
-
- 4.形参赋值
- 4.形参赋值
-
- 5.变量提升
- 5.变量提升
-
- 6.代码执行
- 6.代码执行
垃圾回收机制
2、浏览器垃圾回收机制/内存收机制:
3、优化手段:内存优化 ; 手动释放:取消内存的占用即可。
三.作用域和作用域链
四.闭包及其两大作用:保护、保存
我们把函数执行形成私有上下文,来保护和保存私有变量机制称为闭包
。
五.高阶编程:多型函数、柯理化函数、compose组合函数
六.BAT经典面试题
- 题1:
{
function foo() {}
foo = 1;
console.log(foo);//1
}
console.log(foo);//function
/*
EC(G):{
大括号中出现let/const/function...都会被认为是块级作用域;
function的只提前声明,不会提前赋值;
1. 变量提升:foo
2. 代码执行:EC(block)块级作用域{
AO: foo-----AF0 改foo=1
1 变量提升 foo
2 代码执行: function foo() { } ==》AF0
//foo在全局声明过,为了兼容es3,浏览器会把这行代 码之前对foo操作映射到全局 :把全局的foo---AF0
foo = 1
}
代码执行2:console.log(foo);//function foo(){}
}
*/
- 题2:
let x = 1;
function A(y) {
let x = 2;
function B(z) {
console.log(x + y + z);
}
return B;
}
let c = A(2);
c(3); //==>7
- 题3:
let x = 5;
function fn(x) {
return function (y) {
console.log(y + ++x);
};
}
let f = fn(6);
f(7); //14
fn(8)(9); //18
f(10); // 18
console.log(x); //5
- 题4:
let a = 0,
b = 0;
function A(a) {
A = function (b) {
console.log(a + b++);
};
console.log(a++);
}
A(1); //1
A(2); //4
- 题5:
var a = 10,
b = 11,
c = 12; //c=3
function test(a) {
a = 1;
var b = 2;
c = 3;
}
test(10);
console.log(a, b, c);
/*解析:
EC(G)
变量提升:a=10 ,b=11,c=12 ,
function test(){} scope:EC(G) 形参:a 存放到AF0中
代码执行1:test(10)---AF0(10)
{
形参赋值:a=10 改a=1
变量提升:a=1(找到自己形参改), b=2,c=3(找到上级修改)
代码执行:
}
代码执行2:console.log(a, b, c);//10,11,3
*/
- 题6:
var a = 4;
function b(x, y, a) {
console.log(a);
arguments[2] = 10;
console.log(a);
}
a = b(1, 2, 3);
console.log(a);
// 3 10 undefined
/*
EC(G):{
变量提升:a=4,
function b(){...} scope:EC(G) 形参:x,y,a 存到AF0中
代码执行:a=b(1,2,3) -->a=AF0(1,2,3) 形成EC(A)
console.log(a)==>undefined
}
EC(A)私有上下文 进栈执行{
形参赋值:x=1,y=2,a=3,a=10
变量提升
代码执行: console.log(a); ==>3
arguments[2] = 10; a=10
console.log(a); ==>10
函数执行完毕后返回给a=undefined
}
*/
- 题7:初始化实参集合
function func(x, y, z) {
x = 100;
console.log(arguments[0]);
y = 200;
console.log(arguments[1]);
z = 300;
console.log(arguments[2]);
}
func(1, 2); //100 200 undefined
/*
执行函数时要初始化实参集合arguments:{0:10,1:20,length:2}
形参赋值:x=10 ,y=20 ,z=undefined
映射关系:x---argunments[0]
y---argunments[1]
z---argunments[2]
*/
- 题8:
var a = 9;
function fn() {
a = 0;
return function (b) {
return b + a++;
};
}
var f = fn();
console.log(f(5));
console.log(fn()(5));
console.log(f(5));
console.log(a);
/*
EC(G)全局上下文:{
VO: a=9 fn=AF0 f=AF0() a=0 f=BF0 a=1,a=0 ,a=1,a+1=2
变量提升:a
代码执行:f=fn()
形成EC(Fn)上下文:{
AO:
变量提升:
代码执行:a=0 //把全局的a改为0
return function (b) { return b + a++; };把这个函数作为结果赋值给f=BF0
}
}
AF0函数堆:{`
a = 0;
return function (b) {
return b + a++;
};
`}
BF0函数堆:{`
function (b) { return b + a++; };
`}
全局中代码执行1: console.log(f(5));
{
console.log(f(5));相当于打印console.log(5+a++);//5+0=5
私有:b=5
全局:a=0 a++=0 然后把全局的a+1
}
全局中代码执行2:console.log(fn()(5));//5
{
执行fn(),又把a=0了,返回function (b) { return b + a++;};
再把5传进去执行这个函数 5+0=5 全局下a+1
}
全局中代码执行3:console.log(f(5))// 5+1=6 全局a+1
全局中代码执行4:console.log(a);//2
*/
- 题9:
var test = (function (i) {
return function () {
alert(i * 2);
};
})(2);
test(5); //4
- 题10:
var x = 5,
y = 6;
function func() {
x += y;
func = function (y) {
console.log(y + --x);
};
console.log(x, y);
}
func(4);
func(3);
console.log(x, y);
/*
EC(G)全局上下文:{
VO: x=5,y=6,func=BF0,x=11,x=10
变量提升:x,y,func,
代码执行:func(4)---AF0(4)
形成EC(Fn)上下文:{
AO:
变量提升:
代码执行1:x+=y ==》x=11赋值给全局
代码执行2:func=BF0 形参:y
代码执行3:console.log(x, y);//输出11,6
}
代码执行func(3)---BF0(3)
形成EC(Fn1)上下文:{
AO:y=3
变量提升:
代码执行1:y+--x =3+10=13 全局x=10 //输出13
}
代码执行:console.log(x, y);//10,6
}
AF0函数堆:{
x += y;
func = function (y) { console.log(y + --x); };
console.log(x, y);
}
BF0函数堆:{
console.log(y + --x);
}
*/
- 题11:
function fun(n, o) {
console.log(o);
return {
fun: function (m) {
return fun(m, n);
},
};
}
var c = fun(0).fun(1);
c.fun(2);
c.fun(3);
/*
EC(G)全局上下文:{
VO: fun=AF0 ,c=fun(0).fun(1)[此刻要执行函数],c=BF0
变量提升:fun,c,
代码执行:c=fun(0).fun(1)--》AF(0).AF0(1)
形成EC(Fn1)上下文:{
AO:n=1,o=0
变量提升:
代码执行1:console.log(o);//undefined
代码执行2:return { fun: function (m) {return fun(m, n); },返回了一个对象,形成一个对象堆存储BF0
代码执行3:BF0.fun(1)执行了BF0对象的fun方法,传参数1===》执行BF1(1)里面的函数
形成BF1(1)上下文:{
AO:m=1
变量提升:
代码执行:return fun(m, n) //m=1自己的,n=0上级的,==》return fun(1,0)===>0
并且还返回了BF0对象给c
}
};
}
代码执行:c.fun(2);==>m=2,n=1==>fun(2,1)//1
代码执行:c.fun(3);==>m=3,n=1==>fun(3,1)//1
}
AF0函数堆 形参为:n,o {
console.log(o);
return {
fun: function (m) {return fun(m, n); },
};
}
BF0对象堆{
` fun: BF1 `
}
BF1函数堆{
function (m) {return fun(m, n)
}
*/
- 题12:
let a = 1;
function fn1() {
let a1 = 2;
function fn2() {
let a2 = 3;
function fn3() {
let a3 = 4;
a = a1 + a2 + a3;
console.log(a);
}
fn3();
}
fn2();
}
fn1();//9
- 题13:1.函数作为参数被传递
let a = 100;
function f1() {
console.log(a);
}
function f2(f) {
let a = 200;
f();
}
f2(f1);//100
- 题14:2.函数作为返回值被返回
function create(){
//函数时在这里面创建的,会优先使用这里面的数据
let a=100
return function(){
console.log(a)
}
}
let fn=create()
let a=200
fn();//100
- 题15:3.闭包应用:隐藏数据,只提供api
function createCache() {
const data = {}; //隐藏起来了
return {
set: function (key, value) {
data[key] = value;
},
get: function (key) {
return data[key];
},
};
}
const c = createCache();
c.set("a", 100);
console.log(c.get("a"));
- 题16:
let a;
for (let i = 0; i < 10; i++) {
a = document.createElement("a");
a.innerHTML = i + "<br>";
a.addEventListener("click", function (e) {
e.preventDefault();
alert(i);
});
document.body.appendChild(a);
}
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!