Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

this-想说爱你不容易 #29

Open
qianlongo opened this issue Aug 19, 2018 · 0 comments
Open

this-想说爱你不容易 #29

qianlongo opened this issue Aug 19, 2018 · 0 comments
Labels

Comments

@qianlongo
Copy link
Owner

前言

javascript中的this是啥东西?为啥我们经常被他搞得晕头转向不知所以?他是恶魔?是天使 ?是怪胎?让我们一起来揭开它那神秘的面纱。

他是个啥

首先this是Javascript语言的关键字之一,指函数运行时的当前对象。那既然和函数运行有关,js中函数有哪些调用模式呢?

  1. 纯粹的函数调用
  2. 对象的方法调用
  3. 构造函数调用
  4. apply、call调用

我擦,有木有一千只草泥马在心里蹦腾不息,人家是要弄懂this,你这又是整的哪一出

我们慢慢来,一步步从这些调用模式中探究this这个神奇的远古神兽

纯粹的函数调用

函数调用 即 functionName () 模式,这也是我们使用的最多的一种方式,其属于全局调用,浏览中默认情况下函数内部的this指向window,当然是在非严格模式下。

this.name = 'qianlong';

function showName () {
  console.log(this.name);
  console.log(this === window);
}

showName() 
 
// qianlong
// true

对象的方法调用

当一个函数作为对象的某个属性方法被调用的时候

var obj = {
  name: 'qianlong',
  showName: function () {
    console.log(this.name);
  }
};

obj.showName();
// qianlong

可以看出this指向是obj这个对象,其实本质上讲函数调用形式内部this就是指向调用它的那个对象

上面的例子相当于

window.showName()

这也是为什么可以读取到全局定义的name属性的原因。

再来

var showName = function () {
    console.log(this.name);
  },
  obj = {
    name: 'qianlong',
    showName: showName
  };

  obj.showName();

这个时候输出的是什么呢

结果是不变的,在js中,一切都是对象,而这里也只是将,obj的showName属性指向,showNmae函数的引用地址。

继续

当我们把showName方法赋值给了一个变量,又会有什么事情发生呢?

var obj = {
  name: 'qianlong',
  showName: function () {
    console.log(this.name);
  }
};

var tempShowName = obj.showName;

tempShowName()

// undefined

为什么不是期望的那样输出 qianlong呢。obj的showName方法是一个对象,当把它赋值给了tempShowName变量,此时便和obj没有什么关系了,而这个时候的调用和下面是等价的。

window.tempShowName()

window上此事并没有name属性,自然输出是undefined

构造函数调用

当使用 new 去调用一个构造函数的时候,内部的this,指向的是实例化出来的对象。

var Person = function (name, sex) {
  this.name = name;
  this.sex = sex;
  console.log(this);
};

var p1 = new Person('qianlong', 'boy');

// Person {name: 'qianlong', sex: 'boy'};

构造函数也是函数,所以当你用普通调用方式调用时

var Person = function (name, sex) {
  this.name = name;
  this.sex = sex;
  console.log(this);
};

Person('qianlong', 'boy');

// 这个时候相当于给window对象添加了name和sex两个属性。

window.name // 'qianlong'
window.sex // 'boy'

apply、call调用

使用call和apply方式去调用一个函数的时候,内部的this指向的是传进来的第一个参数,当第一个参数是undefined或者null的时候,依旧指向window

关于call和apply欢迎查看另一篇文章

js中call、apply、bind那些事

var showName = function () {
      console.log(this);
    };

showName() // window
showName.call(undefined) // window
showName.call(null) // window
showName.call({name: 'qianlong'}) // {name: 'qianlong'}

箭头函数

在 ES6 的新规范中,加入了箭头函数,它和普通函数最不一样的一点就是 this 的指向,普通函数中的this,是运行时候决定的,而箭头函数却是定义时候就决定了。

var obj = {
  name: 'qianlong',
  showName: function () {
    console.log(this.name);
  },
  showNameLater: function () {
    setTimeout(() => {
      console.log(this.name);
    }, 1000)
  }		
};

obj.showNameLater();

// qianlong
var obj = {
  name: 'qianlong',
  showName: () => {
    console.log(this.name);
  }
};

obj.showName();
// undefined

一些坑

  1. setTimeout
var obj = {
  name: 'qianlong',
  showName: function () {
    console.log(this.name);
  },
  showNameLater: function () { 
    setTimeout(this.showName, 1000);
  }
};

obj.showNameLater();

// undefined

这里在执行setTimeout这个函数的时候传了obj的showName函数作为第一个参数,其效果与

var showName = obj.showName

是相同的。而setTimeout内部其实也是执行了传进去这个函数而已,即。

showName();

还记得这种调用方式和window.showName()是类似的效果吗?这个时候输入为undefined也就好理解了。

那么怎么解决这个问题呢,毕竟我们期望的效果是输出qianlong

var obj = {
  name: 'qianlong',
  showName: function () {
    console.log(this.name);
  },
  showNameLater: function () { 
    var self = this;
    setTimeout(function () {
      self.showName();
    }, 1000);
  }
};

obj.showNameLater();

或者

var obj = {
  name: 'qianlong',
  showName: function () {
    console.log(this.name);
  },
  showNameLater: function () { 
    setTimeout(this.showName.bind(this), 1000);
  }
};

obj.showNameLater();
  1. setTimeout

尼玛坑爹啊,居然还是因为你。

'use strict';

function show() {
  console.log(this);
}

show(); // undefined 

setTimeout(show, 1); // window

在严格模式下面,函数调用的时候没有指定this的情况下,内部this的表现为undefined,但是setTimeout却不同,其内部默认还是指向window。

  1. 为构造函数指定this
var Person = function (name, sex) {
  this.name = name;
  this.sex = sex;
};

var p1 = new Person.call({});

// Uncaught TypeError: Person.call is not a constructor

这里报错了,原因是我们去 new 了 Person.call 函数 ,这里的函数不是一个构造函数;

当然解决方式也是有的。

var Person = function (name, sex) {
  this.name = name;
  this.sex = sex;
};

var p1 = new (Person.bind({}))('qianlong', 'sex');

// Person {name: "qianlong", sex: "sex"}
  1. 为箭头函数指定this
var show = (str) => {
  console.log(str);
  console.log(this);
};

show('qianlong');
// qianlong
// window

show.call({name: 'qianlong'}, 'qianlong');
// qianlong
// window

可以看到使用call来手动改变箭头函数中的this的时候,无法成功。 箭头函数中的 this 在定义它的时候已经决定了(执行定义它的作用域中的 this),与如何调用以及在哪里调用它无关,包括 (call, apply, bind) 等操作都无法改变它的 this。

结语

文章可能有些疏漏与错误之处,欢迎各位指正。

@qianlongo qianlongo added the JS label Aug 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant