zoukankan      html  css  js  c++  java
  • this深入理解

    This、call、apply

    This:
    JavaScript的this总是指向一个对象。

    而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。

    this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式

    this 实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用

    在理解 this 是什么❓首先必须要找到"调用位置",然后判断符合那种规则。

    当一个函数被调用时,会创建一个"执行上下文环境":

    1. 包含函数在哪里被调用 ( 调用栈 )
    2. 函数调用方法。
    3. 传入的参数等信息。
    4. this 就是记录的其中一个属性,会在函数执行的过程中用到。

    This指向大致可以分为如下四类:

    1. 作为对象的方法调用。
    2. 作为普通函数调用。
    3. 构造器调用
    4. Function.prototype.callFunction.prototype.apply 调用

    1、作为对象的方法调用

    当函数作为对象的方法被调用时, this 指向该对象

    // 当函数作为对象的方法被调用时、this指向该对象
    var obj = {
      a:1,
      getA: function(){
        alert(this === obj);//true
        alert(this.a);//1
      }
    }
    

    1-1、隐式绑定

    另一条需要考虑的规则是:

    调用位置是 否有上下文 — "对象" ,或者说

    是否被某个对象拥有( 包含 )

    function foo(){
    	console.log(this.a);
    }
    var obj = {
      	a:2,
      	foo:foo
    }
    obj.foo();
    ## 1:无论是直接在obj中定义、还是先定义再添加为引用属性,这个函数严格来说都不属于obj对象。
    ## 2:然而,调用位置会使用obj上下文来引用函数,因此你可以说函数被调用时obj对象"拥有"或者"包含"了它。
    ## 3:无论我们如何称呼这个模式,当foo()被调用时,它的落脚点确实指向obj对象。当函数引用有上下文对象时,"隐式绑定"规则会把函数调用中的"this"绑定到这个上下文对象。因为调用foo()时this被绑定到obj,所以this.a和obj.a是一样的
    ## 4:对象属性引用链中,只有最后一层( 最顶层 )会影响到调用位置
    

    1-2、隐式绑定丢失

    最常见的隐式绑定问题:

    被"隐式绑定"的函数会丢失绑定对象,也就是说它会应用"默认绑定",从而把this绑定到全局对象或者"undefined"上,取决于是否是"严格模式"

    解决办法:使用"显示绑定"的方案 call(...)和apply(…) 的"硬绑定"模式

    // 丢失案例一:堆内存地址的引用
    function foo(){
      console.log(this.a);
    }
    var obj = {
      a:2,
      foo:foo
    }
    var bar = obj.foo;
    var a = "oops,global";
    bar();// oops,global
    ## 虽然bar是obj.foo的一个引用。
    ## 但是实际上,它引用的是foo函数本身,是foo函数在堆内存空间的地址(复制的是指针的指向)
    ## 本质上:bar() == foo();
    ## 因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
    
    // 丢失案例二:非常常见并且非常出乎意料的情况(参数传递)
    function foo(){
      	console.log(this.a);
    }
    function doFoo(fn){
      	fn();
    }
    var obj = {
      	a:2,
      	foo:foo
    }
    var a = "oops,global";
    doFoo(obj.foo);
    ## 参数传递其实"就是"一种隐式赋值,因此我们传入函数时也会被隐式赋值。
    

    引用类型参数传递问题

    引用类型:引用类型传递的是指针的方向

    function setName(obj){
      	obj.name = 'aaa';
      	return obj;
    }
    var person = new Object();
    person.name = 'bbb';
    var newPerson = setName(person);
    console.log(person.name + ' || ' + newPerson.name);
    

    http://www.cnblogs.com/zareb/p/5699571.html

    function setName(obj) {
    	obj.name = 'aaa';
    	var obj = new Object(); // 如果是按引用传递的,此处传参进来obj应该被重新引用新的内存单元
    	obj.name = 'ccc';
    	return obj;
    }
    
    var person = new Object();
    	person.name = 'bbb';
    
    var newPerson = setName(person);
    console.log(person.name);
    console.log(newPerson.name);
    

    2、作为普通函数调用 ( this - 默认绑定 )

    当函数不作为对象的属性被调用时,也就是我们常说的普通函数方式,此时的 this 总是指向全局对象。在浏览器的 JavaScript 里,这个全局对象是 window 对象。

    // 使用普通函数时、其内的this总是指向window
    // 在代码中,getName()是直接使用不带任何修饰的函数引用进行调用的。因此只能使用"默认绑定",无法应用其他规则。
    // 如果是严格模式( strict mode ),那么全局对象将无法使用默认规则,因此this会绑定到"undefined"上
    // 案例一:
    window.name = 'globalName';
    var getName = function(){
      return this.name;
    }
    console.log(getName());//globalName
    // 案例二:引用getName在堆内存中的地址
    window.name = 'globalName';
    var myObject = {
      name:'ntscshen',
      getName:function(){
        return this.name;
      }
    }
    var myName = myObject.getName;
    console.log(myName());
    // 案例三:在事件函数内部、有一个局部的方法。此方法被调用时,方法内部的this指向了window
    document.getElementById('div1').onclick = function(){
      console.log(this.id);// 'div1'
      //var _this = this;// 简单的解决方案
      var callBack = function(){
        //console.log(_this.id);// 'div1'
        console.log(this.id);// 'window'
      }
      callBack();
    }
    ## 没当你想要把this和"词法"作用域的查找混合使用时,一定要提醒自己,这是无法实现的。
    

    3、构造器调用

    4、 Function.prototype.callFunction.prototype.apply

    4-1:硬绑定

    硬绑定的典型应用场景就是:创建一个包裹函数,传入所有的参数并返回接收到的所有值。

    // 案例一:
    function foo(){
    	console.log(this.a);
    }
    var obj = {a:2}
    var bar = function(){
      	foo.call(obj);
    };
    bar();
    setTimeout(bar,100);
    bar.call(window);
    // 我们首先创建了函数bar(),并在它的内部手动调用了foo.call(obj),因此强制把foo的this绑定到了obj。无论之后如何调用函数bar,它总会手动在obj上调用foo。这种绑定是一种显示的强制绑定,因此称之为"硬绑定"
    // 案例二:
    function foo(){
      	console.log(this.a,something);
    	return this.a + something;
    }
    function bind(fn, obj){
      	return function(){
          	return fn.apply(obj, arguments);
      	};
    }
    var obj = {a:2};
    var b = bar(3);
    console.log(b);
    // 由于硬绑定是一种非常常用的模式,所以在ES5中提供了内置的方法Function.prototype.bind,它的用法如下
    
    居敬持志~ Coding
  • 相关阅读:
    168. Excel Sheet Column Title
    171. Excel Sheet Column Number
    264. Ugly Number II java solutions
    152. Maximum Product Subarray java solutions
    309. Best Time to Buy and Sell Stock with Cooldown java solutions
    120. Triangle java solutions
    300. Longest Increasing Subsequence java solutions
    63. Unique Paths II java solutions
    221. Maximal Square java solutions
    279. Perfect Squares java solutions
  • 原文地址:https://www.cnblogs.com/ntscshen/p/6240238.html
Copyright © 2011-2022 走看看