this疏通 一

作者: 姚屹晨 | 来源:发表于2017-09-10 20:09 被阅读30次

    该笔记参考自:《你不知道的JavaScript上卷》

    一.关于this

    1.this是一个很特别的关键字,被自动定义在所有函数的作用域中。
    2.为什么要使用this?

    ①编写一个函数,将一个字符串中的字符全部转化为大写的形式,如:"yyc"--->"YYC"

    function convertToUpperCase(s){
        return s.toUpperCase();
    }
    var name = 'yyc';
    convertToUpperCase(name);
    >>>"YYC"
    

    ②但是,在实际情况下往往不会单独声明一个变量来仅仅表示一个人的名字,往往将一个人用一个对象表示,而他/她的名字仅代表是该对象的一个属性。

    function convertToUpperCase(obj){
        return obj.name.toUpperCase();
    }
    var person1 = {
        name: 'yyc'
    };
    convertToUpperCase(person1);
    >>>"YYC"
    
    //多个人
    function convertToUpperCase(context){
        return context.name.toUpperCase();
    }
    var person1 = {
        name: 'yyc'
    };
    var person2 = {
        name: 'asan'
    };
    var person3 = {
        name: 'tuhao'
    };
    convertToUpperCase(person1);
    >>>"YYC"
    
    convertToUpperCase(person2);
    >>>"ASAN"
    
    convertToUpperCase(person3);
    "TUHAO"
    

    ③现在增加一个功能:来yyc和大家打个招呼,同时将你的名字全部用大写表示喔。

    function convertToUpperCase(context){
        return context.name.toUpperCase();
    }
    function speak(context){
        var greeting = "Hello,l'm " + convertToUpperCase(context);
        return greeting;
    }
    var person1 = {
        name: 'yyc'
    };
    speak(person1);
    >>>"Hello,l'm YYC"
    

    ④现在可以引入this了,说好听点,让代码变得更加优雅。说白了,就是可以偷懒了。

    function convertToUpperCase(){
        return this.name.toUpperCase();
    }
    function speak(){
        var greeting = "Hello,l'm " + convertToUpperCase.call(this);
        return greeting;
    }
    var person1 = {
        name: 'yyc'
    };
    var person = {
        name: 'asan'
    };
    convertToUpperCase.call(person1);
    >>>"YYC"
    
    convertToUpperCase.call(person2);
    >>>"ASAN"
    
    speak.call(person1);
    >>>"Hello,l'm YYC"
    
    speak.call(person2);
    >>>"Hello,l'm ASAN"
    
    3.误解之一:this指向函数自身
    function foo(num){
        console.log('foo: ' + num);
        this.count++;
    }
    foo.count = 0;
    for(var i = 0;i < 10;i++){
        if(i > 5){
            foo(i);
        }
    }
    console.log(foo.count);
    >>>
    foo: 6
    foo: 7
    foo: 8
    foo: 9
    0
    

    ①输出结果表明foo()函数的确被调用了4次,但是foo.count仍然为0,证明this不是指向函数foo的。

    ②那么foo()函数中的this到底指向什么呢?

    • 全局对象

    ③为什么是全局对象呢?

    • 因为foo()函数的调用类型是:独立函数调用,也就是默认绑定。

    ④那怎么判断是默认绑定呢?

    • 在代码中,foo()是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则。

    ⑤现在让我们看一下全局对象中的count变量的值吧?

    count;
    >>>NaN
    

    ⑥为什么是NaN

    undefined++;
    >>>NaN
    

    ⑦啥意思?

    • 因为当foo()函数被独立调用时,执行this.count++;语句,而全局对象中并没有count这个属性,由于LHS查询,全局对象好心创建了一个属性,但是该属性的初始值为undefined。
    4.解决方法:

    ①看到红灯,右转。

    function foo(num){
        data.count++;
    }
    var data = {
        count: 0
    };
    for(var i = 0;i < 10;i++){
        if(i > 5){
            foo(i);
        }
    }
    console.log(data.count);
    >>>4
    

    ②通过具名函数名,从函数内部引用它自身:

    function foo(num){
        foo.count++;
    }
    foo.count = 0;
    for(var i = 0;i < 10;i++){
        if(i > 5){
            foo(i);
        }
    }
    console.log(foo.count);
    >>>4
    

    ③非要用this?,那就强制把this指向foo()函数:

    function foo(num){
        this.count++;
    }
    foo.count = 0;
    for(var i = 0;i < 10;i++){
        if(i > 5){
            foo.call(foo,i);
        }
    }
    console.log(foo.count);
    >>>4
    
    5.作用域---回炉一下

    ①作用域是根据名称查找变量的一套规则。

    LHS查询和RHS查询分别是什么?
    LHS查询:Left Hand Side,试图找到某个变量的容器本身,从而对其赋值。
    RHS查询:Retrieve His Source,仅仅找到某个变量的值。

    ③那么如何区分这两种查询?

    //LHS查询----针对变量b
    function foo(a){
        b = a + 1;
        return b;
    }
    foo(2);
    >>>3
    
    • 第一次对变量b进行LHS查询时,并没有在foo()函数中找到其声明,同时在全局作用域中仍未找到该变量的声明,因此全局作用域会创建一个具有该名称的变量。
    b;
    >>>3
    
    //RHS----针对变量b
    function foo(a){
        return a+b;
    }
    foo(2);
    >>>Uncaught ReferenceError: b is not defined
    
    • 此时,对变量b进行的是RHS查询,因为仅需要知道该变量的值即可,不需要对其赋值。不过,同上,因为在foo()和全局作用域中均没有对变量b的声明,这个时候,没有就是没有,返回ReferenceError

    ④小结

    • 不成功的RHS引用会导致抛出ReferenceError异常。不成功的LHS引用会导致自动隐式地创建一个全局变量(非严格模式下),该变量使用LHS引用的目标作为标识符,或者抛出ReferenceError异常(严格模式下)。
    6.this误解之二:指向其函数的词法作用域
    function foo(){
        var a = 2;
        bar();
    }
    function bar(){
        console.log(this.a);
    }
    foo();
    >>>undefined
    

    ①因为输出为undefined,而不是2,证明this并不是指向其函数的作用域。

    ②需要明确一点:this在任何情况下都不会指向函数的词法作用域。

    ③还有个小问题?为什么上面的输出是undefined,而不是ReferenceError

    • 当一个变量进行RHS查询,如果在任何作用域中都没有找个变量的声明,则输出ReferenceError异常!
    • 但当你查找对象中的一个属性时,若该属性不存在,则返回undefined
    var obj = {};
    obj.a;
    >>>undefined
    
    • 之前的代码片段中foo()函数内部的this指向全局对象,内部没有a属性,因此返回undefined.

    相关文章

      网友评论

        本文标题:this疏通 一

        本文链接:https://www.haomeiwen.com/subject/iuytsxtx.html