金沙国际官网_金沙国际平台登录

因为这个金沙国际官网_金沙国际平台登录网站与很多的大型澳门赌场都有合作,金沙国际官网_金沙国际平台登录尽职尽责,高效执行,保持好奇心,不断学习,追求卓越,点击进入金沙国际官网_金沙国际平台登录马上体验吧,所以现在也正式地开始了营业。

您的位置:金沙国际官网 > web前端 > 前端基础进阶,轻松学习

前端基础进阶,轻松学习

发布时间:2019-11-01 17:35编辑:web前端浏览(74)

    前端基础进阶(二):执行上下文详细图解

    2017/02/21 · 基础技术 · 执行上下文

    原文出处: 波同学   

    图片 1

    先随便放张图

    我们在JS学习初期或者面试的时候常常会遇到考核变量提升的思考题。比如先来一个简单一点的。

    JavaScript

    console.log(a); // 这里会打印出什么? var a = 20;

    1
    2
    console.log(a);   // 这里会打印出什么?
    var a = 20;

    暂时先不管这个例子,我们先引入一个JavaScript中最基础,但同时也是最重要的一个概念执行上下文(Execution Context)

    每次当控制器转到可执行代码的时候,就会进入一个执行上下文。执行上下文可以理解为当前代码的执行环境,它会形成一个作用域。JavaScript中的运行环境大概包括三种情况。

    • 全局环境:JavaScript代码运行起来会首先进入该环境
    • 函数环境:当函数被调用执行时,会进入当前函数中执行代码
    • eval

    因此在一个JavaScript程序中,必定会产生多个执行上下文,在我的上一篇文章中也有提到,JavaScript引擎会以堆栈的方式来处理它们,这个堆栈,我们称其为函数调用栈(call stack)。栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文。

    当代码在执行过程中,遇到以上三种情况,都会生成一个执行上下文,放入栈中,而处于栈顶的上下文执行完毕之后,就会自动出栈。为了更加清晰的理解这个过程,根据下面的例子,结合图示给大家展示。

    JavaScript

    var color = 'blue'; function changeColor() { var anotherColor = 'red'; function swapColors() { var tempColor = anotherColor; anotherColor = color; color = tempColor; } swapColors(); } changeColor();

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var color = 'blue';
     
    function changeColor() {
        var anotherColor = 'red';
     
        function swapColors() {
            var tempColor = anotherColor;
            anotherColor = color;
            color = tempColor;
        }
     
        swapColors();
    }
     
    changeColor();

    我们用ECStack来表示处理执行上下文组的堆栈。我们很容易知道,第一步,首先是全局上下文入栈。

    图片 2

    第一步:全局上下文入栈

    全局上下文入栈之后,其中的可执行代码开始执行,直到遇到了changeColor(),这一句激活函数changeColor创建它自己的执行上下文,因此第二步就是changeColor的执行上下文入栈。

    图片 3

    第二步:changeColor的执行上下文入栈

    changeColor的上下文入栈之后,控制器开始执行其中的可执行代码,遇到swapColors()之后又激活了一个执行上下文。因此第三步是swapColors的执行上下文入栈。

    图片 4

    第三步:swapColors的执行上下文入栈

    在swapColors的可执行代码中,再没有遇到其他能生成执行上下文的情况,因此这段代码顺利执行完毕,swapColors的上下文从栈中弹出。

    图片 5

    第四步:swapColors的执行上下文出栈

    swapColors的执行上下文弹出之后,继续执行changeColor的可执行代码,也没有再遇到其他执行上下文,顺利执行完毕之后弹出。这样,ECStack中就只身下全局上下文了。

    图片 6

    第五步:changeColor的执行上下文出栈

    全局上下文在浏览器窗口关闭后出栈。

    注意:函数中,遇到return能直接终止可执行代码的执行,因此会直接将当前上下文弹出栈。

    图片 7

    整个过程

    详细了解了这个过程之后,我们就可以对执行上下文总结一些结论了。

    • 单线程
    • 同步执行,只有栈顶的上下文处于执行中,其他上下文需要等待
    • 全局上下文只有唯一的一个,它在浏览器关闭时出栈
    • 函数的执行上下文的个数没有限制
    • 每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此。

    为了巩固一下执行上下文的理解,我们再来绘制一个例子的演变过程,这是一个简单的闭包例子。

    JavaScript

    function f1(){ var n=999; function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function f1(){
        var n=999;
        function f2(){
            alert(n);
        }
        return f2;
    }
    var result=f1();
    result(); // 999

    因为f1中的函数f2在f1的可执行代码中,并没有被调用执行,因此执行f1时,f2不会创建新的上下文,而直到result执行时,才创建了一个新的。具体演变过程如下。

    图片 8

    上例演变过程

    下一篇文章继续总结执行上下文的创建过程与变量对象,求持续关注与点赞,谢谢大家。

    前端基础进阶系列目录

    前端基础进阶系列我会持续更新,欢迎大家关注我公众号isreact,新的文章更新了我会在公众号里第一时间通知大家。也欢迎大家来简书关注我。

    1 赞 2 收藏 评论

    图片 9

    深入浅出妙用 Javascript 中 apply、call、bind

    2015/09/24 · JavaScript · 4 评论 · apply, bind, call

    本文作者: 伯乐在线 - chokcoco 。未经作者许可,禁止转载!
    欢迎加入伯乐在线 专栏作者。

    这篇文章实在是很难下笔,因为网上相关文章不胜枚举。

    巧合的是前些天看到阮老师的一篇文章的一句话:

    “对我来说,博客首先是一种知识管理工具,其次才是传播工具。我的技术文章,主要用来整理我还不懂的知识。我只写那些我还没有完全掌握的东西,那些我精通的东西,往往没有动力写。炫耀从来不是我的动机,好奇才是。”

    对于这句话,不能赞同更多,也让我下决心好好写这篇,网上文章虽多,大多复制粘贴,且晦涩难懂,我希望能够通过这篇文章,能够清晰的提升对apply、call、bind的认识,并且列出一些它们的妙用加深记忆。

       apply、call

    在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。

    JavaScript 的一大特点是,函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念。

    先来一个栗子:

    JavaScript

    function fruits() {} fruits.prototype = { color: "red", say: function() { console.log("My color is " + this.color); } } var apple = new fruits; apple.say(); //My color is red

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function fruits() {}
     
    fruits.prototype = {
        color: "red",
        say: function() {
            console.log("My color is " + this.color);
        }
    }
     
    var apple = new fruits;
    apple.say();    //My color is red

    但是如果我们有一个对象banana= {color : “yellow”} ,我们不想对它重新定义 say 方法,那么我们可以通过 call 或 apply 用 apple 的 say 方法:

    JavaScript

    banana = { color: "yellow" } apple.say.call(banana); //My color is yellow apple.say.apply(banana); //My color is yellow

    1
    2
    3
    4
    5
    banana = {
        color: "yellow"
    }
    apple.say.call(banana);     //My color is yellow
    apple.say.apply(banana);    //My color is yellow

    所以,可以看出 call 和 apply 是为了动态改变 this 而出现的,当一个 object 没有某个方法(本栗子中banana没有say方法),但是其他的有(本栗子中apple有say方法),我们可以借助call或apply用其它对象的方法来操作。

    apply、call 的区别

    对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。例如,有一个函数定义如下:

    JavaScript

    var func = function(arg1, arg2) { };

    1
    2
    3
    var func = function(arg1, arg2) {
     
    };

    就可以通过如下方式来调用:

    JavaScript

    func.call(this, arg1, arg2); func.apply(this, [arg1, arg2])

    1
    2
    func.call(this, arg1, arg2);
    func.apply(this, [arg1, arg2])

    其中 this 是你想指定的上下文,他可以是任何一个 JavaScript 对象(JavaScript 中一切皆对象),call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。

    JavaScript 中,某个函数的参数数量是不固定的,因此要说适用条件的话,当你的参数是明确知道数量时用 call 。

    而不确定的时候用 apply,然后把参数 push 进数组传递进去。当参数数量不确定时,函数内部也可以通过 arguments 这个数组来遍历所有的参数。

    为了巩固加深记忆,下面列举一些常用用法:

    1、数组之间追加

    JavaScript

    var array1 = [12 , "foo" , {name "Joe"} , -2458]; var array2 = ["Doe" , 555 , 100]; Array.prototype.push.apply(array1, array2); /* array1 值为 [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */

    1
    2
    3
    4
    var array1 = [12 , "foo" , {name "Joe"} , -2458];
    var array2 = ["Doe" , 555 , 100];
    Array.prototype.push.apply(array1, array2);
    /* array1 值为  [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */

    2、获取数组中的最大值和最小值

    JavaScript

    var numbers = [5, 458 , 120 , -215 ]; var maxInNumbers = Math.max.apply(Math, numbers), //458 maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458

    1
    2
    3
    var  numbers = [5, 458 , 120 , -215 ];
    var maxInNumbers = Math.max.apply(Math, numbers),   //458
        maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458

    number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。

    3、验证是否是数组(前提是toString()方法没有被重写过)

    JavaScript

    functionisArray(obj){ returnObject.prototype.toString.call(obj) === '[object Array]' ; }

    1
    2
    3
    functionisArray(obj){
        returnObject.prototype.toString.call(obj) === '[object Array]' ;
    }

    4、类(伪)数组使用数组方法

    JavaScript

    var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));

    1
    var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));

    Javascript中存在一种名为伪数组的对象结构。比较特别的是 arguments 对象,还有像调用 getElementsByTagName , document.childNodes 之类的,它们返回NodeList对象都属于伪数组。不能应用 Array下的 push , pop 等方法。

    但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象,这样 domNodes 就可以应用 Array 下的所有方法了。

    深入理解运用apply、call

    下面就借用一道面试题,来更深入的去理解下 apply 和 call 。

    定义一个 log 方法,让它可以代理 console.log 方法,常见的解决方法是:

    JavaScript

    function log(msg) { console.log(msg); } log(1); //1 log(1,2); //1

    1
    2
    3
    4
    5
    function log(msg) {
      console.log(msg);
    }
    log(1);    //1
    log(1,2);    //1

    上面方法可以解决最基本的需求,但是当传入参数的个数是不确定的时候,上面的方法就失效了,这个时候就可以考虑使用 apply 或者 call,注意这里传入多少个参数是不确定的,所以使用apply是最好的,方法如下:

    JavaScript

    function log(){ console.log.apply(console, arguments); }; log(1); //1 log(1,2); //1 2

    1
    2
    3
    4
    5
    function log(){
      console.log.apply(console, arguments);
    };
    log(1);    //1
    log(1,2);    //1 2

    接下来的要求是给每一个 log 消息添加一个”(app)”的前辍,比如:

    JavaScript

    log("hello world"); //(app)hello world

    1
    log("hello world");    //(app)hello world

    该怎么做比较优雅呢?这个时候需要想到arguments参数是个伪数组,通过 Array.prototype.slice.call 转化为标准数组,再使用数组方法unshift,像这样:

    JavaScript

    function log(){ var args = Array.prototype.slice.call(arguments); args.unshift('(app)'); console.log.apply(console, args); };

    1
    2
    3
    4
    5
    6
    function log(){
      var args = Array.prototype.slice.call(arguments);
      args.unshift('(app)');
     
      console.log.apply(console, args);
    };

    bind

    说完了 apply 和 call ,再来说说bind。bind() 方法与 apply 和 call 很相似,也是可以改变函数体内 this 的指向。

    MDN的解释是:bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

    直接来看看具体如何使用,在常见的单体模式中,通常我们会使用 _this , that , self 等保存 this ,这样我们可以在改变了上下文之后继续引用到它。 像这样:

    JavaScript

    var foo = { bar : 1, eventBind: function(){ var _this = this; $('.someClass').on('click',function(event) { /* Act on the event */ console.log(_this.bar); //1 }); } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var foo = {
        bar : 1,
        eventBind: function(){
            var _this = this;
            $('.someClass').on('click',function(event) {
                /* Act on the event */
                console.log(_this.bar);     //1
            });
        }
    }

    由于 Javascript 特有的机制,上下文环境在 eventBind:function(){ } 过渡到 $(‘.someClass’).on(‘click’,function(event) { }) 发生了改变,上述使用变量保存 this 这些方式都是有用的,也没有什么问题。当然使用 bind() 可以更加优雅的解决这个问题:

    JavaScript

    var foo = { bar : 1, eventBind: function(){ $('.someClass').on('click',function(event) { /* Act on the event */ console.log(this.bar); //1 }.bind(this)); } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var foo = {
        bar : 1,
        eventBind: function(){
            $('.someClass').on('click',function(event) {
                /* Act on the event */
                console.log(this.bar);      //1
            }.bind(this));
        }
    }

    在上述代码里,bind() 创建了一个函数,当这个click事件绑定在被调用的时候,它的 this 关键词会被设置成被传入的值(这里指调用bind()时传入的参数)。因此,这里我们传入想要的上下文 this(其实就是 foo ),到 bind() 函数中。然后,当回调函数被执行的时候, this 便指向 foo 对象。再来一个简单的栗子:

    JavaScript

    var bar = function(){ console.log(this.x); } bar(); // undefined var func = bar.bind(foo); func(); // 3

    1
    2
    3
    4
    5
    6
    7
    var bar = function(){
        console.log(this.x);
    }
     
    bar(); // undefined
    var func = bar.bind(foo);
    func(); // 3

    这里我们创建了一个新的函数 func,当使用 bind() 创建一个绑定函数之后,它被执行的时候,它的 this 会被设置成 foo , 而不是像我们调用 bar() 时的全局作用域。

    有个有趣的问题,如果连续 bind() 两次,亦或者是连续 bind() 三次那么输出的值是什么呢?像这样:

    JavaScript

    var bar = function(){ console.log(this.x); } var foo = { x:3 } var sed = { x:4 } var func = bar.bind(foo).bind(sed); func(); //? var fiv = { x:5 } var func = bar.bind(foo).bind(sed).bind(fiv); func(); //?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    var bar = function(){
        console.log(this.x);
    }
    var foo = {
        x:3
    }
    var sed = {
        x:4
    }
    var func = bar.bind(foo).bind(sed);
    func(); //?
     
    var fiv = {
        x:5
    }
    var func = bar.bind(foo).bind(sed).bind(fiv);
    func(); //?

    答案是,两次都仍将输出 3 ,而非期待中的 4 和 5 。原因是,在Javascript中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。

    apply、call、bind比较

    那么 apply、call、bind 三者相比较,之间又有什么异同呢?何时使用 apply、call,何时使用 bind 呢。简单的一个栗子:

    JavaScript

    var obj = { x: 81, }; var foo = { getX: function() { return this.x; } } console.log(foo.getX.bind(obj)()); //81 console.log(foo.getX.call(obj)); //81 console.log(foo.getX.apply(obj)); //81

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var obj = {
        x: 81,
    };
     
    var foo = {
        getX: function() {
            return this.x;
        }
    }
     
    console.log(foo.getX.bind(obj)());  //81
    console.log(foo.getX.call(obj));    //81
    console.log(foo.getX.apply(obj));   //81

    三个输出的都是81,但是注意看使用 bind() 方法的,他后面多了对括号。

    也就是说,区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。

    再总结一下:

    • apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
    • apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
    • apply 、 call 、bind 三者都可以利用后续参数传参;
    • bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。

    本文实例出现的所有代码,在我的github上可以下载。

    打赏支持我写出更多好文章,谢谢!

    打赏作者

    轻松学习 JavaScript (4):函数中的 arguments 对象

    2017/11/11 · JavaScript · arguments

    原文出处: Dhananjay Kumar   译文出处:码农网 – 小峰   

    JavaScript函数具有像数组一样的对象,这些对象称为arguments,与传递给函数的参数相对应。传递给JavaScript函数的所有参数都可以使用arguments对象来引用。

    现在我们开始学习,仔细看下面列出的代码:

    function add(num1, num2) { var res = num1 + num2; return res; } var r = add(7, 8); console.log(r);

    1
    2
    3
    4
    5
    6
    function add(num1, num2) {
        var res = num1 + num2;
        return res;
    }
    var r = add(7, 8);
    console.log(r);

    在上面的函数中,num1和num2是两个参数。你可以使用名为num1和num2的arguments来引用这些参数。除了arguments名称之外,你还可以使用JavaScript数组,如对象arguments来引用它们。所以,上面的函数可以重写,如下所示:

    function add(num1, num2) { var res = arguments[0] + arguments[1]; return res; } var r = add(7, 8); console.log(r);

    1
    2
    3
    4
    5
    6
    function add(num1, num2) {
        var res = arguments[0] + arguments[1];
        return res;
    }
    var r = add(7, 8);
    console.log(r);

    在JavaScript函数中,arguments对象用于访问或引用传递给函数的所有参数。arguments对象是可用于函数的局部变量。arguments对象的长度相当于传递给函数的arguments数量。请看下面的代码,作为输出将得到2,因为有两个arguments传递给函数:

    function add(num1, num2) { var res = arguments.length; return res; } var r = add(7, 8); console.log(r);

    1
    2
    3
    4
    5
    6
    function add(num1, num2) {
        var res = arguments.length;
        return res;
    }
    var r = add(7, 8);
    console.log(r);

    打赏支持我写出更多好文章,谢谢!

    任选一种支付方式

    图片 10 图片 11

    2 赞 16 收藏 4 评论

    arguments对象不是纯数组

    JavaScript的arguments对象不是纯粹的JavaScript数组。你不能对arguments对象执行诸如push,pop,slice等操作。正如你将在下面列出的代码中所看到的那样,执行push操作会引发异常,因为arguments.push不是函数。

    function add(num1, num2) { arguments.push(78); var res = num1 + num2; return res; }

    1
    2
    3
    4
    5
    function add(num1, num2) {
        arguments.push(78);
        var res = num1 + num2;
        return res;
    }

    本文由金沙国际官网发布于web前端,转载请注明出处:前端基础进阶,轻松学习

    关键词: