JavaScript 词法作用域&作用域链

作用域是用来规定变量或函数访问其他数据的权限。

JavaScript 语言采用的是词法作用域(静态作用域)规则的。这是否与我们常说JavaScript 中的全局作用域,函数作用域,块级作用域有冲突呢?没有冲突!JavaScript 语言采用的是词法作用域,是语言层面的,而全局作用域等是被包含的。

词法作用域与动态作用域

大多数现在程序设计语言都是采用静态作用域规则,而只有为数不多的几种语言采用动态作用域规则。 动态作用域和静态作用域,决定的是作用域链的顺序。

词法作用域

函数的作用域在函数定义的时候就决定了,跟函数调用的位置无关。比如:JavaScript 中词法分析是在编译阶段决定的,也就是说,在执行之前就已经确定了作用域的范围。

function foo() {
	console.log(name);
}

function func() { 
	var name = 'dog'
	foo()
}

var name = 'pig'
func()//pig

因为JavaScript采用的词法作用域,自foo()定义开始,它就是属于全局作用域。所以无论将它放置何处调用,它都是在全局作用域中执行。执行 foo(),从 foo()内部查找是否有局部变量 name。如果没有,就到沿着其作用域链查找,此处直接就到全局作用域了。全局作用域存在变量name,就打印出pig。

动态作用域

动态作用域与词法作用域相对,函数的作用域是在函数调用的时候才决定的。

//scope.bash
value=1
function foo () {
	    echo $value;
    }
function func() {
	   local value=2;
	   foo;
	}
func

创建一个.bash文件,输入以上代码,执行可得:2
在这里插入图片描述
执行 foo 函数,依然是从 foo 函数内部查找是否有局部变量 value。如果没有,就从调用函数的作用域,也就是 func函数内部查找 value 变量,所以结果会打印 2

JavaScript 中的作用域

在JavaScript 中,作用域又划分为:全局作用域、函数作用域以及块级作用域。

我们把定义在全局作用域中显示声明的变量,称为全局变量,定义在函数里面或块级作用域显示声明的变量称之为局部变量。

全局作用域、函数作用域自然不必多说,在此简述一下ES6提出的块级作用域。

块级作用域

任何一对花括号 {} 中的语句集都属于一个块,ES6规定包含有let,const{}会被提升为块级作用域,块级作用域内被let,const声明的变量对外都是不可见的。

这里的花括号一般指以下几种情形(不包括函数情形):

// 条件语句
if () {}

// switch语句
switch () {}

// for / while循环语句
for () {}
while () {}

// try...catch语句
try () catch (err) {}

// 单大括号
{}

最简单的块级作用域

{
	let age = 24;
	var name = 'Joe';
}

该age变量对外是不可见的,name对外可见。

JavaScript 作用域链

当访问一个变量时,解释器会首先在当前作用域查找标示符(变量名),如果没有找到,就去父作用域找,直到找到该变量的标示符或者不在父作用域中,这样由多个作用域形成的链表就叫做作用域链

作用域是分层的,内层作用域可以访问作用域链上的外层作用域声明的变量,外层的作用域却不可访问内层作用域显示声明的变量。

隐式声明的变量都是全局变量,你可以在内层作用域声明,然后外层作用域也能够访问到该变量的。
值得注意的是:隐式声明的变量不存在变量提升,如果在函数内部,还需要那个函数执行后方可生效。

下图为一个简单的作用域链:

在这里插入图片描述

案例分析

let name = 'name'

function f() {
	console.log(this.name);
}

const obj = {
	name: 'obj',
	foo() {
		console.log(this.name);
	},
	faz: function() {
		console.log(this.name);
	},
	bar: () => {
		console.log(this.name);
	},
	info: {
		foo: () => {
			console.log(this.name);
		},
		fun: f
	}

}

obj.foo()
obj.faz()
obj.bar()
obj.info.foo()
obj.info.fun()

在这里插入图片描述
this可以改变调用关系,但是没有改变作用域。
箭头函数没有this,原因是他没有原型对象。箭头函数依赖于它外边第一层作用域的this
浏览器全局对象是windowwindow自身具备name属性,默认为空字符串。
let 声明的全局变量不会作为window的属性。
this指向最终调用对象
最后是undefined的原因是,this指向了obj.info对象,该对象没有nane属性


不加this,直接打印5个name。函数作用域在定义的时候就决定好的了,对象自身不具备作用域,所以对象里面的5个方法都是属于全局作用域,全局作用域定义了let name = 'name'


目前,只有对象方法的简写法(getter属性描述符的方法)可以让 JavaScript 引擎确认,定义的是对象的方法。对象内其他形式的方法只能称为对属性的赋值。

const obj={
	foo(){},//对象的方法
	faz:function(){}//属性的赋值
}

foo(){}是伴着obj生成的,占据对象内存空间的,faz:function(){},编译阶段就将function(){}开辟新的内存空间装载好,之后faz指向该函数(函数也是对象)地址,是引用关系。

对象自身不具备作用域的,所以对象里面的“方法”也不会形成作用域。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值