js中this

209 阅读4分钟

this

关于this的指向问题,一直都很困惑,趁此机会就好好的研究一下,this到底指向了什么。
首先要明确的是,this并不是指的函数自身

function foo(){
    console.log(this)
}
foo() //window,并不是foo

this是运行时绑定的,而不是编写绑定的,因此this取决于执行的上下文,也就是函数的调用条件。

在《你不知道的javascript(上卷)》中提到了this的四种绑定规则,如果不知道的可以看一下书,也可以参考别人总结好的this的四种绑定规则

其实就可以总结为:

this执行主体:谁把他执行的

1、函数执行,看前面是否有.点,如果有.点,.点前面是谁this就是谁,如果没有.点,this是window(严格模式是undefined),自执行函数中的this一般都是window

fn() this->window

obj.fn() this->obj

obj.__proto__.fn() this->obj.__proto__

2、给元素的实践行为绑定方法(DOM0/DOM2),事件触发,方法会执行,此时方法中的this一般都是当前元素本身

box.onclick = function(){//=>this->box}

box.addEventListener('onclick',function(){

 //this->box

})

3、 ES6箭头函数:this指的是内部的this就是外层代码块的this

// 普通函数
function foo() {
  setTimeout(function() {
    console.log('id:', this.id);
  });
}

var id = 21;
foo.call({ id: 42 }); //21

// 箭头函数
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;
foo.call({ id: 42 }); //42
// 上面的匿名函数定义时所在的执行环境就是foo函数,所以匿名函数内部的this执向始终会和foo函数的this执向保持一致,不会更改,如同下面的这个案例
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;
foo(); //21(没有用call)

4、特殊情况:IE8及以下,基于attachEvent完成DOM2事件绑定,this是不准确的

box.attachEvent('onclick',function(){

 //this->window

})

面试题

整理出一批面试题

第1题

var a = 10;
var foo = {
  a: 20,
  bar: function() {
    var a = 30;
    console.log(this);
    return this.a;
  }
};
foo.bar();
(foo.bar)()
(foo.bar = foo.bar)();
(foo.bar, foo.bar)(); 

// 第2题

function t() {
  this.x = 2;
}
t();
console.log(window.x); 

// 第3题

 var obj = {
  x: 1,
  y: 2,
  t: function() {
    console.log(this.x);
  }
};
obj.t(); //1

var dog = { x: 11 };
dog.t = obj.t;
dog.t(); //11

show = function() {
  console.log("show" + this.x);
};

dog.t = show;
dog.t(); //show11

// 第4题

name = "this is window";
var obj1 = {
  name: "php",
  t: function() {
    console.log(this.name);
  }
};
var dog1 = {
  name: "huzi"
};

obj1.t(); //php

dog1.t = obj1.t;

var tmp = dog1.t;
tmp(); //this is window

(dog1.t = obj1.t)(); //this is window
dog1.t.call(obj1); // php

// 第5题

var number = 2;
var obj = {
  number: 4,
  fn1: (function() {
    var number;
    this.number *= 2; //window.number*=2=4

    number = number * 2; //NaN
    number = 3; //number=3
    return function() {
      var num = this.number;
      this.number *= 2;
      console.log(num);
      number *= 3; //number引用的是匿名函数中的number,不是全局number
      alert(number);
    };
  })(),

  db2: function() {
    this.number *= 2;
  }
};

var fn1 = obj.fn1;

alert(number); // 4

fn1(); //4,9,window.number=8

obj.fn1(); //4,27,obj.num=4*2=8

alert(window.number); //8

alert(obj.number); //8

// 第6题

var x = 3;
var y = 4;
var obj = {
  x: 1,
  y: 6,
  getX: function() {
    var x = 5;
    return (function() {
      return this.x;
    })();
  },
  getY: function() {
    var y = 7;
    return this.y;
  }
};
console.log(obj.getX()); //3
console.log(obj.getY()); //6

//第7题

var name = "the window";
var object = {
  name: "My Object",
  getName: function() {
    return this.name;
  }
};
object.getName();
object.getName(); 
(object.getName = object.getName)();
(object.getName, object.getName())();

// 第8题

var a = 10;
var obt = {
  a: 20,
  fn: function() {
    var a = 30;
    console.log(this.a);
  }
};
obt.fn(); //20
obt.fn.call(); //10
obt.fn(); // 20
// 左边括号内是一个逗号表达式,逗号表达式的值是逗号分隔的最后一个表达式的值,在这里就是obt.fn。
// 注意在js代码中书写函数名的时候,除了函数定义,函数名后面带有括号的情况,是对函数的调用(执行);
// 如果不带括号,则是获得了一个指向函数的指针,但并没有实际执行它。这里的逗号表达式返回了obt.fn的指针,它正确的指向了这个函数,
// 但是与成员运算符相比,有一个重大的区别:丢失了上下文环境,即函数内部的this不再绑定为obt,而是指向全局对象window。
(obt.fn, obt.fn)(); //10
new obt.fn(); //undefined

// 第9题

function a(xx) {
  this.x = xx;
  return this;
}
var x = a(5); //x=window
var y = a(6); //y=window

console.log(x.x); // 6
console.log(y.x); // 6 

// 第10题

this.x = 9;
var module = {
  x: 81,
  getX: function() {
    return this.x;
  }
};

module.getX(); // 81

var retrieveX = module.getX;
retrieveX(); //9
var boundGetX = retrieveX.bind(module);
boundGetX(); //81 

// 第11题

var A = function(name) {
  this.name = name;
};
var B = function() {
  A.apply(this, arguments);
};
B.prototype.getName = function() {
  return this.name;
};
var b = new B("sven");
console.log(b.getName()); //seven 

// 第12题

function foo(el) {
  console.log(el, this.id);
}

var obj = {
  id: "awesome"
};

[1, 2, 3].forEach(foo, obj);

// 第13题

function foo() {
  console.log(this.a);
}

var obj1 = {
  a: 2,
  foo: foo
};

var obj2 = {
  a: 3,
  foo: foo
};

obj1.foo(); //2
obj2.foo(); // 3

obj1.foo.call(obj2); //3
obj2.foo.call(obj1); //2

第14题

var a=10;
var foo={
  a:20,
  bar:function(){
      var a=30;
      console.log(this)
      console.log(this.a)
      return this.a;
    }
};
foo.bar()  //foo,this.a==20;
(foo.bar)() // window,this.a==10
(foo.bar=foo.bar)() // window,this.a==10
(foo.bar,foo.bar)() // window,this.a==10

第15题 //解析

function Foo() {
    getName = function () { alert (1); };
    return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}
 
//请写出以下输出结果:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();

//参考:4