-
执行上下文
1.1 对变量来说,js在执行代码之前只先声明变量,不执行赋值语句。
变量赋值是在执行到赋值语句的时候才进行。
所以在已经声明的变量之前访问这个变量,访问到的是js自动给的初始值undefinedconsole.log(a); // undefined var a = 10; // 进行赋值 console.log(a); // 101.2 对
this来说,直接给this赋值
1.3 在处理函数声明时和1.1情况一样处理
在处理函数表达式时,是对函数名直接赋值的,能够提前访问到函数console.log(f1); // function f1() {} function f1() {} console.log(f2); // undefined var f2 = function () {}总结:
生成执行上下文做的数据准备工作:
1. 变量声明默认赋值为undefined
2. this直接赋值
3. 函数声明直接赋值1.4 执行上下文可以有这三个代码段环境:全局代码、函数体、
eval代码
对函数来说,在函数中除了总结中说了数据之外,还有其他数据:函数的参数、arguments变量、自由变量的取值作用域,这三个也是直接赋值的
1.4.1 函数每被调用一次,都会产生一个新的执行上下文环境
1.4.2 函数在定义的时候就已经确定了函数体内自由变量的作用域var a = 'kimi'; function fn(x){ console.log("arguments", arguments); // arguments对象 console.log("x", x); // 参数 console.log("a", a); // 自由变量 } function bar(f){ var a = 10; f(); // 要到创建这个函数的那个作用域中取值 } fn('emoji'); bar(fn); 打印输出: // arguments Arguments ["emoji", callee: ƒ, Symbol(Symbol.iterator): ƒ] // x emoji // a kimi // arguments Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ] // x undefined // a kimi1.4.3 自由变量
在fn作用域中使用的变量a没有在fn作用域中声明,对fn作用域来说,a就是一个自由变量
1.4.4 作用域链
如果要在函数中找当前作用域中没有声明的自由变量,要到创建这个函数的作用域中去找,没有找到就继续向上直到找到全局作用域为止。不管这个函数是在哪里调用的var a = 10; function fn(){ var b = 20; function bar(){ console.log(a+b); } return bar; } var x = fn(), b = 200; x(); // 30-
fn()的this指向是x,x调用fn函数 - 先在当前作用域
bar中去找,没有就在fn函数中查找b,因为bar中没有定义变量,所以ab对bar来说都是自由变量,要找ab要到创建bar函数的作用域中去找,那就是fn,fn中有b就b=20,fn中没有找到a,就接着到创建fn的作用域,全局作用域中去找,找到a=10
-
-
this
this的取值是执行上下文中的一部分,每次调用函数都会产生一个新的执行上下文。只有在函数被执行调用的时候才能确定this指向。
2.1 构造函数的this指向(new)构造出来的实例对象
在构造函数的prototype中,this也指向实例对象function Fn() { this.name = 'emoji'; this.year = 1988; } Fn.prototype.getName = function(){ console.log(this.name); } var f1 = new Fn(); f1.getName(); // 'emoji'2.2 函数作为对象的一个属性并且被对象调用时,函数中的
this指向该对象
2.3 函数用call、apply调用时,this的值是传入的对象的值
2.4 全局调用时,this是window
2.5 作为普通函数调用时,this是window
2.6 作为html元素事件中的this时,this是当前事件发生的目标元素 -
作用域
只有全局作用域和函数作用域,每个函数创建都会创建一个自己的作用域
一个作用域中会存在多个执行上下文环境
js没有块({}中的语句)级作用域,所以不要在块中声明变量,一般都提前进行声明var i = 10; if(i>1){ var name = 'emoji'; } console.log(name); // emoji作用域类似于地盘的概念,作用域就像隐形的围墙,将自己地盘上的代码围住
除了全局作用域外,每个函数都会创建自己的作用域,这是在函数定义的时候就确定好的
作用域中的变量的值是在执行过程中确定的,作用域不涉及变量,变量是作用域对应的执行上下文环境来影响的
作用域有上下级的关系,上下级关系的确定看函数在哪个作用域下创建,
bar的上级就是fn作用域最大的用处就是隔离变量,不同作用域下同名变量不会冲突
TODO 作用域链的查找过程
var a = 10, b = 20; // 全局作用域 function fn() { // fn 的作用域 var a = 100, b = 200; function bar() { // bar 的作用域 var a = 1000, b = 2000; } bar(100); bar(200); } fn(10);执行上下文环境和作用域说明:
- 在加载代码的时候,全局上下文环境 变量、函数声明 然后赋值
- 执行到
fn(10),生成调用fn函数的上下文环境,压栈,当前活动上下文环境是fn - 执行到
bar(100),生成调用bar函数的上下文环境,压栈,当前活动上下文环境是bar,执行完bar(100),当前上下文环境销毁;执行bar(200),同上面一样,bar(200)执行完销毁后回到上级fn的上下文环境中,fn为活动状态 - 执行完
fn(10)后,销毁fn(10)的上下文环境,回到全局上下文环境中,当前活动状态为全局
-
闭包
TODO 自己写一个闭包
什么是闭包?
函数a内部有一个函数b,函数b可以访问到函数a中的变量,函数b就是闭包
闭包存在的意义:可以间接访问函数内部的变量function A() { let a = 1 window.B = function () { console.log(a) } } A() B() // 1匿名函数是在
A函数内被声明的,所以从创建这个函数的内部找变量循环中使用闭包解决
var定义函数的问题涉及事件循环机制,
settimeout机制function wait(message){ setTimeout(function timer(){ console.log(message); }, 1000); } wait("Hello Message"); // 'Hello Message'延迟函数的回调会在循环结束时才执行
for(var i = 1; i <= 5; i++){ setTimeout(function timer() { // 执行这个函数的时候for循环已经执行完 console.log(i); }, i*1000); } // 6改造成闭包,在循环内使用立即调用函数表达式(IIFE),以便在每次迭代中强制创建变量的一个新副本:
for(var i = 1; i <= 5; i++){ (function (j) { setTimeout(function timer() { console.log(j); }, j*1000) })(i); }第二种就是使用
setTimeout的第三个参数,这个参数会被当成timer函数的参数传入。for (var i = 1; i <= 5; i++) { setTimeout( function timer(j) { console.log(j) }, i * 1000, i ) }第三种就是使用
let定义i来解决问题了,这个也是最为推荐的方式-
let和var的区别(待补充)
用let声明的变量,不存在变量提升。而且要求必须 等let声明语句执行完之后,变量才能使用,不然会报Uncaught ReferenceError错误。
for (let i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i) }, i * 1000) }4.1 函数作为返回值
function fn(){ var max = 10; return function bar(x){ if( x > max ){ console.log(x); } } } var f1 = fn(); f1(15); // 15- bar函数作为返回值赋值给变量f1
- 涉及跨作用域取值
4.2 函数作为参数传递
var max =10, fn = function (x){ if(x > max){console.log(x);} }; (function (f){ var max = 100; f(15); })(fn); // 15 > 10 -> 15- 要到创建这个函数的作用域上下文环境中去找变量
4.3 在执行上下文栈中,提到当一个函数被调用完成之后,这个函数的执行上下文环境会被销毁,包括其中的变量。但是有些情况下函数调用完成后,其执行上下文环境不会被直接销毁
function fn(){ var max = 10; return function bar(x){ if(x > max){ console.log(x); } } } var f1 = fn(), max = 100; f1(15); // 15- 代码执行前生成全局上下文环境,并在执行的时候对变量赋值,这时候全局上下文环境是活动状态。
max -> undefined - 执行
f1 = fn(),调用fn函数,这时候产生了fn执行上下文环境,压栈,为活动状态 - 但是在
f1变量赋值时fn()调用完的时候,这里不会销毁fn函数的上下文执行环境,因为fn函数内部返回的是一个函数bar,函数bar会创建一个独立的作用域,bar是在执行fn函数时创建的,fn其实早就执行结束了,但是上下文环境还留着。如果销毁了fn,bar中的max就找不到值了。 - 执行
f1(15)函数调用bar,max在bar中是自由变量,需要在创建bar的函数fn中找,找到Max=10,就取10
-
JS基础知识-作用域和闭包
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
推荐阅读更多精彩内容
- 引言 可以说是取名废了,把这几个关键词放在一起也是因为看完犀牛书相关章节和很多技术文章之后觉得这几个概念是相互渗透...
- 王福朋 - 博客园 —— 《 深入理解javascript原型和闭包》 目录:深入理解javascript原型和闭...
- 1.对象是什么 对象就是若干属性的集合。 在JS中一切引用类型都是对象:数组是对象,函数是对象,对象还是对象。对象...
