作用域和闭包(以及this的用法)

执行上下文

在介绍作用域特性之前,我们先来回顾一下js的执行上下文(详细介绍:https://www.jianshu.com/p/8f19e45fd1f1
一段<script>或者一个函数之内,都会去生成一个执行环境(execution context,EC)或称之为执行上下文。
当一段JS代码执行的时候,JS解释器会通过两个阶段去产生一个EC。
1.创建阶段
o创建变量对象VO
o设置[[Scope]]属性的值: (指向作用域链)
o设置this的值: (指向一个EC,默认undefined)
2.初始化变量对象(即设置变量的值、函数的引用),然后解释/执行代码。
注意事项:
全局:针对一段<script>,它会生成一个全局的执行上下文,在执行之前会先去把“变量定义”和“函数声明”拿出来装在对象VO。
函数:针对一个函数,它会生成一个函数执行上下文,在函数执行之前会先把“变量定义”、“函数声明”、“arguments”拿出来封装在对象VO里边。

先执行变量定义后,变量值默认为undefined;
函数声明后不会立即执行,如果输出一下函数,能看到函数体所有的代码;而需要调用函数后(例如:fn();),函数体中的代码才会执行.
ps: arguments 是JavaScript里的一个内置对象,所有的函数都有属于自己的一个arguments对象,它包括了函所要调用的参数。
ps: 函数申明和函数表达式的区别
function fn(name){} 这是一个函数声明,而var a = function(){}这是函数表达式;
如果是函数表达式的写法,在执行之前先拿出来处理的就是var a;了,这时候a只是一个普遍变量(值为undefined)不是函数,这点要注意。

作用域

作用域指的是变量的适用范围。(详细介绍:https://www.w3cschool.cn/javascript_prototype/y2cjfozt.html

js无块级作用域(不过es6中let定义的变量只在代码块中有效,也就有了块级作用域)

 if (true){
    var name = ‘zhao‘;
}
console.log(name); //zhao,这里依然可以获取到上面代码块里边的变量。

只有函数和全局作用域

var a = 100
function fn(){
    var a =200;
    console.log(‘fn‘,a); // fn 200
}
console.log(‘global‘,a); //  global 100,获取不到函数内部的变量。

作用域链

var a = 100;
function fn1(){
    var b =200;
    function fn2(){
        var c =300;
        // 当前作用域没有定义的变量,即“自由变量”,a和b都是
        console.log(a); // 100
        console.log(b); // 200
        console.log(c); // 300
    }
    fn2();
}
fn1();

PS:自由变量因为在当前作用域没有定义,所以只能去父级作用域查找.
(注意:父级作用域是在函数“定义”的时候就已经确定了的,自由变量这种一层层往父级查找的链式结构也就是“作用域链”)

闭包

闭包实际上是对js作用域链的一种应用形式;主要利用了作用域链从父级函数获取变量的特性,从外部调用父级函数局部变量并且互不污染,或者子函数循环利用父级函数的变量达到某种计算用途。

闭包特性一:调用函数内部的变量,利用作用域链原理,能获取函数fn1的父级函数的局部变量进行计算。
闭包特性二:让这些变量的值始终保持在内存中,不会再fn1调用后被自动清除,再次执行fn1的时候还能继续上一次的计算。
注意:fn2创建的时候与fn1是相互独立的,其中的变量a也互不影响,好比父亲给每个孩子都准备了一个新的存钱罐。

场景一:函数作为返回值

function F1(){
    var a =100;
    // 返回一个函数(函数作为返回值),为了阅读方便也可以先定义一个函数,然后retrun函数名。
    return function(){
        console.log(a); //自由变量,去父作用域寻找
    }
    // 另外放回函数的形式也不只是return,同样在这里以事件的形式绑定在Dom上也是一样,再或者调用其他方法传递一个函数出去。
}
var f1 = F1();
var a=200;
f1(); // 100

场景二:函数作为参数传递

var b=111;
function f1(){
    var a =100;
    console.log(a,b);
}
function f2(fn){
    var a =200;
    var b=222;
    fn();
}
f2(f1); // 100,111
//并且如果a在F1()没有定义的话,就会报错而不是获取f2中的a,因为它的定义时的父级作用域及之上(即全局作用域)都没有定义a;

应用举例一:setTimeout中的参数传递。
由于直接setTimeout(function(){},200)这么写的话,没办法传参,所以可以用闭包的形式来做。

var Fn=function(num){
    var a=10;
    return function(){
        var b=0;
        a+=num;
        b+=num;
        console.log(a,b);
    }
}
var fn1=Fn(1);
var fn2=Fn(1);
//闭包特性一:调用函数内部的变量,利用作用域链原理,能获取函数fn1的父级函数的局部变量a进行计算。
setTimeout(fn1,200); //输出的a=11,b=1;
//闭包特性二:让变量a的值始终保持在内存中,不会在fn1调用后被自动清除,再次执行fn1的时候还能继续上一次的计算。
setTimeout(fn1,500); //输出的a=12,b=1;
//特性二的注意事项:fn2创建的时候与fn1是相互独立的,对应的父级函数Fn的变量a也互不影响,好比父亲在每个孩子出生时都准备了一个新的存钱罐,每个孩子都用自己的。
setTimeout(fn2,800); //输出的a=11,b=1;

应用举例二:创建10个<a>标签,点击的时候弹出来对应的序号。

<body>
<script type="text/javascript">

    for(var i=0;i<10;i++){

        (function(i){

            var a=document.createElement(‘a‘);
            a.innerHTML=i+‘<br>‘;
            document.body.appendChild(a);
            a.addEventListener(‘click‘,function(e){
                e.preventDefault();  //取消默认事件,指a标签
                alert(i);
            });

        })(i);

    }
</script>
</body>

this

核心:this要在执行时才能确认值,定义时无法确认。

var a = {
    name: ‘A‘,
    fn: function (){
        console.log(this.name);
    }
}
a.fn(); // this === a (即使是b.a.fn(),this也是a)
a.fn.call({name: ‘B‘});  // this === {name: ‘B‘}
var fn1 = a.fn;
fn1();  // this === window

this几种不同的运用场景
1、作为构造函数执行:(例如:new Foo(),this指向这个新对象)
2、作为对象属性执行:(this指向对象)
3、作为普通函数执行:(this指向window)
4、call()、apply()、bind():(this指向传入的第一个对象参数,bind只有一个参数)
参考:https://www.w3cschool.cn/jsnote/jsnote-this.html

call,apply、bind都属于Function.prototype的一个方法,他们的作用改变函数的调用对象,它是JavaScript引擎内在实现的,因为属于Function.prototype,所以每个Function对象实例(就是每个方法)都有call,apply,bind属性。既然作为方法的属性,那它们的使用就当然是针对方法的了,这几个方法是容易混淆的。
call,apply的用法差不多,只是参数稍微不同;(apply()接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。call()方法第一个参数与apply()方法相同,但传递给函数的参数必须列举出来。)

// 以最简单window对象为例
function sum(num1, num2) {
return num1 + num2;
}
console.log(sum.call(window, 10, 10)); //20
console.log(sum.apply(window,[10,10])); //20   这两都相当于window.sum(10,10);
// 即语法:foo.call(this, arg1,arg2,arg3) == foo.apply(this, arguments) 

而bind的用法有一点差别。(只是传一个参数对象,然后返回一个函数给接受的变量,再另外调用执行。)

window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
var OSayColor = sayColor.bind(o);
OSayColor(); //blue 

详情:https://www.w3cschool.cn/xqw2e7/9m2x12y0.html

原文地址:https://blog.51cto.com/ycgit/2353103

时间: 2024-10-13 01:33:43

作用域和闭包(以及this的用法)的相关文章

python 函数的作用域,闭包函数的用法

一.三元表达式 if条件成功的值    if  条件   else else条件成功的值 #if条件成立的结果 if 条件 else else条件成立的结果 # a = 20 # b = 10 # c = 5 if a>b else 10 # print(c) 二.函数的命名空间 命名空间一共分为三种: 全局命名空间 局部命名空间 内置命名空间 *内置命名空间中存放了python解释器为我们提供的名字:input,print,str,list,tuple...它们都是我们熟悉的,拿过来就可以用的

JavaScript函数,作用域以及闭包

JavaScript函数,作用域以及闭包 1. 函数 (1). 函数定义:函数使用function关键字定义,它可以用在函数定义表达式或者函数声明定义. a. 函数的两种定义方式: * function functionName() {} * var functionName = function(){} b. 两种函数定义不同之处 1). 声明提前问题 函数声明语句   :声明与函数体一起提前 函数定义表达式 :声明提前,但是函数体不会提前 请看下面图示:绿色线上面实在js初始加载的时候,查看

js最基础知识回顾3(字符串拼接,数据类型,变量类型,变量作用域和闭包,运算符,流程控制,)

一.javaScript组成     1.ECMAScript:解释器.翻译 ---------------------------------------------------------几乎没有兼容性问题     2.DOM:Document Object Model --------操作HTML的能力----document--------有一些兼容性问题     3.BOM:Browser Object Model -------------浏览器---------------wind

JavaScript this 局部变量全局变量 作用域 作用域链 闭包

从阮老师博客的一道测试题说起: 代码段一: var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()()); 代码段二: var name = "The Window"; var object

