this深刻理解

This、call、apply

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

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

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

this 其实是在函数被调用时发生的绑定,它指向什么彻底取决于函数在哪里被调用浏览器

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

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

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

This指向大体能够分为以下四类:

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

一、做为对象的方法调用

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

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

1-一、隐式绑定

另外一条须要考虑的规则是:this

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

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

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-二、隐式绑定丢失

最多见的隐式绑定问题:

被"隐式绑定"的函数会丢失绑定对象,也就是说它会应用"默认绑定",从而把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);

二、做为普通函数调用 ( 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和"词法"做用域的查找混合使用时,必定要提醒本身,这是没法实现的。

三、构造器调用

四、 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,它的用法以下
相关文章
相关标签/搜索