资讯专栏INFORMATION COLUMN

this - 想说爱你不容易

zhaot / 313人阅读

摘要:构造函数也是函数,所以当你用普通调用方式调用时这个时候相当于给对象添加了和两个属性。为构造函数指定这里报错了,原因是我们去了函数,这里的函数不是一个构造函数当然解决方式也是有的。

前言

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

他是个啥

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

纯粹的函数调用

对象的方法调用

构造函数调用

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();

2. setTimeout

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

"use strict";

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

show(); // undefined 

setTimeout(show, 1); // window

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

3. 为构造函数指定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"}

4. 为箭头函数指定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。

结语

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86732.html

相关文章

  • 浏览器缓存,想说爱你容易

    摘要:浏览器缓存的分类浏览器缓存主要有两类缓存协商和彻底缓存,也有称之为协商缓存和强缓存。只能被终端用户的浏览器缓存,不允许等中继缓存服务器对其缓存。 今天小微开店宝在测试环境发布更新的时候,同事问:为什么我需要手动清理浏览器缓存才能看到变更?难道系统上线后也需要客户自己清理浏览器缓存吗!看来,这个坑需要我来填了。 什么是浏览器缓存 浏览器缓存(Brower Caching)是浏览器在本地磁...

    Imfan 评论0 收藏0
  • 无需Flash录视频——HTML5中级进阶

    摘要:比如就会报出警告,并执行出错。视频的宽高,并不会因为填写的数值比例不合法而失真。通过绑定事件,来获取视频片段数据,并在内存中累积。执行之后会停止触发事件。录制结束后,把累计的片段数据保存为对象,并从浏览器下载存为视频文件。 前言 HTML5的权限越来越大了,浏览器可以直接调用摄像头、麦克风了,好激动啊。我们要用纯洁的HTML代码造出自己的天地。 视频采集 本篇介绍的栗子 都是在chro...

    Cruise_Chan 评论0 收藏0
  • 2016年前端盘点合集

    摘要:年已经过去,这一年前端领域发生了什么有哪些技术和项目引人注目工程师们观点和看法又有怎样的变化在此,整理了一些对过去的年盘点的资料,一是希望能借此提高自己的姿势水平,二是希望能为年的学习有所指导。 2016年已经过去,这一年前端领域发生了什么?有哪些技术和项目引人注目?工程师们观点和看法又有怎样的变化?在此,整理了一些对过去的2016年盘点的资料,一是希望能借此提高自己的姿势水平,二是希...

    aisuhua 评论0 收藏0
  • JavaScript深入浅出

    摘要:理解的函数基础要搞好深入浅出原型使用原型模型,虽然这经常被当作缺点提及,但是只要善于运用,其实基于原型的继承模型比传统的类继承还要强大。中文指南基本操作指南二继续熟悉的几对方法,包括,,。商业转载请联系作者获得授权,非商业转载请注明出处。 怎样使用 this 因为本人属于伪前端,因此文中只看懂了 8 成左右,希望能够给大家带来帮助....(据说是阿里的前端妹子写的) this 的值到底...

    blair 评论0 收藏0

发表评论

0条评论

zhaot

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<