作用域

所谓作用域就是:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。

function scope(){
var foo = "global";
if(window.getComputedStyle){
var a = "I'm if";
console.log("if:"+foo); //if:global
}
while(1){
var b = "I'm while";
console.log("while:"+foo);//while:global
break;
}
!function (){
var c = "I'm function";
console.log("function:"+foo);//function:global
}();
console.log(
foo,//global
a, // I'm if
b, // I'm while
c // c is not defined
);
}
scope();
  • scope函数中定义的foo变量,除过自身可以访问以外,还可以在if语句、while语句和内嵌的匿名函数中访问。 因此,foo的作用域就是scope函数体。
  • 在javascript中,if、while、for 等代码块不能形成独立的作用域。因此,javascript中没有块级作用域,只有函数作用域。

但是,在JS中有一种特殊情况:

如果一个变量没有使用var声明,window便拥有了该属性,因此这个变量的作用域不属于某一个函数体,而是window对象。

function varscope(){
foo = "I'm in function";
console.log(foo);//I'm in function
}
varscope();
console.log(window.foo); //I'm in function

作用域链

 所谓作用域链就是:一个函数体中嵌套了多层函数体,并在不同的函数体中定义了同一变量, 当其中一个函数访问这个变量时,便会形成一条作用域链。

foo = "window";
function first(){
var foo = "first";
function second(){
var foo = "second";
console.log(foo);
}
function third(){
console.log(foo);
}
second(); //second
third(); //first
}
first();

当执行second时,JS引擎会将second的作用域放置链表的头部,其次是first的作用域,最后是window对象,于是会形成如下作用域链:

second->first->window,  此时,JS引擎沿着该作用域链查找变量foo, 查到的是 second

当执行third时,third形成的作用域链:third->first->window, 因此查到的是:frist

作用域链延长(with/catch)

with 和 catch 语句主要用来临时扩展作用域链, 将语句中传递的变量对象添加到作用域的头部。语句结束后,原作用域链恢复正常。

//with语句
foo = "window";
function first(){
var foo = "first";
function second(){
var foo = "second";
console.log(foo);
}
function third(obj){
console.log(foo); //first
with (obj){
console.log(foo); //obj
}
console.log(foo); //first
}
var obj = {foo:'obj'};
third(obj);
}
first(); //catch语句
var e = new Error('a');
try {
throw new Error('b');
} catch (e) {
console.log(e.message); //b
} 

在执行third()时,传递了一个obj对象,obj 中有属性foo, 在执行with语句时,JS引擎将obj放置在了原链表的头部,于是形成的作用域链如下:

obj->third->first->window, 此时查找到的foo就是obj中的foo,因此输出的是 obj

而在with之前和之后,都是沿着原来的链表进行查找,从而说明在with语句结束后,作用域链已恢复正常。

this 关键字

在一个函数中,this总是指向当前函数的所有者对象,this总是在运行时才能确定其具体的指向, 也才能知道它的调用对象。

window.name = "window";
function f(){
console.log(this.name);
}
f();//window var obj = {name:'obj'};
f.call(obj); //obj

在执行f()时,此时f()的调用者是window对象,因此输出 window

f.call(obj) 是把f()放在obj对象上执行,相当于obj.f(),此时f 中的this就是obj,所以输出的是 obj

实战应用

demo1:

var foo = "window";
var obj = {
foo : "obj",
getFoo : function() {
return function() {
return this.foo;
};
}
};
var f = obj.getFoo();
f(); //window

demo2:

var foo = "window";
var obj = {
foo : "obj",
getFoo : function() {
var that = this;
return function(){
return that.foo;
};
}
};
var f = obj.getFoo();
f(); //obj

代码解析

// demo1:
//执行var f = obj.getFoo()返回的是一个匿名函数,相当于:
var f = function(){
return this.foo;
}
// f() 相当于window.f(), 因此f中的this指向的是window对象,this.foo相当于window.foo, 所以f()返回"window" // demo2:
// 执行var f = obj.getFoo() 同样返回匿名函数,即:
var f = function(){
return that.foo;
}
// 唯一不同的是f中的this变成了that, 要知道that是哪个对象之前,先确定f的作用域链:f->getFoo->window 并在该链条上查找that,
// 此时可以发现that指代的是getFoo中的this, getFoo中的this指向其运行时的调用者,
// 从var f = obj.getFoo() 可知此时this指向的是obj对象,因此that.foo 就相当于obj.foo,所以f()返回 "obj"

如对结果有疑惑,欢迎讨论!

原创发布 @一像素  2015.12

