zoukankan      html  css  js  c++  java
  • js 中 this 的指向问题

    this 的值取决于代码的执行环境(execution context),this 存在于函数中,在声明时不会被绑定,只在调用时被绑定。只要记住,谁调用,绑定谁,就不会糊涂。

    1.默认绑定

    函数直接在全局环境下执行,严格模式下 this 为 undefined,非严格模式下 this 为 window。

    function f(){
    	console.log(this);
    }
    f();	//window
    
    'use strict'
    function f(){
    	console.log(this);
    }
    f();	//undefined
    

    注意:这里仅指在函数内部的 this,在全局环境下,this 都指向 window,不管是不是严格模式

    "use strict";
    console.log(this === window);	//true
    

    2.隐式绑定

    函数存在对象中,用该对象调用该函数

    var obj = {
    	f(){
    		console.log(this)
    	}
    };
    obj.f();	//obj
    

    这里 this 指向,并不是因为函数 f 声明在 obj 内,而是因为 obj 调用了 f(),如果函数 f 声明在全局环境里也是可以的

    var f = function() {
    	console.log(this)
    };
    var obj = {
    	f: f
    };
    obj.f();
    

    函数是在堆空间中存放,然后把地址存放在 obj 对象的 f 属性下,原始的对象以字典结构保存,具体的可以看这篇博文:JavaScript 的 this 原理

    如果你直接把函数赋值给对象的属性,则 this 也是指向该对象

    document.onclick = function() {
    	console.log(this)
    }
    

    这相当于给 document 对象的 onclick 属性赋值,this 自然就指向 document 对象。

    如果函数暴露给了全局,则 this 指向全局

    var obj = {
    	f: function (){
    		return function (){
    			console.log(this)
    		}
    	}
    };
    obj.f()();	//window,严格模式为undefined
    

    如果函数为私有函数,则也是指向全局

    var obj = {
    	f: function (){
    		console.log(this);	//obj
    		(function (){
    			console.log(this);	//window
    		})();
    	}
    };
    obj.f();
    

    对于这种函数内部的私有函数,没办法定义它的执行环境,所以引擎内部把他扔给了 window 对象,所以 this 指向 window。

    3.强绑定

    这个很好记,就是用 call,apply,bind 函数改变 this 的指向,这个是绑定谁就指向谁。这里需要注意的是 bind 函数,因为他的返回值是一个函数,这个函数的 this 是不可被修改的。

    function f(){
    	console.log(this)
    }
    var obj1 = {name:'obj1'};
    var obj2 = {name:'obj2'};
    var bf = f.bind(obj1);
    bf.call(obj2);	//output:{name: "obj1"}
    

    4.构造函数中的 this

    构造函数如果和普通函数一样使用,那没什么区别

    var x = 0;
    function A(x) {
    	this.x = x;
    }
    A(1);
    console.log(x)	//output: 1,这里的 this 指向 window
    

    因为 new 操作符,绑定了 this,MDN上面有 new 操作符做了哪些事:

    new 关键字会进行如下的操作:

    1. 创建一个空的简单JavaScript对象(即**{}**);
    2. 链接该对象(即设置该对象的构造函数)到另一个对象 ;(这里指实现原型链,把对象的原型 _proto_ 指向构造函数的 prototype )
    3. 将步骤1新创建的对象作为**this**的上下文 ;(这里的上下文就是指执行环境)
    4. 如果该函数没有返回对象,则返回**this**
    function A(x) {
    	this.x = x;
    	this.f = function (){
    		console.log(this)
    	}
    }
    var a = new A(1);
    a.f();	//output: 
    

    5.箭头函数与 this

    首先,箭头函数是没有 this 的,它的 this 来源于它声明时的外部环境。

    阮一峰老师的《ECMAScript 6 入门》这本书里面有个例子挺好的,我就直接拿来用了

    function foo() {
      return () => {
        return () => {
          return () => {
            console.log('id:', this.id);
          };
        };
      };
    }
    
    var f = foo.call({id: 1});
    
    var t1 = f.call({id: 2})()(); // id: 1
    var t2 = f().call({id: 3})(); // id: 1
    var t3 = f()().call({id: 4}); // id: 1
    

    为什么执行一次 call 函数后,后面都不会改变 this 的指向了呢?

    让我们用 Babel 把 foo 函数转换成 ES5 的写法后一切就会明了了:

    "use strict";
    
    function foo() {
      var _this = this;
        
      return function () {
        return function () {
          return function () {
            console.log('id:', _this.id);
          };
        };
      };
    }
    

    只要一执行 foo(),this 的值就被固定了,_this 中存的是绑定的对象的地址。

    在学习 Vue 时,经常能看见这样的提醒:

    注意,不应该使用箭头函数来定义 method 函数 (例如 plus: () => this.a++)。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.a 将是 undefined。

    不止是 methods ,computed、watch 等函数也是一样,看一下如果用箭头函数会怎样把:

    var a = 3;
    var vm = new Vue({
    	data: {
    		a: 1
    	},
    	methods: {
    		plus: () => {
    			this.a++
    		}
    	}
    });
    vm.plus();
    console.log(vm.a,a);	//output: 1	4
    

    这里,this 绑定的是 window,如果不用箭头函数,当然是 vm。

    6.回调函数中的 this

    其实,回调函数的 this 和主函数的调用对象没有什么关系,主要看主函数的内部怎么去处理这个回调函数。

    function foo(f) {
    	console.log(this);
    	f();
    }
    var obj = {name: 'obj'};
    foo.call(obj, function() {
    	console.log(this)
    })
    

    当然,讨论这个是没有意义的,主要是要记住一些特殊的异步函数的 callback 内部的 this 指向,如:

    setTimeout 函数的 callback 内部的 this 永远指向 window,无论在何处执行 setTimeout ,当然,这不包括回调为箭头函数。还有 addEventListener 的 callback 中的 this 指向绑定事件的 DOM 对象 。

    如果回调函数是箭头函数呢?

    同样的你把箭头函数转换成 ES5 语法代码,就很明了。

    document.addEventListener('click',()=>{
    	console.log(this)
    },false);	//window 或者 undefined
    

    Babel 转换:

    "use strict";
    
    var _this = void 0;
    
    document.addEventListener('click', function () {
      console.log(_this);
    }, false);	//undefined
    

    当需要函数作为参数时,使用箭头函数一定要注意 this ,箭头函数绑定了父级作用域的上下文。比方说上面 Vue 的那个例子,如果我一定要用箭头函数可以吗?我觉得是可以的:

    var vm = new Vue({
    	data: {
    		a: 1
    	},
    	methods: {
    		plus: () => {
    			vm.a++
    		}
    	}
    });
    vm.plus();
    console.log(vm.a)
    

    把 this 换成具体的对象就解决啦,哈哈。

  • 相关阅读:
    正则表达式分组()、不捕获(?:)和断言(?<=)详解
    正则匹配IP
    正则匹配中文
    SPL--Serializable
    JavaScript中原型和原型链
    JavaScript中变量和函数声明的提升
    运行gulp提示:Task function must be specified
    vue-router 去掉#
    学以致用 ---- vue子组件→父组件通信
    删除node_modules
  • 原文地址:https://www.cnblogs.com/arduka/p/13277310.html
Copyright © 2011-2022 走看看