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
  • 相关阅读:
    SQL Server 查看存储过程执行次数的方法
    css背景图片拉伸 以及100% 满屏显示
    时间倒计时
    对于解决 缓存问题
    HTML5 隐藏地址栏 兼容IOS 与安卓
    多行文字实现垂直居中 css3
    div中溢出文字用点代替
    左侧固定 右侧自适应 布局
    两个DIV第一个用了定位后 如何让两个DIV 落在一起
    String.Format,DateTime日期时间格式化
  • 原文地址:https://www.cnblogs.com/ntscshen/p/6240238.html
Copyright © 2011-2022 走看看