我的编程空间,编程开发者的网络收藏夹
学习永远不晚

JavaScript中的this实例分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

JavaScript中的this实例分析

本篇内容主要讲解“JavaScript中的this实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JavaScript中的this实例分析”吧!

    普通函数中的 this

    我们来看例题:请给出下面代码的运行结果。

    例题1

    function f1 () {   console.log(this)}f1() // window

    普通函数在非严格的全局环境下调用时,其中的 this 指向的是 window。

    例题2

    "use strict"function f1 () {   console.log(this)}f1() // undefined

    用了严格模式 "use strict",严格模式下无法再意外创建全局变量,所以 this 不为 window 而为 undefined。

    注意:babel 转成 ES6 的,babel 会自动给 js 文件上加上严格模式。

    箭头函数中的 this

    在箭头函数中,this 的指向是由外层(函数或全局)作用域来决定的。

    例题3

    const Animal = {    getName: function() {        setTimeout(function() {            console.log(this)        })    }}Animal.getName() // window

    此时 this 指向 window。这里也印证了那句经典的话:“匿名函数的 this 永远指向 window”。

    如果要让 this 指向 Animal 这个对象,则可以巧用箭头函数来解决。

    例题4

    const Animal = {    getName: function() {        setTimeout(() => {            console.log(this)        })    }}Animal.getName() // {getName: ƒ}

    严格模式对箭头函数没有效果

    例题5

    "use strict";const f1 = () => {   console.log(this)}f1() // window

    我们都知道箭头函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。普通函数使用了严格模式 this 会指向 undefined 但箭头函数依然指向了 window。

    函数作为对象的方法中的 this

    例题6

    const obj = {    name: "coboy",     age: 18,     add: function() {        console.log(this, this.name, this.age)    }};obj.add(); // {name: "coboy", age: 18, add: ƒ} "coboy" 18

    在对象方法中,作为对象的一个方法被调用时,this 指向调用它所在方法的对象。也就是开头我们所说的那句:“谁调用了它,它就指向谁”,在这里很明显是 obj 调用了它。

    例题7

    const obj = {    name: "coboy",     age: 18,     add: function() {        console.log(this, this.name, this.age)    }};const fn = obj.addfn() // window

    这个时候 this 则仍然指向 window。obj 对象方法 add 赋值给 fn 之后,fn 仍然在 window 的全局环境中执行,所以 this 仍然指向 window。

    例题8

    const obj = {    name: "coboy",     age: 18,     add: function() {        function fn() {            console.log(this)        }        fn()    }};obj.add() // window

    如果在对象方法内部声明一个函数,这个函数的 this 在对象方法执行的时候指向就不是这个对象了,而是指向 window 了。

    同样想要 this 指向 obj 可以通过箭头函数来实现:

    const obj = {    name: "coboy",     age: 18,     add: function() {        const fn = () => {            console.log(this)        }        fn()    }};obj.add() // obj

    再次说明箭头函数的 this 是由外层函数作用域或者全局作用域决定的。

    上下文对象调用中的 this

    例题9

    const obj = {    name: "coboy",     age: 18,     add: function() {        return this    }};console.log(obj.add() === obj) // true

    参考上文我们很容易知道 this 就是指向 obj 对象本身,所以返回 true。

    例题10

    const animal = {    name: "coboy",     age: 18,     dog: {        name: 'cobyte',        getName: function() {            console.log(this.name)        }    }};animal.dog.getName() // 'cobyte'

    如果函数中的 this 是被上一级的对象所调用的,那么 this 指向的就是上一级的对象,也就是开头所说的:“谁调用了它,它就指向谁”。

    例题11

    const obj1 = {    txt: 'coboy1',    getName: function() {        return this.txt    }}const obj2 = {    txt: 'coboy2',    getName: function() {        return obj1.getName()    }}const obj3 = {    txt: 'coboy3',    getName: function() {        return obj2.getName()    }}console.log(obj1.getName()) console.log(obj2.getName())console.log(obj3.getName())

    三个最终都打印了coboy1。

    执行 obj3.getName 里面返回的是 obj2.getName 里面返回的结果,obj2.getName 里面返回的是 obj1.getName 的结果,obj1.getName 返回的结果就是 'coboy1'。

    如果上面的题改一下:

    例题12

    const obj1 = {    txt: 'coboy1',    getName: function() {        return this.txt    }}const obj2 = {    txt: 'coboy2',    getName: function() {        return obj1.getName()    }}const obj3 = {    txt: 'coboy3',    getName: function() {        const fn = obj1.getName        return fn()    }}console.log(obj1.getName()) console.log(obj2.getName())console.log(obj3.getName())

    这个时候输出了 coboy1, coboy1, undefined。

    最后一个其实在上面例题5中已经有说明了。通过 const fn = obj1.getName 的赋值进行了“裸奔”调用,因此这里的 this 指向了 window,运行结果当然是 undefined。

    例题13

    上述的例题10中的 obj2.getName() 如果要它输出‘coboy2’,如果不使用 bind、call、apply 方法该怎么做?

    const obj1 = {    txt: 'coboy1',    getName: function() {        return this.txt    }}const obj2 = {    txt: 'coboy2',    getName: obj1.getName}console.log(obj1.getName()) console.log(obj2.getName())

    上述方法同样说明了那个重要的结论:this 指向最后调用它的对象。

    我们将函数 obj1 的 getName 函数挂载到了 obj2 的对象上,getName 最终作为 obj2 对象的方法被调用。

    在构造函数中的 this

    通过 new 操作符来构建一个构造函数的实例对象,这个构造函数中的 this 就指向这个新的实例对象。同时构造函数 prototype 属性下面方法中的 this 也指向这个新的实例对象。

    例题14

    function Animal(){    console.log(this) // Animal {}}const a1 = new Animal();console.log(a1) // Animal {}
    function Animal(){    this.txt = 'coboy';    this.age = 100;}Animal.prototype.getNum = function(){    return this.txt;}const a1 = new Animal();console.log(a1.age) // 100console.log(a1.getNum()) // 'coboy'

    在构造函数中出现显式 return 的情况。

    例题15

    function Animal(){    this.txt = 'coboy'    const obj = {txt: 'cobyte'}    return obj}const a1 = new Animal();console.log(a1.txt) // cobyte

    此时 a1 返回的是空对象 obj。

    例题16

    function Animal(){    this.txt = 'coboy'    return 1}const a1 = new Animal();console.log(a1.txt) // 'coboy'

    由此可以看出,如果构造函数中显式返回一个值,且返回的是一个对象,那么 this 就指向返回的对象,如果返回的不是一个对象,而是基本类型,那么 this 仍然指向实例。

    call,apply,bind 显式修改 this 指向

    call方法

    例题17

    const obj = {    txt: "coboy",     age: 18,     getName: function() {        console.log(this, this.txt)    }};const obj1 = {    txt: 'cobyte'}obj.getName(); // this指向objobj.getName.call(obj1); // this指向obj1obj.getName.call(); // this指向window

    apply方法

    例题18

    const obj = {    txt: "coboy",     age: 18,     getName: function() {        console.log(this, this.txt)    }};const obj1 = {    txt: 'cobyte'}obj.getName.apply(obj1) // this指向obj1obj.getName.apply() // this指向window

    call 方法和 apply 方法的区别

    例题19

    const obj = {    txt: "coboy",     age: 18,     getName: function(name1, name2) {        console.log(this, name1, name2)    }};const obj1 = {    txt: 'cobyte'}obj.getName.call(obj1, 'coboy1', 'coboy2')obj.getName.apply(obj1, ['coboy1', 'coboy2'])

    可见 call 和 apply 主要区别是在传参上。apply 方法与 call 方法用法基本相同,不同点主要是 call() 方法的第二个参数和之后的参数可以是任意数据类型,而 apply 的第二个参数是数组类型或者 arguments 参数集合。

    bind 方法

    例题20

    const obj = {    txt: "coboy",     age: 18,     getName: function() {        console.log(this.txt)    }};const obj2 = {    txt: "cobyte"}const newGetName = obj.getName.bind(obj2)newGetName() // this指向obj2obj.getName() // this仍然指向obj

    bind() 方法也能修改 this 指向,不过调用 bind() 方法不会执行 getName()函数,也不会改变 getName() 函数本身,只会返回一个已经修改了 this 指向的新函数,这个新函数可以赋值给一个变量,调用这个变量新函数才能执行 getName()。

    call() 方法和 bind() 方法的区别在于

    • bind 的返回值是函数,并且不会自动调用执行。

    • 两者后面的参数的使用也不同。call 是 把第二个及以后的参数作为原函数的实参传进去, 而 bind 实参在其传入参数的基础上往后获取参数执行。

    例题21

    function fn(a, b, c){     console.log(a, b, c); }const fn1 = fn.bind({abc : 123},600);fn(100,200,300) // 100,200,300 fn1(100,200,300) // 600,100,200 fn1(200,300) // 600,200,300 fn.call({abc : 123},600) // 600,undefined,undefinedfn.call({abc : 123},600,100,200) // 600,100,200

    this 优先级

    我们通常把通过 call、apply、bind、new 对 this 进行绑定的情况称为显式绑定,而把根据调用关系确定 this 指向的情况称为隐式绑定。那么显示绑定和隐式绑定谁的优先级更高呢?

    例题22

    function getName() {    console.log(this.txt)}const obj1 = {    txt: 'coboy1',    getName: getName}const obj2 = {    txt: 'coboy2',    getName: getName}obj1.getName.call(obj2) // 'coboy2'obj2.getName.apply(obj1) // 'coboy1'

    可以看出 call、apply 的显示绑定比隐式绑定优先级更高些。

    例题23

    function getName(name) {   this.txt = name}const obj1 = {}const newGetName = getName.bind(obj1)newGetName('coboy')console.log(obj1) // {txt: "coboy"}

    当再使用 newGetName 作为构造函数时。

    const obj2 = new newGetName('cobyte')console.log(obj2.txt) // 'cobyte'

    这个时候新对象中的 txt 属性值为 'cobyte'。

    newGetName 函数本身是通过 bind 方法构造的函数,其内部已经将this绑定为 obj1,当它再次作为构造函数通过 new 被调用时,返回的实例就已经和 obj1 解绑了。也就是说,new 绑定修改了 bind 绑定中的 this 指向,所以 new 绑定的优先级比显式 bind 绑定的更高。

    例题24

    function getName() {   return name => {      return this.txt   }}const obj1 = { txt: 'coboy1' }const obj2 = { txt: 'coboy2' }const newGetName = getName.call(obj1)console.log(newGetName.call(obj2)) // 'coboy1'

    由于 getName 中的 this 绑定到了 obj1 上,所以 newGetName(引用箭头函数) 中的 this 也会绑到 obj1 上,箭头函数的绑定无法被修改。

    例题25

    var txt = 'good boy'const getName = () => name => {    return this.txt}const obj1 = { txt: 'coboy1' }const obj2 = { txt: 'coboy2' }const newGetName = getName.call(obj1)console.log(newGetName.call(obj2)) // 'good boy'

    例题26

    const txt = 'good boy'const getName = () => name => {    return this.txt}const obj1 = { txt: 'coboy1' }const obj2 = { txt: 'coboy2' }const newGetName = getName.call(obj1)console.log(newGetName.call(obj2)) // undefined

    const 声明的变量不会挂到 window 全局对象上,所以 this 指向 window 时,自然也找不到 txt 变量了。

    箭头函数的 this 绑定无法修改

    例题27

    function Fn() {    return txt => {        return this.txt    }}const obj1 = {    txt: 'coboy'}const obj2 = {    txt: 'cobyte'}const f = Fn.call(obj1)console.log(f.call(obj2)) // 'coboy'

    由于 Fn 中的 this 绑定到了 obj1 上,所以 f 中的 this 也会绑定到 obj1 上, 箭头函数的绑定无法被修改。

    例题28

    var txt = '意外不'const Fn = () => txt => {   return this.txt}const obj1 = {    txt: 'coboy'}const obj2 = {    txt: 'cobyte'}const f = Fn.call(obj1)console.log(f.call(obj2)) // '意外不'

    如果将 var 声明方式改成 const 或 let 则最后输出为 undefined,原因是使用 const 或 let 声明的变量不会挂载到 window 全局对象上。因此,this 指向 window 时,自然也找不到 txt 变量了。

    从手写 new 操作符中去理解 this

    有一道经典的面试题,JS 的 new 操作符到底做了什么?

    • 创建一个新的空对象

    • 把这个新的空对象的隐式原型(__proto__)指向构造函数的原型对象(prototype

    • 把构造函数中的 this 指向新创建的空对象并且执行构造函数返回执行结果

    • 判断返回的执行结果是否是引用类型,如果是引用类型则返回执行结果,new 操作失败,否则返回创建的新对象

    function create(Fn,...args){    // 1、创建一个空的对象    let obj = {}; // let obj = Object.create({});    // 2、将空对象的原型prototype指向构造函数的原型    Object.setPrototypeOf(obj,Fn.prototype); // obj.__proto__ = Fn.prototype     // 以上 1、2步还可以通过 const obj = Object.create(Fn.prototype) 实现    // 3、改变构造函数的上下文(this),并将参数传入    let result = Fn.apply(obj,args);    // 4、如果构造函数执行后,返回的结果是对象类型,则直接将该结果返回,否则返回 obj 对象    return result instanceof Object ? result : obj;    // return typeof result === 'object' && result != null ? result : obj}

    一般情况下构造函数没有返回值,但是作为函数,是可以有返回值的,这就解析了上面例题15和例题16的原因了。 在 new 的时候,会对构造函数的返回值做一些判断:如果返回值是基础数据类型,则忽略返回值,如果返回值是引用数据类型,则使用 return 的返回,也就是 new 操作符无效。

    从手写 call、apply、bind 中去理解 this

    手写 call 的实现

    Function.prototype.myCall = function (context, ...args) {    context = context || window    // 创建唯一的属性防止污染    const key = Symbol()    // this 就是绑定的那个函数    context[key] = this    const result = context[key](...args)    delete context[key]    return result}
    • myCall 中的 this 指向谁?

    myCall 已经设置在 Function 构造函数的原型对象(prototype)上了,所以每个函数都可以调用 myCall 方法,比如函数 Fn.myCall(),根据 this 的确定规律:“谁调用它,this 就指向谁”,所以myCall方法内的 this 就指向了调用的函数,也可以说是要绑定的那个函数。

    • Fn.myCall(obj) 本质就是把函数 Fn 赋值到 对象 obj 上,然后通过对象 obj.Fn() 来执行函数 Fn,那么最终又回到那个 this 的确定规律:“谁调用它,this 就指向谁”,因为对象 obj 调用了 Fn 所以 Fn 内部的 this 就指向了对象 obj。

    手写 apply 的实现

    apply 的实现跟 call 的实现基本是一样的,因为他们的使用方式也基本一样,只是传参的方式不一样。apply 的参数必须以数组的形式传参。

    Function.prototype.myApply = function (context, args) {    if(!Array.isArray(args)) {       new Error('参数必须是数组')    }    context = context || window    // 创建唯一的属性防止污染    const key = Symbol()    // this 就是绑定的那个函数    context[key] = this    const result = context[key](...args)    delete context[key]    return result}

    手写 bind 的实现

    bind 和 call、apply 方法的区别是它不会立即执行,它是返回一个改变了 this 指向的函数,在绑定的时候可以传参,之后执行的时候,还可以继续传参数数。这个就是一个典型的闭包行为了,是不是。

    我们先来实现一个简单版的:

    Function.prototype.myBind = function (ctx, ...args) {    // 根据上文我们可以知道 this 就是调用的那个函数    const self = this    return function bound(...newArgs) {        // 在再次执行的的时候去改变 this 的指向        return self.apply(ctx, [...args, ...newArgs])    }}

    但是,就如之前在 this 优先级分析那里所展示的规则:bind 返回的函数如果作为构造函数通过 new 操作符进行实例化操作的话,绑定的 this 就会实效。

    为了实现这样的规则,我们就需要区分这两种情况的调用方式,那么怎么区分呢?首先返回出去的是 bound 函数,那么 new 操作符实例化的就是 bound 函数。通过上文 “从手写 new 操作符中去理解 this” 中我们可以知道当函数被 new 进行实例化的时候, 构造函数内部的 this 就是指向实例化的对象,那么判断一个函数是否是一个实例化的对象的构造函数时可以通过 intanceof 操作符判断。

    知识点: instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

    Function.prototype.myBind = function (ctx, ...args) {    // 根据上文我们可以知道 this 就是调用的那个函数    const self = this    return function bound(...newArgs) {        // new Fn 的时候, this 是 Fn 的实例对象        if(this instanceof Fn) {            return new self(...args, ...newArgs)        }        // 在再次执行的的时候去改变 this 的指向        return self.apply(ctx, [...args, ...newArgs])    }}

    另外我们也可以通过上文的实现 new 操作符的代码来实现 bind 里面的 new 操作。

    完整的复杂版:

    Function.prototype.myBind = function (ctx) {  const self = this  if (!Object.prototype.toString.call(self) === '[object Function]') {    throw TypeError('myBind must be called on a function');  }  // 对 context 进行深拷贝,防止 bind 执行后返回函数未执行期间,context 被修改  ctx = JSON.parse(JSON.stringify(ctx)) || window;  const args = Array.prototype.slice.call(arguments, 1);    const create = function (conFn) {    const obj = {};        obj.__proto__ = conFn.prototype;        const res = conFn.apply(obj, Array.prototype.slice.call(arguments,1));    return typeof res === 'object' && res != null ? res : obj;  };  const bound = function () {    // new 操作符操作的时候    if (this instanceof bound) {      return create(self, args.concat(Array.prototype.slice.call(arguments)));    }    return self.apply(ctx, args.concat(Array.prototype.slice.call(arguments)));  };  return bound;};

    为什么显式绑定的 this 要比隐式绑定的 this 优先级要高

    通过上面的实现原理,我们就可以理解为什么上面的 this 优先级中通过 call、apply、bind 和 new 操作符的显式绑定的 this 要比隐式绑定的 this 优先级要高了。例如上面的 obj1.getName.call(obj2) 中的 getName 方法本来是通过 obj1 来调用的,但通过 call 方法之后,实际 getName 方法变成了 obj2.getName() 来执行了。

    到此,相信大家对“JavaScript中的this实例分析”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    JavaScript中的this实例分析

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档

    猜你喜欢

    JavaScript中的this实例分析

    本篇内容主要讲解“JavaScript中的this实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JavaScript中的this实例分析”吧!普通函数中的 this我们来看例题:请给出下
    2023-07-02

    JavaScript之this指向实例分析

    今天小编给大家分享一下JavaScript之this指向实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。默认绑定,全局
    2023-06-30

    JavaScript中this指向的示例分析

    小编给大家分享一下JavaScript中this指向的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!this先看代码:方法中function test()
    2023-06-25

    JavaScript中的this关键词指向实例分析

    这篇文章主要讲解了“JavaScript中的this关键词指向实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JavaScript中的this关键词指向实例分析”吧!1、es5中的th
    2023-06-30

    JavaScript中this指向问题的示例分析

    这篇文章将为大家详细讲解有关JavaScript中this指向问题的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1. 箭头函数箭头函数排在第一个是因为它的 this 不会被改变,所以只要当前函数
    2023-06-14

    JQuery中this的指向实例分析

    这篇文章主要介绍“JQuery中this的指向实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“JQuery中this的指向实例分析”文章能帮助大家解决问题。JavaScript中的this不总
    2023-06-30

    C++的this指针实例分析

    这篇文章主要介绍了C++的this指针实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C++的this指针实例分析文章都会有所收获,下面我们一起来看看吧。this指针是存在与类的成员函数中,指向被调用函数
    2023-06-27

    Laravel中$this->app的示例分析

    这篇文章主要介绍了Laravel中$this->app的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。断点调试寻找对应文件,忽略次要步骤,仅描述核心动作,‘/’表示i
    2023-06-20

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录