摘要:因为一旦代码丢出来了,还会涉及到继承,构造函数,原型链,闭包等一系列问题在后面等着你,面试管为了掏你的底细会一问再问,问到你懵逼。不可以当作构造函数,也就是说,不可以使用命令,否则会抛出一个错误。
上一篇2018年3月面试心得《跨域问题》
话说我在面试的时候,有那么几天,不知道是中了什么邪,面试的几家公司开始疯狂的问我this,
各种的this,绕着弯的问我this,后来我做梦都是this、this、this……你妹的this!
那么先从面试官会怎么问你来说吧。
面试官会问什么样的问题呢请说一下作用域和上下文(this)
this会指向哪里
如何改变this的指向
出各种绕弯子的题目让你说this是啥
其实这个虽然题目我记得不多了,但是真的问起来还是很恶心的。
因为一旦代码丢出来了,还会涉及到继承,构造函数,原型链,闭包等一系列问题在后面等着你,面试管为了掏你的底细会一问再问,问到你懵逼。
那么我们先来说说第一个问题,作用域和上下文。
什么是作用域?
问得好,我也不知道,待我查一下………………
作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。换句话说,作用域决定了代码区块中变量和其他资源的可见性。
emmmm…………不好理解的话,我说一个比喻吧。
现在我为一个房子专门定一个木窗窗沿,这个窗沿只适合这个房子里面所有需要用的地方,其他房子不能用,那么这个房子就是我木窗的作用域~~~
就像我在
function 房子(){ var 木窗; }
我的木窗只属于我的房子,我不可以在房子的外面,例如小区里面直接拿到我的木窗,我必须进入房子去找这个木窗,这是不可以改变的作用域。
然后我们再来看一下
var 景色 = "大海" function 房子() { var 景色 = "大草原"; this.木窗 = function() { console.log(景色) } 木窗() } 房子()
请问这个时候输出的景色是什么呢~
机智的小朋友肯定会说是大草原~~~
为什么呢~
因为景色我从下往上找啊,找到最近的一张图我就不用继续找了,拿出来用就好啦。
怎么样,惊不惊喜,意不意外,刺不刺激~~~
js中有全局作用域,函数作用域,块级作用域(es6)。
全局作用域
很好解释,我所有地方都可以调用的到的,就像路边的广告牌,我们都可以看得到,不需要想办法进谁家里去看。
函数作用域
是产生在函数中的,一个函数内部会出现一块作用域。可以这么理解,函数是我们的房子,我们站在房子外面(全局)的时候,无法看到房子里面的东西。但是我们站在房子里面(函数内部),是可以去透过窗子凑凑整个外面的世界的。
块级作用域
哇塞这个就厉害了,在我们es6里面,新增了一些像let、const之类的语法,可以产生一个块级作用域。
大家应该都做过一个简单的题目,就是一个for循环里面丢一个setTimeout,下面放出代码。
for(var i = 0; i<5; i++) { setTimeout(function() { console.log(i) }, 1000) }
我们看一下这一个题目,首先会让你回答打印出来的是什么。
机智的小伙伴肯定会回答:5 5 5 5 5
为什么咧,因为这个var的i成了全局的,并不是只在循环里面去使用,setTimeout是一个异步函数,所以我们执行完了for才会去console.log。
这个还涉及到同步异步之类的,会多带带开一章来讲,光是基础都已经让人头昏眼花,啊西吧~
那如果我们要让他12345怎么办呢,可以把i保留下来做一个闭包传入,还有一个最方便的办法就是用let来声明他。
ps: 不过一般面试官会跟你说不要用es6,给我一个es5的办法2333333333
=3=
另外说一下let和const和var主要的区别:
如果你要在块级声明的变量存在全局变量,但是块级作用域内又let或者const了一个局部变量,导致后者绑定这个块级作用域,就会…boom…爆炸,也就是报错~
var大家都知道,进行变量提升,你在第十行声明赋值,我们代码执行的时候其实是会放到最顶部先声明,再执行到第十行的位置进行复制,还没赋值前都是undefind。但是let和const没有哦,如果你提前实用的话就等着报错吧~
const声明一个只读的常量。一旦声明,常量的值就不能改变。但是呢,你可以改变他的属性。
就像
const i = 10; i = 9; // 这样是不行的!!! const u = {a: "a",b: "b"} u.b = "c" // 这样是可以的
其实很好理解,不过我还是举个?吧……
我const一个大爷,我改变这个大爷的属性,类似给他换个衣服或者换个鞋子,可是大爷还是大爷所以不会报错。
但是我const一个妹子,完事了把妹子改成了大爷,那我不依,报错。
那什么是上下文呢?
这个也好理解,比如我窗户装在客厅,看到的是客厅里面的景色,装到了厨房,就是厨房的景色,当然这个上下文是可以更改的,我可以个这个窗沿里面贴上珠穆拉玛峰的照片,那么这个上下文就被我改了。
function 房子() { var 景色 = "大草原"; this.木窗 = function() { console.log(this.景色) } 木窗() } 房子()
你猜打印出来的是什么~~~
当然是undefind!
想什么呢~不会以为是大草原吧~
为什么咧,因为他的this变了。
下面我把这行代码改为直接打印this,打印出来的是一个window。
为什么?因为我调用房子的地方在window下,我调用了房子,房子调用了木窗,所以this成了window。
那么我们刚刚是不是在房子里面给了一张大草原的画呢,这是不是在房子外面(window层)就找不到了呢,所以是个window。
哈哈哈哈哈
如果你在这里就昏了,那么一定要往下看,不然你永远都搞不清楚this到底会指向哪里。
再说一下第三个问题,改变this的指向。
这个其实也很简单,call或者apply都可以,这个自己看api去~考官会问你两个的区别的,乖宝宝要自己看文档哦~
最后一题,绕着弯子让你说this是啥。
既然你看到了这里,那么正题开始了!!!
到底this指向哪里!!!问得好,我自己也懵逼。
没关系,我们可以一起捋一捋。
容老夫先沐浴更衣,焚香祈祷你们不要被我带歪了。
首先,跟我一起读一遍下面的两句话(当然,如果有补充可以留言):
1: 谁调用指向谁,没有谁就是window2: 除了call、apply、bind和箭头函数
首先我们在全局下打印一下this,控制台输出的是window,这个没有疑问吧~
我们再来通过函数调用一下
function 房子() { this.木窗 = function() { console.log(this) } 木窗() } 房子()
那么我们再回过头去看刚刚的房子窗子。
我们是哪里开始调用房子的?
当然是window,其实房子()就等同于window.房子()。
所以呢,调用房子的是window,那么房子最底下会调用木窗,所以真正的调用者被抓出来了。
window => 房子 => 木窗
函数的调用者就是this,请抓住始作俑者,这道题就是window这个小婊渣,就是他~
那么我们再看下一个。
通过对象的属性来调用:
var obj = { say: function() { console.log(this); } }; obj.say();
打印的结果是: {say: ƒ}
谁调用就指向谁,这个obj.say()的执行方法调用者是前面的obj,所以当前的这个this指向了obj。
再看一个
var obj = { say: function() { var hehe = function() { console.log(this) } hehe() } }; obj.say();
这个this指向了window。
为什么?因为这个hehe并不是obj上面的属性,没有找到调用者是谁,所以默认指向window。
下面我们来看一下其余几个指向谁。
call和apply大家都知道,可以改变this的指向。
var obj = {a: "a"}; function b() { console.log(this) } b.apply(obj)
打印结果:{a: "a"}
call差不多,两者差别只是参数传的不一样,一个可以传数组。
具体情况,宝宝们文档走一波~
然后再来看一下bind
哇塞这个也厉害了
直接上代码
var axiba = {hehe: "hehe"} var obj = { a: "a", b:function() { console.log(this) }.bind(axiba) }; b() // 或者 var axiba = {hehe: "hehe"} var obj = { a: "a", b:function() { console.log(this) } }; var u = obj.b.bind(axiba) u()
执行的是神马~
当然是指向axiba~
最后一个,就是es6的箭头函数
我要多带带为箭头函数加个粗
官网逛一逛,发现了几句话。
箭头函数有几个使用注意点。(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。
要理解其实也很容易,首先……
上代码……
function foo() { setTimeout(() => { console.log("id:", this.id); }, 100); } var id = 21; foo.call({ id: 42 });
代码中,setTimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到 100 毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是42。
所以以后有人问你es6的箭头函数的this和es5函数中的this的区别,那么就是es6不可改变,始终指向定义的对象,es5是根据调用环境的,如果没有调用者,默认window。
因为考官会出得题目千千万,题海战术几乎无用,我之前也是作用域和上下文傻傻分不清,虽然现在也懵懂阶段,不过学无止尽嘛。
有什么表达不当的地方,悄悄告诉我哦~
么么哒
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/93816.html
摘要:在上家公司裸辞之后,经过一段时间休整,月中下旬面试了一些公司,由于本人框架使用的是,所以面试题涉及到框架的都是,现将面试题整理一下列举常用的特性。事件冒泡以及事件捕获。其他前端分页和后端分页优缺点。包含多个子节点及孙节点,遍历。 在上家公司裸辞之后,经过一段时间休整,5月中下旬面试了一些公司,由于本人框架使用的是vue,所以面试题涉及到框架的都是vue,现将面试题整理一下: es6 ...
摘要:在上家公司裸辞之后,经过一段时间休整,月中下旬面试了一些公司,由于本人框架使用的是,所以面试题涉及到框架的都是,现将面试题整理一下列举常用的特性。事件冒泡以及事件捕获。其他前端分页和后端分页优缺点。包含多个子节点及孙节点,遍历。 在上家公司裸辞之后,经过一段时间休整,5月中下旬面试了一些公司,由于本人框架使用的是vue,所以面试题涉及到框架的都是vue,现将面试题整理一下: es6 ...
Create by jsliang on 2019-2-11 15:30:34 Recently revised in 2019-3-17 21:30:36 Hello 小伙伴们,如果觉得本文还不错,记得给个 star , 小伙伴们的 star 是我持续更新的动力!GitHub 地址 并不是只有特定的季节才能跑路,只因为人跑得多了,这条路就定下来了。 金三银四跳槽季,jsliang 于 2019...
摘要:前言腾讯一面,相比阿里一面来说,腾讯一面先给打电话预定时间,这也给了我们这些面试者去准备的时间。其实闭包也就是指有权访问另一个函数作用域的函数而已。常用的创建闭包的方法就是在函数内部创建另一个函数。 前言 腾讯一面,相比阿里一面来说,腾讯一面先给打电话预定时间,这也给了我们这些面试者去准备的时间。但是也正是因为这种确定性,也有在等待电话的时候的心情的忐忑。 背景 我是一名大三学生,大一...
摘要:自我沉淀工作有周报月总结季度年终等各种总结,那么自我学习呢也一样,今天写下的点滴,就是对明天的自己最好的馈赠礼物。 showImg(https://segmentfault.com/img/bVbm9ZZ?w=1008&h=298); 前言 岁月不居,时节如流,转眼间都到2019年1月中旬了,时间过的好快,说好的周末睡到自然醒,但还是跟以往一样,到上班时间就醒了,这算不算心里只有工作呢...
阅读 2170·2021-09-02 15:11
阅读 1492·2019-08-30 15:43
阅读 2053·2019-08-29 13:48
阅读 2771·2019-08-26 13:55
阅读 2090·2019-08-23 15:09
阅读 2875·2019-08-23 14:40
阅读 3404·2019-08-23 14:23
阅读 2587·2019-08-23 14:20