• javascript 之this指针-11


     前言

    在《javascript 之执行环境-08》文中说到,当JavaScript代码执行一段可执行代码时,会创建对应的执行上下文(execution context)。对于每个执行上下文,都有三个重要属性:

    • 变量对象(Variable object,VO)
    • 作用域链(Scope chain)
    • this

      JavaScript中的this跟其他语言有些不一样,比如Java .net语言中的this是在代码的执行阶段是不可变的,而JavaScript的this是在调用阶段进行绑定。也因为这一性质给了this很大的灵活性,即当函数在不同的调用方式下都可能会导致this的值不同;

    定义

      this 对象是在运行时基于函数的执行环境绑定的,跟函数的调用位置有关而不是声明的位置;可以理解为this是在函数调用阶段绑定,也就是执行上下文创建的阶段进行赋值,保存在变量对象中;

    四种绑定规则

    new 构造函数绑定,this指向新创建的对象

     1 function createPerson(){
     2        return new person();
     3     }
     4     function person() {
     5         this.name = "Joel";
     6         this.say=function(){
     7             console.log(p)
     8             console.log('hello' + this.name)
     9         }
    10     }
    11     var p = new person();
    12     p.say();
    13     console.log(p.name);//Joel
    14     console.log('开始')
    15     var t=createPerson();
    16     t.say();

    不管是直接new 还是通过function createPerson 函数返回的对象,this 都是指向了新创建出来的对象;

    显示绑定,this指向传进去的对象

     1     //call() apply() bind() 显示绑定
     2     window.color = "red";
     3     var Obj = { color: "blue" };
     4     function sayColor() {
     5         console.log(this.color);
     6     }
     7     sayColor();// red this window
     8     sayColor.call(this);//red
     9     sayColor.call(window);//red
    10     sayColor.call(Obj);// blue 把this指针改为对象Obj
    11     sayColor.apply(Obj);//blue 把this指针改为对象Obj
    12     sayColor.bind(Obj)();//blue 把this指针改为对象Obj

    如果你把null/undefined作为this的绑定对象传入call/apply/bind,这些值在调用时会被忽略,实际用的是默认的绑定规则;

    1   function foo() {
    2       console.log(this.a)
    3     }
    4    var a=2;
    5     foo.call(null);//2
    6     foo.call(undefined);//2

    隐士绑定

    以对象的方法形式调用,this指向当前这个对象

     1    function foo(){
     2         console.log(this)//{a: 2, name: "Joel", foo: ƒ}
     3         console.log(this.a)//2
     4     }
     5     var obj={
     6         a:2,
     7         name:'Joel',
     8         foo:foo
     9     };
    10     obj.foo();

    这时this指向当前obj对象,但是如果换种写法会造成this 丢失问题。

     1     function foo(){
     2         console.log(this)//{a: 2, name: "Joel", foo: ƒ}
     3         console.log(this.a)//2
     4     }
     5     var obj={
     6         a:2,
     7         name:'Joel',
     8         foo:foo
     9     };
    10     obj.foo();
    11     //this 丢失的问题
    12     var t= obj.foo;
    13     t(); //window undefined

    变量t此时保存的是函数的引用跟obj已经没有关系,所以此时this指向window。

    默认绑定 严格模式下this 绑定到undefined,否则绑定到全局对象 window

     1   function foo(){
     2         console.log(this)//window
     3         console.log(this.a)//Joel
     4     }
     5     var a='Joel';
     6     foo();
     7 
     8     //严格模式
     9     function fo(){
    10         'use strict'  //严格模式
    11         console.log(this)//undefined
    12         console.log(this.b)//报错 Cannot read property 'b' of undefined
    13     }
    14     var b='Joel';
    15     fo();

    以上是基本的this绑定规则,其中new、显示绑定很容易判断,其中比较容易错的是容易把默认绑定误认为是隐士绑定 如匿名函数、闭包、函数当做参数等;

    独立调用:this 指向window

     1   var name='Joel',age=12;
     2     function say(){
     3         function say1(){
     4             console.log(this.name);//window
     5             function say2(){
     6                 name+='-l'
     7                 console.log(this.name);//window
     8             }
     9             say2()
    10         }
    11         say1();
    12     }
    13     say();
    14 
    15     //匿名函数
    16     (function(){
    17        console.log(this.name)
    18     })()

    function 当做参数传递其实跟独立调用一样的原理

     1 function foo() {
     2         console.log(this)//window
     3         console.log(this.a)//oops global
     4     }
     5     function doFoo(fn) {
     6         console.log(this);//window
     7         fn();//类似与 foo()
     8     }
     9     var obj = {
    10         a: 2,
    11         foo: foo
    12     }
    13     var a = 'oops global';
    14     doFoo(obj.foo);

    同理setTimeout 也是一样

     1  var obj = {
     2         a: 2,
     3         foo: function() {
     4             console.log(this); 
     5         },
     6         foo2: function() {
     7             console.log(this);   //this 指向 obj 
     8             setTimeout(this.foo, 1000);   // this 指向 window 
     9         }
    10     }
    11     var a = 'oops global';
    12     obj.foo2();

    闭包中的this

     1     var name = "Joel";
     2     var obj = {
     3         name: "My object",
     4         getName: function() {
     5             // var that = this;   // 将getNameFunc()的this保存在that变量中
     6             return function() {
     7                 return this.name;
     8             };
     9         }
    10     }
    11     console.log(obj.getName()());   // "Joel"

    这里的虽然是对象的方法形式调用obj.getName(),在getName中的this是指向obj,但是返回的匿名函数中的this 为什么是window呢?

    把最后的一句拆成两个步骤执行:

    1 var t=obj.getName();
    2 t();

    是不是有点像在独立调用呢?如果需要访问obj中的name,只需要把this对象缓存起来,在匿名函数中访问即可,把var that=this;去掉注释即可;

    总结

    • this 是变量对象的一个属性,是在调用时被绑定的,跟函数的调用位置有关而不是声明的位置;
    • 找到调用位置,根据绑定规则来分析this 绑定;
    • 默认绑定严格模式下this 绑定到undefined,否则绑定到全局对象 window;

    思考题

     1     var length = 5;
     2     var obj = {
     3         foo: function (fn) {
     4             console.log(this.length); // this => obj
     5             fn(); // this => window
     6             arguments[0](); // this => arguments
     7             var bar = arguments[0];
     8             bar(); // this => window
     9         },
    10         length: 10
    11     }
    12     var fn = function () {
    13         console.log(this.length);
    14     }
    15     obj.foo(fn);
    16     //10, 5, 1, 5
  • 相关阅读:
    洛谷 P2986 [USACO10MAR]伟大的奶牛聚集Great Cow Gat…(树规)
    STREAMING #5 题解 3.高位网络
    冲刺NOIP2015提高组复赛模拟试题(五) 3.破坏基地
    冲刺NOIP2015提高组复赛模拟试题(五)2.道路修建
    冲刺NOIP2015提高组复赛模拟试题(五)1.数学作业
    洛谷P1186 玛丽卡 spfa+删边
    清北学堂 day6 花
    清北学堂 day6 兔子
    C++ STL 全排列函数
    flash分区的意义
  • 原文地址:https://www.cnblogs.com/longbensong/p/8276155.html
Copyright © 2020-2023  润新知