Javascript this 关键字 详解

  一、this指向构造函数实例化对象

  在上篇文章中,我们提到了使用new和不使用new调用构造函数的区别,如下例:

  

复制代码 代码如下:

  function Benjamin(username, sex) {

  this.username = username;

  this.sex = sex;

  }

  var benjamin = new Benjamin("zuojj", "male");

  //Outputs: Benjamin{sex: "male",username: "zuojj"}

  console.log(benjamin);

  var ben = Benjamin("zhangsan", "female");

  //Outputs: undefined

  console.log(ben);

  当构造函数当做普通函数被调用时,并没有返回值,同时this指向全局对象。那么我们如何来避免因为缺少new关键字,而产生的问题呢?

  

复制代码 代码如下:

  function Benjamin(username, sex) {

  //Check whether "this" is a "Benjamin" object

  if(this instanceof Benjamin) {

  this.username = username;

  this.sex = sex;

  }else {

  return new Benjamin(username, sex);

  }

  }

  var benjamin = new Benjamin("zuojj", "male");

  //Outputs: Benjamin{sex: "male",username: "zuojj"}

  console.log(benjamin);

  var ben = Benjamin("zhangsan", "female");

  //Outputs: Benjamin {username: "zhangsan", sex: "female"}

  console.log(ben);

  在上例中,我们首先检查this是否是Benjammin的实例,如果不是,使用new自动调用构造函数,并实例化,这意味着,我们不再需要担心,遗漏new关键字实例化构造函数。当然这样我们可能会养成一个坏的习惯,如果避免这种现象呢?我们可以抛出一个错误,像下面这样:

  

复制代码 代码如下:

  function Benjamin(username, sex) {

  //Check whether "this" is a "Benjamin" object

  if(this instanceof Benjamin) {

  this.username = username;

  this.sex = sex;

  }else {

  // If not, throw error.

  throw new Error("`Benjamin` invoked without `new`");

  }

  }

  二、this指向调用该函数的对象

  看下面的例子:

  

复制代码 代码如下:

  var x = 10;

  var obj = {

  x: 10,

  output: function() {

  //Outputs: true

  console.log(this === obj);

  return this.x;

  },

  innerobj: {

  x: 30,

  output: function() {

  //Outputs: true

  console.log(this === obj.innerobj);

  return this.x;

  }

  }

  };

  //Outputs: 10

  console.log(obj.output());

  //Outputs: 30

  console.log(obj.innerobj.output());

  三、this指向全局对象

  在上面讨论构造函数的时候我们也讨论到不适用new的时候,this会指向全局对象,下面我们来看看两种常见的容易犯错的实例:

  

复制代码 代码如下:

  var x = 100;

  var obj = {

  x: 10,

  output: function() {

  (function() {

  //Outputs: true

  console.log(this === window);

  //Outputs: Inner: 100

  console.log("Inner:" + this.x);

  })();

  return this.x;

  }

  };

  //Outputs: 10

  console.log(obj.output());

  在使用闭包的时候,作用域发生变化,this指向window(浏览器中)。

  

复制代码 代码如下:

  var x = 100;

  var obj = {

  x: 10,

  output: function() {

  return this.x;

  }

  };

  var output = obj.output;

  //Outputs: 10

  console.log(obj.output());

  //Outputs: 100

  console.log(output());

  var obj2 = {

  x: 30,

  output: obj.output

  }

  //Outputs: 30

  console.log(obj2.output());

  此时this始终指向函数调用时的对象。

  四、this指向apply/call()方法指派的对象

  

复制代码 代码如下:

  var x = 100;

  var obj = {

  x: 10,

  output: function() {

  return this.x;

  }

  };

  //Outputs: 10

  console.log(obj.output());

  var obj2 = {

  x: 40,

  output: obj.output

  }

  //Outputs: 40

  console.log(obj.output.call(obj2));

  //Outputs: 10

  console.log(obj2.output.apply(obj));

  五、callback函数內的this指向调用该callback的函数的this所指向的对象

  

复制代码 代码如下:

  //<input type="text" value="3" id="txt_username">

  $("#username").on("click", function() {

  console.log(this.value);

  });

  六、Function.prototype.bind中的this

  The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

  实例一:

  

复制代码 代码如下:

  function person() {

  return this.name;

  }

  //Function.prototype.bind

  var per = person.bind({

  name: "zuojj"

  });

  console.log(per);

  var obj = {

  name: "Ben",

  person: person,

  per: per

  };

  //Outputs: Ben, zuojj

  console.log(obj.person(), obj.per());

  实例二:

  

复制代码 代码如下:

  this.x = 9;

  var module = {

  x: 81,

  getX: function() { return this.x; }

  };

  //Outputs: 81

  console.log(module.getX());

  var getX = module.getX;

  //Outputs: 9, because in this case, "this" refers to the global object

  console.log(getX);

  // create a new function with 'this' bound to module

  var boundGetX = getX.bind(module);

  //Outputs: 81

  console.log(boundGetX());