Python--高阶函数、函数嵌套、名称空间及变量作用域、闭包、装饰器

1.高阶函数(map/reduce/filter) 高阶函数是指函数的参数可以是函数 这篇总结几个常用的高阶函数:map/reduce/filter map函数.reduce函数.filter函数都是Python中的内建函数. map函数 map函数的作用是将一个函数作用于一个序列的每一个元素,一行代码即可完成,不需要用我们平常喜欢用的循环.map将运算进行了抽象,我们能一眼就看出这个函数是对一个序列的每个元素进行了同样的一个操作.map()函数接收两个参数,一个是函数,一个是Iterable,

JavaScript【5】高级特性(作用域、闭包、对象)

笔记来自<Node.js开发指南>BYVoid编著 1.作用域 if (true) { var somevar = 'value'; } console.log(somevar); JavaScript的作用域完全是由函数决定的,if.for语句中的花括号不是独立的作用域. 1.1.函数作用域 作用域是通过函数来定义的,在一个函数中定义的变量只对这个函数内部可见,我们称为函数作用域.在函数中引用一个变量时,JavaScript会先搜索当前函数作用域,或者称为"局部作用域",

JS中的作用域和闭包

作用域:在编程语言中,作用域控制着变量与参数的可见性及生命周期.JS确实有函数作用域,那意味着定义在函数中的参数和变量在函数外部是不可见的,而且在一个函数中的任何位置定义的变量在该函数中的任何地方都是可见的. var a = 1; var fs = function (){ var b = 2; var c = 4 var fun = function (){ var c = 3; alert(a) //输出1 alert(b) //输出2 alert(c) //输出3 } fun(); } f

关于闭包的终级用法

最近艾伦在研究jQuery的sizzle选择器,和我分享了一个sizzle里边关于闭包的高级用法,说它高级,是因为它用的特别巧妙,代码我们都能看明白,但是不一定能想到要这样去用闭包.然后他得意地笑了,然后说道:”现在知道为什么我坚持要看源码了吧,这样的用法,看明白了,就是你的知识积累“.不得不承认,艾伦确实是一个比较肯钻研的人.sizzle里边的闭包用法,我已经记不清了,但是那个思路我还是记下来,后面会附上自己理解后的模拟代码. code 1: function createClure(){ r

函数放到onload里面,在html里面执行函数会报错-----作用域和闭包相关问题

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>修改显示隐藏</title> <meta name="description" content