JavaScript中的this实例分析(javascript,this,开发技术)

时间:2024-05-04 06:03:56 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

    JavaScript%E4%B8%AD%E7%9A%84this%E5%AE%9E%E4%BE%8B%E5%88%86%E6%9E%90

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

例题1

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

例题2

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

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

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

例题3

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

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

例题4

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

例题5

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

例题6

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

例题7

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

例题8

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

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

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

例题9

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

例题10

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

例题11

三个最终都打印了coboy1。

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

如果上面的题改一下:

例题12

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

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

例题13

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

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

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

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

例题14

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

例题15

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

例题16

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

call方法

例题17

apply方法

例题18

call 方法和 apply 方法的区别

例题19

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

bind 方法

例题20

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

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

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

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

例题21

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

例题22

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

例题23

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

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

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

例题24

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

例题25

例题26

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

例题27

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

例题28

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

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

创建一个新的空对象

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

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

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

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

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

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

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

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

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

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

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

完整的复杂版:

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

本文:JavaScript中的this实例分析的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:如何用C语言数组实现三子棋游戏下一篇:

5 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18