作用域

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

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. 一起学 Java(三) 集合框架、数据结构、泛型

    一.Java 集合框架 集合框架是一个用来代表和操纵集合的统一架构.所有的集合框架都包含如下内容: 接口:是代表集合的抽象数据类型.接口允许集合独立操纵其代表的细节.在面向对象的语言,接口通常形成一个 ...

  2. 【前端性能】高性能滚动 scroll 及页面渲染优化

    最近在研究页面渲染及web动画的性能问题,以及拜读<CSS SECRET>(CSS揭秘)这本大作. 本文主要想谈谈页面优化之滚动优化. 主要内容包括了为何需要优化滚动事件,滚动与页面渲染的 ...

  3. SSH实战 · 唯唯乐购项目(下)

    后台模块 一:后台用户模块 引入后台管理页面 创建adminuser表: CREATE TABLE `adminuser` (   `uid` int(11) NOT NULL AUTO_INCREM ...

  4. LeetCode-3LongestSubstringWithoutRepeatingCharacters(C#)

    # 题目 3. Longest Substring Without Repeating Characters Given a string, find the length of the longes ...

  5. 使用python抓取婚恋网用户数据并用决策树生成自己择偶观

    最近在看<机器学习实战>的时候萌生了一个想法,自己去网上爬一些数据按照书上的方法处理一下,不仅可以加深自己对书本的理解,顺便还可以在github拉拉人气.刚好在看决策树这一章,书里面的理论 ...

  6. Jquery的事件操作和文档操作

    对于熟悉前端开发的小伙伴,相信对于Jquery一定不陌生,相对于JavaScript的繁琐,Jquery更加的简洁,当然简洁不意味着简单,我们可以使用Jquery完成我们想要实现全部功能,这里为小白们 ...

  7. JavaScript动画-碰撞检测

    ▓▓▓▓▓▓ 大致介绍 碰撞检测是指在页面中有多个元素时,拖拽一个元素会出现碰撞问题,碰撞检测是以模拟拖拽和磁性吸附中的范围限定为基础的 效果:碰撞检测 ▓▓▓▓▓▓ 碰撞检测 先来看看碰撞检测的原理 ...

  8. 谈谈一些有趣的CSS题目(五)-- 单行居中,两行居左,超过两行省略

    开本系列,讨论一些有趣的 CSS 题目,抛开实用性而言,一些题目为了拓宽一下解决问题的思路,此外,涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题中有你感觉 ...

  9. css常用hack

    原文地址:css常用hack 突然想起今天早上在CNZZ看到的统计数据,使用IE6.7的用户比例还真多,看到之后我的心都碎了.微软都放弃了为毛还有这么多人不死心? 所以说,IE下的兼容还是得做的. – ...

  10. 【从零开始学BPM,Day3】自定义表单开发

    [课程主题] 主题:5天,一起从零开始学习BPM [课程形式] 1.为期5天的短任务学习 2.每天观看一个视频,视频学习时间自由安排. [第三天课程] 1.课程概要 Step 1 软件下载:H3 BP ...