位置: IT常识 - 正文

记录--一道字节面试题引出的this指向问题(一字节的范围)

编辑:rootadmin
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 var length = 10; function fn () { return this.length + 1; } var obj = { length: 5, test1: function () { return fn(); } ... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

推荐整理分享记录--一道字节面试题引出的this指向问题(一字节的范围),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:一个字节,一个字节简记为,一个字节多少秒,一条数据多少字节,一个字节多少秒,一字节最多能编出,一个字节简记为,一个字节,内容如对您有帮助,希望把文章链接给更多的朋友!

var length = 10;function fn () {return this.length + 1;}var obj = {length: 5,test1: function () {return fn();}}obj.test2 = fn;console.log(obj.test1()); // 11console.log(fn() === obj.test2()); // false

看上面这段代码,这就是字节面试官准备的一道面试题,在面试过程中答得不是很好也没有完全做对。主要还是由于前两道题答得不是很好影响到后面答这道题时大脑是懵逼状态,再加上紧张让自己一时不知道这道题的考点是什么,进而影响到后续的面试状态。

其实这题不难,就是考JS中的基础:this的指向问题,也就是这篇文章要聊的主题。

this的原理

this指向的值总是取决于它的执行环境。执行环境是代码执行时当前的环境或者作用域,在运行时,JavaScript会维护一个执行环境栈;最顶部的那个是调用时所使用的执行环境,当执行环境变化时,this的值也会改变。

其实理解this的指向是非常重要的,至少它能让你在实际开发中少走弯路,少写bug,从而更好地掌握JavaScript这门语言。接下来将从一些栗子?来介绍this在各个场景下的指向问题,便于更好地去理解this这个在面试中经常遇见的基础问题。

一、全局环境的this

在JavaScript中,如果this是在全局环境定义的,默认就是指向全局对象。而对于浏览器来说,全局对象就是window对象,所以this就是指向就是window对象。那如果是Node.js呢?由于Node.js没有window对象,所以this不能指向window对象;但是Node.js有global对象呀,也就是说在Node.js的全局作用域中this的指向将会是global对象。

console.log(this === window); // true

二、函数上下文调用2.1 函数直接调用

普通函数内部的this指向有两种情况:严格模式和非严格模式。

非严格模式下,this默认指向全局对象,如下:

function fn1() {console.log(this);}fn1(); // window

严格模式下,this为undefined。如下:

function fn2() {"use strict"; // 严格模式console.log(this);}fn1(); // undefined2.2 对象中的this

在对象方法中,this的指向就是调用该方法的所在对象。此时的this可以访问到该方法所在对象下的任意属性,如下:

const name = '张三';const obj = {name: '李四',fn: function() {console.log(this);console.log(this.name);}}obj.fn();

代码中定义了一个对象,对象中有个fn()方法,调用这个方法打印结果如下:

可以看到,打印出来的this的值为obj这个对象,也就是fn()所在的对象,进而此时的this就能访问到该对象的所有属性。

如果fn()方法中返回的是一个匿名函数,在匿名函数中访问this.name,结果是什么呢?

const name = '张三';const obj = {name: '李四',fn: function() {return function() {console.log(this);console.log(this.name);}}}const fn = obj.fn();fn();

匿名函数的执行环境是全局作用域,那这里this打印出来的值就是window对象,而this.name则是window对象下的name属性,即this.name的值为'张三'。结果如下图:

2.3 原型链中this

原型链中的this指向也是指向调用它的对象,如下:

const obj = {fn: function() {return this.a + this.b;}}const obj1 = Object.create(obj);obj1.a = 1;obj1.b = 2;obj1.fn(); // 3

可以看到,obj1中没有属性fn,当执行obj1.fn()时,会去查找obj1的原型链,找到fn()方法后就执行代码,这与函数内部this指向对象obj1没有任何关系。但是这里的this指向就是obj1,所以只需要记住谁调用就指向谁。

2.4 构造函数中的this

构造函数中,如果显式返回一个值并且返回的是个对象,那么this就指向这个返回的对象;如果返回的不是一个对象,那么this指向的就是实例对象。如下:

function fn() {this.name = '张三';const obj = {};return obj;}const fn1 = new fn();console.log(fn1); // {}console.log(fn1.name); // undefined

上述代码将会打印结果为undefined,此时fn1是返回的对象obj,如下:

function fn() {this.name = '张三';return 1;}const fn1 = new fn();console.log(fn1);console.log(fn1.name);

上述代码则会打印结果为'张三',此时的fn1是返回的fn实例对象的this。

2.5 call/apply/bind改变this

call、apply、bind作用是改变函数执行时的上下文,也就是改变函数运行时的this指向。

2.5.1 call

call接受两个参数,第一个参数是this的指向,第二个参数是函数接收的参数,以参数列表形式传入。call改变this指向后原函数会立即执行,且只是临时改变this指向一次。

function fn(...args) {console.log(this);console.log(args);}let obj = {name:"张三"}fn.call(obj, 1, 2);fn(1, 2);

可以看到,call让this的指向变成了obj,而直接调用fn()方法this的指向是window对象。

如果把第一个参数改为null、undefined,那结果是怎样的呢?答案:this的指向是window对象。如下:

fn.call(null, 1, 2);fn.call(undefined, 1, 2);

2.5.2 apply

apply第一个参数也是this的指向,第二个参数是以数组形式传入。同样apply改变this指向后原函数会立即执行,且只是临时改变this指向一次。

function fn(...args) {console.log(this);console.log(args);}let obj = {name:"张三"}fn.apply(obj, [1, 2]);fn([1, 2]);记录--一道字节面试题引出的this指向问题(一字节的范围)

可以看到,apply让this的指向变成了obj,而直接调用fn()方法this的指向是window对象。

同样,如果第一个参数为null、undefined,this默认指向window。

apply和call唯一的不同就是第二个参数的传入形式:apply需要数组形式,而call则是列表形式。

2.5.3 bind

bind第一参数也是this的指向,第二个参数需要参数列表形式(但是这个参数列表可以分多次传入)。bind改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。

function fn(...args) {console.log(this);console.log(args);}let obj = {name:"张三"}const fn1 = fn.bind(obj);fn1(1, 2);fn(1, 2);

2.5.4 小结

从上面可以看到,apply、call、bind三者的区别在于:

都可以改变函数的this对象指向;第一个参数都是this要指向的对象,如果没有这个参数或参数为undefined或null,则默认指向window对象;都可以传参,apply是数组,call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入;bind是返回绑定this之后的函数,apply、call则是立即执行。三、箭头函数中的this

箭头函数本身没有this的,而是根据外层上下文作用域来决定的。箭头函数的this指向的是它在定义时所在的对象,而非执行时所在的对象,故箭头函数的this是固定不变的。还有就是无论箭头函数嵌套多少层,也只有一个this的存在,这个this就是外层代码块中的this。如下:

const name = '张三';const obj = {name: '李四',fn: () => {console.log(this); // windowconsole.log(this.name); // 打印李四?错的,是张三}}obj.fn();

看上述代码,都会以为fn是绑定在obj对象上的,但其实它是绑定在window对象上的。为什么呢?

fn所在的作用域其实是最外层的js环境,因为没有其他函数的包裹,然后最外成的js环境指向的是window对象,故这里的this指向的就是window对象。

那要作何修改使它能永远指向obj对象呢?如下:

const name = '张三';const obj = {name: '李四',fn: functon() {let test = () => console.log(this.name); // 李四return test; // 返回箭头函数}}const fn1 = obj.fn();fn1();

上述代码就能让其永远的绑定在obj对象上了,如果想用call来改变也是改变不了的,如下:

const obj1 = {name: '王二'}fn1.call(obj1); // 李四fn1.call(); // 李四fn1.call(null, obj1); // 李四

最后是使用箭头函数需要注意以下几点:

不能使用new构造函数不绑定arguments,用rest参数...解决没有原型属性箭头函数不能当做Generator函数,不能使用yield关键字箭头函数的this永远指向其上下文的this,任何方法都改变不了其指向,如call,bind,apply四、globalThis

来看看MDN是怎么描述globalThis的:

globalThis提供了一个标准的方式来获取不同环境下的全局this对象(也就是全局对象自身)。不像window或者self这些属性,它确保可以在有无窗口的各种环境下正常工作。所以,你可以安心的使用globalThis,不必担心它的运行环境。为便于记忆,你只需要记住,全局作用域中的this就是globalThis。

尽管如此,还是要看看globalThis在现有各种浏览器上的兼容情况,如下图:

可以看到兼容情况还是可以,除了IE浏览器。。。

下面就演示实现一个手写call()方法,其中就使用到globalThis来获取全局this对象,如下:

function myCall(context, ...args) {if (context === null) context = globalThis;if (context !== 'object') context = new Object(context);const key = symbol();const res = context[key](...args);delete context[key];return res;}五、this的优先级

this的绑定规则有4种:默认绑定,隐式绑定,显式绑定和new绑定。那下面分别来说说:

5.1 默认绑定var name = '张三';function fn1() {// 'use strict';var name = '李四';console.log(this.name); // 张三}fn1(); // window

默认绑定一般是函数直接调用。函数在全局环境调用执行时,this就代表全局对象Global,严格模式下就绑定的是undefined

5.2 隐式绑定

隐式绑定就是谁调用就是指向谁,如下:

const name = '张三';const obj = {name: '李四',fn: function() {console.log(this.name); // 李四}}obj.fn(); // obj调用就是指向obj

接着看下面两个栗子?,如下:

// 链式调用const name = '张三';const obj = {name: '李四',fn: function() {console.log(this.name);}}const obj1 = {name: '王二',foo: obj}const obj2 = {name: '赵五',foo: obj1}obj2.foo.foo.fn(); // 打印:李四(指向obj,本质还是obj调用的)

再来看一个,就可以更清楚了,如下:

const name = '张三';const obj = {name: '李四',fn: function() {console.log(this.name);}}const obj1 = {name: '王二',foo: obj.fn}obj1.foo(); // 打印:王二(指向obj1)5.3 显式绑定

call、apply、bind对this绑定的情况就称为显式绑定。

const name = '张三';const obj = {name: '李四',fn: function() {console.log(this); // objconsole.log(this.name); // 李四}}const obj1 = {name: '王二'}obj.fn.call(obj1); // obj1 王二obj.fn.apply(obj1); // obj1 王二const fn1 = obj.fn.bind(obj1);fn1(); // obj1 王二5.4 new绑定

执行new操作的时候,将创建一个新的对象,并且将构造函数的this指向所创建的新对象。

function foo() {this.name = '张三';}let obj = new foo();console.log(obj.name); // 张三

其实new操作符会做以下工作:

创建一个新的对象obj将对象与构建函数通过原型链连接起来将构建函数中的this绑定到新建的对象obj上根据构建函数返回类型作判断,如果是原始值则被忽略,如果是返回对象,需要正常处理

手动实现new代码,如下:

function myNew(fn, ...args) {// 1.创建一个新对象const obj = {};// 2.新对象原型指向构造函数原型对象obj.__proto__ = fn.prototype;// 3.将构建函数的this指向新对象let result = fn.apply(obj, args);// 4.根据返回值判断return result instanceof Object ? result : obj;}

测试一下,如下:

function Animal(name, age) {this.name = name;this.age = age;}Animal.prototype.bark = function() {console.log(this.name);}let dog = myNew(Animal, 'maybe', 4);console.log(dog);dog.bark();

5.5 小结

this的优先级:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

至此,this的相关原理知识就介绍完了,现在转过头再去看字节的那道面试题是不是就可以轻松作答了。this是javascript中最基础的知识点,往往就是这些最基础的知识让我们在很多面试过程中一次次倒下,所以基础知识真的很有必要再去深入了解,这样不管是在以后的工作还是面试中都能够让我们自信十足,不再折戟沉沙。

本文转载于:https://juejin.cn/post/7089690603755143199如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

本文链接地址:https://www.jiuchutong.com/zhishi/311748.html 转载请保留说明!

上一篇:Python中的并行和并发是什么(python并发和并行)

下一篇:Java获取/resources目录下的资源文件方法(java获取resources下文件路径)

  • 微信不绑定银行卡可以收转账吗(微信不绑定银行卡可以使用零钱吗)

    微信不绑定银行卡可以收转账吗(微信不绑定银行卡可以使用零钱吗)

  • wps怎么自动排序123(wps怎么自动排序大小)

    wps怎么自动排序123(wps怎么自动排序大小)

  • 云备份怎么关闭(华为云备份怎么关闭)

    云备份怎么关闭(华为云备份怎么关闭)

  • 苹果耳机3代什么时候出(苹果耳机3代什么时候出的新款)

    苹果耳机3代什么时候出(苹果耳机3代什么时候出的新款)

  • wifi突然断网怎么回事(wifi突然断网怎么找到断网时间)

    wifi突然断网怎么回事(wifi突然断网怎么找到断网时间)

  • 1200m和1900m路由器区别(1200m路由器和1900m路由器的区别)

    1200m和1900m路由器区别(1200m路由器和1900m路由器的区别)

  • 根据手机号可以查到个人信息吗(根据手机号可以找到人的姓名吗)

    根据手机号可以查到个人信息吗(根据手机号可以找到人的姓名吗)

  • qq电话挂了后没记录(qq电话挂了后没记录怎么找)

    qq电话挂了后没记录(qq电话挂了后没记录怎么找)

  • qq等级的图标分别代表(qq等级图例)

    qq等级的图标分别代表(qq等级图例)

  • 苹果正在准备更新要等多久(苹果正在准备更新可以断网吗)

    苹果正在准备更新要等多久(苹果正在准备更新可以断网吗)

  • 电脑开机停在正在启动windows(电脑开机停在正在关机)

    电脑开机停在正在启动windows(电脑开机停在正在关机)

  • ftp是什么协议(ftp协议指什么)

    ftp是什么协议(ftp协议指什么)

  • 华为手机应该怎么隐藏应用(华为手机应该怎样录屏)

    华为手机应该怎么隐藏应用(华为手机应该怎样录屏)

  • 小米9下巴宽度(小米下巴宽度排名)

    小米9下巴宽度(小米下巴宽度排名)

  • 黑猫投诉怎样才能把帖子删除(黑猫投诉怎样才能完成)

    黑猫投诉怎样才能把帖子删除(黑猫投诉怎样才能完成)

  • 联想小新怎么强制关机(联想小新怎么强制关机重启电脑)

    联想小新怎么强制关机(联想小新怎么强制关机重启电脑)

  • 怎么查店铺电话(怎么查店铺电话拼多多)

    怎么查店铺电话(怎么查店铺电话拼多多)

  • 苹果11怎么用两个微信(苹果11怎么用两个桌面设置)

    苹果11怎么用两个微信(苹果11怎么用两个桌面设置)

  • ps白色的参数是多少(ps白色的参数是灰色的)

    ps白色的参数是多少(ps白色的参数是灰色的)

  • ps加边框怎么加(ps边框怎么加颜色)

    ps加边框怎么加(ps边框怎么加颜色)

  • 拼多多的差评怎么改(拼多多差评怎样才能让别人看不到差评?)

    拼多多的差评怎么改(拼多多差评怎样才能让别人看不到差评?)

  • 小米cc9e防水级别(小米cc9pro防水等级)

    小米cc9e防水级别(小米cc9pro防水等级)

  • 台式电脑哪个地方插u盘(台式电脑哪个地方辐射最大)

    台式电脑哪个地方插u盘(台式电脑哪个地方辐射最大)

  • iphone11香港发售时间(苹果11香港报价)

    iphone11香港发售时间(苹果11香港报价)

  • iphone8p返回上一级(ipone8返回上一层)

    iphone8p返回上一级(ipone8返回上一层)

  • 小米6如何设置开关机(小米6如何设置隐藏应用)

    小米6如何设置开关机(小米6如何设置隐藏应用)

  • 微信小程序 - 完美解决 web-view 公众号文章或第三方网站分享转发后,打开提示 “无法打开该页面,不支持打开” 或 “页面不存在”(IOS 苹果系统打开是空白页,安卓系统会有提示)超详细排查(微信小程序完美修真攻略)

    微信小程序 - 完美解决 web-view 公众号文章或第三方网站分享转发后,打开提示 “无法打开该页面,不支持打开” 或 “页面不存在”(IOS 苹果系统打开是空白页,安卓系统会有提示)超详细排查(微信小程序完美修真攻略)

  • 增值税什么时候计入成本
  • 个人出租房屋的个人所得税税率
  • 一般纳税人和小规模纳税人哪个合适
  • 企业计提增值税 附加税
  • 我国的税种有哪些种类
  • 发票超过三个月不能开
  • 预付和预收可以合并吗
  • 未完工工程如何做绩效评价
  • 水费抵扣进项税税率是多少
  • 汇算清缴需要多久
  • 建筑业购买车辆怎么入账
  • 如何处理没有录音的人
  • 酒店住宿可以开免税发票吗?
  • 专票已经抵扣购买方如何处理
  • 劳务费增值税专用发票虚开一万元怎么补救
  • 店面转让出去收到的钱如何做账?
  • 公司租土地建厂房应该怎么做账呢?
  • 会计当年是指哪一年
  • 购房专票可以抵扣进项税吗为什么
  • 英雄联盟登录失败7502013
  • 鸿蒙系统怎么升级3.0
  • 存货跌价准备是谁的备抵账户
  • 暂估成本跨月账务处理
  • 为什么预付账款是流动资产
  • 个人综合所得适用什么税率?
  • 生育津贴申请表打印
  • 现金解款单的使用范围
  • 超率累进税率和超倍累进税率
  • 代理出口退税款退到哪
  • 银行的抵债资产有几种处置办法
  • PHP:mcrypt_enc_get_iv_size()的用法_Mcrypt函数
  • PHP:JDToJulian()的用法_日历函数
  • php数据导出到excel
  • vue 自动部署
  • php调试函数
  • 汇算清缴补充公告
  • 机器学习中的数据预处理方法与步骤
  • struts2漏洞检测工具下载
  • api接口使用方法
  • php类的定义
  • ps渐变透明效果怎么弄
  • 一件代发退货如何处理
  • 公司银行账号注销需要什么资料
  • 代扣代缴个税手续费返还文件
  • mysql使用中遇到的困难和问题
  • 发票作废是什么样的
  • 企业印花税的计算公式是什么
  • 受雇于两家公司补税怎么算
  • 原始凭证的基本内容有会计分录吗
  • 财政应返还额度年末有余额吗?
  • 工商年报纳税总额从哪看
  • 单位收到已交个人社保
  • 新政府会计制度与旧制度区别
  • 其他货币资金怎么结转
  • 发票金额大于实际报销怎么做账
  • 企业接受捐赠如何确认收入
  • 员工的生育津贴怎么发放
  • 预付一年房租费的会计分录
  • 个人咨询服务费个税怎么算
  • 注册工贸公司要多少资金
  • 专票和普票的区别税点差多少
  • 其他应收款的认定
  • 商贸公司库存表怎么做
  • mysql事务命令
  • win8怎么运行
  • VMware虚拟机中安装MATE桌面环境
  • compaq-rba.exe是哪个方面的进程 作用是什么 compaq-rba进程查询
  • 电脑联想windows7
  • 怎么看win8.1的版本
  • win10大更新2021要多久
  • 全面理解全面把握全面落实
  • linux编译链接命令
  • dos命令到一个文件夹
  • Node.js中的包管理工具是什么
  • 用nodejs搭建服务器
  • jquery.ztree中文api
  • libgdx robovm admob IOS SDK的绑定
  • javascript 类
  • 纳税申报过了申报期未申报怎么办
  • 河北国家税务局官网站
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设