JS核心系列:浅谈函数的作用域的更多相关文章

  1. C#核心基础--浅谈类和对象的概念

    浅谈类和对象的概念 一.什么是类?什么是对象? 学习一门面向对象编程语言,我们必须得知道什么是类?什么是对象? 类(Class)实际上是对某种类型的对象定义变量和方法的原型.它表示对现实生活中一类具有 ...

  2. JS核心系列:浅谈原型对象和原型链

    在Javascript中,万物皆对象,但对象也有区别,大致可以分为两类,即:普通对象(Object)和函数对象(Function). 一般而言,通过new Function产生的对象是函数对象,其他对 ...

  3. JS核心系列:浅谈 call apply 与 bind

    在JavaScript 中,call.apply 和 bind 是 Function 对象自带的三个方法,这三个方法的主要作用是改变函数中的 this 指向,从而可以达到`接花移木`的效果.本文将对这 ...

  4. JS 浅谈函数柯里化,不明觉厉

    在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术.这个技术由 Christopher ...

  5. JS核心系列:原型对象

    在JS中,每当创建一个函数对象f1 时,该对象中都会内置一些属性,其中包括prototype和proto, prototype即原型对象. 每一个构造函数都有一个与之相关联的对象,该对象称之为原型对象 ...

  6. 12.24 ES6浅谈--块级作用域,let

    第一部分:ES6新增了块级作用域,let关键字用于声明变量,相较于var而言,let关键字不存在声明提前. 1.ES6真正的出现了块级作用域,使用双花括号括住并在其中用let声明变量,会存在暂时性死区 ...

  7. JS核心系列:理解 new 的运行机制

    和其他高级语言一样 javascript 中也有 new 运算符,我们知道 new 运算符是用来实例化一个类,从而在内存中分配一个实例对象. 但在 javascript 中,万物皆对象,为什么还要通过 ...

  8. Node.js:Buffer浅谈

    Javascript在客户端对于unicode编码的数据操作支持非常友好,但是对二进制数据的处理就不尽人意.Node.js为了能够处理二进制数据或非unicode编码的数据,便设计了Buffer类,该 ...

  9. 浅谈JavaScript的作用域

    前段时间学了下JavaScript作用域,这个东西在JavaScript非常重要,也是JavaScript很基础的东西,正如少林里面基础武功,有了基础,才能学绝世武功. 作用域的作用是啥?一套设计良好 ...

随机推荐

  1. react-router 组件式配置与对象式配置小区别

    1. react-router 对象式配置 和 组件式配置    组件式配置(Redirect) ----对应---- 对象式配置(onEnter钩子) IndexRedirect -----对应-- ...

  2. python开发编译器

    引言 最近刚刚用python写完了一个解析protobuf文件的简单编译器,深感ply实现词法分析和语法分析的简洁方便.乘着余热未过,头脑清醒,记下一点总结和心得,方便各位pythoner参考使用. ...

  3. C语言 · 查找整数 · 基础练习

    问题描述 给出一个包含n个整数的数列,问整数a在数列中的第一次出现是第几个. 输入格式 第一行包含一个整数n. 第二行包含n个非负整数,为给定的数列,数列中的每个数都不大于10000. 第三行包含一个 ...

  4. 菜鸟学Struts2——Results

    在对Struts2的Action学习之后,对Struts2的Result进行学习.主要对Struts2文档Guides中的Results分支进行学习,如下图: 1.Result Types(Resul ...

  5. JavaScript String对象

    本编主要介绍String 字符串对象. 目录 1. 介绍:阐述 String 对象的说明以及定义方式. 2. 实例属性:介绍 String 对象的实例属性: length. 3. 实例方法:介绍 St ...

  6. 前端学HTTP之字符集

    前面的话 HTTP报文中可以承载以任何语言表示的内容,就像它能承载图像.影片或任何类型的媒体那样.对HTTP来说,实体主体只是二进制信息的容器而已.为了支持国际性内容,服务器需要告知客户端每个文档的字 ...

  7. 旺财速啃H5框架之Bootstrap(一)

    接下来的时间里,我将和大家一起对当前非常流行的前端框架Bootstrap进行速度的学习,以案例的形式.对刚开始想学习Bootstrap的同学而找不着边的就很有帮助了.如果你想详细的学习Bootstra ...

  8. js学习之变量、作用域和内存问题

    js学习之变量.作用域和内存问题 标签(空格分隔): javascript 变量 1.基本类型和引用类型: 基本类型值:Undefined, Null, Boolean, Number, String ...

  9. js学习之函数的参数传递

    我们都知道在 ECMAScript 中,数据类型分为原始类型(又称值类型/基本类型)和引用类型(又称对象类型):这里我将按照这两种类型分别对函数进行传参,看一下到底发生了什么. 参数的理解 首先,我们 ...

  10. jQuery幻灯片插件autoPic

    原文地址:Jquery自定义幻灯片插件 插件效果图: 演示地址:autoPic项目地址:autoPic 欢迎批评指正!