摘要:所以相同点是,在全局范围内,全局变量终究是属于老大的。只生效一次引入了。只生效一次在箭头函数中,与封闭词法环境的保持一致。我通常把这些原始函数叫做构造函数。在里面你可以嵌套函数,也就是你可以在函数里面定义函数。
只有掌握了JavaScript中的this操作符你才算正式迈入JavaScript这门语言的门槛!我们一直都在用这个框架,那个框架,但往往忽视掉了js最基础的东西,笔者认为这些基础往往才是走下去,走深,最不可或缺的东西.那我们就一起来学习一下js中神奇的this吧--------笔者查看了大量有关this的文章,有些是按照自己思路写的,有些是直接引用其他作者成熟的思路的文章1.什么是this?
学习一个知识首先要理解他的字面含义,通过翻译我们知道,this的含义是这,这个(指较近的人或事物)的意思。那么我们结合现实,我们说的“这”在不同的环境所指的事物是不一样的。那么在JavaScript中this在不同的环境调用所表达的含义也是非常丰富的。如果你觉得JavaScript中的this和其他面向对象语言Java一样,是指存储在实例属性中的值,那你就大错特错了。JavaScript中的this有着在这门语言中不可或缺魔力。
2.运行的宿主(环境)不同this含义也是不一样宿主(环境)解释
JS的运行环境一般由宿主环境和执行期环境共同构成,宿主环境是由外壳程序(如web浏览器就是一个外壳程序)生成,执行期环境是由嵌入到外壳程序中的JS引擎(/JS解释器)生成的,在执行期环境JS可以生成内置静态对象、初始化执行环境等。
对于JavaScript,宿主环境最常见的是web浏览器,浏览器提供了一个JavaScript运行的环境,这个环境里面,需要提供一些接口,好让JavaScript引擎能够和宿主环境对接。JavaScript引擎才是真正执行JavaScript代码的地方,常见的引擎有V8(目前最快JavaScript引擎、Google生产)、JavaScript core
但是环境不是唯一的,也就是JavaScript不仅仅能够在浏览器里面跑,也能在其他提供了宿主环境的程序里面跑,最常见的就是nodejs。同样作为一个宿主环境,nodejs也有自己的JavaScript引擎--V8。根据官方的定义:
Node.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications
在浏览器里,在全局范围内,this等价于window对象。用var声明一个变量和给this或者window添加属性是等价的
说明:在浏览器中,window对象同时也是全局对象
在node环境里,如果使用REPL(Read-Eval-Print Loop,简称REPL:读取-求值-输出,是一个简单的,交互式的编程环境)来执行程序,this并不是最高级的命名空间,最高级的是global.
> this === global true
但在node环境里,如果执行一个js脚本,在全局范围内,this以一个空对象开始作为最高级的命名空间,这个时候,它和global不是等价的。
index.js 文件在node环境中执行 console.log(this) //Object {} console.log(this === global); //false
在node环境里,在全局范围内,如果你用REPL执行一个脚本文件,用var声明一个变量并不会和在浏览器里面一样将这个变量添加给this。
index.js 文件在node环境中执行 var foo = "bar"; console.log(this.foo);//undefined
但是如果你不是用REPL执行脚本文件,而是直接执行代码,结果和在浏览器里面是一样的(神坑)
> var foo = "bar"; > this.foo bar > global.foo bar
在node环境里,用REPL运行脚本文件的时候,如果在声明变量的时候没有使用var或者let,这个变量会自动添加到global对象,但是不会自动添加给this对象。如果是直接执行代码,则会同时添加给global和this
index.js 文件在node环境中执行 foo = "bar"; console.log(this.foo);//undefined console.log(global.foo);//bar
上面的几种种情况可能大家已经绕晕了,总结起来就是:在浏览器里面this是老大,它等价于window对象,如果你声明一些全局变量(不管在任何地方),这些变量都会作为this的属性。在node里面,有两种执行JavaScript代码的方式,一种是直接执行写好的JavaScript文件,另外一种是直接在里面执行一行行代码。对于直接运行一行行JavaScript代码的方式,global才是老大,this和它是等价的。在这种情况下,和浏览器比较相似,也就是声明一些全局变量会自动添加给老大global,顺带也会添加给this。但是在node里面直接脚本文件就不一样了,你声明的全局变量不会自动添加到this,但是会添加到global对象。所以相同点是,在全局范围内,全局变量终究是属于老大的。4.函数(function)中的this
说明:在函数内部,this的值取决于函数被调用的方式
无论是在浏览器环境还是node环境,除了在DOM事件处理程序里或者给出了thisArg(接下来会讲到)外,如果不是用new调用,在函数里面使用this都是指代全局范围的this。
index.js 文件在node环境中执行 foo = "bar"; function testThis () { this.foo = "foo"; } console.log(global.foo);//bar testThis(); console.log(global.foo);//foo
说明:因为上面代码不在严格模式下,且this的值不是由该调用设置的,所以this的值默认指向全局对象。
除非你使用严格模式,这时候this就会变成undefined。
说明:然而,在严格模式下,this将保持他进入执行环境时的值,所以下面的this将会默认为undefined,所以,在严格模式下,如果 this 没有被执行环境(execution context)定义,那它将保持为 undefined。
如果你在调用函数的时候在前面使用了new,this就会变成一个新的值,和global的this脱离干系。
函数里面的this其实相对比较好理解,如果我们在一个函数里面使用this,需要注意的就是我们调用函数的方式,如果是正常的方式调用函数,this指代全局的this,如果我们加一个new,这个函数就变成了一个构造函数,我们就创建了一个实例,this指代这个实例,这个和其他面向对象的语言很像。另外,写JavaScript很常做的一件事就是绑定事件处理程序,也就是诸如button.addEventListener(‘click’, fn, false)之类的,如果在fn里面需要使用this,this指代事件处理程序对应的对象,也就是button。
如果想把this的值从一个环境传到另一个环境,就要用到call或者apply的方法。
说明:使用 call 和 apply 函数的时候要注意,如果传递给 this 的值不是一个对象,JavaScript 会尝试使用内部 ToObject 操作将其转换为对象。因此,如果传递的值是一个原始值比如 7 或 "foo",那么就会使用相关构造函数将它转换为对象,所以原始值 7 会被转换为对象,像 new Number(7) 这样,而字符串 "foo" 转化成 new String("foo") 这样,例如:下面代码
ECMAScript 5 引入了 Function.prototype.bind。调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,但是在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论这个函数是如何被调用的。
ECMAScript 5 引入了 Function.prototype.bind。调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,但是在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论这个函数是如何被调用的。
在箭头函数中,this与封闭词法环境的this保持一致。在全局代码中,它将被设置为全局对象:
5.原型(prototype)中的this你创建的每一个函数都是函数对象,他们会自动获取一个特殊的属性prototype,你可以给这个属性赋值。当你用new的方式调用一个函数的时候,你就能通过this访问你给prototype赋的值了。
当你使用new为你的函数创建多个实例的时候,这些实例会共享你给prototype设定的值。对于下面的例子,当你调用this.foo的时候,都会返回相同的值,除非你在某个实例里面重写了自己的this.foo
实例里面的this是一个特殊的对象。你可以把this想成一种获取prototype的值的一种方式。当你在一个实例里面直接给this添加属性的时候,会隐藏prototype中与之同名的属性。如果你想访问prototype中的这个属性值而不是你自己设定的属性值,你可以通过在实例里面删除你自己添加的属性的方式来实现。
或者你也能直接通过引用函数对象的prototype 来获得你需要的值。
此时的this是指,构造函数的原型上的方法至于为什么看下面
通过一个函数创建的实例会共享这个函数的prototype属性的值,如果你给这个函数的prototype赋值一个Array,那么所有的实例都会共享这个Array,除非你在实例里面重写了这个Array,这种情况下,函数的prototype的Array就会被隐藏掉。
实际上你可以通过把多个函数的prototype链接起来的从而形成一个原型链,因此this就会魔法般地沿着这条原型链往上查找直到找你你需要引用的值。
一些人利用原型链的特性来在JavaScript模仿经典的面向对象的继承方式。任何给用于构建原型链的函数的this的赋值的语句都会隐藏原型链上游的相同的属性。
我喜欢把被赋值给prototype的函数叫做方法。在上面的例子中,我已经使用过方法了,如logFoo。这些方法有着相同的prototype,即创建这些实力的原始函数。我通常把这些原始函数叫做构造函数。在prototype里面定义的方法里面使用this会影响到当前实例的原型链的上游的this。这意味着你直接给this赋值的时候,隐藏了原型链上游的相同的属性值。这个实例的任何方法都会使用这个最新的值而不是原型里面定义的这个相同的值。
在JavaScript里面你可以嵌套函数,也就是你可以在函数里面定义函数。嵌套函数可以通过闭包捕获父函数的变量,但是这个函数没有继承this
在doIt里面的this是global对象或者在严格模式下面是undefined。这是造成很多不熟悉JavaScript的人深陷 this陷阱的根源。在这种情况下事情变得非常糟糕,就像你把一个实例的方法当作一个值,把这个值当作函数参数传递给另外一个函数但是却不把这个实例传递给这个函数一样。在这种情况下,一个方法里面的环境变成了全局范围,或者在严格模式下面的undefined。
一些人喜欢先把this捕获到一个变量里面,通常这个变量叫做self,来避免上面这种情况的发生
但是当你需要把一个方法作为一个值传递给一个函数的时候并不管用。
你可以通过bind将实例和方法一切传递给函数来解决这个问题,bind是一个函数定义在所有函数和方法的函数对象上面
你同样可以使用apply和call来在新的上下文中调用方法或函数。
你可以用bind来代替任何一个函数或者方法的this,即便它没有赋值给实例的初始prototype。
你应该避免在构造函数里面返回任何东西,因为这可能代替本来应该返回的实例。
怪的是,如果你在构造函数里面返回了一个原始值,上面所述的情况并不会发生并且返回语句被忽略了。最好不要在你将通过new调用的构造函数里面返回任何类型的数据,即便你知道自己正在做什么。如果你想创建一个工厂模式,通过一个函数来创建一个实例,这个时候不要使用new来调用函数。当然这个建议是可选的。
你可以通过使用Object.create来避免使用new,这样同样能够创建一个实例。
在这种情况下并不会调用构造函数
因为Object.create不会调用构造函数的特性在你继承模式下你想通过原型链重写构造函数的时候非常有用。
6.对象(object )中的this在一个对象的一个函数里,你可以通过this来引用这个对象的其他属性。这个用new来新建一个实例是不一样的。
注意,没有使用new,没有使用Object.create,也没有使用函数调用创建一个对象。你也可以将对象当作一个实例将函数绑定到上面。
当你用这种方式使用this的时候,并不会越出当前的对象。只有有相同直接父元素的属性才能通过this共享变量
你可以直接通过对象引用你需要的属性
7.DOM(event)中的this在一个HTML DOM事件处理程序里面,this始终指向这个处理程序被所绑定到的HTML DOM节点
除非你自己通过bind切换了上下文
8.HTML 中的this在HTML节点的属性里面,你可以放置JavaScript代码,this指向了这个元素
到此结束~~~~~~~~~
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/105551.html
摘要:本期推荐文章类内存泄漏及如何避免,由于微信不能访问外链,点击阅读原文就可以啦。四种常见的内存泄漏划重点这是个考点意外的全局变量未定义的变量会在全局对象创建一个新变量,如下。因为老版本的是无法检测节点与代码之间的循环引用,会导致内存泄漏。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第一期,本周的主题...
摘要:首次运行代码时,会创建一个全局执行上下文并到当前的执行栈中。执行上下文的创建执行上下文分两个阶段创建创建阶段执行阶段创建阶段确定的值,也被称为。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第一期,本周的主题是调用堆栈,,今天是第一天 本计划一共28期,每期重点攻克一个面试重难点,如果你还不了解本进...
摘要:使用上一篇文章的例子来说明下自由变量进阶期深入浅出图解作用域链和闭包访问外部的今天是今天是其中既不是参数,也不是局部变量,所以是自由变量。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第二期,本周的主题是作用域闭包,今天是第7天。 本计划一共28期,每期重点攻克一个面试重难点,如果你还不了解本进阶计...
摘要:箭头函数的寻值行为与普通变量相同,在作用域中逐级寻找。题目这次通过构造函数来创建一个对象,并执行相同的个方法。 我们知道this绑定规则一共有5种情况: 1、默认绑定(严格/非严格模式) 2、隐式绑定 3、显式绑定 4、new绑定 5、箭头函数绑定 其实大部分情况下可以用一句话来概括,this总是指向调用该函数的对象。 但是对于箭头函数并不是这样,是根据外层(函数或者全局)作用域(...
摘要:本计划一共期,每期重点攻克一个面试重难点,如果你还不了解本进阶计划,点击查看前端进阶的破冰之旅本期推荐文章深入之执行上下文栈和深入之变量对象,由于微信不能访问外链,点击阅读原文就可以啦。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第一期,本周的主题是调用堆栈,今天是第二天。 本计划一共28期,每期...
摘要:之前文章详细介绍了的使用,不了解的查看进阶期。不同的引擎有不同的限制,核心限制在,有些引擎会抛出异常,有些不抛出异常但丢失多余参数。存储的对象能动态增多和减少,并且可以存储任何值。这边采用方法来实现,拼成一个函数。 之前文章详细介绍了 this 的使用,不了解的查看【进阶3-1期】。 call() 和 apply() call() 方法调用一个函数, 其具有一个指定的 this 值和分...
阅读 1368·2021-10-19 11:42
阅读 716·2021-09-22 16:04
阅读 1866·2021-09-10 11:23
阅读 1837·2021-07-29 14:48
阅读 1246·2021-07-26 23:38
阅读 2812·2019-08-30 15:54
阅读 1024·2019-08-30 11:25
阅读 1693·2019-08-29 17:23