博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
好程序员web前端带你了解JS的作用域链
阅读量:6486 次
发布时间:2019-06-23

本文共 2506 字,大约阅读时间需要 8 分钟。

好程序员web前端带你了解JS的作用域链,我们都知道js是一个基于对象的语言,系统内置各种对象。

而window作为一个天然存在的全局对象,它承担了所有全局资源的存储。

我们使用的任何全局变量,都是window下的。也就是说,在js中,实际上没有任何对象、方法是可以独立的,它们必须依赖于某个对象才可以被访问或执行。

就像alert(),它的完整写法是window.alert()

parseInt(), 完整写法是window.parseInt()

所有放在window对象下的资源,访问时可以默认省略window

但有一种情况非常特殊,例如函数中的局部变量:

function Person(){

var name = “abc”;

}

当我们试图访问这个name属性时

console.log(newPerson().name);

结果是undefined

我们只能在函数内部访问:

function Person(){

var name = “abc”;

console.log(name);

}

这种属性,在构造函数中,也被称为私有属性,我们必须提供一种对外公开的方法才可以被外界访问。例如:

function Person(){

var name = “abc”;

this.getName = function(){

returnname;

}

this.setName= function(_name){

name = _name;

}

}

这是一个典型的作用域问题, 似乎我们每个人都知道。

但这似乎也违反了我们的一个常识:那就是在js中,所有资源都必须依赖对象才能存在,不可独立使用。比如说:

function aaa(){

function bbb(){ }

bbb();

}

这段代码看上去并没有错,但是请问bbb这个函数为什么可以独立存在呢?如果我们把它换成这样:

function aaa(){

function bbb(){ }

window.bbb();

}

结果是运行错误!

那如果换成这样呢?

function aaa(){

function bbb(){ }

this.bbb();

}

结果还是运行错误!

那么我们不禁要发问了,bbb这个函数到底是属于哪个对象的?

当我们在调用一个函数的时候,浏览器会为这个函数的执行开辟一块内存区域用来存储这个方法在执行的临时数据。而对象作为js的基本存储单位,因此,临时数据实际上都被保存到了一个对象当中,这个对象,就是我们平时所说的执行上下文环境

当我们调用aaa函数时,例如window.aaa()

浏览器会为这一次函数的执行,创建一个执行上下文环境对象,我们暂时给它起个名字,叫做contextAAA吧,当然它是临时的,因为函数执行完它也就消失了。

那我们的代码实际上会变成这样:

function aaa(){

function bbb(){ }

contextAAA.bbb();

}

尽管contextAAA对象是看不见的,但它确实存在。

而当我们执行bbb函数时,浏览器会再次为这一次函数调用创建一个临时的执行上下文环境,我们暂且叫它contextBBB

那么contextAAA 和contextBBB以及window之间会形成链条关系。

举个例子来说明吧

var num = 888;

function aaa(){

var num = 100;

function bbb(){

var num= 200;

console.log(num);

}

bbb();

}

aaa();

那么contextAAA 如下:

contextAAA = {

num :100,

bbb : function(){ … },

parentContext: window//父级上下文对象

}

那么contextBBB如下:

contextBBB = {

num : 200,

parentContext: contextAAA //父级上下文对象

}

因此我们发现,在父级上下文对象中,我们没有办法访问到子级上下对象,这是一个单向链表,这就是全局不能访问局部的原因。

而bbb函数中打印出的num应该是多少呢?这取决在上下文对象中的查找顺序,顺序大概是这样的:

首先在当前上下文对象contextBBB中,找一下有没有num变量,找到就直接打印。以我们目前的代码看,结果应该是200

我们把代码改造一下:

var num= 888;

function aaa(){

var num = 100;

function bbb(){

console.log(num);

}

bbb();

}

aaa();

由于这次在contextBBB对象中找不到num变量了,因此它会从父级上下文对象中查找,也就是contextAAA里面的num,因此打印的结果是100;

我们再把代码改造一下

var num= 888;

function aaa(){

function bbb(){

console.log(num);

}

bbb();

}

aaa();

由于这次连contextAAA对象里也找不到了,会再次向它的父级上下文对象,也就是window查找,因此打印结果是888

contextAAA和contextBBB的父子关系,在你写代码的一刻就决定了,这就是作用域。

function aaa(){

var num = 10;

function bbb(){ console.log(num); }

}

而代码执行时,产生的上下文对象,是链表的关系。这就是我们所说的作用域链,它的原理跟原型链是一样的。

理解了这一点,也能弄明白闭包的原理。

function aaa(){

var num = 10;

return functionbbb(){ console.log(num); }

}

aaa( )( )

尽管bbb函数通过return在全局范围被执行了,但作用域的链表关系并没有发生改变,因此,bbb函数依然可以访问num这个局部变量。

转载地址:http://ifpuo.baihongyu.com/

你可能感兴趣的文章
Jenkins学习七:Jenkins的授权和访问控制
查看>>
PowerDesigner将PDM导出生成WORD文档(转)
查看>>
路由器扫描的Java源码
查看>>
Android经常使用的五种弹出对话框
查看>>
经常使用命令 echo、@、call、pause、rem
查看>>
Activity的启动模式详解
查看>>
[改善Java代码]构造函数尽量简化
查看>>
优化PHP程序的方法(温故知新)
查看>>
阿里开源Mysql分布式中间件:Cobar
查看>>
JavaScript对数组的处理(一)
查看>>
[iBoard 电子学堂][第八卷 设计任意波发生器]第一篇 iBoard 任意波发生器简介
查看>>
[iBoard 电子学堂][第〇卷 电子基础]第二篇 电路图与印刷电路板
查看>>
配置JAVA环境
查看>>
java环境变量
查看>>
浅谈API设计
查看>>
java----OO的概念和设计原则(转)
查看>>
Java 实现 淘宝秒杀 聚划算 自己主动提醒 源代码
查看>>
如何下载一个物种的全部EST序列 | NCBI | 表达序列标签
查看>>
代码编辑器Sublime Text 3 免费使用方法与简体中文汉化包下载
查看>>
5款最适合新手的包管理器
查看>>